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