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