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