1 /* 2 * Copyright (c) 1999-2009 Apple Inc. 3 * Copyright (c) 2005, 2016-2018 Robert N. M. Watson 4 * All rights reserved. 5 * 6 * Portions of this software were developed by BAE Systems, the University of 7 * Cambridge Computer Laboratory, and Memorial University under DARPA/AFRL 8 * contract FA8650-15-C-7558 ("CADETS"), as part of the DARPA Transparent 9 * Computing (TC) research program. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 3. Neither the name of Apple Inc. ("Apple") nor the names of 20 * its contributors may be used to endorse or promote products derived 21 * from this software without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR 27 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 31 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 32 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 33 * POSSIBILITY OF SUCH DAMAGE. 34 */ 35 36 #include <sys/cdefs.h> 37 #include <sys/param.h> 38 #include <sys/capsicum.h> 39 #include <sys/fcntl.h> 40 #include <sys/filedesc.h> 41 #include <sys/libkern.h> 42 #include <sys/linker.h> 43 #include <sys/malloc.h> 44 #include <sys/mount.h> 45 #include <sys/proc.h> 46 #include <sys/rwlock.h> 47 #include <sys/sem.h> 48 #include <sys/sbuf.h> 49 #include <sys/sx.h> 50 #include <sys/syscall.h> 51 #include <sys/sysctl.h> 52 #include <sys/sysent.h> 53 #include <sys/vnode.h> 54 55 #include <bsm/audit.h> 56 #include <bsm/audit_kevents.h> 57 #include <security/audit/audit.h> 58 #include <security/audit/audit_private.h> 59 60 /* 61 * Hash table functions for the audit event number to event class mask 62 * mapping. 63 */ 64 #define EVCLASSMAP_HASH_TABLE_SIZE 251 65 struct evclass_elem { 66 au_event_t event; 67 au_class_t class; 68 LIST_ENTRY(evclass_elem) entry; 69 }; 70 struct evclass_list { 71 LIST_HEAD(, evclass_elem) head; 72 }; 73 74 static MALLOC_DEFINE(M_AUDITEVCLASS, "audit_evclass", "Audit event class"); 75 static struct rwlock evclass_lock; 76 static struct evclass_list evclass_hash[EVCLASSMAP_HASH_TABLE_SIZE]; 77 78 #define EVCLASS_LOCK_INIT() rw_init(&evclass_lock, "evclass_lock") 79 #define EVCLASS_RLOCK() rw_rlock(&evclass_lock) 80 #define EVCLASS_RUNLOCK() rw_runlock(&evclass_lock) 81 #define EVCLASS_WLOCK() rw_wlock(&evclass_lock) 82 #define EVCLASS_WUNLOCK() rw_wunlock(&evclass_lock) 83 84 /* 85 * Hash table maintaining a mapping from audit event numbers to audit event 86 * names. For now, used only by DTrace, but present always so that userspace 87 * tools can register and inspect fields consistently even if DTrace is not 88 * present. 89 * 90 * struct evname_elem is defined in audit_private.h so that audit_dtrace.c can 91 * use the definition. 92 */ 93 #define EVNAMEMAP_HASH_TABLE_MODULE "etc_security_audit_event" 94 #define EVNAMEMAP_HASH_TABLE_SIZE 251 95 struct evname_list { 96 LIST_HEAD(, evname_elem) enl_head; 97 }; 98 99 static MALLOC_DEFINE(M_AUDITEVNAME, "audit_evname", "Audit event name"); 100 static struct sx evnamemap_lock; 101 static struct evname_list evnamemap_hash[EVNAMEMAP_HASH_TABLE_SIZE]; 102 103 #define EVNAMEMAP_LOCK_INIT() sx_init(&evnamemap_lock, "evnamemap_lock"); 104 #define EVNAMEMAP_RLOCK() sx_slock(&evnamemap_lock) 105 #define EVNAMEMAP_RUNLOCK() sx_sunlock(&evnamemap_lock) 106 #define EVNAMEMAP_WLOCK() sx_xlock(&evnamemap_lock) 107 #define EVNAMEMAP_WUNLOCK() sx_xunlock(&evnamemap_lock) 108 109 /* 110 * Look up the class for an audit event in the class mapping table. 111 */ 112 au_class_t 113 au_event_class(au_event_t event) 114 { 115 struct evclass_list *evcl; 116 struct evclass_elem *evc; 117 au_class_t class; 118 119 EVCLASS_RLOCK(); 120 evcl = &evclass_hash[event % EVCLASSMAP_HASH_TABLE_SIZE]; 121 class = 0; 122 LIST_FOREACH(evc, &evcl->head, entry) { 123 if (evc->event == event) { 124 class = evc->class; 125 goto out; 126 } 127 } 128 out: 129 EVCLASS_RUNLOCK(); 130 return (class); 131 } 132 133 /* 134 * Insert a event to class mapping. If the event already exists in the 135 * mapping, then replace the mapping with the new one. 136 * 137 * XXX There is currently no constraints placed on the number of mappings. 138 * May want to either limit to a number, or in terms of memory usage. 139 */ 140 void 141 au_evclassmap_insert(au_event_t event, au_class_t class) 142 { 143 struct evclass_list *evcl; 144 struct evclass_elem *evc, *evc_new; 145 146 /* 147 * Pessimistically, always allocate storage before acquiring mutex. 148 * Free if there is already a mapping for this event. 149 */ 150 evc_new = malloc(sizeof(*evc), M_AUDITEVCLASS, M_WAITOK); 151 152 EVCLASS_WLOCK(); 153 evcl = &evclass_hash[event % EVCLASSMAP_HASH_TABLE_SIZE]; 154 LIST_FOREACH(evc, &evcl->head, entry) { 155 if (evc->event == event) { 156 evc->class = class; 157 EVCLASS_WUNLOCK(); 158 free(evc_new, M_AUDITEVCLASS); 159 return; 160 } 161 } 162 evc = evc_new; 163 evc->event = event; 164 evc->class = class; 165 LIST_INSERT_HEAD(&evcl->head, evc, entry); 166 EVCLASS_WUNLOCK(); 167 } 168 169 void 170 au_evclassmap_init(void) 171 { 172 int i; 173 174 EVCLASS_LOCK_INIT(); 175 for (i = 0; i < EVCLASSMAP_HASH_TABLE_SIZE; i++) 176 LIST_INIT(&evclass_hash[i].head); 177 178 /* 179 * Set up the initial event to class mapping for system calls. 180 * 181 * XXXRW: Really, this should walk all possible audit events, not all 182 * native ABI system calls, as there may be audit events reachable 183 * only through non-native system calls. It also seems a shame to 184 * frob the mutex this early. 185 */ 186 for (i = 0; i < SYS_MAXSYSCALL; i++) { 187 if (sysent[i].sy_auevent != AUE_NULL) 188 au_evclassmap_insert(sysent[i].sy_auevent, 0); 189 } 190 } 191 192 /* 193 * Look up the name for an audit event in the event-to-name mapping table. 194 */ 195 int 196 au_event_name(au_event_t event, char *name) 197 { 198 struct evname_list *enl; 199 struct evname_elem *ene; 200 int error; 201 202 error = ENOENT; 203 EVNAMEMAP_RLOCK(); 204 enl = &evnamemap_hash[event % EVNAMEMAP_HASH_TABLE_SIZE]; 205 LIST_FOREACH(ene, &enl->enl_head, ene_entry) { 206 if (ene->ene_event == event) { 207 strlcpy(name, ene->ene_name, EVNAMEMAP_NAME_SIZE); 208 error = 0; 209 goto out; 210 } 211 } 212 out: 213 EVNAMEMAP_RUNLOCK(); 214 return (error); 215 } 216 217 /* 218 * Insert a event-to-name mapping. If the event already exists in the 219 * mapping, then replace the mapping with the new one. 220 * 221 * XXX There is currently no constraints placed on the number of mappings. 222 * May want to either limit to a number, or in terms of memory usage. 223 * 224 * XXXRW: Accepts truncated name -- but perhaps should return failure instead? 225 * 226 * XXXRW: It could be we need a way to remove existing names...? 227 * 228 * XXXRW: We handle collisions between numbers, but I wonder if we also need a 229 * way to handle name collisions, for DTrace, where probe names must be 230 * unique? 231 */ 232 void 233 au_evnamemap_insert(au_event_t event, const char *name) 234 { 235 struct evname_list *enl; 236 struct evname_elem *ene, *ene_new; 237 238 /* 239 * Pessimistically, always allocate storage before acquiring lock. 240 * Free if there is already a mapping for this event. 241 */ 242 ene_new = malloc(sizeof(*ene_new), M_AUDITEVNAME, M_WAITOK | M_ZERO); 243 EVNAMEMAP_WLOCK(); 244 enl = &evnamemap_hash[event % EVNAMEMAP_HASH_TABLE_SIZE]; 245 LIST_FOREACH(ene, &enl->enl_head, ene_entry) { 246 if (ene->ene_event == event) { 247 EVNAME_LOCK(ene); 248 (void)strlcpy(ene->ene_name, name, 249 sizeof(ene->ene_name)); 250 EVNAME_UNLOCK(ene); 251 EVNAMEMAP_WUNLOCK(); 252 free(ene_new, M_AUDITEVNAME); 253 return; 254 } 255 } 256 ene = ene_new; 257 mtx_init(&ene->ene_lock, "au_evnamemap", NULL, MTX_DEF); 258 ene->ene_event = event; 259 (void)strlcpy(ene->ene_name, name, sizeof(ene->ene_name)); 260 LIST_INSERT_HEAD(&enl->enl_head, ene, ene_entry); 261 EVNAMEMAP_WUNLOCK(); 262 } 263 264 /* 265 * If /etc/security/audit_event has been preloaded by the boot loader, parse 266 * it to build an initial set of event number<->name mappings. 267 */ 268 static void 269 au_evnamemap_init_preload(void) 270 { 271 caddr_t kmdp; 272 char *endptr, *line, *nextline, *ptr; 273 const char *evnum_str, *evname; 274 size_t size; 275 long evnum; 276 u_int lineno; 277 278 kmdp = preload_search_by_type(EVNAMEMAP_HASH_TABLE_MODULE); 279 if (kmdp == NULL) 280 return; 281 ptr = preload_fetch_addr(kmdp); 282 size = preload_fetch_size(kmdp); 283 284 /* 285 * Parse preloaded configuration file "in place". Assume that the 286 * last character is a new line, meaning that we can replace it with a 287 * nul byte safely. We can then use strsep(3) to process the full 288 * buffer. 289 */ 290 ptr[size - 1] = '\0'; 291 292 /* 293 * Process line by line. 294 */ 295 nextline = ptr; 296 lineno = 0; 297 while ((line = strsep(&nextline, "\n")) != NULL) { 298 /* 299 * Skip any leading white space. 300 */ 301 while (line[0] == ' ' || line[0] == '\t') 302 line++; 303 304 /* 305 * Skip blank lines and comment lines. 306 */ 307 if (line[0] == '\0' || line[0] == '#') { 308 lineno++; 309 continue; 310 } 311 312 /* 313 * Parse each line -- ":"-separated tuple of event number, 314 * event name, and other material we are less interested in. 315 */ 316 evnum_str = strsep(&line, ":"); 317 if (evnum_str == NULL || *evnum_str == '\0') { 318 printf("%s: Invalid line %u - evnum strsep\n", 319 __func__, lineno); 320 lineno++; 321 continue; 322 } 323 evnum = strtol(evnum_str, &endptr, 10); 324 if (*evnum_str == '\0' || *endptr != '\0' || 325 evnum <= 0 || evnum > UINT16_MAX) { 326 printf("%s: Invalid line %u - evnum strtol\n", 327 __func__, lineno); 328 lineno++; 329 continue; 330 } 331 evname = strsep(&line, ":"); 332 if (evname == NULL || *evname == '\0') { 333 printf("%s: Invalid line %u - evname strsp\n", 334 __func__, lineno); 335 lineno++; 336 continue; 337 } 338 au_evnamemap_insert(evnum, evname); 339 lineno++; 340 } 341 } 342 343 void 344 au_evnamemap_init(void) 345 { 346 int i; 347 348 EVNAMEMAP_LOCK_INIT(); 349 for (i = 0; i < EVNAMEMAP_HASH_TABLE_SIZE; i++) 350 LIST_INIT(&evnamemap_hash[i].enl_head); 351 au_evnamemap_init_preload(); 352 } 353 354 /* 355 * The DTrace audit provider occasionally needs to walk the entries in the 356 * event-to-name mapping table, and uses this public interface to do so. A 357 * write lock is acquired so that the provider can safely update its fields in 358 * table entries. 359 */ 360 void 361 au_evnamemap_foreach(au_evnamemap_callback_t callback) 362 { 363 struct evname_list *enl; 364 struct evname_elem *ene; 365 int i; 366 367 EVNAMEMAP_WLOCK(); 368 for (i = 0; i < EVNAMEMAP_HASH_TABLE_SIZE; i++) { 369 enl = &evnamemap_hash[i]; 370 LIST_FOREACH(ene, &enl->enl_head, ene_entry) 371 callback(ene); 372 } 373 EVNAMEMAP_WUNLOCK(); 374 } 375 376 #ifdef KDTRACE_HOOKS 377 /* 378 * Look up an event-to-name mapping table entry by event number. As evname 379 * elements are stable in memory, we can return the pointer without the table 380 * lock held -- but the caller will need to lock the element mutex before 381 * accessing element fields. 382 * 383 * NB: the event identifier in elements is stable and can be read without 384 * holding the evname_elem lock. 385 */ 386 struct evname_elem * 387 au_evnamemap_lookup(au_event_t event) 388 { 389 struct evname_list *enl; 390 struct evname_elem *ene; 391 392 EVNAMEMAP_RLOCK(); 393 enl = &evnamemap_hash[event % EVNAMEMAP_HASH_TABLE_SIZE]; 394 LIST_FOREACH(ene, &enl->enl_head, ene_entry) { 395 if (ene->ene_event == event) 396 goto out; 397 } 398 ene = NULL; 399 out: 400 EVNAMEMAP_RUNLOCK(); 401 return (ene); 402 } 403 #endif /* !KDTRACE_HOOKS */ 404