1 /*-
2 * Copyright (c) 2004, 2009 Apple Inc.
3 * Copyright (c) 2006, 2016 Robert N. M. Watson
4 * All rights reserved.
5 *
6 * Portions of this software were developed by BAE Systems, the University of
7 * Cambridge Computer Laboratory, and Memorial University under DARPA/AFRL
8 * contract FA8650-15-C-7558 ("CADETS"), as part of the DARPA Transparent
9 * Computing (TC) research program.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. Neither the name of Apple Inc. ("Apple") nor the names of
20 * its contributors may be used to endorse or promote products derived
21 * from this software without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR
27 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
31 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
32 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33 * POSSIBILITY OF SUCH DAMAGE.
34 */
35
36 #include <config/config.h>
37
38 #include <bsm/libbsm.h>
39
40 #include <ctype.h>
41 #include <errno.h>
42 #include <string.h>
43 #include <strings.h>
44 #ifdef HAVE_PTHREAD_MUTEX_LOCK
45 #include <pthread.h>
46 #endif
47 #include <stdio.h>
48 #include <stdlib.h>
49
50 #ifndef HAVE_STRLCAT
51 #include <compat/strlcat.h>
52 #endif
53 #ifndef HAVE_STRLCPY
54 #include <compat/strlcpy.h>
55 #endif
56
57 #include <sys/stat.h>
58
59 /*
60 * Parse the contents of the audit_control file to return the audit control
61 * parameters. These static fields are protected by 'mutex'.
62 */
63 static FILE *fp = NULL;
64 static char linestr[AU_LINE_MAX];
65 static char *delim = ":";
66
67 static char inacdir = 0;
68 static char ptrmoved = 0;
69
70 #ifdef HAVE_PTHREAD_MUTEX_LOCK
71 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
72 #endif
73
74 /*
75 * Audit policy string token table for au_poltostr() and au_strtopol().
76 */
77 struct audit_polstr {
78 long ap_policy;
79 const char *ap_str;
80 };
81
82 static struct audit_polstr au_polstr[] = {
83 { AUDIT_CNT, "cnt" },
84 { AUDIT_AHLT, "ahlt" },
85 { AUDIT_ARGV, "argv" },
86 { AUDIT_ARGE, "arge" },
87 { AUDIT_SEQ, "seq" },
88 { AUDIT_WINDATA, "windata" },
89 { AUDIT_USER, "user" },
90 { AUDIT_GROUP, "group" },
91 { AUDIT_TRAIL, "trail" },
92 { AUDIT_PATH, "path" },
93 { AUDIT_SCNT, "scnt" },
94 { AUDIT_PUBLIC, "public" },
95 { AUDIT_ZONENAME, "zonename" },
96 { AUDIT_PERZONE, "perzone" },
97 { -1, NULL }
98 };
99
100 /*
101 * Returns the string value corresponding to the given label from the
102 * configuration file.
103 *
104 * Must be called with mutex held.
105 */
106 static int
getstrfromtype_locked(const char * name,char ** str)107 getstrfromtype_locked(const char *name, char **str)
108 {
109 char *type, *nl;
110 char *tokptr;
111 char *last;
112
113 *str = NULL;
114
115 if ((fp == NULL) && ((fp = fopen(AUDIT_CONTROL_FILE, "r")) == NULL))
116 return (-1); /* Error */
117
118 while (1) {
119 if (fgets(linestr, AU_LINE_MAX, fp) == NULL) {
120 if (ferror(fp))
121 return (-1);
122 return (0); /* EOF */
123 }
124
125 if (linestr[0] == '#')
126 continue;
127
128 /* Remove trailing new line character and white space. */
129 nl = strchr(linestr, '\0') - 1;
130 while (nl >= linestr && ('\n' == *nl || ' ' == *nl ||
131 '\t' == *nl)) {
132 *nl = '\0';
133 nl--;
134 }
135
136 tokptr = linestr;
137 if ((type = strtok_r(tokptr, delim, &last)) != NULL) {
138 if (strcmp(name, type) == 0) {
139 /* Found matching name. */
140 *str = last;
141 return (0); /* Success */
142 }
143 }
144 }
145 }
146
147 /*
148 * Convert a given time value with a multiplier (seconds, hours, days, years) to
149 * seconds. Return 0 on success.
150 */
151 static int
au_timetosec(time_t * seconds,u_long value,char mult)152 au_timetosec(time_t *seconds, u_long value, char mult)
153 {
154 if (NULL == seconds)
155 return (-1);
156
157 switch(mult) {
158 case 's':
159 /* seconds */
160 *seconds = (time_t)value;
161 break;
162
163 case 'h':
164 /* hours */
165 *seconds = (time_t)value * 60 * 60;
166 break;
167
168 case 'd':
169 /* days */
170 *seconds = (time_t)value * 60 * 60 * 24;
171 break;
172
173 case 'y':
174 /* years. Add a day for each 4th (leap) year. */
175 *seconds = (time_t)value * 60 * 60 * 24 * 364 +
176 ((time_t)value / 4) * 60 * 60 * 24;
177 break;
178
179 default:
180 return (-1);
181 }
182 return (0);
183 }
184
185 /*
186 * Convert a given disk space value with a multiplier (bytes, kilobytes,
187 * megabytes, gigabytes) to bytes. Return 0 on success.
188 */
189 static int
au_spacetobytes(size_t * bytes,u_long value,char mult)190 au_spacetobytes(size_t *bytes, u_long value, char mult)
191 {
192 if (NULL == bytes)
193 return (-1);
194
195 switch(mult) {
196 case 'B':
197 case ' ':
198 /* Bytes */
199 *bytes = (size_t)value;
200 break;
201
202 case 'K':
203 /* Kilobytes */
204 *bytes = (size_t)value * 1024;
205 break;
206
207 case 'M':
208 /* Megabytes */
209 *bytes = (size_t)value * 1024 * 1024;
210 break;
211
212 case 'G':
213 /* Gigabytes */
214 *bytes = (size_t)value * 1024 * 1024 * 1024;
215 break;
216
217 default:
218 return (-1);
219 }
220 return (0);
221 }
222
223 /*
224 * Convert a policy to a string. Return -1 on failure, or >= 0 representing
225 * the actual size of the string placed in the buffer (excluding terminating
226 * nul).
227 */
228 ssize_t
au_poltostr(int policy,size_t maxsize,char * buf)229 au_poltostr(int policy, size_t maxsize, char *buf)
230 {
231 int first = 1;
232 int i = 0;
233
234 if (maxsize < 1)
235 return (-1);
236 buf[0] = '\0';
237
238 do {
239 if (policy & au_polstr[i].ap_policy) {
240 if (!first && strlcat(buf, ",", maxsize) >= maxsize)
241 return (-1);
242 if (strlcat(buf, au_polstr[i].ap_str, maxsize) >=
243 maxsize)
244 return (-1);
245 first = 0;
246 }
247 } while (NULL != au_polstr[++i].ap_str);
248
249 return (strlen(buf));
250 }
251
252 /*
253 * Convert a string to a policy. Return -1 on failure (with errno EINVAL,
254 * ENOMEM) or 0 on success.
255 */
256 int
au_strtopol(const char * polstr,int * policy)257 au_strtopol(const char *polstr, int *policy)
258 {
259 char *bufp, *string;
260 char *buffer;
261 int i, matched;
262
263 *policy = 0;
264 buffer = strdup(polstr);
265 if (buffer == NULL)
266 return (-1);
267
268 bufp = buffer;
269 while ((string = strsep(&bufp, ",")) != NULL) {
270 matched = i = 0;
271
272 do {
273 if (strcmp(string, au_polstr[i].ap_str) == 0) {
274 *policy |= au_polstr[i].ap_policy;
275 matched = 1;
276 break;
277 }
278 } while (NULL != au_polstr[++i].ap_str);
279
280 if (!matched) {
281 free(buffer);
282 errno = EINVAL;
283 return (-1);
284 }
285 }
286 free(buffer);
287 return (0);
288 }
289
290 /*
291 * Rewind the file pointer to beginning.
292 */
293 static void
setac_locked(void)294 setac_locked(void)
295 {
296 static time_t lastctime = 0;
297 struct stat sbuf;
298
299 ptrmoved = 1;
300 if (fp != NULL) {
301 /*
302 * Check to see if the file on disk has changed. If so,
303 * force a re-read of the file by closing it.
304 */
305 if (fstat(fileno(fp), &sbuf) < 0)
306 goto closefp;
307 if (lastctime != sbuf.st_ctime) {
308 lastctime = sbuf.st_ctime;
309 closefp:
310 fclose(fp);
311 fp = NULL;
312 return;
313 }
314
315 fseek(fp, 0, SEEK_SET);
316 }
317 }
318
319 void
setac(void)320 setac(void)
321 {
322
323 #ifdef HAVE_PTHREAD_MUTEX_LOCK
324 pthread_mutex_lock(&mutex);
325 #endif
326 setac_locked();
327 #ifdef HAVE_PTHREAD_MUTEX_LOCK
328 pthread_mutex_unlock(&mutex);
329 #endif
330 }
331
332 /*
333 * Close the audit_control file.
334 */
335 void
endac(void)336 endac(void)
337 {
338
339 #ifdef HAVE_PTHREAD_MUTEX_LOCK
340 pthread_mutex_lock(&mutex);
341 #endif
342 ptrmoved = 1;
343 if (fp != NULL) {
344 fclose(fp);
345 fp = NULL;
346 }
347 #ifdef HAVE_PTHREAD_MUTEX_LOCK
348 pthread_mutex_unlock(&mutex);
349 #endif
350 }
351
352 /*
353 * Return audit directory information from the audit control file.
354 */
355 int
getacdir(char * name,int len)356 getacdir(char *name, int len)
357 {
358 char *dir;
359 int ret = 0;
360
361 /*
362 * Check if another function was called between successive calls to
363 * getacdir.
364 */
365 #ifdef HAVE_PTHREAD_MUTEX_LOCK
366 pthread_mutex_lock(&mutex);
367 #endif
368 if (inacdir && ptrmoved) {
369 ptrmoved = 0;
370 if (fp != NULL)
371 fseek(fp, 0, SEEK_SET);
372 ret = 2;
373 }
374 if (getstrfromtype_locked(DIR_CONTROL_ENTRY, &dir) < 0) {
375 #ifdef HAVE_PTHREAD_MUTEX_LOCK
376 pthread_mutex_unlock(&mutex);
377 #endif
378 return (-2);
379 }
380 if (dir == NULL) {
381 #ifdef HAVE_PTHREAD_MUTEX_LOCK
382 pthread_mutex_unlock(&mutex);
383 #endif
384 return (-1);
385 }
386 if (strlen(dir) >= (size_t)len) {
387 #ifdef HAVE_PTHREAD_MUTEX_LOCK
388 pthread_mutex_unlock(&mutex);
389 #endif
390 return (-3);
391 }
392 strlcpy(name, dir, len);
393 #ifdef HAVE_PTHREAD_MUTEX_LOCK
394 pthread_mutex_unlock(&mutex);
395 #endif
396 return (ret);
397 }
398
399 /*
400 * Return 1 if dist value is set to 'yes' or 'on'.
401 * Return 0 if dist value is set to something else.
402 * Return negative value on error.
403 */
404 int
getacdist(void)405 getacdist(void)
406 {
407 char *str;
408 int ret;
409
410 #ifdef HAVE_PTHREAD_MUTEX_LOCK
411 pthread_mutex_lock(&mutex);
412 #endif
413 setac_locked();
414 if (getstrfromtype_locked(DIST_CONTROL_ENTRY, &str) < 0) {
415 #ifdef HAVE_PTHREAD_MUTEX_LOCK
416 pthread_mutex_unlock(&mutex);
417 #endif
418 return (-2);
419 }
420 if (str == NULL) {
421 #ifdef HAVE_PTHREAD_MUTEX_LOCK
422 pthread_mutex_unlock(&mutex);
423 #endif
424 return (0);
425 }
426 if (strcasecmp(str, "on") == 0 || strcasecmp(str, "yes") == 0)
427 ret = 1;
428 else
429 ret = 0;
430 #ifdef HAVE_PTHREAD_MUTEX_LOCK
431 pthread_mutex_unlock(&mutex);
432 #endif
433 return (ret);
434 }
435
436 /*
437 * Return the minimum free diskspace value from the audit control file.
438 */
439 int
getacmin(int * min_val)440 getacmin(int *min_val)
441 {
442 char *min;
443
444 #ifdef HAVE_PTHREAD_MUTEX_LOCK
445 pthread_mutex_lock(&mutex);
446 #endif
447 setac_locked();
448 if (getstrfromtype_locked(MINFREE_CONTROL_ENTRY, &min) < 0) {
449 #ifdef HAVE_PTHREAD_MUTEX_LOCK
450 pthread_mutex_unlock(&mutex);
451 #endif
452 return (-2);
453 }
454 if (min == NULL) {
455 #ifdef HAVE_PTHREAD_MUTEX_LOCK
456 pthread_mutex_unlock(&mutex);
457 #endif
458 return (-1);
459 }
460 *min_val = atoi(min);
461 #ifdef HAVE_PTHREAD_MUTEX_LOCK
462 pthread_mutex_unlock(&mutex);
463 #endif
464 return (0);
465 }
466
467 /*
468 * Return the desired trail rotation size from the audit control file.
469 */
470 int
getacfilesz(size_t * filesz_val)471 getacfilesz(size_t *filesz_val)
472 {
473 char *str;
474 size_t val;
475 char mult;
476 int nparsed;
477
478 #ifdef HAVE_PTHREAD_MUTEX_LOCK
479 pthread_mutex_lock(&mutex);
480 #endif
481 setac_locked();
482 if (getstrfromtype_locked(FILESZ_CONTROL_ENTRY, &str) < 0) {
483 #ifdef HAVE_PTHREAD_MUTEX_LOCK
484 pthread_mutex_unlock(&mutex);
485 #endif
486 return (-2);
487 }
488 if (str == NULL) {
489 #ifdef HAVE_PTHREAD_MUTEX_LOCK
490 pthread_mutex_unlock(&mutex);
491 #endif
492 errno = EINVAL;
493 return (-1);
494 }
495
496 /* Trim off any leading white space. */
497 while (*str == ' ' || *str == '\t')
498 str++;
499
500 nparsed = sscanf(str, "%ju%c", (uintmax_t *)&val, &mult);
501
502 switch (nparsed) {
503 case 1:
504 /* If no multiplier then assume 'B' (bytes). */
505 mult = 'B';
506 /* fall through */
507 case 2:
508 if (au_spacetobytes(filesz_val, val, mult) == 0)
509 break;
510 /* fall through */
511 default:
512 errno = EINVAL;
513 #ifdef HAVE_PTHREAD_MUTEX_LOCK
514 pthread_mutex_unlock(&mutex);
515 #endif
516 return (-1);
517 }
518
519 /*
520 * The file size must either be 0 or >= MIN_AUDIT_FILE_SIZE. 0
521 * indicates no rotation size.
522 */
523 if (*filesz_val < 0 || (*filesz_val > 0 &&
524 *filesz_val < MIN_AUDIT_FILE_SIZE)) {
525 #ifdef HAVE_PTHREAD_MUTEX_LOCK
526 pthread_mutex_unlock(&mutex);
527 #endif
528 filesz_val = 0L;
529 errno = EINVAL;
530 return (-1);
531 }
532 #ifdef HAVE_PTHREAD_MUTEX_LOCK
533 pthread_mutex_unlock(&mutex);
534 #endif
535 return (0);
536 }
537
538 static int
getaccommon(const char * name,char * auditstr,int len)539 getaccommon(const char *name, char *auditstr, int len)
540 {
541 char *str;
542
543 #ifdef HAVE_PTHREAD_MUTEX_LOCK
544 pthread_mutex_lock(&mutex);
545 #endif
546 setac_locked();
547 if (getstrfromtype_locked(name, &str) < 0) {
548 #ifdef HAVE_PTHREAD_MUTEX_LOCK
549 pthread_mutex_unlock(&mutex);
550 #endif
551 return (-2);
552 }
553
554 /*
555 * getstrfromtype_locked() can return NULL for an empty value -- make
556 * sure to handle this by coercing the NULL back into an empty string.
557 */
558 if (str != NULL && (strlen(str) >= (size_t)len)) {
559 #ifdef HAVE_PTHREAD_MUTEX_LOCK
560 pthread_mutex_unlock(&mutex);
561 #endif
562 return (-3);
563 }
564 strlcpy(auditstr, str != NULL ? str : "", len);
565 #ifdef HAVE_PTHREAD_MUTEX_LOCK
566 pthread_mutex_unlock(&mutex);
567 #endif
568 return (0);
569 }
570
571 /*
572 * Return the system audit value from the audit contol file.
573 */
574 int
getacflg(char * auditstr,int len)575 getacflg(char *auditstr, int len)
576 {
577
578 return (getaccommon(FLAGS_CONTROL_ENTRY, auditstr, len));
579 }
580
581 /*
582 * Return the non attributable flags from the audit contol file.
583 */
584 int
getacna(char * auditstr,int len)585 getacna(char *auditstr, int len)
586 {
587
588 return (getaccommon(NA_CONTROL_ENTRY, auditstr, len));
589 }
590
591 /*
592 * Return the policy field from the audit control file.
593 */
594 int
getacpol(char * auditstr,size_t len)595 getacpol(char *auditstr, size_t len)
596 {
597
598 return (getaccommon(POLICY_CONTROL_ENTRY, auditstr, len));
599 }
600
601 int
getachost(char * auditstr,size_t len)602 getachost(char *auditstr, size_t len)
603 {
604
605 return (getaccommon(HOST_CONTROL_ENTRY, auditstr, len));
606 }
607
608 /*
609 * Set expiration conditions.
610 */
611 static int
setexpirecond(time_t * age,size_t * size,u_long value,char mult)612 setexpirecond(time_t *age, size_t *size, u_long value, char mult)
613 {
614
615 if (isupper(mult) || ' ' == mult)
616 return (au_spacetobytes(size, value, mult));
617 else
618 return (au_timetosec(age, value, mult));
619 }
620
621 /*
622 * Return the expire-after field from the audit control file.
623 */
624 int
getacexpire(int * andflg,time_t * age,size_t * size)625 getacexpire(int *andflg, time_t *age, size_t *size)
626 {
627 char *str;
628 int nparsed;
629 u_long val1, val2;
630 char mult1, mult2;
631 char andor[AU_LINE_MAX];
632
633 *age = 0L;
634 *size = 0LL;
635 *andflg = 0;
636
637 #ifdef HAVE_PTHREAD_MUTEX_LOCK
638 pthread_mutex_lock(&mutex);
639 #endif
640 setac_locked();
641 if (getstrfromtype_locked(EXPIRE_AFTER_CONTROL_ENTRY, &str) < 0) {
642 #ifdef HAVE_PTHREAD_MUTEX_LOCK
643 pthread_mutex_unlock(&mutex);
644 #endif
645 return (-2);
646 }
647 if (str == NULL) {
648 #ifdef HAVE_PTHREAD_MUTEX_LOCK
649 pthread_mutex_unlock(&mutex);
650 #endif
651 return (-1);
652 }
653
654 /* First, trim off any leading white space. */
655 while (*str == ' ' || *str == '\t')
656 str++;
657
658 nparsed = sscanf(str, "%lu%c%[ \tadnorADNOR]%lu%c", &val1, &mult1,
659 andor, &val2, &mult2);
660
661 switch (nparsed) {
662 case 1:
663 /* If no multiplier then assume 'B' (Bytes). */
664 mult1 = 'B';
665 /* fall through */
666 case 2:
667 /* One expiration condition. */
668 if (setexpirecond(age, size, val1, mult1) != 0) {
669 #ifdef HAVE_PTHREAD_MUTEX_LOCK
670 pthread_mutex_unlock(&mutex);
671 #endif
672 return (-1);
673 }
674 break;
675
676 case 5:
677 /* Two expiration conditions. */
678 if (setexpirecond(age, size, val1, mult1) != 0 ||
679 setexpirecond(age, size, val2, mult2) != 0) {
680 #ifdef HAVE_PTHREAD_MUTEX_LOCK
681 pthread_mutex_unlock(&mutex);
682 #endif
683 return (-1);
684 }
685 if (strcasestr(andor, "and") != NULL)
686 *andflg = 1;
687 else if (strcasestr(andor, "or") != NULL)
688 *andflg = 0;
689 else {
690 #ifdef HAVE_PTHREAD_MUTEX_LOCK
691 pthread_mutex_unlock(&mutex);
692 #endif
693 return (-1);
694 }
695 break;
696
697 default:
698 #ifdef HAVE_PTHREAD_MUTEX_LOCK
699 pthread_mutex_unlock(&mutex);
700 #endif
701 return (-1);
702 }
703
704 #ifdef HAVE_PTHREAD_MUTEX_LOCK
705 pthread_mutex_unlock(&mutex);
706 #endif
707 return (0);
708 }
709 /*
710 * Return the desired queue size from the audit control file.
711 */
712 int
getacqsize(int * qsz_val)713 getacqsize(int *qsz_val)
714 {
715 char *str;
716 int nparsed;
717
718 #ifdef HAVE_PTHREAD_MUTEX_LOCK
719 pthread_mutex_lock(&mutex);
720 #endif
721 setac_locked();
722 if (getstrfromtype_locked(QSZ_CONTROL_ENTRY, &str) < 0) {
723 #ifdef HAVE_PTHREAD_MUTEX_LOCK
724 pthread_mutex_unlock(&mutex);
725 #endif
726 return (-2);
727 }
728 if (str == NULL) {
729 #ifdef HAVE_PTHREAD_MUTEX_LOCK
730 pthread_mutex_unlock(&mutex);
731 #endif
732 *qsz_val = USE_DEFAULT_QSZ;
733 return (0);
734 }
735
736 /* Trim off any leading white space. */
737 while (*str == ' ' || *str == '\t')
738 str++;
739
740 nparsed = sscanf(str, "%d", (int *)qsz_val);
741
742 if (nparsed != 1) {
743 errno = EINVAL;
744 #ifdef HAVE_PTHREAD_MUTEX_LOCK
745 pthread_mutex_unlock(&mutex);
746 #endif
747 return (-1);
748 }
749
750 /* The queue size must either be 0 or < AQ_MAXHIGH */
751 if (*qsz_val < 0 || *qsz_val > AQ_MAXHIGH) {
752 #ifdef HAVE_PTHREAD_MUTEX_LOCK
753 pthread_mutex_unlock(&mutex);
754 #endif
755 qsz_val = 0L;
756 errno = EINVAL;
757 return (-1);
758 }
759 #ifdef HAVE_PTHREAD_MUTEX_LOCK
760 pthread_mutex_unlock(&mutex);
761 #endif
762 return (0);
763 }
764