1 /*- 2 * Copyright (c) 2004, 2009 Apple Inc. 3 * Copyright (c) 2006, 2016 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 <config/config.h> 37 38 #include <bsm/libbsm.h> 39 40 #include <ctype.h> 41 #include <errno.h> 42 #include <string.h> 43 #include <strings.h> 44 #ifdef HAVE_PTHREAD_MUTEX_LOCK 45 #include <pthread.h> 46 #endif 47 #include <stdio.h> 48 #include <stdlib.h> 49 50 #ifndef HAVE_STRLCAT 51 #include <compat/strlcat.h> 52 #endif 53 #ifndef HAVE_STRLCPY 54 #include <compat/strlcpy.h> 55 #endif 56 57 #include <sys/stat.h> 58 59 /* 60 * Parse the contents of the audit_control file to return the audit control 61 * parameters. These static fields are protected by 'mutex'. 62 */ 63 static FILE *fp = NULL; 64 static char linestr[AU_LINE_MAX]; 65 static char *delim = ":"; 66 67 static char inacdir = 0; 68 static char ptrmoved = 0; 69 70 #ifdef HAVE_PTHREAD_MUTEX_LOCK 71 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; 72 #endif 73 74 /* 75 * Audit policy string token table for au_poltostr() and au_strtopol(). 76 */ 77 struct audit_polstr { 78 long ap_policy; 79 const char *ap_str; 80 }; 81 82 static struct audit_polstr au_polstr[] = { 83 { AUDIT_CNT, "cnt" }, 84 { AUDIT_AHLT, "ahlt" }, 85 { AUDIT_ARGV, "argv" }, 86 { AUDIT_ARGE, "arge" }, 87 { AUDIT_SEQ, "seq" }, 88 { AUDIT_WINDATA, "windata" }, 89 { AUDIT_USER, "user" }, 90 { AUDIT_GROUP, "group" }, 91 { AUDIT_TRAIL, "trail" }, 92 { AUDIT_PATH, "path" }, 93 { AUDIT_SCNT, "scnt" }, 94 { AUDIT_PUBLIC, "public" }, 95 { AUDIT_ZONENAME, "zonename" }, 96 { AUDIT_PERZONE, "perzone" }, 97 { -1, NULL } 98 }; 99 100 /* 101 * Returns the string value corresponding to the given label from the 102 * configuration file. 103 * 104 * Must be called with mutex held. 105 */ 106 static int 107 getstrfromtype_locked(const char *name, char **str) 108 { 109 char *type, *nl; 110 char *tokptr; 111 char *last; 112 113 *str = NULL; 114 115 if ((fp == NULL) && ((fp = fopen(AUDIT_CONTROL_FILE, "r")) == NULL)) 116 return (-1); /* Error */ 117 118 while (1) { 119 if (fgets(linestr, AU_LINE_MAX, fp) == NULL) { 120 if (ferror(fp)) 121 return (-1); 122 return (0); /* EOF */ 123 } 124 125 if (linestr[0] == '#') 126 continue; 127 128 /* Remove trailing new line character and white space. */ 129 nl = strchr(linestr, '\0') - 1; 130 while (nl >= linestr && ('\n' == *nl || ' ' == *nl || 131 '\t' == *nl)) { 132 *nl = '\0'; 133 nl--; 134 } 135 136 tokptr = linestr; 137 if ((type = strtok_r(tokptr, delim, &last)) != NULL) { 138 if (strcmp(name, type) == 0) { 139 /* Found matching name. */ 140 *str = last; 141 return (0); /* Success */ 142 } 143 } 144 } 145 } 146 147 /* 148 * Convert a given time value with a multiplier (seconds, hours, days, years) to 149 * seconds. Return 0 on success. 150 */ 151 static int 152 au_timetosec(time_t *seconds, u_long value, char mult) 153 { 154 if (NULL == seconds) 155 return (-1); 156 157 switch(mult) { 158 case 's': 159 /* seconds */ 160 *seconds = (time_t)value; 161 break; 162 163 case 'h': 164 /* hours */ 165 *seconds = (time_t)value * 60 * 60; 166 break; 167 168 case 'd': 169 /* days */ 170 *seconds = (time_t)value * 60 * 60 * 24; 171 break; 172 173 case 'y': 174 /* years. Add a day for each 4th (leap) year. */ 175 *seconds = (time_t)value * 60 * 60 * 24 * 364 + 176 ((time_t)value / 4) * 60 * 60 * 24; 177 break; 178 179 default: 180 return (-1); 181 } 182 return (0); 183 } 184 185 /* 186 * Convert a given disk space value with a multiplier (bytes, kilobytes, 187 * megabytes, gigabytes) to bytes. Return 0 on success. 188 */ 189 static int 190 au_spacetobytes(size_t *bytes, u_long value, char mult) 191 { 192 if (NULL == bytes) 193 return (-1); 194 195 switch(mult) { 196 case 'B': 197 case ' ': 198 /* Bytes */ 199 *bytes = (size_t)value; 200 break; 201 202 case 'K': 203 /* Kilobytes */ 204 *bytes = (size_t)value * 1024; 205 break; 206 207 case 'M': 208 /* Megabytes */ 209 *bytes = (size_t)value * 1024 * 1024; 210 break; 211 212 case 'G': 213 /* Gigabytes */ 214 *bytes = (size_t)value * 1024 * 1024 * 1024; 215 break; 216 217 default: 218 return (-1); 219 } 220 return (0); 221 } 222 223 /* 224 * Convert a policy to a string. Return -1 on failure, or >= 0 representing 225 * the actual size of the string placed in the buffer (excluding terminating 226 * nul). 227 */ 228 ssize_t 229 au_poltostr(int policy, size_t maxsize, char *buf) 230 { 231 int first = 1; 232 int i = 0; 233 234 if (maxsize < 1) 235 return (-1); 236 buf[0] = '\0'; 237 238 do { 239 if (policy & au_polstr[i].ap_policy) { 240 if (!first && strlcat(buf, ",", maxsize) >= maxsize) 241 return (-1); 242 if (strlcat(buf, au_polstr[i].ap_str, maxsize) >= 243 maxsize) 244 return (-1); 245 first = 0; 246 } 247 } while (NULL != au_polstr[++i].ap_str); 248 249 return (strlen(buf)); 250 } 251 252 /* 253 * Convert a string to a policy. Return -1 on failure (with errno EINVAL, 254 * ENOMEM) or 0 on success. 255 */ 256 int 257 au_strtopol(const char *polstr, int *policy) 258 { 259 char *bufp, *string; 260 char *buffer; 261 int i, matched; 262 263 *policy = 0; 264 buffer = strdup(polstr); 265 if (buffer == NULL) 266 return (-1); 267 268 bufp = buffer; 269 while ((string = strsep(&bufp, ",")) != NULL) { 270 matched = i = 0; 271 272 do { 273 if (strcmp(string, au_polstr[i].ap_str) == 0) { 274 *policy |= au_polstr[i].ap_policy; 275 matched = 1; 276 break; 277 } 278 } while (NULL != au_polstr[++i].ap_str); 279 280 if (!matched) { 281 free(buffer); 282 errno = EINVAL; 283 return (-1); 284 } 285 } 286 free(buffer); 287 return (0); 288 } 289 290 /* 291 * Rewind the file pointer to beginning. 292 */ 293 static void 294 setac_locked(void) 295 { 296 static time_t lastctime = 0; 297 struct stat sbuf; 298 299 ptrmoved = 1; 300 if (fp != NULL) { 301 /* 302 * Check to see if the file on disk has changed. If so, 303 * force a re-read of the file by closing it. 304 */ 305 if (fstat(fileno(fp), &sbuf) < 0) 306 goto closefp; 307 if (lastctime != sbuf.st_ctime) { 308 lastctime = sbuf.st_ctime; 309 closefp: 310 fclose(fp); 311 fp = NULL; 312 return; 313 } 314 315 fseek(fp, 0, SEEK_SET); 316 } 317 } 318 319 void 320 setac(void) 321 { 322 323 #ifdef HAVE_PTHREAD_MUTEX_LOCK 324 pthread_mutex_lock(&mutex); 325 #endif 326 setac_locked(); 327 #ifdef HAVE_PTHREAD_MUTEX_LOCK 328 pthread_mutex_unlock(&mutex); 329 #endif 330 } 331 332 /* 333 * Close the audit_control file. 334 */ 335 void 336 endac(void) 337 { 338 339 #ifdef HAVE_PTHREAD_MUTEX_LOCK 340 pthread_mutex_lock(&mutex); 341 #endif 342 ptrmoved = 1; 343 if (fp != NULL) { 344 fclose(fp); 345 fp = NULL; 346 } 347 #ifdef HAVE_PTHREAD_MUTEX_LOCK 348 pthread_mutex_unlock(&mutex); 349 #endif 350 } 351 352 /* 353 * Return audit directory information from the audit control file. 354 */ 355 int 356 getacdir(char *name, int len) 357 { 358 char *dir; 359 int ret = 0; 360 361 /* 362 * Check if another function was called between successive calls to 363 * getacdir. 364 */ 365 #ifdef HAVE_PTHREAD_MUTEX_LOCK 366 pthread_mutex_lock(&mutex); 367 #endif 368 if (inacdir && ptrmoved) { 369 ptrmoved = 0; 370 if (fp != NULL) 371 fseek(fp, 0, SEEK_SET); 372 ret = 2; 373 } 374 if (getstrfromtype_locked(DIR_CONTROL_ENTRY, &dir) < 0) { 375 #ifdef HAVE_PTHREAD_MUTEX_LOCK 376 pthread_mutex_unlock(&mutex); 377 #endif 378 return (-2); 379 } 380 if (dir == NULL) { 381 #ifdef HAVE_PTHREAD_MUTEX_LOCK 382 pthread_mutex_unlock(&mutex); 383 #endif 384 return (-1); 385 } 386 if (strlen(dir) >= (size_t)len) { 387 #ifdef HAVE_PTHREAD_MUTEX_LOCK 388 pthread_mutex_unlock(&mutex); 389 #endif 390 return (-3); 391 } 392 strlcpy(name, dir, len); 393 #ifdef HAVE_PTHREAD_MUTEX_LOCK 394 pthread_mutex_unlock(&mutex); 395 #endif 396 return (ret); 397 } 398 399 /* 400 * Return 1 if dist value is set to 'yes' or 'on'. 401 * Return 0 if dist value is set to something else. 402 * Return negative value on error. 403 */ 404 int 405 getacdist(void) 406 { 407 char *str; 408 int ret; 409 410 #ifdef HAVE_PTHREAD_MUTEX_LOCK 411 pthread_mutex_lock(&mutex); 412 #endif 413 setac_locked(); 414 if (getstrfromtype_locked(DIST_CONTROL_ENTRY, &str) < 0) { 415 #ifdef HAVE_PTHREAD_MUTEX_LOCK 416 pthread_mutex_unlock(&mutex); 417 #endif 418 return (-2); 419 } 420 if (str == NULL) { 421 #ifdef HAVE_PTHREAD_MUTEX_LOCK 422 pthread_mutex_unlock(&mutex); 423 #endif 424 return (0); 425 } 426 if (strcasecmp(str, "on") == 0 || strcasecmp(str, "yes") == 0) 427 ret = 1; 428 else 429 ret = 0; 430 #ifdef HAVE_PTHREAD_MUTEX_LOCK 431 pthread_mutex_unlock(&mutex); 432 #endif 433 return (ret); 434 } 435 436 /* 437 * Return the minimum free diskspace value from the audit control file. 438 */ 439 int 440 getacmin(int *min_val) 441 { 442 char *min; 443 444 #ifdef HAVE_PTHREAD_MUTEX_LOCK 445 pthread_mutex_lock(&mutex); 446 #endif 447 setac_locked(); 448 if (getstrfromtype_locked(MINFREE_CONTROL_ENTRY, &min) < 0) { 449 #ifdef HAVE_PTHREAD_MUTEX_LOCK 450 pthread_mutex_unlock(&mutex); 451 #endif 452 return (-2); 453 } 454 if (min == NULL) { 455 #ifdef HAVE_PTHREAD_MUTEX_LOCK 456 pthread_mutex_unlock(&mutex); 457 #endif 458 return (-1); 459 } 460 *min_val = atoi(min); 461 #ifdef HAVE_PTHREAD_MUTEX_LOCK 462 pthread_mutex_unlock(&mutex); 463 #endif 464 return (0); 465 } 466 467 /* 468 * Return the desired trail rotation size from the audit control file. 469 */ 470 int 471 getacfilesz(size_t *filesz_val) 472 { 473 char *str; 474 size_t val; 475 char mult; 476 int nparsed; 477 478 #ifdef HAVE_PTHREAD_MUTEX_LOCK 479 pthread_mutex_lock(&mutex); 480 #endif 481 setac_locked(); 482 if (getstrfromtype_locked(FILESZ_CONTROL_ENTRY, &str) < 0) { 483 #ifdef HAVE_PTHREAD_MUTEX_LOCK 484 pthread_mutex_unlock(&mutex); 485 #endif 486 return (-2); 487 } 488 if (str == NULL) { 489 #ifdef HAVE_PTHREAD_MUTEX_LOCK 490 pthread_mutex_unlock(&mutex); 491 #endif 492 errno = EINVAL; 493 return (-1); 494 } 495 496 /* Trim off any leading white space. */ 497 while (*str == ' ' || *str == '\t') 498 str++; 499 500 nparsed = sscanf(str, "%ju%c", (uintmax_t *)&val, &mult); 501 502 switch (nparsed) { 503 case 1: 504 /* If no multiplier then assume 'B' (bytes). */ 505 mult = 'B'; 506 /* fall through */ 507 case 2: 508 if (au_spacetobytes(filesz_val, val, mult) == 0) 509 break; 510 /* fall through */ 511 default: 512 errno = EINVAL; 513 #ifdef HAVE_PTHREAD_MUTEX_LOCK 514 pthread_mutex_unlock(&mutex); 515 #endif 516 return (-1); 517 } 518 519 /* 520 * The file size must either be 0 or >= MIN_AUDIT_FILE_SIZE. 0 521 * indicates no rotation size. 522 */ 523 if (*filesz_val < 0 || (*filesz_val > 0 && 524 *filesz_val < MIN_AUDIT_FILE_SIZE)) { 525 #ifdef HAVE_PTHREAD_MUTEX_LOCK 526 pthread_mutex_unlock(&mutex); 527 #endif 528 filesz_val = 0L; 529 errno = EINVAL; 530 return (-1); 531 } 532 #ifdef HAVE_PTHREAD_MUTEX_LOCK 533 pthread_mutex_unlock(&mutex); 534 #endif 535 return (0); 536 } 537 538 static int 539 getaccommon(const char *name, char *auditstr, int len) 540 { 541 char *str; 542 543 #ifdef HAVE_PTHREAD_MUTEX_LOCK 544 pthread_mutex_lock(&mutex); 545 #endif 546 setac_locked(); 547 if (getstrfromtype_locked(name, &str) < 0) { 548 #ifdef HAVE_PTHREAD_MUTEX_LOCK 549 pthread_mutex_unlock(&mutex); 550 #endif 551 return (-2); 552 } 553 554 /* 555 * getstrfromtype_locked() can return NULL for an empty value -- make 556 * sure to handle this by coercing the NULL back into an empty string. 557 */ 558 if (str != NULL && (strlen(str) >= (size_t)len)) { 559 #ifdef HAVE_PTHREAD_MUTEX_LOCK 560 pthread_mutex_unlock(&mutex); 561 #endif 562 return (-3); 563 } 564 strlcpy(auditstr, str != NULL ? str : "", len); 565 #ifdef HAVE_PTHREAD_MUTEX_LOCK 566 pthread_mutex_unlock(&mutex); 567 #endif 568 return (0); 569 } 570 571 /* 572 * Return the system audit value from the audit contol file. 573 */ 574 int 575 getacflg(char *auditstr, int len) 576 { 577 578 return (getaccommon(FLAGS_CONTROL_ENTRY, auditstr, len)); 579 } 580 581 /* 582 * Return the non attributable flags from the audit contol file. 583 */ 584 int 585 getacna(char *auditstr, int len) 586 { 587 588 return (getaccommon(NA_CONTROL_ENTRY, auditstr, len)); 589 } 590 591 /* 592 * Return the policy field from the audit control file. 593 */ 594 int 595 getacpol(char *auditstr, size_t len) 596 { 597 598 return (getaccommon(POLICY_CONTROL_ENTRY, auditstr, len)); 599 } 600 601 int 602 getachost(char *auditstr, size_t len) 603 { 604 605 return (getaccommon(HOST_CONTROL_ENTRY, auditstr, len)); 606 } 607 608 /* 609 * Set expiration conditions. 610 */ 611 static int 612 setexpirecond(time_t *age, size_t *size, u_long value, char mult) 613 { 614 615 if (isupper(mult) || ' ' == mult) 616 return (au_spacetobytes(size, value, mult)); 617 else 618 return (au_timetosec(age, value, mult)); 619 } 620 621 /* 622 * Return the expire-after field from the audit control file. 623 */ 624 int 625 getacexpire(int *andflg, time_t *age, size_t *size) 626 { 627 char *str; 628 int nparsed; 629 u_long val1, val2; 630 char mult1, mult2; 631 char andor[AU_LINE_MAX]; 632 633 *age = 0L; 634 *size = 0LL; 635 *andflg = 0; 636 637 #ifdef HAVE_PTHREAD_MUTEX_LOCK 638 pthread_mutex_lock(&mutex); 639 #endif 640 setac_locked(); 641 if (getstrfromtype_locked(EXPIRE_AFTER_CONTROL_ENTRY, &str) < 0) { 642 #ifdef HAVE_PTHREAD_MUTEX_LOCK 643 pthread_mutex_unlock(&mutex); 644 #endif 645 return (-2); 646 } 647 if (str == NULL) { 648 #ifdef HAVE_PTHREAD_MUTEX_LOCK 649 pthread_mutex_unlock(&mutex); 650 #endif 651 return (-1); 652 } 653 654 /* First, trim off any leading white space. */ 655 while (*str == ' ' || *str == '\t') 656 str++; 657 658 nparsed = sscanf(str, "%lu%c%[ \tadnorADNOR]%lu%c", &val1, &mult1, 659 andor, &val2, &mult2); 660 661 switch (nparsed) { 662 case 1: 663 /* If no multiplier then assume 'B' (Bytes). */ 664 mult1 = 'B'; 665 /* fall through */ 666 case 2: 667 /* One expiration condition. */ 668 if (setexpirecond(age, size, val1, mult1) != 0) { 669 #ifdef HAVE_PTHREAD_MUTEX_LOCK 670 pthread_mutex_unlock(&mutex); 671 #endif 672 return (-1); 673 } 674 break; 675 676 case 5: 677 /* Two expiration conditions. */ 678 if (setexpirecond(age, size, val1, mult1) != 0 || 679 setexpirecond(age, size, val2, mult2) != 0) { 680 #ifdef HAVE_PTHREAD_MUTEX_LOCK 681 pthread_mutex_unlock(&mutex); 682 #endif 683 return (-1); 684 } 685 if (strcasestr(andor, "and") != NULL) 686 *andflg = 1; 687 else if (strcasestr(andor, "or") != NULL) 688 *andflg = 0; 689 else { 690 #ifdef HAVE_PTHREAD_MUTEX_LOCK 691 pthread_mutex_unlock(&mutex); 692 #endif 693 return (-1); 694 } 695 break; 696 697 default: 698 #ifdef HAVE_PTHREAD_MUTEX_LOCK 699 pthread_mutex_unlock(&mutex); 700 #endif 701 return (-1); 702 } 703 704 #ifdef HAVE_PTHREAD_MUTEX_LOCK 705 pthread_mutex_unlock(&mutex); 706 #endif 707 return (0); 708 } 709 /* 710 * Return the desired queue size from the audit control file. 711 */ 712 int 713 getacqsize(int *qsz_val) 714 { 715 char *str; 716 int nparsed; 717 718 #ifdef HAVE_PTHREAD_MUTEX_LOCK 719 pthread_mutex_lock(&mutex); 720 #endif 721 setac_locked(); 722 if (getstrfromtype_locked(QSZ_CONTROL_ENTRY, &str) < 0) { 723 #ifdef HAVE_PTHREAD_MUTEX_LOCK 724 pthread_mutex_unlock(&mutex); 725 #endif 726 return (-2); 727 } 728 if (str == NULL) { 729 #ifdef HAVE_PTHREAD_MUTEX_LOCK 730 pthread_mutex_unlock(&mutex); 731 #endif 732 *qsz_val = USE_DEFAULT_QSZ; 733 return (0); 734 } 735 736 /* Trim off any leading white space. */ 737 while (*str == ' ' || *str == '\t') 738 str++; 739 740 nparsed = sscanf(str, "%d", (int *)qsz_val); 741 742 if (nparsed != 1) { 743 errno = EINVAL; 744 #ifdef HAVE_PTHREAD_MUTEX_LOCK 745 pthread_mutex_unlock(&mutex); 746 #endif 747 return (-1); 748 } 749 750 /* The queue size must either be 0 or < AQ_MAXHIGH */ 751 if (*qsz_val < 0 || *qsz_val > AQ_MAXHIGH) { 752 #ifdef HAVE_PTHREAD_MUTEX_LOCK 753 pthread_mutex_unlock(&mutex); 754 #endif 755 qsz_val = 0L; 756 errno = EINVAL; 757 return (-1); 758 } 759 #ifdef HAVE_PTHREAD_MUTEX_LOCK 760 pthread_mutex_unlock(&mutex); 761 #endif 762 return (0); 763 } 764