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