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