bsm_control.c revision 293161
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 */
58static FILE	*fp = NULL;
59static char	linestr[AU_LINE_MAX];
60static char	*delim = ":";
61
62static char	inacdir = 0;
63static char	ptrmoved = 0;
64
65#ifdef HAVE_PTHREAD_MUTEX_LOCK
66static pthread_mutex_t	mutex = PTHREAD_MUTEX_INITIALIZER;
67#endif
68
69/*
70 * Audit policy string token table for au_poltostr() and au_strtopol().
71 */
72struct audit_polstr {
73	long		 ap_policy;
74	const char	*ap_str;
75};
76
77static 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 */
101static int
102getstrfromtype_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 */
150static int
151au_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 */
188static int
189au_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 */
227ssize_t
228au_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 */
255int
256au_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 */
292static void
293setac_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;
308closefp:
309			fclose(fp);
310			fp = NULL;
311			return;
312		}
313
314		fseek(fp, 0, SEEK_SET);
315	}
316}
317
318void
319setac(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 */
334void
335endac(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 */
354int
355getacdir(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 */
403int
404getacdist(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 */
438int
439getacmin(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 */
469int
470getacfilesz(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
537static int
538getaccommon(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 */
574int
575getacflg(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 */
584int
585getacna(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 */
594int
595getacpol(char *auditstr, size_t len)
596{
597
598	return (getaccommon(POLICY_CONTROL_ENTRY, auditstr, len));
599}
600
601int
602getachost(char *auditstr, size_t len)
603{
604
605	return (getaccommon(HOST_CONTROL_ENTRY, auditstr, len));
606}
607
608/*
609 * Set expiration conditions.
610 */
611static int
612setexpirecond(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 */
624int
625getacexpire(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