1 /*- 2 * SPDX-License-Identifier: BSD-3-Clause 3 * 4 * Copyright (c) 1999-2009 Apple Inc. 5 * Copyright (c) 2005, 2016-2017 Robert N. M. Watson 6 * All rights reserved. 7 * 8 * Portions of this software were developed by BAE Systems, the University of 9 * Cambridge Computer Laboratory, and Memorial University under DARPA/AFRL 10 * contract FA8650-15-C-7558 ("CADETS"), as part of the DARPA Transparent 11 * Computing (TC) research program. 12 * 13 * Redistribution and use in source and binary forms, with or without 14 * modification, are permitted provided that the following conditions 15 * are met: 16 * 1. Redistributions of source code must retain the above copyright 17 * notice, this list of conditions and the following disclaimer. 18 * 2. Redistributions in binary form must reproduce the above copyright 19 * notice, this list of conditions and the following disclaimer in the 20 * documentation and/or other materials provided with the distribution. 21 * 3. Neither the name of Apple Inc. ("Apple") nor the names of 22 * its contributors may be used to endorse or promote products derived 23 * from this software without specific prior written permission. 24 * 25 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND 26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 28 * ARE DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR 29 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 30 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 31 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 33 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 34 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 35 * POSSIBILITY OF SUCH DAMAGE. 36 */ 37 38 #include <sys/param.h> 39 #include <sys/capsicum.h> 40 #include <sys/fcntl.h> 41 #include <sys/filedesc.h> 42 #include <sys/libkern.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 struct aue_open_event { 61 int aoe_flags; 62 au_event_t aoe_event; 63 }; 64 65 static const struct aue_open_event aue_open[] = { 66 { O_RDONLY, AUE_OPEN_R }, 67 { (O_RDONLY | O_CREAT), AUE_OPEN_RC }, 68 { (O_RDONLY | O_CREAT | O_TRUNC), AUE_OPEN_RTC }, 69 { (O_RDONLY | O_TRUNC), AUE_OPEN_RT }, 70 { O_RDWR, AUE_OPEN_RW }, 71 { (O_RDWR | O_CREAT), AUE_OPEN_RWC }, 72 { (O_RDWR | O_CREAT | O_TRUNC), AUE_OPEN_RWTC }, 73 { (O_RDWR | O_TRUNC), AUE_OPEN_RWT }, 74 { O_WRONLY, AUE_OPEN_W }, 75 { (O_WRONLY | O_CREAT), AUE_OPEN_WC }, 76 { (O_WRONLY | O_CREAT | O_TRUNC), AUE_OPEN_WTC }, 77 { (O_WRONLY | O_TRUNC), AUE_OPEN_WT }, 78 }; 79 80 static const struct aue_open_event aue_openat[] = { 81 { O_RDONLY, AUE_OPENAT_R }, 82 { (O_RDONLY | O_CREAT), AUE_OPENAT_RC }, 83 { (O_RDONLY | O_CREAT | O_TRUNC), AUE_OPENAT_RTC }, 84 { (O_RDONLY | O_TRUNC), AUE_OPENAT_RT }, 85 { O_RDWR, AUE_OPENAT_RW }, 86 { (O_RDWR | O_CREAT), AUE_OPENAT_RWC }, 87 { (O_RDWR | O_CREAT | O_TRUNC), AUE_OPENAT_RWTC }, 88 { (O_RDWR | O_TRUNC), AUE_OPENAT_RWT }, 89 { O_WRONLY, AUE_OPENAT_W }, 90 { (O_WRONLY | O_CREAT), AUE_OPENAT_WC }, 91 { (O_WRONLY | O_CREAT | O_TRUNC), AUE_OPENAT_WTC }, 92 { (O_WRONLY | O_TRUNC), AUE_OPENAT_WT }, 93 }; 94 95 static const int aue_msgsys[] = { 96 /* 0 */ AUE_MSGCTL, 97 /* 1 */ AUE_MSGGET, 98 /* 2 */ AUE_MSGSND, 99 /* 3 */ AUE_MSGRCV, 100 }; 101 static const int aue_msgsys_count = sizeof(aue_msgsys) / sizeof(int); 102 103 static const int aue_semsys[] = { 104 /* 0 */ AUE_SEMCTL, 105 /* 1 */ AUE_SEMGET, 106 /* 2 */ AUE_SEMOP, 107 }; 108 static const int aue_semsys_count = sizeof(aue_semsys) / sizeof(int); 109 110 static const int aue_shmsys[] = { 111 /* 0 */ AUE_SHMAT, 112 /* 1 */ AUE_SHMDT, 113 /* 2 */ AUE_SHMGET, 114 /* 3 */ AUE_SHMCTL, 115 }; 116 static const int aue_shmsys_count = sizeof(aue_shmsys) / sizeof(int); 117 118 /* 119 * Check whether an event is auditable by comparing the mask of classes this 120 * event is part of against the given mask. 121 */ 122 int 123 au_preselect(au_event_t event, au_class_t class, au_mask_t *mask_p, int sorf) 124 { 125 au_class_t effmask = 0; 126 127 if (mask_p == NULL) 128 return (-1); 129 130 /* 131 * Perform the actual check of the masks against the event. 132 */ 133 if (sorf & AU_PRS_SUCCESS) 134 effmask |= (mask_p->am_success & class); 135 136 if (sorf & AU_PRS_FAILURE) 137 effmask |= (mask_p->am_failure & class); 138 139 if (effmask) 140 return (1); 141 else 142 return (0); 143 } 144 145 /* 146 * Convert sysctl names and present arguments to events. 147 */ 148 au_event_t 149 audit_ctlname_to_sysctlevent(int name[], uint64_t valid_arg) 150 { 151 152 /* can't parse it - so return the worst case */ 153 if ((valid_arg & (ARG_CTLNAME | ARG_LEN)) != (ARG_CTLNAME | ARG_LEN)) 154 return (AUE_SYSCTL); 155 156 switch (name[0]) { 157 /* non-admin "lookups" treat them special */ 158 case KERN_OSTYPE: 159 case KERN_OSRELEASE: 160 case KERN_OSREV: 161 case KERN_VERSION: 162 case KERN_ARGMAX: 163 case KERN_CLOCKRATE: 164 case KERN_BOOTTIME: 165 case KERN_POSIX1: 166 case KERN_NGROUPS: 167 case KERN_JOB_CONTROL: 168 case KERN_SAVED_IDS: 169 case KERN_OSRELDATE: 170 case KERN_DUMMY: 171 return (AUE_SYSCTL_NONADMIN); 172 173 /* only treat the changeable controls as admin */ 174 case KERN_MAXVNODES: 175 case KERN_MAXPROC: 176 case KERN_MAXFILES: 177 case KERN_MAXPROCPERUID: 178 case KERN_MAXFILESPERPROC: 179 case KERN_HOSTID: 180 case KERN_SECURELVL: 181 case KERN_HOSTNAME: 182 case KERN_PROC: 183 case KERN_FILE: 184 case KERN_PROF: 185 case KERN_NISDOMAINNAME: 186 case KERN_UPDATEINTERVAL: 187 case KERN_NTP_PLL: 188 case KERN_BOOTFILE: 189 case KERN_DUMPDEV: 190 case KERN_IPC: 191 case KERN_PS_STRINGS: 192 case KERN_USRSTACK: 193 case KERN_LOGSIGEXIT: 194 case KERN_IOV_MAX: 195 return ((valid_arg & ARG_VALUE) ? 196 AUE_SYSCTL : AUE_SYSCTL_NONADMIN); 197 198 default: 199 return (AUE_SYSCTL); 200 } 201 /* NOTREACHED */ 202 } 203 204 /* 205 * Convert an open flags specifier into a specific type of open event for 206 * auditing purposes. 207 */ 208 au_event_t 209 audit_flags_and_error_to_openevent(int oflags, int error) 210 { 211 int i; 212 213 /* 214 * Need to check only those flags we care about. 215 */ 216 oflags = oflags & (O_RDONLY | O_CREAT | O_TRUNC | O_RDWR | O_WRONLY); 217 for (i = 0; i < nitems(aue_open); i++) { 218 if (aue_open[i].aoe_flags == oflags) 219 return (aue_open[i].aoe_event); 220 } 221 return (AUE_OPEN); 222 } 223 224 au_event_t 225 audit_flags_and_error_to_openatevent(int oflags, int error) 226 { 227 int i; 228 229 /* 230 * Need to check only those flags we care about. 231 */ 232 oflags = oflags & (O_RDONLY | O_CREAT | O_TRUNC | O_RDWR | O_WRONLY); 233 for (i = 0; i < nitems(aue_openat); i++) { 234 if (aue_openat[i].aoe_flags == oflags) 235 return (aue_openat[i].aoe_event); 236 } 237 return (AUE_OPENAT); 238 } 239 240 /* 241 * Convert a MSGCTL command to a specific event. 242 */ 243 au_event_t 244 audit_msgctl_to_event(int cmd) 245 { 246 247 switch (cmd) { 248 case IPC_RMID: 249 return (AUE_MSGCTL_RMID); 250 251 case IPC_SET: 252 return (AUE_MSGCTL_SET); 253 254 case IPC_STAT: 255 return (AUE_MSGCTL_STAT); 256 257 default: 258 /* We will audit a bad command. */ 259 return (AUE_MSGCTL); 260 } 261 } 262 263 /* 264 * Convert a SEMCTL command to a specific event. 265 */ 266 au_event_t 267 audit_semctl_to_event(int cmd) 268 { 269 270 switch (cmd) { 271 case GETALL: 272 return (AUE_SEMCTL_GETALL); 273 274 case GETNCNT: 275 return (AUE_SEMCTL_GETNCNT); 276 277 case GETPID: 278 return (AUE_SEMCTL_GETPID); 279 280 case GETVAL: 281 return (AUE_SEMCTL_GETVAL); 282 283 case GETZCNT: 284 return (AUE_SEMCTL_GETZCNT); 285 286 case IPC_RMID: 287 return (AUE_SEMCTL_RMID); 288 289 case IPC_SET: 290 return (AUE_SEMCTL_SET); 291 292 case SETALL: 293 return (AUE_SEMCTL_SETALL); 294 295 case SETVAL: 296 return (AUE_SEMCTL_SETVAL); 297 298 case IPC_STAT: 299 return (AUE_SEMCTL_STAT); 300 301 default: 302 /* We will audit a bad command. */ 303 return (AUE_SEMCTL); 304 } 305 } 306 307 /* 308 * Convert msgsys(2), semsys(2), and shmsys(2) system-call variations into 309 * audit events, if possible. 310 */ 311 au_event_t 312 audit_msgsys_to_event(int which) 313 { 314 315 if ((which >= 0) && (which < aue_msgsys_count)) 316 return (aue_msgsys[which]); 317 318 /* Audit a bad command. */ 319 return (AUE_MSGSYS); 320 } 321 322 au_event_t 323 audit_semsys_to_event(int which) 324 { 325 326 if ((which >= 0) && (which < aue_semsys_count)) 327 return (aue_semsys[which]); 328 329 /* Audit a bad command. */ 330 return (AUE_SEMSYS); 331 } 332 333 au_event_t 334 audit_shmsys_to_event(int which) 335 { 336 337 if ((which >= 0) && (which < aue_shmsys_count)) 338 return (aue_shmsys[which]); 339 340 /* Audit a bad command. */ 341 return (AUE_SHMSYS); 342 } 343 344 /* 345 * Convert a command for the auditon() system call to a audit event. 346 */ 347 au_event_t 348 auditon_command_event(int cmd) 349 { 350 351 switch(cmd) { 352 case A_GETPOLICY: 353 return (AUE_AUDITON_GPOLICY); 354 355 case A_SETPOLICY: 356 return (AUE_AUDITON_SPOLICY); 357 358 case A_GETKMASK: 359 return (AUE_AUDITON_GETKMASK); 360 361 case A_SETKMASK: 362 return (AUE_AUDITON_SETKMASK); 363 364 case A_GETQCTRL: 365 return (AUE_AUDITON_GQCTRL); 366 367 case A_SETQCTRL: 368 return (AUE_AUDITON_SQCTRL); 369 370 case A_GETCWD: 371 return (AUE_AUDITON_GETCWD); 372 373 case A_GETCAR: 374 return (AUE_AUDITON_GETCAR); 375 376 case A_GETSTAT: 377 return (AUE_AUDITON_GETSTAT); 378 379 case A_SETSTAT: 380 return (AUE_AUDITON_SETSTAT); 381 382 case A_SETUMASK: 383 return (AUE_AUDITON_SETUMASK); 384 385 case A_SETSMASK: 386 return (AUE_AUDITON_SETSMASK); 387 388 case A_GETCOND: 389 return (AUE_AUDITON_GETCOND); 390 391 case A_SETCOND: 392 return (AUE_AUDITON_SETCOND); 393 394 case A_GETCLASS: 395 return (AUE_AUDITON_GETCLASS); 396 397 case A_SETCLASS: 398 return (AUE_AUDITON_SETCLASS); 399 400 case A_GETPINFO: 401 case A_SETPMASK: 402 case A_SETFSIZE: 403 case A_GETFSIZE: 404 case A_GETPINFO_ADDR: 405 case A_GETKAUDIT: 406 case A_SETKAUDIT: 407 default: 408 return (AUE_AUDITON); /* No special record */ 409 } 410 } 411 412 /* 413 * Create a canonical path from given path by prefixing either the root 414 * directory, or the current working directory. If the process working 415 * directory is NULL, we could use 'rootvnode' to obtain the root directory, 416 * but this results in a volfs name written to the audit log. So we will 417 * leave the filename starting with '/' in the audit log in this case. 418 */ 419 void 420 audit_canon_path_vp(struct thread *td, struct vnode *rdir, struct vnode *cdir, 421 char *path, char *cpath) 422 { 423 struct vnode *vp; 424 char *rbuf, *fbuf, *copy; 425 struct sbuf sbf; 426 int error; 427 428 WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL, "%s: at %s:%d", 429 __func__, __FILE__, __LINE__); 430 431 copy = path; 432 if (*path == '/') { 433 vp = rdir; 434 } else { 435 if (cdir == NULL) { 436 cpath[0] = '\0'; 437 return; 438 } 439 vp = cdir; 440 } 441 MPASS(vp != NULL); 442 /* 443 * NB: We require that the supplied array be at least MAXPATHLEN bytes 444 * long. If this is not the case, then we can run into serious trouble. 445 */ 446 (void) sbuf_new(&sbf, cpath, MAXPATHLEN, SBUF_FIXEDLEN); 447 /* 448 * Strip leading forward slashes. 449 * 450 * Note this does nothing to fully canonicalize the path. 451 */ 452 while (*copy == '/') 453 copy++; 454 /* 455 * Make sure we handle chroot(2) and prepend the global path to these 456 * environments. 457 * 458 * NB: vn_fullpath(9) on FreeBSD is less reliable than vn_getpath(9) 459 * on Darwin. As a result, this may need some additional attention 460 * in the future. 461 */ 462 error = vn_fullpath_global(vp, &rbuf, &fbuf); 463 if (error) { 464 cpath[0] = '\0'; 465 return; 466 } 467 (void) sbuf_cat(&sbf, rbuf); 468 /* 469 * We are going to concatenate the resolved path with the passed path 470 * with all slashes removed and we want them glued with a single slash. 471 * However, if the directory is /, the slash is already there. 472 */ 473 if (rbuf[1] != '\0') 474 (void) sbuf_putc(&sbf, '/'); 475 free(fbuf, M_TEMP); 476 /* 477 * Now that we have processed any alternate root and relative path 478 * names, add the supplied pathname. 479 */ 480 (void) sbuf_cat(&sbf, copy); 481 /* 482 * One or more of the previous sbuf operations could have resulted in 483 * the supplied buffer being overflowed. Check to see if this is the 484 * case. 485 */ 486 if (sbuf_error(&sbf) != 0) { 487 cpath[0] = '\0'; 488 return; 489 } 490 sbuf_finish(&sbf); 491 } 492 493 void 494 audit_canon_path(struct thread *td, int dirfd, char *path, char *cpath) 495 { 496 struct vnode *cdir, *rdir; 497 struct pwd *pwd; 498 cap_rights_t rights; 499 int error; 500 bool vrele_cdir; 501 502 WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL, "%s: at %s:%d", 503 __func__, __FILE__, __LINE__); 504 505 pwd = pwd_hold(td); 506 rdir = pwd->pwd_rdir; 507 cdir = NULL; 508 vrele_cdir = false; 509 if (*path != '/') { 510 if (dirfd == AT_FDCWD) { 511 cdir = pwd->pwd_cdir; 512 } else { 513 error = fgetvp(td, dirfd, cap_rights_init(&rights), &cdir); 514 if (error != 0) { 515 cpath[0] = '\0'; 516 pwd_drop(pwd); 517 return; 518 } 519 vrele_cdir = true; 520 } 521 } 522 523 audit_canon_path_vp(td, rdir, cdir, path, cpath); 524 525 pwd_drop(pwd); 526 if (vrele_cdir) 527 vrele(cdir); 528 } 529