1 /*-
2  * Copyright (c) 2004, 2009 Apple Inc.
3  * Copyright (c) 2006 Robert N. M. Watson
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1.  Redistributions of source code must retain the above copyright
10  *     notice, this list of conditions and the following disclaimer.
11  * 2.  Redistributions in binary form must reproduce the above copyright
12  *     notice, this list of conditions and the following disclaimer in the
13  *     documentation and/or other materials provided with the distribution.
14  * 3.  Neither the name of Apple Inc. ("Apple") nor the names of
15  *     its contributors may be used to endorse or promote products derived
16  *     from this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND
19  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR
22  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
26  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
27  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28  * POSSIBILITY OF SUCH DAMAGE.
29  *
30  * $P4: //depot/projects/trustedbsd/openbsm/libbsm/bsm_control.c#34 $
31  */
32 
33 #include <config/config.h>
34 
35 #include <bsm/libbsm.h>
36 
37 #include <ctype.h>
38 #include <errno.h>
39 #include <string.h>
40 #ifdef HAVE_PTHREAD_MUTEX_LOCK
41 #include <pthread.h>
42 #endif
43 #include <stdio.h>
44 #include <stdlib.h>
45 
46 #ifndef HAVE_STRLCAT
47 #include <compat/strlcat.h>
48 #endif
49 #ifndef HAVE_STRLCPY
50 #include <compat/strlcpy.h>
51 #endif
52 
53 #include <sys/stat.h>
54 
55 /*
56  * Parse the contents of the audit_control file to return the audit control
57  * parameters.  These static fields are protected by 'mutex'.
58  */
59 static FILE	*fp = NULL;
60 static char	linestr[AU_LINE_MAX];
61 static char	*delim = ":";
62 
63 static char	inacdir = 0;
64 static char	ptrmoved = 0;
65 
66 #ifdef HAVE_PTHREAD_MUTEX_LOCK
67 static pthread_mutex_t	mutex = PTHREAD_MUTEX_INITIALIZER;
68 #endif
69 
70 /*
71  * Audit policy string token table for au_poltostr() and au_strtopol().
72  */
73 struct audit_polstr {
74 	long 		 ap_policy;
75 	const char 	*ap_str;
76 };
77 
78 static struct audit_polstr au_polstr[] = {
79 	{ AUDIT_CNT,		"cnt"		},
80 	{ AUDIT_AHLT,		"ahlt"		},
81 	{ AUDIT_ARGV,		"argv"		},
82 	{ AUDIT_ARGE,		"arge"		},
83 	{ AUDIT_SEQ,		"seq"		},
84 	{ AUDIT_WINDATA,	"windata"	},
85 	{ AUDIT_USER,		"user"		},
86 	{ AUDIT_GROUP,		"group"		},
87 	{ AUDIT_TRAIL,		"trail"		},
88 	{ AUDIT_PATH,		"path"		},
89 	{ AUDIT_SCNT,		"scnt"		},
90 	{ AUDIT_PUBLIC,		"public"	},
91 	{ AUDIT_ZONENAME,	"zonename"	},
92 	{ AUDIT_PERZONE,	"perzone"	},
93 	{ -1,			NULL		}
94 };
95 
96 /*
97  * Returns the string value corresponding to the given label from the
98  * configuration file.
99  *
100  * Must be called with mutex held.
101  */
102 static int
103 getstrfromtype_locked(char *name, char **str)
104 {
105 	char *type, *nl;
106 	char *tokptr;
107 	char *last;
108 
109 	*str = NULL;
110 
111 	if ((fp == NULL) && ((fp = fopen(AUDIT_CONTROL_FILE, "r")) == NULL))
112 		return (-1); /* Error */
113 
114 	while (1) {
115 		if (fgets(linestr, AU_LINE_MAX, fp) == NULL) {
116 			if (ferror(fp))
117 				return (-1);
118 			return (0);	/* EOF */
119 		}
120 
121 		if (linestr[0] == '#')
122 			continue;
123 
124 		/* Remove trailing new line character and white space. */
125 		nl = strchr(linestr, '\0') - 1;
126 		while (nl >= linestr && ('\n' == *nl || ' ' == *nl ||
127 			'\t' == *nl)) {
128 			*nl = '\0';
129 			nl--;
130 		}
131 
132 		tokptr = linestr;
133 		if ((type = strtok_r(tokptr, delim, &last)) != NULL) {
134 			if (strcmp(name, type) == 0) {
135 				/* Found matching name. */
136 				*str = strtok_r(NULL, delim, &last);
137 				if (*str == NULL) {
138 					errno = EINVAL;
139 					return (-1); /* Parse error in file */
140 				}
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
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
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
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
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
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
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
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
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 the minimum free diskspace value from the audit control file.
401  */
402 int
403 getacmin(int *min_val)
404 {
405 	char *min;
406 
407 #ifdef HAVE_PTHREAD_MUTEX_LOCK
408 	pthread_mutex_lock(&mutex);
409 #endif
410 	setac_locked();
411 	if (getstrfromtype_locked(MINFREE_CONTROL_ENTRY, &min) < 0) {
412 #ifdef HAVE_PTHREAD_MUTEX_LOCK
413 		pthread_mutex_unlock(&mutex);
414 #endif
415 		return (-2);
416 	}
417 	if (min == NULL) {
418 #ifdef HAVE_PTHREAD_MUTEX_LOCK
419 		pthread_mutex_unlock(&mutex);
420 #endif
421 		return (1);
422 	}
423 	*min_val = atoi(min);
424 #ifdef HAVE_PTHREAD_MUTEX_LOCK
425 	pthread_mutex_unlock(&mutex);
426 #endif
427 	return (0);
428 }
429 
430 /*
431  * Return the desired trail rotation size from the audit control file.
432  */
433 int
434 getacfilesz(size_t *filesz_val)
435 {
436 	char *str;
437 	size_t val;
438 	char mult;
439 	int nparsed;
440 
441 #ifdef HAVE_PTHREAD_MUTEX_LOCK
442 	pthread_mutex_lock(&mutex);
443 #endif
444 	setac_locked();
445 	if (getstrfromtype_locked(FILESZ_CONTROL_ENTRY, &str) < 0) {
446 #ifdef HAVE_PTHREAD_MUTEX_LOCK
447 		pthread_mutex_unlock(&mutex);
448 #endif
449 		return (-2);
450 	}
451 	if (str == NULL) {
452 #ifdef HAVE_PTHREAD_MUTEX_LOCK
453 		pthread_mutex_unlock(&mutex);
454 #endif
455 		errno = EINVAL;
456 		return (1);
457 	}
458 
459 	/* Trim off any leading white space. */
460 	while (*str == ' ' || *str == '\t')
461 		str++;
462 
463 	nparsed = sscanf(str, "%ju%c", (uintmax_t *)&val, &mult);
464 
465 	switch (nparsed) {
466 	case 1:
467 		/* If no multiplier then assume 'B' (bytes). */
468 		mult = 'B';
469 		/* fall through */
470 	case 2:
471 		if (au_spacetobytes(filesz_val, val, mult) == 0)
472 			break;
473 		/* fall through */
474 	default:
475 		errno = EINVAL;
476 #ifdef HAVE_PTHREAD_MUTEX_LOCK
477 		pthread_mutex_unlock(&mutex);
478 #endif
479 		return (-1);
480 	}
481 
482 	/*
483 	 * The file size must either be 0 or >= MIN_AUDIT_FILE_SIZE.  0
484 	 * indicates no rotation size.
485 	 */
486 	if (*filesz_val < 0 || (*filesz_val > 0 &&
487 		*filesz_val < MIN_AUDIT_FILE_SIZE)) {
488 #ifdef HAVE_PTHREAD_MUTEX_LOCK
489 		pthread_mutex_unlock(&mutex);
490 #endif
491 		filesz_val = 0L;
492 		errno = EINVAL;
493 		return (-1);
494 	}
495 #ifdef HAVE_PTHREAD_MUTEX_LOCK
496 	pthread_mutex_unlock(&mutex);
497 #endif
498 	return (0);
499 }
500 
501 /*
502  * Return the system audit value from the audit contol file.
503  */
504 int
505 getacflg(char *auditstr, int len)
506 {
507 	char *str;
508 
509 #ifdef HAVE_PTHREAD_MUTEX_LOCK
510 	pthread_mutex_lock(&mutex);
511 #endif
512 	setac_locked();
513 	if (getstrfromtype_locked(FLAGS_CONTROL_ENTRY, &str) < 0) {
514 #ifdef HAVE_PTHREAD_MUTEX_LOCK
515 		pthread_mutex_unlock(&mutex);
516 #endif
517 		return (-2);
518 	}
519 	if (str == NULL) {
520 #ifdef HAVE_PTHREAD_MUTEX_LOCK
521 		pthread_mutex_unlock(&mutex);
522 #endif
523 		return (1);
524 	}
525 	if (strlen(str) >= (size_t)len) {
526 #ifdef HAVE_PTHREAD_MUTEX_LOCK
527 		pthread_mutex_unlock(&mutex);
528 #endif
529 		return (-3);
530 	}
531 	strlcpy(auditstr, str, len);
532 #ifdef HAVE_PTHREAD_MUTEX_LOCK
533 	pthread_mutex_unlock(&mutex);
534 #endif
535 	return (0);
536 }
537 
538 /*
539  * Return the non attributable flags from the audit contol file.
540  */
541 int
542 getacna(char *auditstr, int len)
543 {
544 	char *str;
545 
546 #ifdef HAVE_PTHREAD_MUTEX_LOCK
547 	pthread_mutex_lock(&mutex);
548 #endif
549 	setac_locked();
550 	if (getstrfromtype_locked(NA_CONTROL_ENTRY, &str) < 0) {
551 #ifdef HAVE_PTHREAD_MUTEX_LOCK
552 		pthread_mutex_unlock(&mutex);
553 #endif
554 		return (-2);
555 	}
556 	if (str == NULL) {
557 #ifdef HAVE_PTHREAD_MUTEX_LOCK
558 		pthread_mutex_unlock(&mutex);
559 #endif
560 		return (1);
561 	}
562 	if (strlen(str) >= (size_t)len) {
563 #ifdef HAVE_PTHREAD_MUTEX_LOCK
564 		pthread_mutex_unlock(&mutex);
565 #endif
566 		return (-3);
567 	}
568 	strlcpy(auditstr, str, len);
569 #ifdef HAVE_PTHREAD_MUTEX_LOCK
570 	pthread_mutex_unlock(&mutex);
571 #endif
572 	return (0);
573 }
574 
575 /*
576  * Return the policy field from the audit control file.
577  */
578 int
579 getacpol(char *auditstr, size_t len)
580 {
581 	char *str;
582 
583 #ifdef HAVE_PTHREAD_MUTEX_LOCK
584 	pthread_mutex_lock(&mutex);
585 #endif
586 	setac_locked();
587 	if (getstrfromtype_locked(POLICY_CONTROL_ENTRY, &str) < 0) {
588 #ifdef HAVE_PTHREAD_MUTEX_LOCK
589 		pthread_mutex_unlock(&mutex);
590 #endif
591 		return (-2);
592 	}
593 	if (str == NULL) {
594 #ifdef HAVE_PTHREAD_MUTEX_LOCK
595 		pthread_mutex_unlock(&mutex);
596 #endif
597 		return (-1);
598 	}
599 	if (strlen(str) >= len) {
600 #ifdef HAVE_PTHREAD_MUTEX_LOCK
601 		pthread_mutex_unlock(&mutex);
602 #endif
603 		return (-3);
604 	}
605 	strlcpy(auditstr, str, len);
606 #ifdef HAVE_PTHREAD_MUTEX_LOCK
607 	pthread_mutex_unlock(&mutex);
608 #endif
609 	return (0);
610 }
611 
612 int
613 getachost(char *auditstr, size_t len)
614 {
615 	char *str;
616 
617 #ifdef HAVE_PTHREAD_MUTEX_LOCK
618 	pthread_mutex_lock(&mutex);
619 #endif
620 	setac_locked();
621 	if (getstrfromtype_locked(AUDIT_HOST_CONTROL_ENTRY, &str) < 0) {
622 #ifdef HAVE_PTHREAD_MUTEX_LOCK
623 		pthread_mutex_unlock(&mutex);
624 #endif
625 		return (-2);
626 	}
627 	if (str == NULL) {
628 #ifdef HAVE_PTHREAD_MUTEX_LOCK
629 		pthread_mutex_unlock(&mutex);
630 #endif
631 		return (1);
632 	}
633 	if (strlen(str) >= len) {
634 #ifdef HAVE_PTHREAD_MUTEX_LOCK
635 		pthread_mutex_unlock(&mutex);
636 #endif
637 		return (-3);
638 	}
639 	strlcpy(auditstr, str, len);
640 #ifdef HAVE_PTHREAD_MUTEX_LOCK
641 	pthread_mutex_unlock(&mutex);
642 #endif
643 	return (0);
644 }
645 
646 /*
647  * Set expiration conditions.
648  */
649 static int
650 setexpirecond(time_t *age, size_t *size, u_long value, char mult)
651 {
652 
653 	if (isupper(mult) || ' ' == mult)
654 		return (au_spacetobytes(size, value, mult));
655 	else
656 		return (au_timetosec(age, value, mult));
657 }
658 
659 /*
660  * Return the expire-after field from the audit control file.
661  */
662 int
663 getacexpire(int *andflg, time_t *age, size_t *size)
664 {
665 	char *str;
666 	int nparsed;
667 	u_long val1, val2;
668 	char mult1, mult2;
669 	char andor[AU_LINE_MAX];
670 
671 	*age = 0L;
672 	*size = 0LL;
673 	*andflg = 0;
674 
675 #ifdef HAVE_PTHREAD_MUTEX_LOCK
676 	pthread_mutex_lock(&mutex);
677 #endif
678 	setac_locked();
679 	if (getstrfromtype_locked(EXPIRE_AFTER_CONTROL_ENTRY, &str) < 0) {
680 #ifdef HAVE_PTHREAD_MUTEX_LOCK
681 		pthread_mutex_unlock(&mutex);
682 #endif
683 		return (-2);
684 	}
685 	if (str == NULL) {
686 #ifdef HAVE_PTHREAD_MUTEX_LOCK
687 		pthread_mutex_unlock(&mutex);
688 #endif
689 		return (1);
690 	}
691 
692 	/* First, trim off any leading white space. */
693 	while (*str == ' ' || *str == '\t')
694 		str++;
695 
696 	nparsed = sscanf(str, "%lu%c%[ \tadnorADNOR]%lu%c", &val1, &mult1,
697 	    andor, &val2, &mult2);
698 
699 	switch (nparsed) {
700 	case 1:
701 		/* If no multiplier then assume 'B' (Bytes). */
702 		mult1 = 'B';
703 		/* fall through */
704 	case 2:
705 		/* One expiration condition. */
706 		if (setexpirecond(age, size, val1, mult1) != 0) {
707 #ifdef HAVE_PTHREAD_MUTEX_LOCK
708 			pthread_mutex_unlock(&mutex);
709 #endif
710 			return (-1);
711 		}
712 		break;
713 
714 	case 5:
715 		/* Two expiration conditions. */
716 		if (setexpirecond(age, size, val1, mult1) != 0 ||
717 		    setexpirecond(age, size, val2, mult2) != 0) {
718 #ifdef HAVE_PTHREAD_MUTEX_LOCK
719 			pthread_mutex_unlock(&mutex);
720 #endif
721 			return (-1);
722 		}
723 		if (strcasestr(andor, "and") != NULL)
724 			*andflg = 1;
725 		else if (strcasestr(andor, "or") != NULL)
726 			*andflg = 0;
727 		else {
728 #ifdef HAVE_PTHREAD_MUTEX_LOCK
729 			pthread_mutex_unlock(&mutex);
730 #endif
731 			return (-1);
732 		}
733 		break;
734 
735 	default:
736 #ifdef HAVE_PTHREAD_MUTEX_LOCK
737 		pthread_mutex_unlock(&mutex);
738 #endif
739 		return (-1);
740 	}
741 
742 #ifdef HAVE_PTHREAD_MUTEX_LOCK
743 	pthread_mutex_unlock(&mutex);
744 #endif
745 	return (0);
746 }
747