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