1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. 23 * 24 * write binary audit records directly to a file. 25 */ 26 27 #define DEBUG 0 28 29 #if DEBUG 30 #define DPRINT(x) { (void) fprintf x; } 31 #else 32 #define DPRINT(x) 33 #endif 34 35 /* 36 * auditd_plugin_open(), auditd_plugin() and auditd_plugin_close() 37 * implement a replacable library for use by auditd; they are a 38 * project private interface and may change without notice. 39 * 40 */ 41 42 #include <assert.h> 43 #include <bsm/audit.h> 44 #include <bsm/audit_record.h> 45 #include <bsm/libbsm.h> 46 #include <errno.h> 47 #include <fcntl.h> 48 #include <libintl.h> 49 #include <netdb.h> 50 #include <pthread.h> 51 #include <secdb.h> 52 #include <signal.h> 53 #include <stdio.h> 54 #include <stdlib.h> 55 #include <string.h> 56 #include <sys/param.h> 57 #include <sys/types.h> 58 #include <time.h> 59 #include <tzfile.h> 60 #include <unistd.h> 61 #include <sys/vfs.h> 62 #include <limits.h> 63 #include <syslog.h> 64 #include <security/auditd.h> 65 #include <audit_plugin.h> 66 67 #define AUDIT_DATE_SZ 14 68 #define AUDIT_FNAME_SZ 2 * AUDIT_DATE_SZ + 2 + MAXHOSTNAMELEN 69 70 /* per-directory status */ 71 #define SOFT_SPACE 0 /* minfree or less space available */ 72 #define PLENTY_SPACE 1 /* more than minfree available */ 73 #define SPACE_FULL 2 /* out of space */ 74 75 #define AVAIL_MIN 50 /* If there are less that this number */ 76 /* of blocks avail, the filesystem is */ 77 /* presumed full. */ 78 79 #define ALLHARD_DELAY 20 /* Call audit_warn(allhard) every 20 seconds */ 80 81 /* minimum reasonable size in bytes to roll over an audit file */ 82 #define FSIZE_MIN 512000 83 84 /* 85 * The directory list is a circular linked list. It is pointed into by 86 * activeDir. Each element contains the pointer to the next 87 * element, the directory pathname, a flag for how much space there is 88 * in the directory's filesystem, and a file handle. Since a new 89 * directory list can be created from auditd_plugin_open() while the 90 * current list is in use, activeDir is protected by log_mutex. 91 */ 92 typedef struct dirlist_s dirlist_t; 93 struct dirlist_s { 94 dirlist_t *dl_next; 95 int dl_space; 96 int dl_flags; 97 char *dl_dirname; 98 char *dl_filename; /* file name (not path) if open */ 99 int dl_fd; /* file handle, -1 unless open */ 100 }; 101 /* 102 * Defines for dl_flags 103 */ 104 #define SOFT_WARNED 0x0001 /* already did soft warning for this dir */ 105 #define HARD_WARNED 0x0002 /* already did hard warning for this dir */ 106 107 #if DEBUG 108 static FILE *dbfp; /* debug file */ 109 #endif 110 111 static pthread_mutex_t log_mutex; 112 static int binfile_is_open = 0; 113 114 static int minfree = -1; 115 static int minfreeblocks; /* minfree in blocks */ 116 117 static dirlist_t *lastOpenDir = NULL; /* last activeDir */ 118 static dirlist_t *activeDir = NULL; /* to be current directory */ 119 static dirlist_t *startdir; /* first dir in the ring */ 120 static int activeCount = 0; /* number of dirs in the ring */ 121 122 static int openNewFile = 0; /* need to open a new file */ 123 static int hung_count = 0; /* count of audit_warn hard */ 124 125 /* flag from audit_plugin_open to audit_plugin_close */ 126 static int am_open = 0; 127 /* preferred dir state */ 128 static int fullness_state = PLENTY_SPACE; 129 130 /* 131 * These are used to implement a maximum size for the auditing 132 * file. binfile_maxsize is set via the 'p_fsize' parameter to the 133 * audit_binfile plugin. 134 */ 135 static uint_t binfile_cursize = 0; 136 static uint_t binfile_maxsize = 0; 137 138 static int open_log(dirlist_t *); 139 140 static void 141 freedirlist(dirlist_t *head) 142 { 143 dirlist_t *n1, *n2; 144 /* 145 * Free up the old directory list if any 146 */ 147 if (head != NULL) { 148 n1 = head; 149 do { 150 n2 = n1->dl_next; 151 free(n1->dl_dirname); 152 free(n1->dl_filename); 153 free(n1); 154 n1 = n2; 155 } while (n1 != head); 156 } 157 } 158 159 dirlist_t * 160 dupdirnode(dirlist_t *node_orig) 161 { 162 dirlist_t *node_new; 163 164 if ((node_new = calloc(1, sizeof (dirlist_t))) == NULL) { 165 return (NULL); 166 } 167 168 if (node_orig->dl_dirname != NULL && 169 (node_new->dl_dirname = strdup(node_orig->dl_dirname)) == NULL || 170 node_orig->dl_filename != NULL && 171 (node_new->dl_filename = strdup(node_orig->dl_filename)) == NULL) { 172 freedirlist(node_new); 173 return (NULL); 174 } 175 176 node_new->dl_next = node_new; 177 node_new->dl_space = node_orig->dl_space; 178 node_new->dl_flags = node_orig->dl_flags; 179 node_new->dl_fd = node_orig->dl_fd; 180 181 return (node_new); 182 } 183 184 /* 185 * add to a linked list of directories available for writing 186 * 187 */ 188 static int 189 growauditlist(dirlist_t **listhead, char *dirlist, 190 dirlist_t *endnode, int *count) 191 { 192 dirlist_t *node; 193 char *bs, *be; 194 dirlist_t **node_p; 195 char *dirname; 196 char *remainder; 197 198 DPRINT((dbfp, "binfile: dirlist=%s\n", dirlist)); 199 200 if (*listhead == NULL) 201 node_p = listhead; 202 else 203 node_p = &(endnode->dl_next); 204 205 node = NULL; 206 while ((dirname = strtok_r(dirlist, ",", &remainder)) != NULL) { 207 dirlist = NULL; 208 209 DPRINT((dbfp, "binfile: p_dir = %s\n", dirname)); 210 211 (*count)++; 212 node = malloc(sizeof (dirlist_t)); 213 if (node == NULL) 214 return (AUDITD_NO_MEMORY); 215 216 node->dl_flags = 0; 217 node->dl_filename = NULL; 218 node->dl_fd = -1; 219 node->dl_space = PLENTY_SPACE; 220 221 node->dl_dirname = malloc((unsigned)strlen(dirname) + 1); 222 if (node->dl_dirname == NULL) 223 return (AUDITD_NO_MEMORY); 224 225 bs = dirname; 226 while ((*bs == ' ') || (*bs == '\t')) /* trim blanks */ 227 bs++; 228 be = bs + strlen(bs) - 1; 229 while (be > bs) { /* trim trailing blanks */ 230 if ((*bs != ' ') && (*bs != '\t')) 231 break; 232 be--; 233 } 234 *(be + 1) = '\0'; 235 (void) strlcpy(node->dl_dirname, bs, AUDIT_FNAME_SZ); 236 237 if (*listhead != NULL) 238 node->dl_next = *listhead; 239 else 240 node->dl_next = node; 241 *node_p = node; 242 node_p = &(node->dl_next); 243 244 } 245 return (0); 246 } 247 248 /* 249 * create a linked list of directories available for writing 250 * 251 * if a list already exists, the two are compared and the new one is 252 * used only if it is different than the old. 253 * 254 * returns -2 for new or changed list, 0 for unchanged list and -1 for 255 * error. (Positive returns are for AUDITD_<error code> values) 256 * 257 */ 258 static int 259 loadauditlist(char *dirstr, char *minfreestr) 260 { 261 dirlist_t *n1, *n2; 262 dirlist_t *listhead = NULL; 263 dirlist_t *thisdir; 264 int node_count = 0; 265 int rc; 266 int temp_minfree = 0; 267 268 static dirlist_t *activeList = NULL; /* directory list */ 269 270 DPRINT((dbfp, "binfile: Loading audit list from audit service " 271 "(audit_binfile)\n")); 272 273 if (dirstr == NULL || minfreestr == NULL) { 274 DPRINT((dbfp, "binfile: internal error")); 275 return (-1); 276 } 277 if ((rc = growauditlist(&listhead, dirstr, NULL, &node_count)) != 0) { 278 return (rc); 279 } 280 if (node_count == 0) { 281 /* 282 * there was a problem getting the directory 283 * list or remote host info from the audit_binfile 284 * configuration even though auditd thought there was 285 * at least 1 good entry 286 */ 287 DPRINT((dbfp, "binfile: " 288 "problem getting directory / libpath list " 289 "from audit_binfile configuration.\n")); 290 return (-1); 291 } 292 293 #if DEBUG 294 /* print out directory list */ 295 if (listhead != NULL) { 296 (void) fprintf(dbfp, "Directory list:\n\t%s\n", 297 listhead->dl_dirname); 298 thisdir = listhead->dl_next; 299 300 while (thisdir != listhead) { 301 (void) fprintf(dbfp, "\t%s\n", thisdir->dl_dirname); 302 thisdir = thisdir->dl_next; 303 } 304 } 305 #endif /* DEBUG */ 306 307 thisdir = listhead; 308 309 /* See if the list has changed (rc = 0 if no change, else 1) */ 310 rc = 0; 311 if (node_count == activeCount) { 312 n1 = listhead; 313 n2 = activeList; 314 do { 315 if (strcmp(n1->dl_dirname, n2->dl_dirname) != 0) { 316 DPRINT((dbfp, 317 "binfile: new dirname = %s\n" 318 "binfile: old dirname = %s\n", 319 n1->dl_dirname, 320 n2->dl_dirname)); 321 rc = -2; 322 break; 323 } 324 n1 = n1->dl_next; 325 n2 = n2->dl_next; 326 } while ((n1 != listhead) && (n2 != activeList)); 327 } else { 328 DPRINT((dbfp, "binfile: dir counts differs\n" 329 "binfile: old dir count = %d\n" 330 "binfile: new dir count = %d\n", 331 activeCount, node_count)); 332 rc = -2; 333 } 334 if (rc == -2) { 335 (void) pthread_mutex_lock(&log_mutex); 336 DPRINT((dbfp, "loadauditlist: close / open audit.log(4)\n")); 337 if (open_log(listhead) == 0) { 338 openNewFile = 1; /* try again later */ 339 } else { 340 openNewFile = 0; 341 } 342 freedirlist(activeList); /* old list */ 343 activeList = listhead; /* new list */ 344 activeDir = startdir = thisdir; 345 activeCount = node_count; 346 (void) pthread_mutex_unlock(&log_mutex); 347 } else { 348 freedirlist(listhead); 349 } 350 351 /* Get the minfree value. */ 352 if (minfreestr != NULL) 353 temp_minfree = atoi(minfreestr); 354 355 if ((temp_minfree < 0) || (temp_minfree > 100)) 356 temp_minfree = 0; 357 358 if (minfree != temp_minfree) { 359 DPRINT((dbfp, "minfree: old = %d, new = %d\n", 360 minfree, temp_minfree)); 361 rc = -2; /* data change */ 362 minfree = temp_minfree; 363 } 364 365 return (rc); 366 } 367 368 /* 369 * getauditdate - get the current time (GMT) and put it in the form 370 * yyyymmddHHMMSS . 371 */ 372 static void 373 getauditdate(char *date) 374 { 375 struct timeval tp; 376 struct timezone tzp; 377 struct tm tm; 378 379 (void) gettimeofday(&tp, &tzp); 380 tm = *gmtime(&tp.tv_sec); 381 /* 382 * NOTE: if we want to use gmtime, we have to be aware that the 383 * structure only keeps the year as an offset from TM_YEAR_BASE. 384 * I have used TM_YEAR_BASE in this code so that if they change 385 * this base from 1900 to 2000, it will hopefully mean that this 386 * code does not have to change. TM_YEAR_BASE is defined in 387 * tzfile.h . 388 */ 389 (void) sprintf(date, "%.4d%.2d%.2d%.2d%.2d%.2d", 390 tm.tm_year + TM_YEAR_BASE, tm.tm_mon + 1, tm.tm_mday, 391 tm.tm_hour, tm.tm_min, tm.tm_sec); 392 } 393 394 395 396 /* 397 * write_file_token - put the file token into the audit log 398 */ 399 static int 400 write_file_token(int fd, char *name) 401 { 402 adr_t adr; /* xdr ptr */ 403 struct timeval tv; /* time now */ 404 char for_adr[AUDIT_FNAME_SZ + AUDIT_FNAME_SZ]; /* plenty of room */ 405 char token_id; 406 short i; 407 408 (void) gettimeofday(&tv, (struct timezone *)0); 409 i = strlen(name) + 1; 410 adr_start(&adr, for_adr); 411 #ifdef _LP64 412 token_id = AUT_OTHER_FILE64; 413 adr_char(&adr, &token_id, 1); 414 adr_int64(&adr, (int64_t *)& tv, 2); 415 #else 416 token_id = AUT_OTHER_FILE32; 417 adr_char(&adr, &token_id, 1); 418 adr_int32(&adr, (int32_t *)& tv, 2); 419 #endif 420 421 adr_short(&adr, &i, 1); 422 adr_char(&adr, name, i); 423 424 if (write(fd, for_adr, adr_count(&adr)) < 0) { 425 DPRINT((dbfp, "binfile: Bad write\n")); 426 return (errno); 427 } 428 return (0); 429 } 430 431 /* 432 * close_log - close the file if open. Also put the name of the 433 * new log file in the trailer, and rename the old file 434 * to oldname. The caller must hold log_mutext while calling 435 * close_log since any change to activeDir is a complete redo 436 * of all it points to. 437 * arguments - 438 * oldname - the new name for the file to be closed 439 * newname - the name of the new log file (for the trailer) 440 */ 441 static void 442 close_log(dirlist_t **lastOpenDir_ptr, char *oname, char *newname) 443 { 444 char auditdate[AUDIT_DATE_SZ+1]; 445 char *name; 446 char oldname[AUDIT_FNAME_SZ+1]; 447 dirlist_t *currentdir = *lastOpenDir_ptr; 448 449 if ((currentdir == NULL) || (currentdir->dl_fd == -1)) 450 return; 451 /* 452 * If oldname is blank, we were called by auditd_plugin_close() 453 * instead of by open_log, so we need to update our name. 454 */ 455 (void) strlcpy(oldname, oname, AUDIT_FNAME_SZ); 456 457 if (strcmp(oldname, "") == 0) { 458 getauditdate(auditdate); 459 460 assert(currentdir->dl_filename != NULL); 461 462 (void) strlcpy(oldname, currentdir->dl_filename, 463 AUDIT_FNAME_SZ); 464 465 name = strrchr(oldname, '/') + 1; 466 (void) memcpy(name + AUDIT_DATE_SZ + 1, auditdate, 467 AUDIT_DATE_SZ); 468 } 469 /* 470 * Write the trailer record and rename and close the file. 471 * If any of the write, rename, or close fail, ignore it 472 * since there is not much else we can do and the next open() 473 * will trigger the necessary full directory logic. 474 * 475 * newname is "" if binfile is being closed down. 476 */ 477 (void) write_file_token(currentdir->dl_fd, newname); 478 if (currentdir->dl_fd >= 0) { 479 (void) fsync(currentdir->dl_fd); 480 (void) close(currentdir->dl_fd); 481 } 482 currentdir->dl_fd = -1; 483 (void) rename(currentdir->dl_filename, oldname); 484 485 DPRINT((dbfp, "binfile: Log closed %s\n", oldname)); 486 487 freedirlist(currentdir); 488 *lastOpenDir_ptr = NULL; 489 } 490 491 492 /* 493 * open_log - open a new file in the current directory. If a 494 * file is already open, close it. 495 * 496 * return 1 if ok, 0 if all directories are full. 497 * 498 * lastOpenDir - used to get the oldfile name (and change it), 499 * to close the oldfile. 500 * 501 * The caller must hold log_mutex while calling open_log. 502 * 503 */ 504 static int 505 open_log(dirlist_t *current_dir) 506 { 507 char auditdate[AUDIT_DATE_SZ + 1]; 508 char oldname[AUDIT_FNAME_SZ + 1] = ""; 509 char newname[AUDIT_FNAME_SZ + 1]; 510 char *name; /* pointer into oldname */ 511 int opened = 0; 512 int error = 0; 513 int newfd = 0; 514 515 static char host[MAXHOSTNAMELEN + 1] = ""; 516 /* previous directory with open log file */ 517 518 if (host[0] == '\0') 519 (void) gethostname(host, MAXHOSTNAMELEN); 520 521 /* Get a filename which does not already exist */ 522 while (!opened) { 523 getauditdate(auditdate); 524 (void) snprintf(newname, AUDIT_FNAME_SZ, 525 "%s/%s.not_terminated.%s", 526 current_dir->dl_dirname, auditdate, host); 527 newfd = open(newname, 528 O_RDWR | O_APPEND | O_CREAT | O_EXCL, 0640); 529 if (newfd < 0) { 530 switch (errno) { 531 case EEXIST: 532 DPRINT((dbfp, 533 "open_log says duplicate for %s " 534 "(will try another)\n", newname)); 535 (void) sleep(1); 536 break; 537 case ENOENT: { 538 /* invalid path */ 539 char *msg; 540 (void) asprintf(&msg, 541 gettext("No such p_dir: %s\n"), 542 current_dir->dl_dirname); 543 DPRINT((dbfp, 544 "open_log says about %s: %s\n", 545 newname, strerror(errno))); 546 __audit_syslog("audit_binfile.so", 547 LOG_CONS | LOG_NDELAY, 548 LOG_DAEMON, LOG_ERR, msg); 549 free(msg); 550 current_dir = current_dir->dl_next; 551 return (0); 552 } 553 default: 554 /* open failed */ 555 DPRINT((dbfp, 556 "open_log says full for %s: %s\n", 557 newname, strerror(errno))); 558 current_dir->dl_space = SPACE_FULL; 559 current_dir = current_dir->dl_next; 560 return (0); 561 } /* switch */ 562 } else 563 opened = 1; 564 } /* while */ 565 566 /* 567 * When we get here, we have opened our new log file. 568 * Now we need to update the name of the old file to 569 * store in this file's header. lastOpenDir may point 570 * to current_dir if the list is only one entry long and 571 * there is only one list. 572 */ 573 if ((lastOpenDir != NULL) && (lastOpenDir->dl_filename != NULL)) { 574 (void) strlcpy(oldname, lastOpenDir->dl_filename, 575 AUDIT_FNAME_SZ); 576 name = (char *)strrchr(oldname, '/') + 1; 577 578 (void) memcpy(name + AUDIT_DATE_SZ + 1, auditdate, 579 AUDIT_DATE_SZ); 580 581 close_log(&lastOpenDir, oldname, newname); 582 } 583 error = write_file_token(newfd, oldname); 584 if (error) { 585 /* write token failed */ 586 (void) close(newfd); 587 588 current_dir->dl_space = SPACE_FULL; 589 current_dir->dl_fd = -1; 590 free(current_dir->dl_filename); 591 current_dir->dl_filename = NULL; 592 current_dir = current_dir->dl_next; 593 return (0); 594 } else { 595 if (current_dir->dl_filename != NULL) { 596 free(current_dir->dl_filename); 597 } 598 current_dir->dl_filename = strdup(newname); 599 current_dir->dl_fd = newfd; 600 601 if (lastOpenDir == NULL) { 602 freedirlist(lastOpenDir); 603 if ((lastOpenDir = dupdirnode(current_dir)) == NULL) { 604 __audit_syslog("audit_binfile.so", 605 LOG_CONS | LOG_NDELAY, 606 LOG_DAEMON, LOG_ERR, gettext("no memory")); 607 return (0); 608 } 609 DPRINT((dbfp, "open_log created new lastOpenDir " 610 "(%s, %s [fd: %d])\n", 611 lastOpenDir->dl_dirname == NULL ? "" : 612 lastOpenDir->dl_dirname, 613 lastOpenDir->dl_filename == NULL ? "" : 614 lastOpenDir->dl_filename, lastOpenDir->dl_fd)); 615 } 616 617 /* 618 * New file opened, so reset file size statistic (used 619 * to ensure audit log does not grow above size limit 620 * set by p_fsize). 621 */ 622 binfile_cursize = 0; 623 624 (void) __logpost(newname); 625 626 DPRINT((dbfp, "binfile: Log opened: %s\n", newname)); 627 return (1); 628 } 629 } 630 631 #define IGNORE_SIZE 8192 632 /* 633 * spacecheck - determine whether the given directory's filesystem 634 * has the at least the space requested. Also set the space 635 * value in the directory list structure. If the caller 636 * passes other than PLENTY_SPACE or SOFT_SPACE, the caller should 637 * ignore the return value. Otherwise, 0 = less than the 638 * requested space is available, 1 = at least the requested space 639 * is available. 640 * 641 * log_mutex must be held by the caller 642 * 643 * -1 is returned if stat fails 644 * 645 * IGNORE_SIZE is one page (Sol 9 / 10 timeframe) and is the default 646 * buffer size written for Sol 9 and earlier. To keep the same accuracy 647 * for the soft limit check as before, spacecheck checks for space 648 * remaining IGNORE_SIZE bytes. This reduces the number of statvfs() 649 * calls and related math. 650 * 651 * globals - 652 * minfree - the soft limit, i.e., the % of filesystem to reserve 653 */ 654 static int 655 spacecheck(dirlist_t *thisdir, int test_limit, size_t next_buf_size) 656 { 657 struct statvfs sb; 658 static int ignore_size = 0; 659 660 ignore_size += next_buf_size; 661 662 if ((test_limit == PLENTY_SPACE) && (ignore_size < IGNORE_SIZE)) 663 return (1); 664 665 assert(thisdir != NULL); 666 667 if (statvfs(thisdir->dl_dirname, &sb) < 0) { 668 thisdir->dl_space = SPACE_FULL; 669 minfreeblocks = AVAIL_MIN; 670 return (-1); 671 } else { 672 minfreeblocks = ((minfree * sb.f_blocks) / 100) + AVAIL_MIN; 673 674 if (sb.f_bavail < AVAIL_MIN) 675 thisdir->dl_space = SPACE_FULL; 676 else if (sb.f_bavail > minfreeblocks) { 677 thisdir->dl_space = fullness_state = PLENTY_SPACE; 678 ignore_size = 0; 679 } else 680 thisdir->dl_space = SOFT_SPACE; 681 } 682 if (thisdir->dl_space == PLENTY_SPACE) 683 return (1); 684 685 return (thisdir->dl_space == test_limit); 686 } 687 688 /* 689 * Parses p_fsize value and contains it within the range FSIZE_MIN and 690 * INT_MAX so using uints won't cause an undetected overflow of 691 * INT_MAX. Defaults to 0 if the value is invalid or is missing. 692 */ 693 static void 694 save_maxsize(char *maxsize) 695 { 696 /* 697 * strtol() returns a long which could be larger than int so 698 * store here for sanity checking first 699 */ 700 long proposed_maxsize; 701 702 if (maxsize != NULL) { 703 /* 704 * There is no explicit error return from strtol() so 705 * we may need to depend on the value of errno. 706 */ 707 errno = 0; 708 proposed_maxsize = strtol(maxsize, (char **)NULL, 10); 709 710 /* 711 * If sizeof(long) is greater than sizeof(int) on this 712 * platform, proposed_maxsize might be greater than 713 * INT_MAX without it being reported as ERANGE. 714 */ 715 if ((errno == ERANGE) || 716 ((proposed_maxsize != 0) && 717 (proposed_maxsize < FSIZE_MIN)) || 718 (proposed_maxsize > INT_MAX)) { 719 binfile_maxsize = 0; 720 DPRINT((dbfp, "binfile: p_fsize parameter out of " 721 "range: %s\n", maxsize)); 722 /* 723 * Inform administrator of the error via 724 * syslog 725 */ 726 __audit_syslog("audit_binfile.so", 727 LOG_CONS | LOG_NDELAY, 728 LOG_DAEMON, LOG_ERR, 729 gettext("p_fsize parameter out of range\n")); 730 } else { 731 binfile_maxsize = proposed_maxsize; 732 } 733 } else { /* p_fsize string was not present */ 734 binfile_maxsize = 0; 735 } 736 737 DPRINT((dbfp, "binfile: set maxsize to %d\n", binfile_maxsize)); 738 } 739 740 /* 741 * auditd_plugin() writes a buffer to the currently open file. The 742 * global "openNewFile" is used to force a new log file for cases such 743 * as the initial open, when minfree is reached, the p_fsize value is 744 * exceeded or the current file system fills up, and "audit -s" with 745 * changed parameters. For "audit -n" a new log file is opened 746 * immediately in auditd_plugin_open(). 747 * 748 * This function manages one or more audit directories as follows: 749 * 750 * If the current open file is in a directory that has not 751 * reached the soft limit, write the input data and return. 752 * 753 * Scan the list of directories for one which has not reached 754 * the soft limit; if one is found, write and return. Such 755 * a writable directory is in "PLENTY_SPACE" state. 756 * 757 * Scan the list of directories for one which has not reached 758 * the hard limit; if one is found, write and return. This 759 * directory in in "SOFT_SPACE" state. 760 * 761 * Oh, and if a write fails, handle it like a hard space limit. 762 * 763 * audit_warn (via __audit_dowarn()) is used to alert an operator 764 * at various levels of fullness. 765 */ 766 /* ARGSUSED */ 767 auditd_rc_t 768 auditd_plugin(const char *input, size_t in_len, uint64_t sequence, char **error) 769 { 770 auditd_rc_t rc = AUDITD_FAIL; 771 int open_status; 772 size_t out_len; 773 /* avoid excess audit_warnage */ 774 static int allsoftfull_warning = 0; 775 static int allhard_pause = 0; 776 static struct timeval next_allhard; 777 struct timeval now; 778 #if DEBUG 779 int statrc; 780 static char *last_file_written_to = NULL; 781 static uint64_t last_sequence = 0; 782 static uint64_t write_count = 0; 783 784 if ((last_sequence > 0) && (sequence != last_sequence + 1)) 785 (void) fprintf(dbfp, 786 "binfile: buffer sequence=%llu but prev=%llu=n", 787 sequence, last_sequence); 788 last_sequence = sequence; 789 790 (void) fprintf(dbfp, "binfile: input seq=%llu, len=%d\n", 791 sequence, in_len); 792 #endif 793 *error = NULL; 794 /* 795 * lock is for activeDir, referenced by open_log() and close_log() 796 */ 797 (void) pthread_mutex_lock(&log_mutex); 798 799 /* 800 * If this would take us over the maximum size, open a new 801 * file, unless maxsize is 0, in which case growth of the 802 * audit log is unrestricted. 803 */ 804 if ((binfile_maxsize != 0) && 805 ((binfile_cursize + in_len) > binfile_maxsize)) { 806 DPRINT((dbfp, "binfile: maxsize exceeded, opening new audit " 807 "file.\n")); 808 openNewFile = 1; 809 } 810 811 while (rc == AUDITD_FAIL) { 812 open_status = 1; 813 if (openNewFile) { 814 open_status = open_log(activeDir); 815 if (open_status == 1) /* ok */ 816 openNewFile = 0; 817 } 818 /* 819 * consider "space ok" return and error return the same; 820 * a -1 means spacecheck couldn't check for space. 821 */ 822 #if !DEBUG 823 if ((open_status == 1) && 824 (spacecheck(activeDir, fullness_state, in_len) != 0)) { 825 #else 826 if ((open_status == 1) && 827 (statrc = spacecheck(activeDir, fullness_state, 828 in_len) != 0)) { 829 DPRINT((dbfp, "binfile: returned from spacecheck\n")); 830 /* 831 * The last copy of last_file_written_to is 832 * never free'd, so there will be one open 833 * memory reference on exit. It's debug only. 834 */ 835 if ((last_file_written_to != NULL) && 836 (strcmp(last_file_written_to, 837 activeDir->dl_filename) != 0)) { 838 DPRINT((dbfp, "binfile: now writing to %s\n", 839 activeDir->dl_filename)); 840 free(last_file_written_to); 841 } 842 DPRINT((dbfp, "binfile: finished some debug stuff\n")); 843 last_file_written_to = 844 strdup(activeDir->dl_filename); 845 #endif 846 out_len = write(activeDir->dl_fd, input, in_len); 847 DPRINT((dbfp, "binfile: finished the write\n")); 848 849 binfile_cursize += out_len; 850 851 if (out_len == in_len) { 852 DPRINT((dbfp, 853 "binfile: write_count=%llu, sequence=%llu," 854 " l=%u\n", 855 ++write_count, sequence, out_len)); 856 allsoftfull_warning = 0; 857 activeDir->dl_flags = 0; 858 859 rc = AUDITD_SUCCESS; 860 break; 861 } else if (!(activeDir->dl_flags & HARD_WARNED)) { 862 DPRINT((dbfp, 863 "binfile: write failed, sequence=%llu, " 864 "l=%u\n", sequence, out_len)); 865 DPRINT((dbfp, "hard warning sent.\n")); 866 __audit_dowarn("hard", activeDir->dl_dirname, 867 0); 868 869 activeDir->dl_flags |= HARD_WARNED; 870 } 871 } else { 872 DPRINT((dbfp, "binfile: statrc=%d, fullness_state=%d\n", 873 statrc, fullness_state)); 874 if (!(activeDir->dl_flags & SOFT_WARNED) && 875 (activeDir->dl_space == SOFT_SPACE)) { 876 DPRINT((dbfp, "soft warning sent\n")); 877 __audit_dowarn("soft", 878 activeDir->dl_dirname, 0); 879 activeDir->dl_flags |= SOFT_WARNED; 880 } 881 if (!(activeDir->dl_flags & HARD_WARNED) && 882 (activeDir->dl_space == SPACE_FULL)) { 883 DPRINT((dbfp, "hard warning sent.\n")); 884 __audit_dowarn("hard", 885 activeDir->dl_dirname, 0); 886 activeDir->dl_flags |= HARD_WARNED; 887 } 888 } 889 DPRINT((dbfp, "binfile: activeDir=%s, next=%s\n", 890 activeDir->dl_dirname, activeDir->dl_next->dl_dirname)); 891 892 activeDir = activeDir->dl_next; 893 openNewFile = 1; 894 895 if (activeDir == startdir) { /* full circle */ 896 if (fullness_state == PLENTY_SPACE) { /* once */ 897 fullness_state = SOFT_SPACE; 898 if (allsoftfull_warning == 0) { 899 allsoftfull_warning++; 900 __audit_dowarn("allsoft", "", 0); 901 } 902 } else { /* full circle twice */ 903 if ((hung_count > 0) && !allhard_pause) { 904 allhard_pause = 1; 905 (void) gettimeofday(&next_allhard, 906 NULL); 907 next_allhard.tv_sec += ALLHARD_DELAY; 908 } 909 910 if (allhard_pause) { 911 (void) gettimeofday(&now, NULL); 912 if (now.tv_sec >= next_allhard.tv_sec) { 913 allhard_pause = 0; 914 __audit_dowarn("allhard", "", 915 ++hung_count); 916 } 917 } else { 918 __audit_dowarn("allhard", "", 919 ++hung_count); 920 } 921 minfreeblocks = AVAIL_MIN; 922 rc = AUDITD_RETRY; 923 *error = strdup(gettext( 924 "all partitions full\n")); 925 (void) __logpost(""); 926 } 927 } 928 } 929 (void) pthread_mutex_unlock(&log_mutex); 930 931 return (rc); 932 } 933 934 935 /* 936 * It may be called multiple times as auditd handles SIGHUP and SIGUSR1 937 * corresponding to the audit(1M) flags -s and -n 938 * 939 * kvlist is NULL only if auditd caught a SIGUSR1 (audit -n), so after the first 940 * time open is called; the reason is -s if kvlist != NULL and -n otherwise. 941 * 942 */ 943 auditd_rc_t 944 auditd_plugin_open(const kva_t *kvlist, char **ret_list, char **error) 945 { 946 int rc = 0; 947 int status; 948 int reason; 949 char *dirlist; 950 char *minfree; 951 char *maxsize; 952 kva_t *kv; 953 954 *error = NULL; 955 *ret_list = NULL; 956 kv = (kva_t *)kvlist; 957 958 if (am_open) { 959 if (kvlist == NULL) 960 reason = 1; /* audit -n */ 961 else 962 reason = 2; /* audit -s */ 963 } else { 964 reason = 0; /* initial open */ 965 #if DEBUG 966 dbfp = __auditd_debug_file_open(); 967 #endif 968 } 969 DPRINT((dbfp, "binfile: am_open=%d, reason=%d\n", am_open, reason)); 970 971 am_open = 1; 972 973 if (kvlist == NULL) { 974 dirlist = NULL; 975 minfree = NULL; 976 maxsize = NULL; 977 } else { 978 dirlist = kva_match(kv, "p_dir"); 979 minfree = kva_match(kv, "p_minfree"); 980 maxsize = kva_match(kv, "p_fsize"); 981 } 982 switch (reason) { 983 case 0: /* initial open */ 984 if (!binfile_is_open) 985 (void) pthread_mutex_init(&log_mutex, NULL); 986 binfile_is_open = 1; 987 openNewFile = 1; 988 /* FALLTHRU */ 989 case 2: /* audit -s */ 990 /* handle p_fsize parameter */ 991 save_maxsize(maxsize); 992 993 fullness_state = PLENTY_SPACE; 994 status = loadauditlist(dirlist, minfree); 995 996 if (status == -1) { 997 (void) __logpost(""); 998 *error = strdup(gettext("no directories configured")); 999 return (AUDITD_RETRY); 1000 } else if (status == AUDITD_NO_MEMORY) { 1001 (void) __logpost(""); 1002 *error = strdup(gettext("no memory")); 1003 return (status); 1004 } else { /* status is 0 or -2 (no change or changed) */ 1005 hung_count = 0; 1006 DPRINT((dbfp, "binfile: loadauditlist returned %d\n", 1007 status)); 1008 } 1009 break; 1010 case 1: /* audit -n */ 1011 (void) pthread_mutex_lock(&log_mutex); 1012 if (open_log(activeDir) == 1) /* ok */ 1013 openNewFile = 0; 1014 (void) pthread_mutex_unlock(&log_mutex); 1015 break; 1016 } 1017 1018 rc = AUDITD_SUCCESS; 1019 *ret_list = NULL; 1020 1021 return (rc); 1022 } 1023 1024 auditd_rc_t 1025 auditd_plugin_close(char **error) 1026 { 1027 *error = NULL; 1028 1029 (void) pthread_mutex_lock(&log_mutex); 1030 close_log(&lastOpenDir, "", ""); 1031 freedirlist(activeDir); 1032 activeDir = NULL; 1033 (void) pthread_mutex_unlock(&log_mutex); 1034 1035 DPRINT((dbfp, "binfile: closed\n")); 1036 1037 (void) __logpost(""); 1038 1039 if (binfile_is_open) { 1040 (void) pthread_mutex_destroy(&log_mutex); 1041 binfile_is_open = 0; 1042 #if DEBUG 1043 } else { 1044 (void) fprintf(dbfp, 1045 "auditd_plugin_close() called when already closed."); 1046 #endif 1047 } 1048 am_open = 0; 1049 return (AUDITD_SUCCESS); 1050 } 1051