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 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 * 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 * 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 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 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 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