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
printclass(au_class_ent_t * p_c)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
setauclass()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
endauclass()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 *
getauclassent()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 *
getauclassent_r(au_class_ent_t * au_class_entry)100 getauclassent_r(au_class_ent_t *au_class_entry)
101 {
102 	int		i, error = 0, found = 0;
103 	char		*s, input[256];
104 	au_class_t	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 (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 (NULL);
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 
129 			/* parse bitfield */
130 			i = strcspn(s, ":");
131 			s[i] = '\0';
132 			if (strncmp(s, "0x", 2) == 0) {
133 				(void) sscanf(&s[2], "%x", &v);
134 			} else {
135 				(void) sscanf(s, "%u", &v);
136 			}
137 			au_class_entry->ac_class = v;
138 			s = &s[i+1];
139 
140 			/* parse class name */
141 			i = strcspn(s, ":");
142 			s[i] = '\0';
143 			(void) strncpy(au_class_entry->ac_name, s,
144 			    AU_CLASS_NAME_MAX);
145 			s = &s[i+1];
146 
147 			/* parse class description */
148 			i = strcspn(s, "\n\0");
149 			s[i] = '\0';
150 			(void) strncpy(au_class_entry->ac_desc, s,
151 			    AU_CLASS_DESC_MAX);
152 
153 			break;
154 		}
155 	}
156 
157 	(void) mutex_unlock(&mutex_classfile);
158 
159 	if (!error && found) {
160 		return (au_class_entry);
161 	} else {
162 		return (NULL);
163 	}
164 }
165 
166 
167 au_class_ent_t *
getauclassnam(char * name)168 getauclassnam(char *name)
169 {
170 	static au_class_ent_t e;
171 	static char	cname[AU_CLASS_NAME_MAX];
172 	static char	cdesc[AU_CLASS_DESC_MAX];
173 
174 	e.ac_name = cname;
175 	e.ac_desc = cdesc;
176 
177 	return (getauclassnam_r(&e, name));
178 }
179 
180 au_class_ent_t *
getauclassnam_r(au_class_ent_t * e,char * name)181 getauclassnam_r(au_class_ent_t *e, char *name)
182 {
183 	while (getauclassent_r(e) != NULL) {
184 		if (strncmp(e->ac_name, name, AU_CLASS_NAME_MAX) == 0) {
185 			return (e);
186 		}
187 	}
188 	return (NULL);
189 }
190 
191 
192 /*
193  * xcacheauclass:
194  *	Read the entire audit_class file into memory.
195  *	Return a pointer to the requested entry in the cache
196  *	or a pointer to an invalid entry if the the class
197  *	requested is not known.
198  *
199  *	Return < 0, do not set result pointer, if error.
200  *	Return   0, set result pointer to invalid entry, if class not in cache.
201  *	Return   1, set result pointer to a valid entry, if class is in cache.
202  */
203 static int
xcacheauclass(au_class_ent_t ** result,char * class_name,au_class_t class_no,int flags)204 xcacheauclass(au_class_ent_t **result, char *class_name, au_class_t class_no,
205     int flags)
206 {
207 	static int	invalid;
208 	static au_class_ent_t **class_tbl;
209 	static int	called_once;
210 	static int	lines = 0;
211 
212 	char		line[256];
213 	FILE		*fp;
214 	au_class_ent_t	*p_class;
215 	int		i;
216 	int		hit = 0;
217 	char		*s;
218 
219 	(void) mutex_lock(&mutex_classcache);
220 	if (called_once == 0) {
221 
222 		/* Count number of lines in the class file */
223 		if ((fp = fopen(au_class_fname, "rF")) == NULL) {
224 			(void) mutex_unlock(&mutex_classcache);
225 			return (-1);
226 		}
227 		while (fgets(line, 256, fp) != NULL) {
228 			s = line + strspn(line, " \t\r\n");
229 			if ((*s == '\0') || (*s == '#')) {
230 				continue;
231 			}
232 			lines++;
233 		}
234 		(void) fclose(fp);
235 		class_tbl = (au_class_ent_t **)calloc((size_t)lines + 1,
236 		    sizeof (class_tbl));
237 		if (class_tbl == NULL) {
238 			(void) mutex_unlock(&mutex_classcache);
239 			return (-2);
240 		}
241 
242 		lines = 0;
243 		setauclass();
244 		/*
245 		 * This call to getauclassent is protected by
246 		 * mutex_classcache, so we don't need to use the thread-
247 		 * safe version (getauclassent_r).
248 		 */
249 		while ((p_class = getauclassent()) != NULL) {
250 			class_tbl[lines] = (au_class_ent_t *)
251 			    malloc(sizeof (au_class_ent_t));
252 			if (class_tbl[lines] == NULL) {
253 				(void) mutex_unlock(&mutex_classcache);
254 				return (-3);
255 			}
256 			class_tbl[lines]->ac_name = strdup(p_class->ac_name);
257 			class_tbl[lines]->ac_class = p_class->ac_class;
258 			class_tbl[lines]->ac_desc = strdup(p_class->ac_desc);
259 #ifdef DEBUG2
260 			printclass(class_tbl[lines]);
261 #endif
262 			lines++;
263 		}
264 		endauclass();
265 		invalid = lines;
266 		class_tbl[invalid] = (au_class_ent_t *)
267 		    malloc(sizeof (au_class_ent_t));
268 		if (class_tbl[invalid] == NULL) {
269 			(void) mutex_unlock(&mutex_classcache);
270 			return (-4);
271 		}
272 		class_tbl[invalid]->ac_name = "invalid class";
273 		class_tbl[invalid]->ac_class = 0;
274 		class_tbl[invalid]->ac_desc = class_tbl[invalid]->ac_name;
275 
276 		called_once = 1;
277 
278 #ifdef DEBUG2
279 		for (i = 0; i <= lines; i++) {
280 			printclass(class_tbl[i]);
281 		}
282 #endif
283 
284 	} /* END if called_once */
285 	*result = class_tbl[invalid];
286 	if (flags & AU_CACHE_NAME) {
287 		for (i = 0; i < lines; i++) {
288 			if (strncmp(class_name, class_tbl[i]->ac_name,
289 			    AU_CLASS_NAME_MAX) == 0) {
290 				*result = class_tbl[i];
291 				hit = 1;
292 				break;
293 			}
294 		}
295 	} else if (flags & AU_CACHE_NUMBER) {
296 		for (i = 0; i < lines; i++) {
297 			if (class_no == class_tbl[i]->ac_class) {
298 				*result = class_tbl[i];
299 				hit = 1;
300 				break;
301 			}
302 		}
303 	}
304 	(void) mutex_unlock(&mutex_classcache);
305 	return (hit);
306 }
307 
308 int
cacheauclass(au_class_ent_t ** result,au_class_t class_no)309 cacheauclass(au_class_ent_t **result, au_class_t class_no)
310 {
311 	return (xcacheauclass(result, "", class_no, AU_CACHE_NUMBER));
312 }
313 
314 int
cacheauclassnam(au_class_ent_t ** result,char * class_name)315 cacheauclassnam(au_class_ent_t **result, char *class_name)
316 {
317 	return (xcacheauclass(result, class_name, (au_class_t)0,
318 	    AU_CACHE_NAME));
319 }
320