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