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