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 2009 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 void 46 setauclass() 47 { 48 (void) mutex_lock(&mutex_classfile); 49 if (au_class_file) { 50 (void) fseek(au_class_file, 0L, 0); 51 } 52 (void) mutex_unlock(&mutex_classfile); 53 } 54 55 56 void 57 endauclass() 58 { 59 (void) mutex_lock(&mutex_classfile); 60 if (au_class_file) { 61 (void) fclose(au_class_file); 62 au_class_file = NULL; 63 } 64 (void) mutex_unlock(&mutex_classfile); 65 } 66 67 /* 68 * getauclassent(): 69 * This is not MT-safe because of the static variables. 70 */ 71 au_class_ent_t * 72 getauclassent() 73 { 74 static au_class_ent_t e; 75 static char cname[AU_CLASS_NAME_MAX]; 76 static char cdesc[AU_CLASS_DESC_MAX]; 77 78 e.ac_name = cname; 79 e.ac_desc = cdesc; 80 81 return (getauclassent_r(&e)); 82 } 83 84 /* 85 * getauclassent_r 86 * This is MT-safe if each thread passes in its own pointer 87 * to the space where the class entry is returned. Becareful 88 * to also allocate space from the cname and cdesc pointers 89 * in the au_class_ent structure. 90 */ 91 au_class_ent_t * 92 getauclassent_r(au_class_ent_t *au_class_entry) 93 { 94 int i, error = 0, found = 0; 95 char *s, input[256]; 96 unsigned long v; 97 98 if (au_class_entry == (au_class_ent_t *)NULL || 99 au_class_entry->ac_name == (char *)NULL || 100 au_class_entry->ac_desc == (char *)NULL) { 101 return ((au_class_ent_t *)NULL); 102 } 103 104 /* open audit class file if it isn't already */ 105 (void) mutex_lock(&mutex_classfile); 106 if (!au_class_file) { 107 if (!(au_class_file = fopen(au_class_fname, "rF"))) { 108 (void) mutex_unlock(&mutex_classfile); 109 return ((au_class_ent_t *)0); 110 } 111 } 112 113 while (fgets(input, 256, au_class_file)) { 114 if (input[0] != '#') { 115 s = input + strspn(input, " \t\r\n"); 116 if ((*s == '\0') || (*s == '#')) { 117 continue; 118 } 119 found = 1; 120 s = input; 121 122 /* parse bitfield */ 123 i = strcspn(s, ":"); 124 s[i] = '\0'; 125 if (strncmp(s, "0x", 2) == 0) { 126 (void) sscanf(&s[2], "%lx", &v); 127 } else { 128 (void) sscanf(s, "%lu", &v); 129 } 130 au_class_entry->ac_class = v; 131 s = &s[i+1]; 132 133 /* parse class name */ 134 i = strcspn(s, ":"); 135 s[i] = '\0'; 136 (void) strncpy(au_class_entry->ac_name, s, 137 AU_CLASS_NAME_MAX); 138 s = &s[i+1]; 139 140 /* parse class description */ 141 i = strcspn(s, "\n\0"); 142 s[i] = '\0'; 143 (void) strncpy(au_class_entry->ac_desc, s, 144 AU_CLASS_DESC_MAX); 145 146 break; 147 } 148 } 149 150 (void) mutex_unlock(&mutex_classfile); 151 152 if (!error && found) { 153 return (au_class_entry); 154 } else { 155 return ((au_class_ent_t *)0); 156 } 157 } 158 159 160 au_class_ent_t * 161 getauclassnam(char *name) 162 { 163 static au_class_ent_t e; 164 static char cname[AU_CLASS_NAME_MAX]; 165 static char cdesc[AU_CLASS_DESC_MAX]; 166 167 e.ac_name = cname; 168 e.ac_desc = cdesc; 169 170 return (getauclassnam_r(&e, name)); 171 } 172 173 au_class_ent_t * 174 getauclassnam_r(au_class_ent_t *e, char *name) 175 { 176 while (getauclassent_r(e) != NULL) { 177 if (strcmp(e->ac_name, name) == 0) { 178 return (e); 179 } 180 } 181 return ((au_class_ent_t *)NULL); 182 } 183 184 185 /* 186 * xcacheauclass: 187 * Read the entire audit_class file into memory. 188 * Return a pointer to the requested entry in the cache 189 * or a pointer to an invalid entry if the the class 190 * requested is not known. 191 * 192 * Return < 0, do not set result pointer, if error. 193 * Return 0, set result pointer to invalid entry, if class not in cache. 194 * Return 1, set result pointer to a valid entry, if class is in cache. 195 */ 196 static int 197 xcacheauclass(au_class_ent_t **result, char *class_name, au_class_t class_no, 198 int flags) 199 { 200 static int invalid; 201 static au_class_ent_t **class_tbl; 202 static int called_once; 203 static int lines = 0; 204 205 char line[256]; 206 FILE *fp; 207 au_class_ent_t *p_class; 208 int i; 209 int hit = 0; 210 char *s; 211 212 (void) mutex_lock(&mutex_classcache); 213 if (called_once == 0) { 214 215 /* Count number of lines in the class file */ 216 if ((fp = fopen(au_class_fname, "rF")) == NULL) { 217 (void) mutex_unlock(&mutex_classcache); 218 return (-1); 219 } 220 while (fgets(line, 256, fp) != NULL) { 221 s = line + strspn(line, " \t\r\n"); 222 if ((*s == '\0') || (*s == '#')) { 223 continue; 224 } 225 lines++; 226 } 227 (void) fclose(fp); 228 class_tbl = (au_class_ent_t **)calloc((size_t)lines + 1, 229 sizeof (au_class_ent_t)); 230 if (class_tbl == NULL) { 231 (void) mutex_unlock(&mutex_classcache); 232 return (-2); 233 } 234 235 lines = 0; 236 setauclass(); 237 /* 238 * This call to getauclassent is protected by 239 * mutex_classcache, so we don't need to use the thread- 240 * safe version (getauclassent_r). 241 */ 242 while ((p_class = getauclassent()) != NULL) { 243 class_tbl[lines] = (au_class_ent_t *) 244 malloc(sizeof (au_class_ent_t)); 245 if (class_tbl[lines] == NULL) { 246 (void) mutex_unlock(&mutex_classcache); 247 return (-3); 248 } 249 class_tbl[lines]->ac_name = strdup(p_class->ac_name); 250 class_tbl[lines]->ac_class = p_class->ac_class; 251 class_tbl[lines]->ac_desc = strdup(p_class->ac_desc); 252 #ifdef DEBUG2 253 printclass(class_tbl[lines]); 254 #endif 255 lines++; 256 } 257 endauclass(); 258 invalid = lines; 259 class_tbl[invalid] = (au_class_ent_t *) 260 malloc(sizeof (au_class_ent_t)); 261 if (class_tbl[invalid] == NULL) { 262 (void) mutex_unlock(&mutex_classcache); 263 return (-4); 264 } 265 class_tbl[invalid]->ac_name = "invalid class"; 266 class_tbl[invalid]->ac_class = 0; 267 class_tbl[invalid]->ac_desc = class_tbl[invalid]->ac_name; 268 269 called_once = 1; 270 271 #ifdef DEBUG2 272 for (i = 0; i <= lines; i++) { 273 printclass(class_tbl[i]); 274 } 275 #endif 276 277 } /* END if called_once */ 278 *result = class_tbl[invalid]; 279 if (flags & AU_CACHE_NAME) { 280 for (i = 0; i < lines; i++) { 281 if (strcmp(class_name, class_tbl[i]->ac_name) == 0) { 282 *result = class_tbl[i]; 283 hit = 1; 284 break; 285 } 286 } 287 } else if (flags & AU_CACHE_NUMBER) { 288 for (i = 0; i < lines; i++) { 289 if (class_no == class_tbl[i]->ac_class) { 290 *result = class_tbl[i]; 291 hit = 1; 292 break; 293 } 294 } 295 } 296 (void) mutex_unlock(&mutex_classcache); 297 return (hit); 298 } 299 300 301 int 302 cacheauclass(au_class_ent_t **result, au_class_t class_no) 303 { 304 return (xcacheauclass(result, "", class_no, AU_CACHE_NUMBER)); 305 } 306 307 308 int 309 cacheauclassnam(au_class_ent_t **result, char *class_name) 310 { 311 return (xcacheauclass(result, class_name, (au_class_t)0, 312 AU_CACHE_NAME)); 313 } 314 315 316 #ifdef DEBUG2 317 void 318 printclass(p_c) 319 au_class_ent_t *p_c; 320 { 321 printf("%x:%s:%s\n", p_c->ac_class, p_c->ac_name, p_c->ac_desc); 322 fflush(stdout); 323 } 324 325 326 #endif 327