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