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