]>
Commit | Line | Data |
---|---|---|
2fc1e207 A |
1 | /* |
2 | * Copyright (c) 2004 Apple Computer, Inc. All rights reserved. | |
3 | * | |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5 | * | |
6 | * "Portions Copyright (c) 2004 Apple Computer, Inc. All Rights | |
7 | * Reserved. This file contains Original Code and/or Modifications of | |
8 | * Original Code as defined in and that are subject to the Apple Public | |
9 | * Source License Version 1.0 (the 'License'). You may not use this file | |
10 | * except in compliance with the License. Please obtain a copy of the | |
11 | * License at http://www.apple.com/publicsource and read it before using | |
12 | * this file. | |
13 | * | |
14 | * The Original Code and all software distributed under the License are | |
15 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
16 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
17 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
18 | * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the | |
19 | * License for the specific language governing rights and limitations | |
20 | * under the License." | |
21 | * | |
22 | * @APPLE_LICENSE_HEADER_END@ | |
23 | */ | |
24 | ||
25 | #include <mach/port.h> | |
26 | #include <mach/mach_error.h> | |
27 | #include <mach/mach_traps.h> | |
28 | #include <mach/mach.h> | |
29 | #include <mach/host_special_ports.h> | |
30 | ||
31 | #include <sys/types.h> | |
32 | #include <sys/mman.h> | |
33 | #include <sys/queue.h> | |
34 | #include <sys/stat.h> | |
34d340d7 | 35 | #include <sys/time.h> |
2fc1e207 A |
36 | #include <sys/wait.h> |
37 | ||
38 | #include <fcntl.h> | |
39 | #include <time.h> | |
40 | #include <stdio.h> | |
41 | #include <stdlib.h> | |
42 | #include <unistd.h> | |
43 | #include <errno.h> | |
44 | #include <syslog.h> | |
45 | #include <signal.h> | |
46 | #include <string.h> | |
47 | #include <notify.h> | |
48 | ||
49 | #include <bsm/audit.h> | |
50 | #include <bsm/audit_uevents.h> | |
51 | #include <bsm/libbsm.h> | |
52 | ||
53 | #include <auditd.h> | |
54 | #include "auditd_control_server.h" | |
55 | #include "audit_triggers_server.h" | |
56 | #define NA_EVENT_STR_SIZE 25 | |
57 | ||
58 | static int ret, minval; | |
59 | static char *lastfile = NULL; | |
60 | ||
61 | static int allhardcount = 0; | |
62 | ||
63 | mach_port_t bp = MACH_PORT_NULL; | |
64 | mach_port_t control_port = MACH_PORT_NULL; | |
65 | mach_port_t signal_port = MACH_PORT_NULL; | |
66 | mach_port_t port_set = MACH_PORT_NULL; | |
67 | ||
68 | #ifndef __BSM_INTERNAL_NOTIFY_KEY | |
69 | #define __BSM_INTERNAL_NOTIFY_KEY "com.apple.audit.change" | |
70 | #endif /* __BSM_INTERNAL_NOTIFY_KEY */ | |
71 | ||
72 | TAILQ_HEAD(, dir_ent) dir_q; | |
73 | ||
74 | ||
75 | /* Error starting auditd */ | |
76 | void fail_exit() | |
77 | { | |
78 | audit_warn_nostart(); | |
79 | exit(1); | |
80 | } | |
81 | ||
82 | /* | |
83 | * Free our local list of directory names | |
84 | */ | |
85 | void free_dir_q() | |
86 | { | |
87 | struct dir_ent *dirent; | |
88 | ||
89 | while ((dirent = TAILQ_FIRST(&dir_q))) { | |
90 | TAILQ_REMOVE(&dir_q, dirent, dirs); | |
91 | free(dirent->dirname); | |
92 | free(dirent); | |
93 | } | |
94 | } | |
95 | ||
96 | /* | |
97 | * generate the timestamp string | |
98 | */ | |
99 | int getTSstr(char *buf, int len) | |
100 | { | |
101 | struct timeval ts; | |
102 | struct timezone tzp; | |
103 | time_t tt; | |
104 | ||
105 | if(gettimeofday(&ts, &tzp) != 0) { | |
106 | return -1; | |
107 | } | |
108 | tt = (time_t)ts.tv_sec; | |
109 | if(!strftime(buf, len, "%Y%m%d%H%M%S", gmtime(&tt))) { | |
110 | return -1; | |
111 | } | |
112 | ||
113 | return 0; | |
114 | } | |
115 | ||
116 | /* | |
117 | * Concat the directory name to the given file name | |
118 | * XXX We should affix the hostname also | |
119 | */ | |
120 | char *affixdir(char *name, struct dir_ent *dirent) | |
121 | { | |
122 | char *fn; | |
123 | char *curdir; | |
124 | const char *sep = "/"; | |
125 | ||
126 | curdir = dirent->dirname; | |
127 | syslog(LOG_INFO, "dir = %s\n", dirent->dirname); | |
128 | ||
129 | fn = (char *) malloc (strlen(curdir) + strlen(sep) | |
130 | + (2 * POSTFIX_LEN) + 1); | |
131 | if(fn == NULL) { | |
132 | return NULL; | |
133 | } | |
134 | strcpy(fn, curdir); | |
135 | strcat(fn, sep); | |
136 | strcat(fn, name); | |
137 | ||
138 | return fn; | |
139 | } | |
140 | ||
141 | /* Close the previous audit trail file */ | |
142 | int close_lastfile(char *TS) | |
143 | { | |
144 | char *ptr; | |
145 | char *oldname; | |
146 | ||
147 | if(lastfile != NULL) { | |
148 | oldname = (char *)malloc(strlen(lastfile) + 1); | |
149 | if(oldname == NULL) { | |
150 | return -1; | |
151 | } | |
152 | strcpy(oldname, lastfile); | |
153 | ||
154 | /* rename the last file -- append timestamp */ | |
155 | ||
156 | if((ptr = strstr(lastfile, NOT_TERMINATED)) != NULL) { | |
157 | *ptr = '.'; | |
158 | strcpy(ptr+1, TS); | |
159 | if(rename(oldname, lastfile) != 0) { | |
160 | syslog(LOG_ERR, "Could not rename %s to %s \n", | |
161 | oldname, lastfile); | |
162 | } | |
163 | else { | |
164 | syslog(LOG_INFO, "renamed %s to %s \n", | |
165 | oldname, lastfile); | |
166 | } | |
167 | } | |
168 | ||
169 | free(lastfile); | |
170 | free(oldname); | |
171 | ||
172 | lastfile = NULL; | |
173 | } | |
174 | ||
175 | return 0; | |
176 | } | |
177 | ||
178 | /* | |
179 | * Create the new file name, swap with existing audit file | |
180 | */ | |
181 | int swap_audit_file() | |
182 | { | |
183 | char timestr[2 * POSTFIX_LEN]; | |
184 | char *fn; | |
185 | char TS[POSTFIX_LEN]; | |
186 | struct dir_ent *dirent; | |
187 | ||
188 | if(getTSstr(TS, POSTFIX_LEN) != 0) { | |
189 | return -1; | |
190 | } | |
191 | ||
192 | strcpy(timestr, TS); | |
193 | strcat(timestr, NOT_TERMINATED); | |
194 | ||
195 | /* try until we succeed */ | |
196 | while((dirent = TAILQ_FIRST(&dir_q))) { | |
197 | if((fn = affixdir(timestr, dirent)) == NULL) { | |
198 | return -1; | |
199 | } | |
200 | ||
201 | syslog(LOG_INFO, "New audit file is %s\n", fn); | |
202 | if (open(fn, O_RDONLY | O_CREAT, S_IRUSR | S_IRGRP) < 0) { | |
203 | perror("File open"); | |
204 | } | |
205 | else if (auditctl(fn) != 0) { | |
206 | syslog(LOG_ERR, "auditctl failed! : %s\n", | |
207 | strerror(errno)); | |
208 | } | |
209 | else { | |
210 | /* Success */ | |
211 | close_lastfile(TS); | |
212 | lastfile = fn; | |
213 | return 0; | |
214 | } | |
215 | ||
216 | /* Tell the administrator about lack of permissions for dirent */ | |
217 | audit_warn_getacdir(dirent->dirname); | |
218 | ||
219 | /* Try again with a different directory */ | |
220 | TAILQ_REMOVE(&dir_q, dirent, dirs); | |
221 | free(dirent->dirname); | |
222 | free(dirent); | |
223 | } | |
224 | return -1; | |
225 | } | |
226 | ||
227 | /* | |
228 | * Read the audit_control file contents | |
229 | */ | |
230 | int read_control_file() | |
231 | { | |
232 | char cur_dir[MAX_DIR_SIZE]; | |
233 | struct dir_ent *dirent; | |
234 | au_qctrl_t qctrl; | |
235 | ||
236 | /* Clear old values */ | |
237 | free_dir_q(); | |
238 | endac(); // force a re-read of the file the next time | |
239 | ||
240 | /* Post that the audit config changed */ | |
241 | notify_post(__BSM_INTERNAL_NOTIFY_KEY); | |
242 | ||
243 | /* Read the list of directories into a local linked list */ | |
244 | /* XXX We should use the reentrant interfaces once they are available */ | |
245 | while(getacdir(cur_dir, MAX_DIR_SIZE) >= 0) { | |
246 | dirent = (struct dir_ent *) malloc (sizeof(struct dir_ent)); | |
247 | if(dirent == NULL) { | |
248 | return -1; | |
249 | } | |
250 | ||
251 | dirent->softlim = 0; | |
252 | dirent->dirname = (char *) malloc (MAX_DIR_SIZE); | |
253 | if(dirent->dirname == NULL) { | |
254 | free(dirent); | |
255 | return -1; | |
256 | } | |
257 | ||
258 | strcpy(dirent->dirname, cur_dir); | |
259 | TAILQ_INSERT_TAIL(&dir_q, dirent, dirs); | |
260 | } | |
261 | ||
262 | allhardcount = 0; | |
263 | ||
264 | if(swap_audit_file() == -1) { | |
265 | syslog(LOG_ERR, "Could not swap audit file\n"); | |
266 | /* | |
267 | * XXX Faulty directory listing? - user should be given | |
268 | * XXX an opportunity to change the audit_control file | |
269 | * XXX switch to a reduced mode of auditing? | |
270 | */ | |
271 | return -1; | |
272 | } | |
273 | ||
274 | /* | |
275 | * XXX There are synchronization problems here | |
276 | * XXX what should we do if a trigger for the earlier limit | |
277 | * XXX is generated here? | |
278 | */ | |
279 | if(0 == (ret = getacmin(&minval))) { | |
280 | ||
281 | syslog(LOG_INFO, "min free = %d\n", minval); | |
282 | ||
283 | if (auditon(A_GETQCTRL, &qctrl, sizeof(qctrl)) != 0) { | |
284 | syslog(LOG_ERR, | |
285 | "could not get audit queue settings\n"); | |
286 | return -1; | |
287 | } | |
288 | qctrl.aq_minfree = minval; | |
289 | if (auditon(A_SETQCTRL, &qctrl, sizeof(qctrl)) != 0) { | |
290 | syslog(LOG_ERR, | |
291 | "could not set audit queue settings\n"); | |
292 | return -1; | |
293 | } | |
294 | } | |
295 | ||
296 | return 0; | |
297 | } | |
298 | ||
299 | /* | |
300 | * Close all log files, control files, and tell the audit system. | |
301 | */ | |
302 | int close_all() | |
303 | { | |
304 | int err_ret = 0; | |
305 | char TS[POSTFIX_LEN]; | |
306 | int aufd; | |
307 | token_t *tok; | |
308 | ||
309 | /* Generate an audit record */ | |
310 | if((aufd = au_open()) == -1) { | |
311 | syslog(LOG_ERR, "Could not create audit shutdown event.\n"); | |
312 | } else { | |
313 | ||
314 | if((tok = au_to_text("auditd::Audit shutdown")) != NULL) { | |
315 | au_write(aufd, tok); | |
316 | } | |
317 | ||
318 | if(au_close(aufd, 1, AUE_audit_shutdown) == -1) { | |
319 | syslog(LOG_ERR, "Could not close audit shutdown event.\n"); | |
320 | } | |
321 | } | |
322 | ||
323 | /* flush contents */ | |
324 | err_ret = auditctl(NULL); | |
325 | if (err_ret != 0) { | |
326 | syslog(LOG_ERR, "auditctl failed! : %s\n", | |
327 | strerror(errno)); | |
328 | err_ret = 1; | |
329 | } | |
330 | if(getTSstr(TS, POSTFIX_LEN) == 0) { | |
331 | close_lastfile(TS); | |
332 | } | |
333 | if(lastfile != NULL) | |
334 | free(lastfile); | |
335 | ||
336 | free_dir_q(); | |
337 | if((remove(AUDITD_PIDFILE) == -1) || err_ret) { | |
338 | syslog(LOG_ERR, "Could not unregister\n"); | |
339 | audit_warn_postsigterm(); | |
340 | return (1); | |
341 | } | |
342 | endac(); | |
343 | syslog(LOG_INFO, "Finished.\n"); | |
344 | return (0); | |
345 | } | |
346 | ||
347 | /* | |
348 | * When we get a signal, we are often not at a clean point. | |
349 | * So, little can be done in the signal handler itself. Instead, | |
350 | * we send a message to the main servicing loop to do proper | |
351 | * handling from a non-signal-handler context. | |
352 | */ | |
353 | static void | |
354 | relay_signal(int signal) | |
355 | { | |
356 | mach_msg_empty_send_t msg; | |
357 | ||
358 | msg.header.msgh_id = signal; | |
359 | msg.header.msgh_remote_port = signal_port; | |
360 | msg.header.msgh_local_port = MACH_PORT_NULL; | |
361 | msg.header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND, 0); | |
362 | mach_msg(&(msg.header), MACH_SEND_MSG|MACH_SEND_TIMEOUT, sizeof(msg), | |
363 | 0, MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); | |
364 | } | |
365 | ||
366 | /* registering the daemon */ | |
367 | int register_daemon() | |
368 | { | |
369 | FILE * pidfile; | |
370 | int fd; | |
371 | pid_t pid; | |
372 | ||
373 | /* Set up the signal hander */ | |
374 | if (signal(SIGTERM, relay_signal) == SIG_ERR) { | |
375 | fail_exit(); | |
376 | } | |
377 | if (signal(SIGCHLD, relay_signal) == SIG_ERR) { | |
378 | fail_exit(); | |
379 | } | |
380 | ||
381 | if ((pidfile = fopen(AUDITD_PIDFILE, "a")) == NULL) { | |
382 | audit_warn_tmpfile(); | |
383 | return -1; | |
384 | } | |
385 | ||
386 | /* attempt to lock the pid file; if a lock is present, exit */ | |
387 | fd = fileno(pidfile); | |
388 | if(flock(fd, LOCK_EX | LOCK_NB) < 0) { | |
389 | syslog(LOG_ERR, "PID file is locked (is another auditd running?).\n"); | |
390 | audit_warn_ebusy(); | |
391 | return -1; | |
392 | } | |
393 | ||
394 | pid = getpid(); | |
395 | ftruncate(fd, 0); | |
396 | if(fprintf(pidfile, "%u\n", pid) < 0) { | |
397 | /* should not start the daemon */ | |
398 | fail_exit(); | |
399 | } | |
400 | ||
401 | fflush(pidfile); | |
402 | return 0; | |
403 | } | |
404 | ||
405 | /* | |
406 | * React to input from the audit tool | |
407 | */ | |
408 | kern_return_t auditd_control(auditd_port, flags) | |
409 | mach_port_t auditd_port; | |
410 | int flags; | |
411 | { | |
412 | int err_ret = 0; | |
413 | ||
414 | switch(flags) { | |
415 | ||
416 | case OPEN_NEW : | |
417 | /* create a new file and swap with the one being used in kernel */ | |
418 | if(swap_audit_file() == -1) { | |
419 | syslog(LOG_ERR, "Error swapping audit file\n"); | |
420 | } | |
421 | break; | |
422 | ||
423 | case READ_FILE : | |
424 | if(read_control_file() == -1) { | |
425 | syslog(LOG_ERR, "Error in audit control file\n"); | |
426 | } | |
427 | break; | |
428 | ||
429 | case CLOSE_AND_DIE : | |
430 | err_ret = close_all(); | |
431 | exit (err_ret); | |
432 | break; | |
433 | ||
434 | default : | |
435 | break; | |
436 | } | |
437 | ||
438 | return KERN_SUCCESS; | |
439 | } | |
440 | ||
441 | /* | |
442 | * Suppress duplicate messages within a 30 second interval. | |
443 | * This should be enough to time to rotate log files without | |
444 | * thrashing from soft warnings generated before the log is | |
445 | * actually rotated. | |
446 | */ | |
447 | #define DUPLICATE_INTERVAL 30 | |
448 | /* | |
449 | * Implementation of the audit_triggers() MIG routine. | |
450 | */ | |
451 | kern_return_t audit_triggers(audit_port, flags) | |
452 | mach_port_t audit_port; | |
453 | int flags; | |
454 | { | |
455 | static int last_flags; | |
456 | static time_t last_time; | |
457 | struct dir_ent *dirent; | |
458 | ||
459 | /* | |
460 | * Suppres duplicate messages from the kernel within the specified interval | |
461 | */ | |
462 | struct timeval ts; | |
463 | struct timezone tzp; | |
464 | time_t tt; | |
465 | ||
466 | if(gettimeofday(&ts, &tzp) == 0) { | |
467 | tt = (time_t)ts.tv_sec; | |
468 | if ((flags == last_flags) && (tt < (last_time + DUPLICATE_INTERVAL))) { | |
469 | return KERN_SUCCESS; | |
470 | } | |
471 | last_flags = flags; | |
472 | last_time = tt; | |
473 | } | |
474 | ||
475 | syslog(LOG_INFO, | |
476 | "audit_triggers() called within auditd with flags = %d\n", | |
477 | flags); | |
478 | /* | |
479 | * XXX Message processing is done here | |
480 | */ | |
481 | dirent = TAILQ_FIRST(&dir_q); | |
482 | if(flags == AUDIT_TRIGGER_LOW_SPACE) { | |
483 | if(dirent && (dirent->softlim != 1)) { | |
484 | TAILQ_REMOVE(&dir_q, dirent, dirs); | |
485 | /* add this node to the end of the list */ | |
486 | TAILQ_INSERT_TAIL(&dir_q, dirent, dirs); | |
487 | audit_warn_soft(dirent->dirname); | |
488 | dirent->softlim = 1; | |
489 | ||
490 | if (TAILQ_NEXT(TAILQ_FIRST(&dir_q), dirs) != NULL && swap_audit_file() == -1) { | |
491 | syslog(LOG_ERR, "Error swapping audit file\n"); | |
492 | } | |
493 | ||
494 | /* | |
495 | * check if the next dir has already reached its | |
496 | * soft limit | |
497 | */ | |
498 | dirent = TAILQ_FIRST(&dir_q); | |
499 | if(dirent->softlim == 1) { | |
500 | /* all dirs have reached their soft limit */ | |
501 | audit_warn_allsoft(); | |
502 | } | |
503 | } | |
504 | else { | |
505 | /* | |
506 | * Continue auditing to the current file | |
507 | * Also generate an allsoft warning | |
508 | * XXX do we want to do this ? | |
509 | */ | |
510 | audit_warn_allsoft(); | |
511 | } | |
512 | } | |
513 | else if (flags == AUDIT_TRIGGER_FILE_FULL) { | |
514 | ||
515 | /* delete current dir, go on to next */ | |
516 | TAILQ_REMOVE(&dir_q, dirent, dirs); | |
517 | audit_warn_hard(dirent->dirname); | |
518 | free(dirent->dirname); | |
519 | free(dirent); | |
520 | ||
521 | if(swap_audit_file() == -1) { | |
522 | syslog(LOG_ERR, "Error swapping audit file in response to AUDIT_TRIGGER_FILE_FULL message\n"); | |
523 | ||
524 | /* Nowhere to write to */ | |
525 | audit_warn_allhard(++allhardcount); | |
526 | } | |
527 | } | |
528 | return KERN_SUCCESS; | |
529 | } | |
530 | ||
531 | /* | |
532 | * Reap our children. | |
533 | */ | |
534 | static void | |
535 | reap_children(void) | |
536 | { | |
537 | pid_t child; | |
538 | int wstatus; | |
539 | ||
540 | while ((child = waitpid(-1, &wstatus, WNOHANG)) > 0) { | |
541 | if (wstatus) { | |
542 | syslog(LOG_INFO, "warn process [pid=%d] %s %d.\n", child, | |
543 | ((WIFEXITED(wstatus)) ? | |
544 | "exited with non-zero status" : | |
545 | "exited as a result of signal"), | |
546 | ((WIFEXITED(wstatus)) ? | |
547 | WEXITSTATUS(wstatus) : | |
548 | WTERMSIG(wstatus))); | |
549 | } | |
550 | } | |
551 | } | |
552 | ||
553 | /* | |
554 | * Handle an RPC call | |
555 | */ | |
556 | boolean_t auditd_combined_server( | |
557 | mach_msg_header_t *InHeadP, | |
558 | mach_msg_header_t *OutHeadP) | |
559 | { | |
560 | mach_port_t local_port = InHeadP->msgh_local_port; | |
561 | ||
562 | if (local_port == signal_port) { | |
563 | int signo = InHeadP->msgh_id; | |
564 | int ret; | |
565 | ||
566 | if (SIGTERM == signo) { | |
567 | ret = close_all(); | |
568 | exit (ret); | |
569 | } else if (SIGCHLD == signo) { | |
570 | reap_children(); | |
571 | return TRUE; | |
572 | } else { | |
573 | syslog(LOG_INFO, "Recevied signal %d.\n", signo); | |
574 | return TRUE; | |
575 | } | |
576 | } else if (local_port == control_port) { | |
577 | boolean_t result; | |
578 | ||
579 | result = audit_triggers_server(InHeadP, OutHeadP); | |
580 | if (!result) | |
581 | result = auditd_control_server(InHeadP, OutHeadP); | |
582 | return result; | |
583 | } | |
584 | syslog(LOG_INFO, "Recevied msg on bad port 0x%x.\n", local_port); | |
585 | return FALSE; | |
586 | } | |
587 | ||
588 | void wait_on_audit_trigger(port_set) | |
589 | mach_port_t port_set; | |
590 | { | |
591 | kern_return_t result; | |
592 | result = mach_msg_server(auditd_combined_server, 4096, port_set, MACH_MSG_OPTION_NONE); | |
593 | syslog(LOG_ERR, "abnormal exit\n"); | |
594 | } | |
595 | ||
596 | /* | |
597 | * Configure the audit controls in the kernel: the event to class mapping, | |
598 | * kernel preselection mask, etc. | |
599 | */ | |
600 | int config_audit_controls(long flags) | |
601 | { | |
602 | au_event_ent_t *ev; | |
603 | au_evclass_map_t evc_map; | |
604 | au_mask_t aumask; | |
605 | int ctr = 0; | |
606 | char naeventstr[NA_EVENT_STR_SIZE]; | |
607 | ||
608 | /* Process the audit event file, obtaining a class mapping for each | |
609 | * event, and send that mapping into the kernel. | |
610 | * XXX There's a risk here that the BSM library will return NULL | |
611 | * for an event when it can't properly map it to a class. In that | |
612 | * case, we will not process any events beyond the one that failed, | |
613 | * but should. We need a way to get a count of the events. | |
614 | */ | |
615 | ||
616 | setauevent(); | |
617 | while((ev = getauevent()) != NULL) { | |
618 | evc_map.ec_number = ev->ae_number; | |
619 | evc_map.ec_class = ev->ae_class; | |
620 | if (auditon(A_SETCLASS, &evc_map, sizeof(au_evclass_map_t)) != 0) { | |
621 | syslog(LOG_ERR, | |
622 | "Failed to register class mapping for event %s", | |
623 | ev->ae_name); | |
624 | } else { | |
625 | ctr++; | |
626 | } | |
627 | free(ev->ae_name); | |
628 | free(ev->ae_desc); | |
629 | free(ev); | |
630 | } | |
631 | endauevent(); | |
632 | if (ctr == 0) | |
633 | syslog(LOG_ERR, "No events to class mappings registered."); | |
634 | else | |
635 | syslog(LOG_INFO, "Registered %d event to class mappings.", ctr); | |
636 | ||
637 | /* Get the non-attributable event string and set the kernel mask | |
638 | * from that. | |
639 | */ | |
640 | if ((getacna(naeventstr, NA_EVENT_STR_SIZE) == 0) | |
641 | && ( getauditflagsbin(naeventstr, &aumask) == 0)) { | |
642 | ||
643 | if (auditon(A_SETKMASK, &aumask, sizeof(au_mask_t))){ | |
644 | syslog(LOG_ERR, | |
645 | "Failed to register non-attributable event mask."); | |
646 | } else { | |
647 | syslog(LOG_INFO, "Registered non-attributable event mask."); | |
648 | } | |
649 | ||
650 | } else { | |
651 | syslog(LOG_ERR,"Failed to obtain non-attributable event mask."); | |
652 | } | |
653 | ||
654 | /* | |
655 | * Set the audit policy flags based on passed in parameter values. | |
656 | */ | |
657 | if (auditon(A_SETPOLICY, &flags, sizeof(flags))) { | |
658 | syslog(LOG_ERR, | |
659 | "Failed to set audit policy."); | |
660 | } | |
661 | ||
662 | return 0; | |
663 | } | |
664 | ||
665 | void setup(long flags) | |
666 | { | |
667 | mach_msg_type_name_t poly; | |
668 | int aufd; | |
669 | token_t *tok; | |
670 | ||
671 | /* Allocate a port set */ | |
672 | if (mach_port_allocate(mach_task_self(), | |
673 | MACH_PORT_RIGHT_PORT_SET, | |
674 | &port_set) != KERN_SUCCESS) { | |
675 | syslog(LOG_ERR, "allocation of port set failed\n"); | |
676 | fail_exit(); | |
677 | } | |
678 | ||
679 | /* Allocate a signal reflection port */ | |
680 | if (mach_port_allocate(mach_task_self(), | |
681 | MACH_PORT_RIGHT_RECEIVE, | |
682 | &signal_port) != KERN_SUCCESS || | |
683 | mach_port_move_member(mach_task_self(), | |
684 | signal_port, | |
685 | port_set) != KERN_SUCCESS) { | |
686 | syslog(LOG_ERR, "allocation of signal port failed\n"); | |
687 | fail_exit(); | |
688 | } | |
689 | ||
690 | /* Allocate a trigger port */ | |
691 | if (mach_port_allocate(mach_task_self(), | |
692 | MACH_PORT_RIGHT_RECEIVE, | |
693 | &control_port) != KERN_SUCCESS || | |
694 | mach_port_move_member(mach_task_self(), | |
695 | control_port, | |
696 | port_set) != KERN_SUCCESS) { | |
697 | syslog(LOG_ERR, "allocation of trigger port failed\n"); | |
698 | fail_exit(); | |
699 | } | |
700 | ||
701 | /* create a send right on our trigger port */ | |
702 | mach_port_extract_right(mach_task_self(), control_port, | |
703 | MACH_MSG_TYPE_MAKE_SEND, &control_port, &poly); | |
704 | ||
705 | TAILQ_INIT(&dir_q); | |
706 | ||
707 | /* register the trigger port with the kernel */ | |
708 | if(host_set_audit_control_port(mach_host_self(), control_port) != KERN_SUCCESS) { | |
709 | syslog(LOG_ERR, "Cannot set Mach control port\n"); | |
710 | fail_exit(); | |
711 | } | |
712 | else { | |
713 | syslog(LOG_ERR, "Mach control port registered\n"); | |
714 | } | |
715 | ||
716 | if(read_control_file() == -1) { | |
717 | syslog(LOG_ERR, "Error reading control file\n"); | |
718 | fail_exit(); | |
719 | } | |
720 | ||
721 | /* Generate an audit record */ | |
722 | if((aufd = au_open()) == -1) { | |
723 | syslog(LOG_ERR, "Could not create audit startup event.\n"); | |
724 | } else { | |
725 | ||
726 | if((tok = au_to_text("auditd::Audit startup")) != NULL) { | |
727 | au_write(aufd, tok); | |
728 | } | |
729 | ||
730 | if(au_close(aufd, 1, AUE_audit_startup) == -1) { | |
731 | syslog(LOG_ERR, "Could not close audit startup event.\n"); | |
732 | } | |
733 | } | |
734 | ||
735 | if (config_audit_controls(flags) == 0) | |
736 | syslog(LOG_INFO, "Initialization successful\n"); | |
737 | else | |
738 | syslog(LOG_INFO, "Initialization failed\n"); | |
739 | } | |
740 | ||
741 | ||
742 | int main(int argc, char **argv) | |
743 | { | |
744 | char ch; | |
745 | long flags = AUDIT_CNT; | |
746 | int debug = 0; | |
747 | ||
748 | while ((ch = getopt(argc, argv, "dhs")) != -1) { | |
749 | switch(ch) { | |
750 | ||
751 | /* debug option */ | |
752 | case 'd': | |
753 | debug = 1; | |
754 | break; | |
755 | ||
756 | /* fail-stop option */ | |
757 | case 's': | |
758 | flags &= ~(AUDIT_CNT); | |
759 | break; | |
760 | ||
761 | /* halt-stop option */ | |
762 | case 'h': | |
763 | flags |= AUDIT_AHLT; | |
764 | break; | |
765 | ||
766 | case '?': | |
767 | default: | |
768 | (void)fprintf(stderr, | |
769 | "usage: auditd [-h | -s]\n"); | |
770 | exit(1); | |
771 | } | |
772 | } | |
773 | ||
774 | openlog("auditd", LOG_CONS | LOG_PID, LOG_DAEMON); | |
775 | syslog(LOG_INFO, "starting...\n"); | |
776 | ||
777 | if (debug == 0 && daemon(0, 0) == -1) { | |
778 | syslog(LOG_ERR, "Failed to daemonize\n"); | |
779 | exit(1); | |
780 | } | |
781 | ||
782 | if(register_daemon() == -1) { | |
783 | syslog(LOG_ERR, "Could not register as daemon\n"); | |
784 | exit(1); | |
785 | } | |
786 | ||
787 | setup(flags); | |
788 | wait_on_audit_trigger(port_set); | |
789 | syslog(LOG_INFO, "exiting.\n"); | |
790 | ||
791 | exit(1); | |
792 | } |