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