bsm_control.c revision 293161
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 */ 58static FILE *fp = NULL; 59static char linestr[AU_LINE_MAX]; 60static char *delim = ":"; 61 62static char inacdir = 0; 63static char ptrmoved = 0; 64 65#ifdef HAVE_PTHREAD_MUTEX_LOCK 66static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; 67#endif 68 69/* 70 * Audit policy string token table for au_poltostr() and au_strtopol(). 71 */ 72struct audit_polstr { 73 long ap_policy; 74 const char *ap_str; 75}; 76 77static 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 */ 101static int 102getstrfromtype_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 */ 150static int 151au_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 */ 188static int 189au_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 */ 227ssize_t 228au_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 */ 255int 256au_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 */ 292static void 293setac_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; 308closefp: 309 fclose(fp); 310 fp = NULL; 311 return; 312 } 313 314 fseek(fp, 0, SEEK_SET); 315 } 316} 317 318void 319setac(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 */ 334void 335endac(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 */ 354int 355getacdir(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 */ 403int 404getacdist(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 */ 438int 439getacmin(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 */ 469int 470getacfilesz(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 537static int 538getaccommon(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 */ 574int 575getacflg(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 */ 584int 585getacna(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 */ 594int 595getacpol(char *auditstr, size_t len) 596{ 597 598 return (getaccommon(POLICY_CONTROL_ENTRY, auditstr, len)); 599} 600 601int 602getachost(char *auditstr, size_t len) 603{ 604 605 return (getaccommon(HOST_CONTROL_ENTRY, auditstr, len)); 606} 607 608/* 609 * Set expiration conditions. 610 */ 611static int 612setexpirecond(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 */ 624int 625getacexpire(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