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