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