1 /*- 2 * Copyright (c) 2004, 2009 Apple Inc. 3 * Copyright (c) 2006 Robert N. M. Watson 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 3. Neither the name of Apple Inc. ("Apple") nor the names of 15 * its contributors may be used to endorse or promote products derived 16 * from this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND 19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 * ARE DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR 22 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 26 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 27 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 * POSSIBILITY OF SUCH DAMAGE. 29 * 30 * $P4: //depot/projects/trustedbsd/openbsm/libbsm/bsm_control.c#34 $ 31 */ 32 33 #include <config/config.h> 34 35 #include <bsm/libbsm.h> 36 37 #include <ctype.h> 38 #include <errno.h> 39 #include <string.h> 40 #ifdef HAVE_PTHREAD_MUTEX_LOCK 41 #include <pthread.h> 42 #endif 43 #include <stdio.h> 44 #include <stdlib.h> 45 46 #ifndef HAVE_STRLCAT 47 #include <compat/strlcat.h> 48 #endif 49 #ifndef HAVE_STRLCPY 50 #include <compat/strlcpy.h> 51 #endif 52 53 #include <sys/stat.h> 54 55 /* 56 * Parse the contents of the audit_control file to return the audit control 57 * parameters. These static fields are protected by 'mutex'. 58 */ 59 static FILE *fp = NULL; 60 static char linestr[AU_LINE_MAX]; 61 static char *delim = ":"; 62 63 static char inacdir = 0; 64 static char ptrmoved = 0; 65 66 #ifdef HAVE_PTHREAD_MUTEX_LOCK 67 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; 68 #endif 69 70 /* 71 * Audit policy string token table for au_poltostr() and au_strtopol(). 72 */ 73 struct audit_polstr { 74 long ap_policy; 75 const char *ap_str; 76 }; 77 78 static struct audit_polstr au_polstr[] = { 79 { AUDIT_CNT, "cnt" }, 80 { AUDIT_AHLT, "ahlt" }, 81 { AUDIT_ARGV, "argv" }, 82 { AUDIT_ARGE, "arge" }, 83 { AUDIT_SEQ, "seq" }, 84 { AUDIT_WINDATA, "windata" }, 85 { AUDIT_USER, "user" }, 86 { AUDIT_GROUP, "group" }, 87 { AUDIT_TRAIL, "trail" }, 88 { AUDIT_PATH, "path" }, 89 { AUDIT_SCNT, "scnt" }, 90 { AUDIT_PUBLIC, "public" }, 91 { AUDIT_ZONENAME, "zonename" }, 92 { AUDIT_PERZONE, "perzone" }, 93 { -1, NULL } 94 }; 95 96 /* 97 * Returns the string value corresponding to the given label from the 98 * configuration file. 99 * 100 * Must be called with mutex held. 101 */ 102 static int 103 getstrfromtype_locked(char *name, char **str) 104 { 105 char *type, *nl; 106 char *tokptr; 107 char *last; 108 109 *str = NULL; 110 111 if ((fp == NULL) && ((fp = fopen(AUDIT_CONTROL_FILE, "r")) == NULL)) 112 return (-1); /* Error */ 113 114 while (1) { 115 if (fgets(linestr, AU_LINE_MAX, fp) == NULL) { 116 if (ferror(fp)) 117 return (-1); 118 return (0); /* EOF */ 119 } 120 121 if (linestr[0] == '#') 122 continue; 123 124 /* Remove trailing new line character and white space. */ 125 nl = strchr(linestr, '\0') - 1; 126 while (nl >= linestr && ('\n' == *nl || ' ' == *nl || 127 '\t' == *nl)) { 128 *nl = '\0'; 129 nl--; 130 } 131 132 tokptr = linestr; 133 if ((type = strtok_r(tokptr, delim, &last)) != NULL) { 134 if (strcmp(name, type) == 0) { 135 /* Found matching name. */ 136 *str = strtok_r(NULL, delim, &last); 137 if (*str == NULL) { 138 errno = EINVAL; 139 return (-1); /* Parse error in file */ 140 } 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 the minimum free diskspace value from the audit control file. 401 */ 402 int 403 getacmin(int *min_val) 404 { 405 char *min; 406 407 #ifdef HAVE_PTHREAD_MUTEX_LOCK 408 pthread_mutex_lock(&mutex); 409 #endif 410 setac_locked(); 411 if (getstrfromtype_locked(MINFREE_CONTROL_ENTRY, &min) < 0) { 412 #ifdef HAVE_PTHREAD_MUTEX_LOCK 413 pthread_mutex_unlock(&mutex); 414 #endif 415 return (-2); 416 } 417 if (min == NULL) { 418 #ifdef HAVE_PTHREAD_MUTEX_LOCK 419 pthread_mutex_unlock(&mutex); 420 #endif 421 return (1); 422 } 423 *min_val = atoi(min); 424 #ifdef HAVE_PTHREAD_MUTEX_LOCK 425 pthread_mutex_unlock(&mutex); 426 #endif 427 return (0); 428 } 429 430 /* 431 * Return the desired trail rotation size from the audit control file. 432 */ 433 int 434 getacfilesz(size_t *filesz_val) 435 { 436 char *str; 437 size_t val; 438 char mult; 439 int nparsed; 440 441 #ifdef HAVE_PTHREAD_MUTEX_LOCK 442 pthread_mutex_lock(&mutex); 443 #endif 444 setac_locked(); 445 if (getstrfromtype_locked(FILESZ_CONTROL_ENTRY, &str) < 0) { 446 #ifdef HAVE_PTHREAD_MUTEX_LOCK 447 pthread_mutex_unlock(&mutex); 448 #endif 449 return (-2); 450 } 451 if (str == NULL) { 452 #ifdef HAVE_PTHREAD_MUTEX_LOCK 453 pthread_mutex_unlock(&mutex); 454 #endif 455 errno = EINVAL; 456 return (1); 457 } 458 459 /* Trim off any leading white space. */ 460 while (*str == ' ' || *str == '\t') 461 str++; 462 463 nparsed = sscanf(str, "%ju%c", (uintmax_t *)&val, &mult); 464 465 switch (nparsed) { 466 case 1: 467 /* If no multiplier then assume 'B' (bytes). */ 468 mult = 'B'; 469 /* fall through */ 470 case 2: 471 if (au_spacetobytes(filesz_val, val, mult) == 0) 472 break; 473 /* fall through */ 474 default: 475 errno = EINVAL; 476 #ifdef HAVE_PTHREAD_MUTEX_LOCK 477 pthread_mutex_unlock(&mutex); 478 #endif 479 return (-1); 480 } 481 482 /* 483 * The file size must either be 0 or >= MIN_AUDIT_FILE_SIZE. 0 484 * indicates no rotation size. 485 */ 486 if (*filesz_val < 0 || (*filesz_val > 0 && 487 *filesz_val < MIN_AUDIT_FILE_SIZE)) { 488 #ifdef HAVE_PTHREAD_MUTEX_LOCK 489 pthread_mutex_unlock(&mutex); 490 #endif 491 filesz_val = 0L; 492 errno = EINVAL; 493 return (-1); 494 } 495 #ifdef HAVE_PTHREAD_MUTEX_LOCK 496 pthread_mutex_unlock(&mutex); 497 #endif 498 return (0); 499 } 500 501 /* 502 * Return the system audit value from the audit contol file. 503 */ 504 int 505 getacflg(char *auditstr, int len) 506 { 507 char *str; 508 509 #ifdef HAVE_PTHREAD_MUTEX_LOCK 510 pthread_mutex_lock(&mutex); 511 #endif 512 setac_locked(); 513 if (getstrfromtype_locked(FLAGS_CONTROL_ENTRY, &str) < 0) { 514 #ifdef HAVE_PTHREAD_MUTEX_LOCK 515 pthread_mutex_unlock(&mutex); 516 #endif 517 return (-2); 518 } 519 if (str == NULL) { 520 #ifdef HAVE_PTHREAD_MUTEX_LOCK 521 pthread_mutex_unlock(&mutex); 522 #endif 523 return (1); 524 } 525 if (strlen(str) >= (size_t)len) { 526 #ifdef HAVE_PTHREAD_MUTEX_LOCK 527 pthread_mutex_unlock(&mutex); 528 #endif 529 return (-3); 530 } 531 strlcpy(auditstr, str, len); 532 #ifdef HAVE_PTHREAD_MUTEX_LOCK 533 pthread_mutex_unlock(&mutex); 534 #endif 535 return (0); 536 } 537 538 /* 539 * Return the non attributable flags from the audit contol file. 540 */ 541 int 542 getacna(char *auditstr, int len) 543 { 544 char *str; 545 546 #ifdef HAVE_PTHREAD_MUTEX_LOCK 547 pthread_mutex_lock(&mutex); 548 #endif 549 setac_locked(); 550 if (getstrfromtype_locked(NA_CONTROL_ENTRY, &str) < 0) { 551 #ifdef HAVE_PTHREAD_MUTEX_LOCK 552 pthread_mutex_unlock(&mutex); 553 #endif 554 return (-2); 555 } 556 if (str == NULL) { 557 #ifdef HAVE_PTHREAD_MUTEX_LOCK 558 pthread_mutex_unlock(&mutex); 559 #endif 560 return (1); 561 } 562 if (strlen(str) >= (size_t)len) { 563 #ifdef HAVE_PTHREAD_MUTEX_LOCK 564 pthread_mutex_unlock(&mutex); 565 #endif 566 return (-3); 567 } 568 strlcpy(auditstr, str, len); 569 #ifdef HAVE_PTHREAD_MUTEX_LOCK 570 pthread_mutex_unlock(&mutex); 571 #endif 572 return (0); 573 } 574 575 /* 576 * Return the policy field from the audit control file. 577 */ 578 int 579 getacpol(char *auditstr, size_t len) 580 { 581 char *str; 582 583 #ifdef HAVE_PTHREAD_MUTEX_LOCK 584 pthread_mutex_lock(&mutex); 585 #endif 586 setac_locked(); 587 if (getstrfromtype_locked(POLICY_CONTROL_ENTRY, &str) < 0) { 588 #ifdef HAVE_PTHREAD_MUTEX_LOCK 589 pthread_mutex_unlock(&mutex); 590 #endif 591 return (-2); 592 } 593 if (str == NULL) { 594 #ifdef HAVE_PTHREAD_MUTEX_LOCK 595 pthread_mutex_unlock(&mutex); 596 #endif 597 return (-1); 598 } 599 if (strlen(str) >= len) { 600 #ifdef HAVE_PTHREAD_MUTEX_LOCK 601 pthread_mutex_unlock(&mutex); 602 #endif 603 return (-3); 604 } 605 strlcpy(auditstr, str, len); 606 #ifdef HAVE_PTHREAD_MUTEX_LOCK 607 pthread_mutex_unlock(&mutex); 608 #endif 609 return (0); 610 } 611 612 int 613 getachost(char *auditstr, size_t len) 614 { 615 char *str; 616 617 #ifdef HAVE_PTHREAD_MUTEX_LOCK 618 pthread_mutex_lock(&mutex); 619 #endif 620 setac_locked(); 621 if (getstrfromtype_locked(AUDIT_HOST_CONTROL_ENTRY, &str) < 0) { 622 #ifdef HAVE_PTHREAD_MUTEX_LOCK 623 pthread_mutex_unlock(&mutex); 624 #endif 625 return (-2); 626 } 627 if (str == NULL) { 628 #ifdef HAVE_PTHREAD_MUTEX_LOCK 629 pthread_mutex_unlock(&mutex); 630 #endif 631 return (1); 632 } 633 if (strlen(str) >= len) { 634 #ifdef HAVE_PTHREAD_MUTEX_LOCK 635 pthread_mutex_unlock(&mutex); 636 #endif 637 return (-3); 638 } 639 strlcpy(auditstr, str, len); 640 #ifdef HAVE_PTHREAD_MUTEX_LOCK 641 pthread_mutex_unlock(&mutex); 642 #endif 643 return (0); 644 } 645 646 /* 647 * Set expiration conditions. 648 */ 649 static int 650 setexpirecond(time_t *age, size_t *size, u_long value, char mult) 651 { 652 653 if (isupper(mult) || ' ' == mult) 654 return (au_spacetobytes(size, value, mult)); 655 else 656 return (au_timetosec(age, value, mult)); 657 } 658 659 /* 660 * Return the expire-after field from the audit control file. 661 */ 662 int 663 getacexpire(int *andflg, time_t *age, size_t *size) 664 { 665 char *str; 666 int nparsed; 667 u_long val1, val2; 668 char mult1, mult2; 669 char andor[AU_LINE_MAX]; 670 671 *age = 0L; 672 *size = 0LL; 673 *andflg = 0; 674 675 #ifdef HAVE_PTHREAD_MUTEX_LOCK 676 pthread_mutex_lock(&mutex); 677 #endif 678 setac_locked(); 679 if (getstrfromtype_locked(EXPIRE_AFTER_CONTROL_ENTRY, &str) < 0) { 680 #ifdef HAVE_PTHREAD_MUTEX_LOCK 681 pthread_mutex_unlock(&mutex); 682 #endif 683 return (-2); 684 } 685 if (str == NULL) { 686 #ifdef HAVE_PTHREAD_MUTEX_LOCK 687 pthread_mutex_unlock(&mutex); 688 #endif 689 return (1); 690 } 691 692 /* First, trim off any leading white space. */ 693 while (*str == ' ' || *str == '\t') 694 str++; 695 696 nparsed = sscanf(str, "%lu%c%[ \tadnorADNOR]%lu%c", &val1, &mult1, 697 andor, &val2, &mult2); 698 699 switch (nparsed) { 700 case 1: 701 /* If no multiplier then assume 'B' (Bytes). */ 702 mult1 = 'B'; 703 /* fall through */ 704 case 2: 705 /* One expiration condition. */ 706 if (setexpirecond(age, size, val1, mult1) != 0) { 707 #ifdef HAVE_PTHREAD_MUTEX_LOCK 708 pthread_mutex_unlock(&mutex); 709 #endif 710 return (-1); 711 } 712 break; 713 714 case 5: 715 /* Two expiration conditions. */ 716 if (setexpirecond(age, size, val1, mult1) != 0 || 717 setexpirecond(age, size, val2, mult2) != 0) { 718 #ifdef HAVE_PTHREAD_MUTEX_LOCK 719 pthread_mutex_unlock(&mutex); 720 #endif 721 return (-1); 722 } 723 if (strcasestr(andor, "and") != NULL) 724 *andflg = 1; 725 else if (strcasestr(andor, "or") != NULL) 726 *andflg = 0; 727 else { 728 #ifdef HAVE_PTHREAD_MUTEX_LOCK 729 pthread_mutex_unlock(&mutex); 730 #endif 731 return (-1); 732 } 733 break; 734 735 default: 736 #ifdef HAVE_PTHREAD_MUTEX_LOCK 737 pthread_mutex_unlock(&mutex); 738 #endif 739 return (-1); 740 } 741 742 #ifdef HAVE_PTHREAD_MUTEX_LOCK 743 pthread_mutex_unlock(&mutex); 744 #endif 745 return (0); 746 } 747