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