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