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