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