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#41 $
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 #include <strings.h>
41 #ifdef HAVE_PTHREAD_MUTEX_LOCK
42 #include <pthread.h>
43 #endif
44 #include <stdio.h>
45 #include <stdlib.h>
46 
47 #ifndef HAVE_STRLCAT
48 #include <compat/strlcat.h>
49 #endif
50 #ifndef HAVE_STRLCPY
51 #include <compat/strlcpy.h>
52 #endif
53 
54 #include <sys/stat.h>
55 
56 /*
57  * Parse the contents of the audit_control file to return the audit control
58  * parameters.  These static fields are protected by 'mutex'.
59  */
60 static FILE	*fp = NULL;
61 static char	linestr[AU_LINE_MAX];
62 static char	*delim = ":";
63 
64 static char	inacdir = 0;
65 static char	ptrmoved = 0;
66 
67 #ifdef HAVE_PTHREAD_MUTEX_LOCK
68 static pthread_mutex_t	mutex = PTHREAD_MUTEX_INITIALIZER;
69 #endif
70 
71 /*
72  * Audit policy string token table for au_poltostr() and au_strtopol().
73  */
74 struct audit_polstr {
75 	long		 ap_policy;
76 	const char	*ap_str;
77 };
78 
79 static struct audit_polstr au_polstr[] = {
80 	{ AUDIT_CNT,		"cnt"		},
81 	{ AUDIT_AHLT,		"ahlt"		},
82 	{ AUDIT_ARGV,		"argv"		},
83 	{ AUDIT_ARGE,		"arge"		},
84 	{ AUDIT_SEQ,		"seq"		},
85 	{ AUDIT_WINDATA,	"windata"	},
86 	{ AUDIT_USER,		"user"		},
87 	{ AUDIT_GROUP,		"group"		},
88 	{ AUDIT_TRAIL,		"trail"		},
89 	{ AUDIT_PATH,		"path"		},
90 	{ AUDIT_SCNT,		"scnt"		},
91 	{ AUDIT_PUBLIC,		"public"	},
92 	{ AUDIT_ZONENAME,	"zonename"	},
93 	{ AUDIT_PERZONE,	"perzone"	},
94 	{ -1,			NULL		}
95 };
96 
97 /*
98  * Returns the string value corresponding to the given label from the
99  * configuration file.
100  *
101  * Must be called with mutex held.
102  */
103 static int
104 getstrfromtype_locked(const char *name, char **str)
105 {
106 	char *type, *nl;
107 	char *tokptr;
108 	char *last;
109 
110 	*str = NULL;
111 
112 	if ((fp == NULL) && ((fp = fopen(AUDIT_CONTROL_FILE, "r")) == NULL))
113 		return (-1); /* Error */
114 
115 	while (1) {
116 		if (fgets(linestr, AU_LINE_MAX, fp) == NULL) {
117 			if (ferror(fp))
118 				return (-1);
119 			return (0);	/* EOF */
120 		}
121 
122 		if (linestr[0] == '#')
123 			continue;
124 
125 		/* Remove trailing new line character and white space. */
126 		nl = strchr(linestr, '\0') - 1;
127 		while (nl >= linestr && ('\n' == *nl || ' ' == *nl ||
128 			'\t' == *nl)) {
129 			*nl = '\0';
130 			nl--;
131 		}
132 
133 		tokptr = linestr;
134 		if ((type = strtok_r(tokptr, delim, &last)) != NULL) {
135 			if (strcmp(name, type) == 0) {
136 				/* Found matching name. */
137 				*str = strtok_r(NULL, delim, &last);
138 				if (*str == NULL) {
139 					errno = EINVAL;
140 					return (-1); /* Parse error in file */
141 				}
142 				return (0); /* Success */
143 			}
144 		}
145 	}
146 }
147 
148 /*
149  * Convert a given time value with a multiplier (seconds, hours, days, years) to
150  * seconds.  Return 0 on success.
151  */
152 static int
153 au_timetosec(time_t *seconds, u_long value, char mult)
154 {
155 	if (NULL == seconds)
156 		return (-1);
157 
158 	switch(mult) {
159 	case 's':
160 		/* seconds */
161 		*seconds = (time_t)value;
162 		break;
163 
164 	case 'h':
165 		/* hours */
166 		*seconds = (time_t)value * 60 * 60;
167 		break;
168 
169 	case 'd':
170 		/* days */
171 		*seconds = (time_t)value * 60 * 60 * 24;
172 		break;
173 
174 	case 'y':
175 		/* years.  Add a day for each 4th (leap) year. */
176 		*seconds = (time_t)value * 60 * 60 * 24 * 364 +
177 		    ((time_t)value / 4) * 60 * 60 * 24;
178 		break;
179 
180 	default:
181 		return (-1);
182 	}
183 	return (0);
184 }
185 
186 /*
187  * Convert a given disk space value with a multiplier (bytes, kilobytes,
188  * megabytes, gigabytes) to bytes.  Return 0 on success.
189  */
190 static int
191 au_spacetobytes(size_t *bytes, u_long value, char mult)
192 {
193 	if (NULL == bytes)
194 		return (-1);
195 
196 	switch(mult) {
197 	case 'B':
198 	case ' ':
199 		/* Bytes */
200 		*bytes = (size_t)value;
201 		break;
202 
203 	case 'K':
204 		/* Kilobytes */
205 		*bytes = (size_t)value * 1024;
206 		break;
207 
208 	case 'M':
209 		/* Megabytes */
210 		*bytes = (size_t)value * 1024 * 1024;
211 		break;
212 
213 	case 'G':
214 		/* Gigabytes */
215 		*bytes = (size_t)value * 1024 * 1024 * 1024;
216 		break;
217 
218 	default:
219 		return (-1);
220 	}
221 	return (0);
222 }
223 
224 /*
225  * Convert a policy to a string.  Return -1 on failure, or >= 0 representing
226  * the actual size of the string placed in the buffer (excluding terminating
227  * nul).
228  */
229 ssize_t
230 au_poltostr(int policy, size_t maxsize, char *buf)
231 {
232 	int first = 1;
233 	int i = 0;
234 
235 	if (maxsize < 1)
236 		return (-1);
237 	buf[0] = '\0';
238 
239 	do {
240 		if (policy & au_polstr[i].ap_policy) {
241 			if (!first && strlcat(buf, ",", maxsize) >= maxsize)
242 				return (-1);
243 			if (strlcat(buf, au_polstr[i].ap_str, maxsize) >=
244 			    maxsize)
245 				return (-1);
246 			first = 0;
247 		}
248 	} while (NULL != au_polstr[++i].ap_str);
249 
250 	return (strlen(buf));
251 }
252 
253 /*
254  * Convert a string to a policy.  Return -1 on failure (with errno EINVAL,
255  * ENOMEM) or 0 on success.
256  */
257 int
258 au_strtopol(const char *polstr, int *policy)
259 {
260 	char *bufp, *string;
261 	char *buffer;
262 	int i, matched;
263 
264 	*policy = 0;
265 	buffer = strdup(polstr);
266 	if (buffer == NULL)
267 		return (-1);
268 
269 	bufp = buffer;
270 	while ((string = strsep(&bufp, ",")) != NULL) {
271 		matched = i = 0;
272 
273 		do {
274 			if (strcmp(string, au_polstr[i].ap_str) == 0) {
275 				*policy |= au_polstr[i].ap_policy;
276 				matched = 1;
277 				break;
278 			}
279 		} while (NULL != au_polstr[++i].ap_str);
280 
281 		if (!matched) {
282 			free(buffer);
283 			errno = EINVAL;
284 			return (-1);
285 		}
286 	}
287 	free(buffer);
288 	return (0);
289 }
290 
291 /*
292  * Rewind the file pointer to beginning.
293  */
294 static void
295 setac_locked(void)
296 {
297 	static time_t lastctime = 0;
298 	struct stat sbuf;
299 
300 	ptrmoved = 1;
301 	if (fp != NULL) {
302 		/*
303 		 * Check to see if the file on disk has changed.  If so,
304 		 * force a re-read of the file by closing it.
305 		 */
306 		if (fstat(fileno(fp), &sbuf) < 0)
307 			goto closefp;
308 		if (lastctime != sbuf.st_ctime) {
309 			lastctime = sbuf.st_ctime;
310 closefp:
311 			fclose(fp);
312 			fp = NULL;
313 			return;
314 		}
315 
316 		fseek(fp, 0, SEEK_SET);
317 	}
318 }
319 
320 void
321 setac(void)
322 {
323 
324 #ifdef HAVE_PTHREAD_MUTEX_LOCK
325 	pthread_mutex_lock(&mutex);
326 #endif
327 	setac_locked();
328 #ifdef HAVE_PTHREAD_MUTEX_LOCK
329 	pthread_mutex_unlock(&mutex);
330 #endif
331 }
332 
333 /*
334  * Close the audit_control file.
335  */
336 void
337 endac(void)
338 {
339 
340 #ifdef HAVE_PTHREAD_MUTEX_LOCK
341 	pthread_mutex_lock(&mutex);
342 #endif
343 	ptrmoved = 1;
344 	if (fp != NULL) {
345 		fclose(fp);
346 		fp = NULL;
347 	}
348 #ifdef HAVE_PTHREAD_MUTEX_LOCK
349 	pthread_mutex_unlock(&mutex);
350 #endif
351 }
352 
353 /*
354  * Return audit directory information from the audit control file.
355  */
356 int
357 getacdir(char *name, int len)
358 {
359 	char *dir;
360 	int ret = 0;
361 
362 	/*
363 	 * Check if another function was called between successive calls to
364 	 * getacdir.
365 	 */
366 #ifdef HAVE_PTHREAD_MUTEX_LOCK
367 	pthread_mutex_lock(&mutex);
368 #endif
369 	if (inacdir && ptrmoved) {
370 		ptrmoved = 0;
371 		if (fp != NULL)
372 			fseek(fp, 0, SEEK_SET);
373 		ret = 2;
374 	}
375 	if (getstrfromtype_locked(DIR_CONTROL_ENTRY, &dir) < 0) {
376 #ifdef HAVE_PTHREAD_MUTEX_LOCK
377 		pthread_mutex_unlock(&mutex);
378 #endif
379 		return (-2);
380 	}
381 	if (dir == NULL) {
382 #ifdef HAVE_PTHREAD_MUTEX_LOCK
383 		pthread_mutex_unlock(&mutex);
384 #endif
385 		return (-1);
386 	}
387 	if (strlen(dir) >= (size_t)len) {
388 #ifdef HAVE_PTHREAD_MUTEX_LOCK
389 		pthread_mutex_unlock(&mutex);
390 #endif
391 		return (-3);
392 	}
393 	strlcpy(name, dir, len);
394 #ifdef HAVE_PTHREAD_MUTEX_LOCK
395 	pthread_mutex_unlock(&mutex);
396 #endif
397 	return (ret);
398 }
399 
400 /*
401  * Return 1 if dist value is set to 'yes' or 'on'.
402  * Return 0 if dist value is set to something else.
403  * Return negative value on error.
404  */
405 int
406 getacdist(void)
407 {
408 	char *str;
409 	int ret;
410 
411 #ifdef HAVE_PTHREAD_MUTEX_LOCK
412 	pthread_mutex_lock(&mutex);
413 #endif
414 	setac_locked();
415 	if (getstrfromtype_locked(DIST_CONTROL_ENTRY, &str) < 0) {
416 #ifdef HAVE_PTHREAD_MUTEX_LOCK
417 		pthread_mutex_unlock(&mutex);
418 #endif
419 		return (-2);
420 	}
421 	if (str == NULL) {
422 #ifdef HAVE_PTHREAD_MUTEX_LOCK
423 		pthread_mutex_unlock(&mutex);
424 #endif
425 		return (0);
426 	}
427 	if (strcasecmp(str, "on") == 0 || strcasecmp(str, "yes") == 0)
428 		ret = 1;
429 	else
430 		ret = 0;
431 #ifdef HAVE_PTHREAD_MUTEX_LOCK
432 	pthread_mutex_unlock(&mutex);
433 #endif
434 	return (ret);
435 }
436 
437 /*
438  * Return the minimum free diskspace value from the audit control file.
439  */
440 int
441 getacmin(int *min_val)
442 {
443 	char *min;
444 
445 #ifdef HAVE_PTHREAD_MUTEX_LOCK
446 	pthread_mutex_lock(&mutex);
447 #endif
448 	setac_locked();
449 	if (getstrfromtype_locked(MINFREE_CONTROL_ENTRY, &min) < 0) {
450 #ifdef HAVE_PTHREAD_MUTEX_LOCK
451 		pthread_mutex_unlock(&mutex);
452 #endif
453 		return (-2);
454 	}
455 	if (min == NULL) {
456 #ifdef HAVE_PTHREAD_MUTEX_LOCK
457 		pthread_mutex_unlock(&mutex);
458 #endif
459 		return (-1);
460 	}
461 	*min_val = atoi(min);
462 #ifdef HAVE_PTHREAD_MUTEX_LOCK
463 	pthread_mutex_unlock(&mutex);
464 #endif
465 	return (0);
466 }
467 
468 /*
469  * Return the desired trail rotation size from the audit control file.
470  */
471 int
472 getacfilesz(size_t *filesz_val)
473 {
474 	char *str;
475 	size_t val;
476 	char mult;
477 	int nparsed;
478 
479 #ifdef HAVE_PTHREAD_MUTEX_LOCK
480 	pthread_mutex_lock(&mutex);
481 #endif
482 	setac_locked();
483 	if (getstrfromtype_locked(FILESZ_CONTROL_ENTRY, &str) < 0) {
484 #ifdef HAVE_PTHREAD_MUTEX_LOCK
485 		pthread_mutex_unlock(&mutex);
486 #endif
487 		return (-2);
488 	}
489 	if (str == NULL) {
490 #ifdef HAVE_PTHREAD_MUTEX_LOCK
491 		pthread_mutex_unlock(&mutex);
492 #endif
493 		errno = EINVAL;
494 		return (-1);
495 	}
496 
497 	/* Trim off any leading white space. */
498 	while (*str == ' ' || *str == '\t')
499 		str++;
500 
501 	nparsed = sscanf(str, "%ju%c", (uintmax_t *)&val, &mult);
502 
503 	switch (nparsed) {
504 	case 1:
505 		/* If no multiplier then assume 'B' (bytes). */
506 		mult = 'B';
507 		/* fall through */
508 	case 2:
509 		if (au_spacetobytes(filesz_val, val, mult) == 0)
510 			break;
511 		/* fall through */
512 	default:
513 		errno = EINVAL;
514 #ifdef HAVE_PTHREAD_MUTEX_LOCK
515 		pthread_mutex_unlock(&mutex);
516 #endif
517 		return (-1);
518 	}
519 
520 	/*
521 	 * The file size must either be 0 or >= MIN_AUDIT_FILE_SIZE.  0
522 	 * indicates no rotation size.
523 	 */
524 	if (*filesz_val < 0 || (*filesz_val > 0 &&
525 		*filesz_val < MIN_AUDIT_FILE_SIZE)) {
526 #ifdef HAVE_PTHREAD_MUTEX_LOCK
527 		pthread_mutex_unlock(&mutex);
528 #endif
529 		filesz_val = 0L;
530 		errno = EINVAL;
531 		return (-1);
532 	}
533 #ifdef HAVE_PTHREAD_MUTEX_LOCK
534 	pthread_mutex_unlock(&mutex);
535 #endif
536 	return (0);
537 }
538 
539 static int
540 getaccommon(const char *name, char *auditstr, int len)
541 {
542 	char *str;
543 
544 #ifdef HAVE_PTHREAD_MUTEX_LOCK
545 	pthread_mutex_lock(&mutex);
546 #endif
547 	setac_locked();
548 	if (getstrfromtype_locked(name, &str) < 0) {
549 #ifdef HAVE_PTHREAD_MUTEX_LOCK
550 		pthread_mutex_unlock(&mutex);
551 #endif
552 		return (-2);
553 	}
554 	if (str == NULL) {
555 #ifdef HAVE_PTHREAD_MUTEX_LOCK
556 		pthread_mutex_unlock(&mutex);
557 #endif
558 		return (-1);
559 	}
560 	if (strlen(str) >= (size_t)len) {
561 #ifdef HAVE_PTHREAD_MUTEX_LOCK
562 		pthread_mutex_unlock(&mutex);
563 #endif
564 		return (-3);
565 	}
566 	strlcpy(auditstr, str, len);
567 #ifdef HAVE_PTHREAD_MUTEX_LOCK
568 	pthread_mutex_unlock(&mutex);
569 #endif
570 	return (0);
571 }
572 
573 /*
574  * Return the system audit value from the audit contol file.
575  */
576 int
577 getacflg(char *auditstr, int len)
578 {
579 
580 	return (getaccommon(FLAGS_CONTROL_ENTRY, auditstr, len));
581 }
582 
583 /*
584  * Return the non attributable flags from the audit contol file.
585  */
586 int
587 getacna(char *auditstr, int len)
588 {
589 
590 	return (getaccommon(NA_CONTROL_ENTRY, auditstr, len));
591 }
592 
593 /*
594  * Return the policy field from the audit control file.
595  */
596 int
597 getacpol(char *auditstr, size_t len)
598 {
599 
600 	return (getaccommon(POLICY_CONTROL_ENTRY, auditstr, len));
601 }
602 
603 int
604 getachost(char *auditstr, size_t len)
605 {
606 
607 	return (getaccommon(HOST_CONTROL_ENTRY, auditstr, len));
608 }
609 
610 /*
611  * Set expiration conditions.
612  */
613 static int
614 setexpirecond(time_t *age, size_t *size, u_long value, char mult)
615 {
616 
617 	if (isupper(mult) || ' ' == mult)
618 		return (au_spacetobytes(size, value, mult));
619 	else
620 		return (au_timetosec(age, value, mult));
621 }
622 
623 /*
624  * Return the expire-after field from the audit control file.
625  */
626 int
627 getacexpire(int *andflg, time_t *age, size_t *size)
628 {
629 	char *str;
630 	int nparsed;
631 	u_long val1, val2;
632 	char mult1, mult2;
633 	char andor[AU_LINE_MAX];
634 
635 	*age = 0L;
636 	*size = 0LL;
637 	*andflg = 0;
638 
639 #ifdef HAVE_PTHREAD_MUTEX_LOCK
640 	pthread_mutex_lock(&mutex);
641 #endif
642 	setac_locked();
643 	if (getstrfromtype_locked(EXPIRE_AFTER_CONTROL_ENTRY, &str) < 0) {
644 #ifdef HAVE_PTHREAD_MUTEX_LOCK
645 		pthread_mutex_unlock(&mutex);
646 #endif
647 		return (-2);
648 	}
649 	if (str == NULL) {
650 #ifdef HAVE_PTHREAD_MUTEX_LOCK
651 		pthread_mutex_unlock(&mutex);
652 #endif
653 		return (-1);
654 	}
655 
656 	/* First, trim off any leading white space. */
657 	while (*str == ' ' || *str == '\t')
658 		str++;
659 
660 	nparsed = sscanf(str, "%lu%c%[ \tadnorADNOR]%lu%c", &val1, &mult1,
661 	    andor, &val2, &mult2);
662 
663 	switch (nparsed) {
664 	case 1:
665 		/* If no multiplier then assume 'B' (Bytes). */
666 		mult1 = 'B';
667 		/* fall through */
668 	case 2:
669 		/* One expiration condition. */
670 		if (setexpirecond(age, size, val1, mult1) != 0) {
671 #ifdef HAVE_PTHREAD_MUTEX_LOCK
672 			pthread_mutex_unlock(&mutex);
673 #endif
674 			return (-1);
675 		}
676 		break;
677 
678 	case 5:
679 		/* Two expiration conditions. */
680 		if (setexpirecond(age, size, val1, mult1) != 0 ||
681 		    setexpirecond(age, size, val2, mult2) != 0) {
682 #ifdef HAVE_PTHREAD_MUTEX_LOCK
683 			pthread_mutex_unlock(&mutex);
684 #endif
685 			return (-1);
686 		}
687 		if (strcasestr(andor, "and") != NULL)
688 			*andflg = 1;
689 		else if (strcasestr(andor, "or") != NULL)
690 			*andflg = 0;
691 		else {
692 #ifdef HAVE_PTHREAD_MUTEX_LOCK
693 			pthread_mutex_unlock(&mutex);
694 #endif
695 			return (-1);
696 		}
697 		break;
698 
699 	default:
700 #ifdef HAVE_PTHREAD_MUTEX_LOCK
701 		pthread_mutex_unlock(&mutex);
702 #endif
703 		return (-1);
704 	}
705 
706 #ifdef HAVE_PTHREAD_MUTEX_LOCK
707 	pthread_mutex_unlock(&mutex);
708 #endif
709 	return (0);
710 }
711