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