xref: /illumos-gate/usr/src/lib/libbsm/common/audit_class.c (revision cb6207858a9fcc2feaee22e626912fba281ac969)
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 void
50 setauclass()
51 {
52 	_mutex_lock(&mutex_classfile);
53 	if (au_class_file) {
54 		(void) fseek(au_class_file, 0L, 0);
55 	}
56 	_mutex_unlock(&mutex_classfile);
57 }
58 
59 
60 void
61 endauclass()
62 {
63 	_mutex_lock(&mutex_classfile);
64 	if (au_class_file) {
65 		(void) fclose(au_class_file);
66 		au_class_file = NULL;
67 	}
68 	_mutex_unlock(&mutex_classfile);
69 }
70 
71 /*
72  * getauclassent():
73  *	This is not MT-safe because of the static variables.
74  */
75 au_class_ent_t *
76 getauclassent()
77 {
78 	static au_class_ent_t e;
79 	static char	cname[AU_CLASS_NAME_MAX];
80 	static char	cdesc[AU_CLASS_DESC_MAX];
81 
82 	e.ac_name = cname;
83 	e.ac_desc = cdesc;
84 
85 	return (getauclassent_r(&e));
86 }
87 
88 /*
89  * getauclassent_r
90  *	This is MT-safe if each thread passes in its own pointer
91  *	to the space where the class entry is returned.  Becareful
92  *	to also allocate space from the cname and cdesc pointers
93  *	in the au_class_ent structure.
94  */
95 au_class_ent_t *
96 getauclassent_r(au_class_entry)
97 	au_class_ent_t *au_class_entry;
98 {
99 	int	i, error = 0, found = 0;
100 	char	*s, input[256];
101 	unsigned long v;
102 
103 	if (au_class_entry == (au_class_ent_t *)NULL ||
104 		au_class_entry->ac_name == (char *)NULL ||
105 		au_class_entry->ac_desc == (char *)NULL) {
106 			return ((au_class_ent_t *)NULL);
107 	}
108 
109 	/* open audit class file if it isn't already */
110 	_mutex_lock(&mutex_classfile);
111 	if (!au_class_file) {
112 		if (!(au_class_file = fopen(au_class_fname, "rF"))) {
113 			_mutex_unlock(&mutex_classfile);
114 			return ((au_class_ent_t *)0);
115 		}
116 	}
117 
118 	while (fgets(input, 256, au_class_file)) {
119 		if (input[0] != '#') {
120 			s = input + strspn(input, " \t\r\n");
121 			if ((*s == '\0') || (*s == '#')) {
122 				continue;
123 			}
124 			found = 1;
125 			s = input;
126 
127 			/* parse bitfield */
128 			i = strcspn(s, ":");
129 			s[i] = '\0';
130 			if (strncmp(s, "0x", 2) == 0) {
131 				(void) sscanf(&s[2], "%lx", &v);
132 			} else {
133 				(void) sscanf(s, "%lu", &v);
134 			}
135 			au_class_entry->ac_class = v;
136 			s = &s[i+1];
137 
138 			/* parse class name */
139 			i = strcspn(s, ":");
140 			s[i] = '\0';
141 			(void) strncpy(au_class_entry->ac_name, s,
142 			    AU_CLASS_NAME_MAX);
143 			s = &s[i+1];
144 
145 			/* parse class description */
146 			i = strcspn(s, "\n\0");
147 			s[i] = '\0';
148 			(void) strncpy(au_class_entry->ac_desc, s,
149 			    AU_CLASS_DESC_MAX);
150 
151 			break;
152 		}
153 	}
154 
155 	_mutex_unlock(&mutex_classfile);
156 
157 	if (!error && found) {
158 		return (au_class_entry);
159 	} else {
160 		return ((au_class_ent_t *)0);
161 	}
162 }
163 
164 
165 au_class_ent_t *
166 #ifdef __STDC__
167 getauclassnam(char *name)
168 #else
169 getauclassnam(name)
170 	char *name;
171 #endif
172 {
173 	static au_class_ent_t e;
174 	static char	cname[AU_CLASS_NAME_MAX];
175 	static char	cdesc[AU_CLASS_DESC_MAX];
176 
177 	e.ac_name = cname;
178 	e.ac_desc = cdesc;
179 
180 	return (getauclassnam_r(&e, name));
181 }
182 
183 au_class_ent_t *
184 #ifdef __STDC__
185 getauclassnam_r(au_class_ent_t *e, char *name)
186 #else
187 getauclassnam_r()
188 	au_class_ent_t *e;
189 	char *name;
190 #endif
191 {
192 	while (getauclassent_r(e) != NULL) {
193 		if (strcmp(e->ac_name, name) == 0) {
194 			return (e);
195 		}
196 	}
197 	return ((au_class_ent_t *)NULL);
198 }
199 
200 
201 /*
202  * xcacheauclass:
203  *	Read the entire audit_class file into memory.
204  *	Return a pointer to the requested entry in the cache
205  *	or a pointer to an invalid entry if the the class
206  *	requested is not known.
207  *
208  *	Return < 0, do not set result pointer, if error.
209  *	Return   0, set result pointer to invalid entry, if class not in cache.
210  *	Return   1, set result pointer to a valid entry, if class is in cache.
211  */
212 static int
213 xcacheauclass(result, class_name, class_no, flags)
214 	au_class_ent_t **result; /* set this pointer to an entry in the cache */
215 	char	*class_name; /* name of class to look up */
216 	au_class_t class_no;
217 	int	flags;
218 {
219 	static int	invalid;
220 	static au_class_ent_t **class_tbl;
221 	static int	called_once;
222 	static int	lines = 0;
223 
224 	char		line[256];
225 	FILE		*fp;
226 	au_class_ent_t	*p_class;
227 	int		i;
228 	int		hit = 0;
229 	char		*s;
230 
231 	_mutex_lock(&mutex_classcache);
232 	if (called_once == 0) {
233 
234 		/* Count number of lines in the class file */
235 		if ((fp = fopen(au_class_fname, "rF")) == NULL) {
236 			_mutex_unlock(&mutex_classcache);
237 			return (-1);
238 		}
239 		while (fgets(line, 256, fp) != NULL) {
240 			s = line + strspn(line, " \t\r\n");
241 			if ((*s == '\0') || (*s == '#')) {
242 				continue;
243 			}
244 			lines++;
245 		}
246 		(void) fclose(fp);
247 		class_tbl = (au_class_ent_t **)calloc((size_t)lines + 1,
248 			sizeof (au_class_ent_t));
249 		if (class_tbl == NULL) {
250 			_mutex_unlock(&mutex_classcache);
251 			return (-2);
252 		}
253 
254 		lines = 0;
255 		setauclass();
256 		/*
257 		 * This call to getauclassent is protected by
258 		 * mutex_classcache, so we don't need to use the thread-
259 		 * safe version (getauclassent_r).
260 		 */
261 		while ((p_class = getauclassent()) != NULL) {
262 			class_tbl[lines] = (au_class_ent_t *)
263 				malloc(sizeof (au_class_ent_t));
264 			if (class_tbl[lines] == NULL) {
265 				_mutex_unlock(&mutex_classcache);
266 				return (-3);
267 			}
268 			class_tbl[lines]->ac_name = strdup(p_class->ac_name);
269 			class_tbl[lines]->ac_class = p_class->ac_class;
270 			class_tbl[lines]->ac_desc = strdup(p_class->ac_desc);
271 #ifdef DEBUG2
272 			printclass(class_tbl[lines]);
273 #endif
274 			lines++;
275 		}
276 		endauclass();
277 		invalid = lines;
278 		class_tbl[invalid] = (au_class_ent_t *)
279 			malloc(sizeof (au_class_ent_t));
280 		if (class_tbl[invalid] == NULL) {
281 			_mutex_unlock(&mutex_classcache);
282 			return (-4);
283 		}
284 		class_tbl[invalid]->ac_name = "invalid class";
285 		class_tbl[invalid]->ac_class = 0;
286 		class_tbl[invalid]->ac_desc = class_tbl[invalid]->ac_name;
287 
288 		called_once = 1;
289 
290 #ifdef DEBUG2
291 		for (i = 0; i <= lines; i++) {
292 			printclass(class_tbl[i]);
293 		}
294 #endif
295 
296 	} /* END if called_once */
297 	*result = class_tbl[invalid];
298 	if (flags & AU_CACHE_NAME) {
299 		for (i = 0; i < lines; i++) {
300 			if (strcmp(class_name, class_tbl[i]->ac_name) == 0) {
301 				*result = class_tbl[i];
302 				hit = 1;
303 				break;
304 			}
305 		}
306 	} else if (flags & AU_CACHE_NUMBER) {
307 		for (i = 0; i < lines; i++) {
308 			if (class_no == class_tbl[i]->ac_class) {
309 				*result = class_tbl[i];
310 				hit = 1;
311 				break;
312 			}
313 		}
314 	}
315 	_mutex_unlock(&mutex_classcache);
316 	return (hit);
317 }
318 
319 
320 int
321 #ifdef __STDC__
322 cacheauclass(au_class_ent_t **result, au_class_t class_no)
323 #else
324 cacheauclass(result, class_no)
325 	au_class_ent_t **result; /* set this pointer to an entry in the cache */
326 	au_class_t class_no;
327 #endif
328 {
329 	return (xcacheauclass(result, "", class_no, AU_CACHE_NUMBER));
330 }
331 
332 
333 int
334 #ifdef __STDC__
335 cacheauclassnam(au_class_ent_t **result, char *class_name)
336 #else
337 cacheauclassnam(result, class_name)
338 	au_class_ent_t **result; /* set this pointer to an entry in the cache */
339 	char	*class_name;
340 #endif
341 {
342 	return (xcacheauclass(result, class_name, (au_class_t)0,
343 		AU_CACHE_NAME));
344 }
345 
346 
347 #ifdef DEBUG2
348 void
349 printclass(p_c)
350 au_class_ent_t *p_c;
351 {
352 	printf("%x:%s:%s\n", p_c->ac_class, p_c->ac_name, p_c->ac_desc);
353 	fflush(stdout);
354 }
355 
356 
357 #endif
358