1*0Sstevel@tonic-gate /*
2*0Sstevel@tonic-gate  * CDDL HEADER START
3*0Sstevel@tonic-gate  *
4*0Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*0Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
6*0Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
7*0Sstevel@tonic-gate  * with the License.
8*0Sstevel@tonic-gate  *
9*0Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*0Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
11*0Sstevel@tonic-gate  * See the License for the specific language governing permissions
12*0Sstevel@tonic-gate  * and limitations under the License.
13*0Sstevel@tonic-gate  *
14*0Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
15*0Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*0Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
17*0Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
18*0Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
19*0Sstevel@tonic-gate  *
20*0Sstevel@tonic-gate  * CDDL HEADER END
21*0Sstevel@tonic-gate  */
22*0Sstevel@tonic-gate /*
23*0Sstevel@tonic-gate  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24*0Sstevel@tonic-gate  * Use is subject to license terms.
25*0Sstevel@tonic-gate  */
26*0Sstevel@tonic-gate 
27*0Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
28*0Sstevel@tonic-gate 
29*0Sstevel@tonic-gate /*
30*0Sstevel@tonic-gate  * Interfaces to audit_class(5)  (/etc/security/audit_class)
31*0Sstevel@tonic-gate  */
32*0Sstevel@tonic-gate 
33*0Sstevel@tonic-gate #include <stdlib.h>
34*0Sstevel@tonic-gate #include <stdio.h>
35*0Sstevel@tonic-gate #include <limits.h>
36*0Sstevel@tonic-gate #include <sys/types.h>
37*0Sstevel@tonic-gate #include <bsm/audit.h>
38*0Sstevel@tonic-gate #include <bsm/libbsm.h>
39*0Sstevel@tonic-gate #include <string.h>
40*0Sstevel@tonic-gate #include <synch.h>
41*0Sstevel@tonic-gate 
42*0Sstevel@tonic-gate static char	au_class_fname[PATH_MAX] = AUDITCLASSFILE;
43*0Sstevel@tonic-gate static FILE	*au_class_file = NULL;
44*0Sstevel@tonic-gate static mutex_t	mutex_classfile = DEFAULTMUTEX;
45*0Sstevel@tonic-gate static mutex_t	mutex_classcache = DEFAULTMUTEX;
46*0Sstevel@tonic-gate 
47*0Sstevel@tonic-gate extern int _mutex_lock(mutex_t *);
48*0Sstevel@tonic-gate extern int _mutex_unlock(mutex_t *);
49*0Sstevel@tonic-gate 
50*0Sstevel@tonic-gate int
51*0Sstevel@tonic-gate #ifdef __STDC__
52*0Sstevel@tonic-gate setauclassfile(char *fname)
53*0Sstevel@tonic-gate #else
54*0Sstevel@tonic-gate setauclassfile(fname)
55*0Sstevel@tonic-gate 	char	*fname;
56*0Sstevel@tonic-gate #endif
57*0Sstevel@tonic-gate {
58*0Sstevel@tonic-gate 	_mutex_lock(&mutex_classfile);
59*0Sstevel@tonic-gate 	if (fname) {
60*0Sstevel@tonic-gate 		(void) strcpy(au_class_fname, fname);
61*0Sstevel@tonic-gate 	}
62*0Sstevel@tonic-gate 	_mutex_unlock(&mutex_classfile);
63*0Sstevel@tonic-gate 	return (0);
64*0Sstevel@tonic-gate }
65*0Sstevel@tonic-gate 
66*0Sstevel@tonic-gate 
67*0Sstevel@tonic-gate void
68*0Sstevel@tonic-gate setauclass()
69*0Sstevel@tonic-gate {
70*0Sstevel@tonic-gate 	_mutex_lock(&mutex_classfile);
71*0Sstevel@tonic-gate 	if (au_class_file) {
72*0Sstevel@tonic-gate 		(void) fseek(au_class_file, 0L, 0);
73*0Sstevel@tonic-gate 	}
74*0Sstevel@tonic-gate 	_mutex_unlock(&mutex_classfile);
75*0Sstevel@tonic-gate }
76*0Sstevel@tonic-gate 
77*0Sstevel@tonic-gate 
78*0Sstevel@tonic-gate void
79*0Sstevel@tonic-gate endauclass()
80*0Sstevel@tonic-gate {
81*0Sstevel@tonic-gate 	_mutex_lock(&mutex_classfile);
82*0Sstevel@tonic-gate 	if (au_class_file) {
83*0Sstevel@tonic-gate 		(void) fclose(au_class_file);
84*0Sstevel@tonic-gate 		au_class_file = NULL;
85*0Sstevel@tonic-gate 	}
86*0Sstevel@tonic-gate 	_mutex_unlock(&mutex_classfile);
87*0Sstevel@tonic-gate }
88*0Sstevel@tonic-gate 
89*0Sstevel@tonic-gate /*
90*0Sstevel@tonic-gate  * getauclassent():
91*0Sstevel@tonic-gate  *	This is not MT-safe because of the static variables.
92*0Sstevel@tonic-gate  */
93*0Sstevel@tonic-gate au_class_ent_t *
94*0Sstevel@tonic-gate getauclassent()
95*0Sstevel@tonic-gate {
96*0Sstevel@tonic-gate 	static au_class_ent_t e;
97*0Sstevel@tonic-gate 	static char	cname[AU_CLASS_NAME_MAX];
98*0Sstevel@tonic-gate 	static char	cdesc[AU_CLASS_DESC_MAX];
99*0Sstevel@tonic-gate 
100*0Sstevel@tonic-gate 	e.ac_name = cname;
101*0Sstevel@tonic-gate 	e.ac_desc = cdesc;
102*0Sstevel@tonic-gate 
103*0Sstevel@tonic-gate 	return (getauclassent_r(&e));
104*0Sstevel@tonic-gate }
105*0Sstevel@tonic-gate 
106*0Sstevel@tonic-gate /*
107*0Sstevel@tonic-gate  * getauclassent_r
108*0Sstevel@tonic-gate  *	This is MT-safe if each thread passes in its own pointer
109*0Sstevel@tonic-gate  *	to the space where the class entry is returned.  Becareful
110*0Sstevel@tonic-gate  *	to also allocate space from the cname and cdesc pointers
111*0Sstevel@tonic-gate  *	in the au_class_ent structure.
112*0Sstevel@tonic-gate  */
113*0Sstevel@tonic-gate au_class_ent_t *
114*0Sstevel@tonic-gate getauclassent_r(au_class_entry)
115*0Sstevel@tonic-gate 	au_class_ent_t *au_class_entry;
116*0Sstevel@tonic-gate {
117*0Sstevel@tonic-gate 	int	i, error = 0, found = 0;
118*0Sstevel@tonic-gate 	char	*s, input[256];
119*0Sstevel@tonic-gate 	unsigned long v;
120*0Sstevel@tonic-gate 
121*0Sstevel@tonic-gate 	if (au_class_entry == (au_class_ent_t *)NULL ||
122*0Sstevel@tonic-gate 		au_class_entry->ac_name == (char *)NULL ||
123*0Sstevel@tonic-gate 		au_class_entry->ac_desc == (char *)NULL) {
124*0Sstevel@tonic-gate 			return ((au_class_ent_t *)NULL);
125*0Sstevel@tonic-gate 	}
126*0Sstevel@tonic-gate 
127*0Sstevel@tonic-gate 	/* open audit class file if it isn't already */
128*0Sstevel@tonic-gate 	_mutex_lock(&mutex_classfile);
129*0Sstevel@tonic-gate 	if (!au_class_file) {
130*0Sstevel@tonic-gate 		if (!(au_class_file = fopen(au_class_fname, "r"))) {
131*0Sstevel@tonic-gate 			_mutex_unlock(&mutex_classfile);
132*0Sstevel@tonic-gate 			return ((au_class_ent_t *)0);
133*0Sstevel@tonic-gate 		}
134*0Sstevel@tonic-gate 	}
135*0Sstevel@tonic-gate 
136*0Sstevel@tonic-gate 	while (fgets(input, 256, au_class_file)) {
137*0Sstevel@tonic-gate 		if (input[0] != '#') {
138*0Sstevel@tonic-gate 			s = input + strspn(input, " \t\r\n");
139*0Sstevel@tonic-gate 			if ((*s == '\0') || (*s == '#')) {
140*0Sstevel@tonic-gate 				continue;
141*0Sstevel@tonic-gate 			}
142*0Sstevel@tonic-gate 			found = 1;
143*0Sstevel@tonic-gate 			s = input;
144*0Sstevel@tonic-gate 
145*0Sstevel@tonic-gate 			/* parse bitfield */
146*0Sstevel@tonic-gate 			i = strcspn(s, ":");
147*0Sstevel@tonic-gate 			s[i] = '\0';
148*0Sstevel@tonic-gate 			if (strncmp(s, "0x", 2) == 0) {
149*0Sstevel@tonic-gate 				(void) sscanf(&s[2], "%lx", &v);
150*0Sstevel@tonic-gate 			} else {
151*0Sstevel@tonic-gate 				(void) sscanf(s, "%lu", &v);
152*0Sstevel@tonic-gate 			}
153*0Sstevel@tonic-gate 			au_class_entry->ac_class = v;
154*0Sstevel@tonic-gate 			s = &s[i+1];
155*0Sstevel@tonic-gate 
156*0Sstevel@tonic-gate 			/* parse class name */
157*0Sstevel@tonic-gate 			i = strcspn(s, ":");
158*0Sstevel@tonic-gate 			s[i] = '\0';
159*0Sstevel@tonic-gate 			(void) strncpy(au_class_entry->ac_name, s,
160*0Sstevel@tonic-gate 			    AU_CLASS_NAME_MAX);
161*0Sstevel@tonic-gate 			s = &s[i+1];
162*0Sstevel@tonic-gate 
163*0Sstevel@tonic-gate 			/* parse class description */
164*0Sstevel@tonic-gate 			i = strcspn(s, "\n\0");
165*0Sstevel@tonic-gate 			s[i] = '\0';
166*0Sstevel@tonic-gate 			(void) strncpy(au_class_entry->ac_desc, s,
167*0Sstevel@tonic-gate 			    AU_CLASS_DESC_MAX);
168*0Sstevel@tonic-gate 
169*0Sstevel@tonic-gate 			break;
170*0Sstevel@tonic-gate 		}
171*0Sstevel@tonic-gate 	}
172*0Sstevel@tonic-gate 
173*0Sstevel@tonic-gate 	_mutex_unlock(&mutex_classfile);
174*0Sstevel@tonic-gate 
175*0Sstevel@tonic-gate 	if (!error && found) {
176*0Sstevel@tonic-gate 		return (au_class_entry);
177*0Sstevel@tonic-gate 	} else {
178*0Sstevel@tonic-gate 		return ((au_class_ent_t *)0);
179*0Sstevel@tonic-gate 	}
180*0Sstevel@tonic-gate }
181*0Sstevel@tonic-gate 
182*0Sstevel@tonic-gate 
183*0Sstevel@tonic-gate au_class_ent_t *
184*0Sstevel@tonic-gate #ifdef __STDC__
185*0Sstevel@tonic-gate getauclassnam(char *name)
186*0Sstevel@tonic-gate #else
187*0Sstevel@tonic-gate getauclassnam(name)
188*0Sstevel@tonic-gate 	char *name;
189*0Sstevel@tonic-gate #endif
190*0Sstevel@tonic-gate {
191*0Sstevel@tonic-gate 	static au_class_ent_t e;
192*0Sstevel@tonic-gate 	static char	cname[AU_CLASS_NAME_MAX];
193*0Sstevel@tonic-gate 	static char	cdesc[AU_CLASS_DESC_MAX];
194*0Sstevel@tonic-gate 
195*0Sstevel@tonic-gate 	e.ac_name = cname;
196*0Sstevel@tonic-gate 	e.ac_desc = cdesc;
197*0Sstevel@tonic-gate 
198*0Sstevel@tonic-gate 	return (getauclassnam_r(&e, name));
199*0Sstevel@tonic-gate }
200*0Sstevel@tonic-gate 
201*0Sstevel@tonic-gate au_class_ent_t *
202*0Sstevel@tonic-gate #ifdef __STDC__
203*0Sstevel@tonic-gate getauclassnam_r(au_class_ent_t *e, char *name)
204*0Sstevel@tonic-gate #else
205*0Sstevel@tonic-gate getauclassnam_r()
206*0Sstevel@tonic-gate 	au_class_ent_t *e;
207*0Sstevel@tonic-gate 	char *name;
208*0Sstevel@tonic-gate #endif
209*0Sstevel@tonic-gate {
210*0Sstevel@tonic-gate 	while (getauclassent_r(e) != NULL) {
211*0Sstevel@tonic-gate 		if (strcmp(e->ac_name, name) == 0) {
212*0Sstevel@tonic-gate 			return (e);
213*0Sstevel@tonic-gate 		}
214*0Sstevel@tonic-gate 	}
215*0Sstevel@tonic-gate 	return ((au_class_ent_t *)NULL);
216*0Sstevel@tonic-gate }
217*0Sstevel@tonic-gate 
218*0Sstevel@tonic-gate 
219*0Sstevel@tonic-gate /*
220*0Sstevel@tonic-gate  * xcacheauclass:
221*0Sstevel@tonic-gate  *	Read the entire audit_class file into memory.
222*0Sstevel@tonic-gate  *	Return a pointer to the requested entry in the cache
223*0Sstevel@tonic-gate  *	or a pointer to an invalid entry if the the class
224*0Sstevel@tonic-gate  *	requested is not known.
225*0Sstevel@tonic-gate  *
226*0Sstevel@tonic-gate  *	Return < 0, do not set result pointer, if error.
227*0Sstevel@tonic-gate  *	Return   0, set result pointer to invalid entry, if class not in cache.
228*0Sstevel@tonic-gate  *	Return   1, set result pointer to a valid entry, if class is in cache.
229*0Sstevel@tonic-gate  */
230*0Sstevel@tonic-gate static int
231*0Sstevel@tonic-gate xcacheauclass(result, class_name, class_no, flags)
232*0Sstevel@tonic-gate 	au_class_ent_t **result; /* set this pointer to an entry in the cache */
233*0Sstevel@tonic-gate 	char	*class_name; /* name of class to look up */
234*0Sstevel@tonic-gate 	au_class_t class_no;
235*0Sstevel@tonic-gate 	int	flags;
236*0Sstevel@tonic-gate {
237*0Sstevel@tonic-gate 	static int	invalid;
238*0Sstevel@tonic-gate 	static au_class_ent_t **class_tbl;
239*0Sstevel@tonic-gate 	static int	called_once;
240*0Sstevel@tonic-gate 	static int	lines = 0;
241*0Sstevel@tonic-gate 
242*0Sstevel@tonic-gate 	char		line[256];
243*0Sstevel@tonic-gate 	FILE		*fp;
244*0Sstevel@tonic-gate 	au_class_ent_t	*p_class;
245*0Sstevel@tonic-gate 	int		i;
246*0Sstevel@tonic-gate 	int		hit = 0;
247*0Sstevel@tonic-gate 	char		*s;
248*0Sstevel@tonic-gate 
249*0Sstevel@tonic-gate 	_mutex_lock(&mutex_classcache);
250*0Sstevel@tonic-gate 	if (called_once == 0) {
251*0Sstevel@tonic-gate 
252*0Sstevel@tonic-gate 		/* Count number of lines in the class file */
253*0Sstevel@tonic-gate 		if ((fp = fopen(au_class_fname, "r")) == NULL) {
254*0Sstevel@tonic-gate 			_mutex_unlock(&mutex_classcache);
255*0Sstevel@tonic-gate 			return (-1);
256*0Sstevel@tonic-gate 		}
257*0Sstevel@tonic-gate 		while (fgets(line, 256, fp) != NULL) {
258*0Sstevel@tonic-gate 			s = line + strspn(line, " \t\r\n");
259*0Sstevel@tonic-gate 			if ((*s == '\0') || (*s == '#')) {
260*0Sstevel@tonic-gate 				continue;
261*0Sstevel@tonic-gate 			}
262*0Sstevel@tonic-gate 			lines++;
263*0Sstevel@tonic-gate 		}
264*0Sstevel@tonic-gate 		(void) fclose(fp);
265*0Sstevel@tonic-gate 		class_tbl = (au_class_ent_t **)calloc((size_t)lines + 1,
266*0Sstevel@tonic-gate 			sizeof (au_class_ent_t));
267*0Sstevel@tonic-gate 		if (class_tbl == NULL) {
268*0Sstevel@tonic-gate 			_mutex_unlock(&mutex_classcache);
269*0Sstevel@tonic-gate 			return (-2);
270*0Sstevel@tonic-gate 		}
271*0Sstevel@tonic-gate 
272*0Sstevel@tonic-gate 		lines = 0;
273*0Sstevel@tonic-gate 		setauclass();
274*0Sstevel@tonic-gate 		/*
275*0Sstevel@tonic-gate 		 * This call to getauclassent is protected by
276*0Sstevel@tonic-gate 		 * mutex_classcache, so we don't need to use the thread-
277*0Sstevel@tonic-gate 		 * safe version (getauclassent_r).
278*0Sstevel@tonic-gate 		 */
279*0Sstevel@tonic-gate 		while ((p_class = getauclassent()) != NULL) {
280*0Sstevel@tonic-gate 			class_tbl[lines] = (au_class_ent_t *)
281*0Sstevel@tonic-gate 				malloc(sizeof (au_class_ent_t));
282*0Sstevel@tonic-gate 			if (class_tbl[lines] == NULL) {
283*0Sstevel@tonic-gate 				_mutex_unlock(&mutex_classcache);
284*0Sstevel@tonic-gate 				return (-3);
285*0Sstevel@tonic-gate 			}
286*0Sstevel@tonic-gate 			class_tbl[lines]->ac_name = strdup(p_class->ac_name);
287*0Sstevel@tonic-gate 			class_tbl[lines]->ac_class = p_class->ac_class;
288*0Sstevel@tonic-gate 			class_tbl[lines]->ac_desc = strdup(p_class->ac_desc);
289*0Sstevel@tonic-gate #ifdef DEBUG2
290*0Sstevel@tonic-gate 			printclass(class_tbl[lines]);
291*0Sstevel@tonic-gate #endif
292*0Sstevel@tonic-gate 			lines++;
293*0Sstevel@tonic-gate 		}
294*0Sstevel@tonic-gate 		endauclass();
295*0Sstevel@tonic-gate 		invalid = lines;
296*0Sstevel@tonic-gate 		class_tbl[invalid] = (au_class_ent_t *)
297*0Sstevel@tonic-gate 			malloc(sizeof (au_class_ent_t));
298*0Sstevel@tonic-gate 		if (class_tbl[invalid] == NULL) {
299*0Sstevel@tonic-gate 			_mutex_unlock(&mutex_classcache);
300*0Sstevel@tonic-gate 			return (-4);
301*0Sstevel@tonic-gate 		}
302*0Sstevel@tonic-gate 		class_tbl[invalid]->ac_name = "invalid class";
303*0Sstevel@tonic-gate 		class_tbl[invalid]->ac_class = 0;
304*0Sstevel@tonic-gate 		class_tbl[invalid]->ac_desc = class_tbl[invalid]->ac_name;
305*0Sstevel@tonic-gate 
306*0Sstevel@tonic-gate 		called_once = 1;
307*0Sstevel@tonic-gate 
308*0Sstevel@tonic-gate #ifdef DEBUG2
309*0Sstevel@tonic-gate 		for (i = 0; i <= lines; i++) {
310*0Sstevel@tonic-gate 			printclass(class_tbl[i]);
311*0Sstevel@tonic-gate 		}
312*0Sstevel@tonic-gate #endif
313*0Sstevel@tonic-gate 
314*0Sstevel@tonic-gate 	} /* END if called_once */
315*0Sstevel@tonic-gate 	*result = class_tbl[invalid];
316*0Sstevel@tonic-gate 	if (flags & AU_CACHE_NAME) {
317*0Sstevel@tonic-gate 		for (i = 0; i < lines; i++) {
318*0Sstevel@tonic-gate 			if (strcmp(class_name, class_tbl[i]->ac_name) == 0) {
319*0Sstevel@tonic-gate 				*result = class_tbl[i];
320*0Sstevel@tonic-gate 				hit = 1;
321*0Sstevel@tonic-gate 				break;
322*0Sstevel@tonic-gate 			}
323*0Sstevel@tonic-gate 		}
324*0Sstevel@tonic-gate 	} else if (flags & AU_CACHE_NUMBER) {
325*0Sstevel@tonic-gate 		for (i = 0; i < lines; i++) {
326*0Sstevel@tonic-gate 			if (class_no == class_tbl[i]->ac_class) {
327*0Sstevel@tonic-gate 				*result = class_tbl[i];
328*0Sstevel@tonic-gate 				hit = 1;
329*0Sstevel@tonic-gate 				break;
330*0Sstevel@tonic-gate 			}
331*0Sstevel@tonic-gate 		}
332*0Sstevel@tonic-gate 	}
333*0Sstevel@tonic-gate 	_mutex_unlock(&mutex_classcache);
334*0Sstevel@tonic-gate 	return (hit);
335*0Sstevel@tonic-gate }
336*0Sstevel@tonic-gate 
337*0Sstevel@tonic-gate 
338*0Sstevel@tonic-gate int
339*0Sstevel@tonic-gate #ifdef __STDC__
340*0Sstevel@tonic-gate cacheauclass(au_class_ent_t **result, au_class_t class_no)
341*0Sstevel@tonic-gate #else
342*0Sstevel@tonic-gate cacheauclass(result, class_no)
343*0Sstevel@tonic-gate 	au_class_ent_t **result; /* set this pointer to an entry in the cache */
344*0Sstevel@tonic-gate 	au_class_t class_no;
345*0Sstevel@tonic-gate #endif
346*0Sstevel@tonic-gate {
347*0Sstevel@tonic-gate 	return (xcacheauclass(result, "", class_no, AU_CACHE_NUMBER));
348*0Sstevel@tonic-gate }
349*0Sstevel@tonic-gate 
350*0Sstevel@tonic-gate 
351*0Sstevel@tonic-gate int
352*0Sstevel@tonic-gate #ifdef __STDC__
353*0Sstevel@tonic-gate cacheauclassnam(au_class_ent_t **result, char *class_name)
354*0Sstevel@tonic-gate #else
355*0Sstevel@tonic-gate cacheauclassnam(result, class_name)
356*0Sstevel@tonic-gate 	au_class_ent_t **result; /* set this pointer to an entry in the cache */
357*0Sstevel@tonic-gate 	char	*class_name;
358*0Sstevel@tonic-gate #endif
359*0Sstevel@tonic-gate {
360*0Sstevel@tonic-gate 	return (xcacheauclass(result, class_name, (au_class_t)0,
361*0Sstevel@tonic-gate 		AU_CACHE_NAME));
362*0Sstevel@tonic-gate }
363*0Sstevel@tonic-gate 
364*0Sstevel@tonic-gate 
365*0Sstevel@tonic-gate #ifdef DEBUG2
366*0Sstevel@tonic-gate void
367*0Sstevel@tonic-gate printclass(p_c)
368*0Sstevel@tonic-gate au_class_ent_t *p_c;
369*0Sstevel@tonic-gate {
370*0Sstevel@tonic-gate 	printf("%x:%s:%s\n", p_c->ac_class, p_c->ac_name, p_c->ac_desc);
371*0Sstevel@tonic-gate 	fflush(stdout);
372*0Sstevel@tonic-gate }
373*0Sstevel@tonic-gate 
374*0Sstevel@tonic-gate 
375*0Sstevel@tonic-gate #endif
376