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 */
60static FILE	*fp = NULL;
61static char	linestr[AU_LINE_MAX];
62static char	*delim = ":";
63
64static char	inacdir = 0;
65static char	ptrmoved = 0;
66
67#ifdef HAVE_PTHREAD_MUTEX_LOCK
68static pthread_mutex_t	mutex = PTHREAD_MUTEX_INITIALIZER;
69#endif
70
71/*
72 * Audit policy string token table for au_poltostr() and au_strtopol().
73 */
74struct audit_polstr {
75	long		 ap_policy;
76	const char	*ap_str;
77};
78
79static 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 */
103static int
104getstrfromtype_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 */
152static int
153au_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 */
190static int
191au_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 */
229ssize_t
230au_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 */
257int
258au_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 */
294static void
295setac_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;
310closefp:
311			fclose(fp);
312			fp = NULL;
313			return;
314		}
315
316		fseek(fp, 0, SEEK_SET);
317	}
318}
319
320void
321setac(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 */
336void
337endac(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 */
356int
357getacdir(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 */
405int
406getacdist(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 */
440int
441getacmin(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 */
471int
472getacfilesz(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
539static int
540getaccommon(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 */
576int
577getacflg(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 */
586int
587getacna(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 */
596int
597getacpol(char *auditstr, size_t len)
598{
599
600	return (getaccommon(POLICY_CONTROL_ENTRY, auditstr, len));
601}
602
603int
604getachost(char *auditstr, size_t len)
605{
606
607	return (getaccommon(HOST_CONTROL_ENTRY, auditstr, len));
608}
609
610/*
611 * Set expiration conditions.
612 */
613static int
614setexpirecond(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 */
626int
627getacexpire(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