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