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) 2009, 2010, Oracle and/or its affiliates. All rights reserved. 23 * 24 * send audit records to remote host 25 * 26 */ 27 28 /* 29 * auditd_plugin_open(), auditd_plugin() and auditd_plugin_close() 30 * implement a replaceable library for use by auditd; they are a 31 * project private interface and may change without notice. 32 */ 33 34 #include <arpa/inet.h> 35 #include <assert.h> 36 #include <audit_plugin.h> 37 #include <bsm/audit.h> 38 #include <bsm/audit_record.h> 39 #include <bsm/libbsm.h> 40 #include <errno.h> 41 #include <fcntl.h> 42 #include <gssapi/gssapi.h> 43 #include <libintl.h> 44 #include <netdb.h> 45 #include <pthread.h> 46 #include <rpc/rpcsec_gss.h> 47 #include <secdb.h> 48 #include <signal.h> 49 #include <stdio.h> 50 #include <stdlib.h> 51 #include <string.h> 52 #include <strings.h> 53 #include <ctype.h> 54 #include <sys/param.h> 55 #include <sys/socket.h> 56 #include <sys/types.h> 57 #include <unistd.h> 58 #include <poll.h> 59 60 #include "audit_remote.h" 61 62 #define DEFAULT_RETRIES 3 /* default connection retries */ 63 #define DEFAULT_TIMEOUT 5 /* default connection timeout (in secs) */ 64 #define NOSUCCESS_DELAY 20 /* unsuccessful delivery to all p_hosts */ 65 66 #define FL_SET B_TRUE /* set_fdfl(): set the flag */ 67 #define FL_UNSET B_FALSE /* set_fdfl(): unset the flag */ 68 69 static int nosuccess_cnt; /* unsuccessful delivery counter */ 70 71 72 static int retries = DEFAULT_RETRIES; /* connection retries */ 73 int timeout = DEFAULT_TIMEOUT; /* connection timeout */ 74 static int timeout_p_timeout = -1; /* p_timeout attr storage */ 75 76 /* time reset mechanism; x .. timeout_p_timeout */ 77 #define RST_TIMEOUT(x) (x != -1 ? x : DEFAULT_TIMEOUT) 78 79 /* semi-exponential timeout back off; x .. attempts, y .. timeout */ 80 #define BOFF_TIMEOUT(x, y) (x < 3 ? y * 2 * x : y * 8) 81 82 /* general plugin lock */ 83 pthread_mutex_t plugin_mutex = PTHREAD_MUTEX_INITIALIZER; 84 85 static struct hostlist_s *current_host; 86 static struct hostlist_s *hosts; 87 88 extern struct transq_hdr_s transq_hdr; 89 static long transq_count_max; 90 extern pthread_mutex_t transq_lock; 91 92 extern pthread_t recv_tid; 93 94 extern boolean_t notify_pipe_ready; 95 extern int notify_pipe[2]; 96 97 #if DEBUG 98 FILE *dfile; /* debug file */ 99 #endif 100 101 /* 102 * set_transq_count_max() - sets the transq_count_max value based on kernel 103 * audit queue high water mark. This is backup solution for a case, when the 104 * plugin audit_control(4) option lacks (intentionally) the qsize option. 105 */ 106 static auditd_rc_t 107 set_transq_count_max() 108 { 109 struct au_qctrl qctrl; 110 111 if (auditon(A_GETQCTRL, (caddr_t)&qctrl, 0) != -1) { 112 transq_count_max = qctrl.aq_hiwater; 113 DPRINT((dfile, "Transmission queue max length set to %ld\n", 114 transq_count_max)); 115 return (AUDITD_SUCCESS); 116 } 117 118 DPRINT((dfile, "Setting the transmission queue max length failed\n")); 119 return (AUDITD_RETRY); 120 } 121 122 /* 123 * get_port_default() - set the default port number; note, that "solaris-audit" 124 * used below in the code is the IANA assigned service name for the secure 125 * remote solaris audit logging. 126 */ 127 static auditd_rc_t 128 get_port_default(int *port_default) 129 { 130 131 struct servent serventry; 132 char serventry_buf[1024]; 133 134 if (getservbyname_r("solaris-audit", "tcp", &serventry, 135 (char *)&serventry_buf, sizeof (serventry_buf)) == NULL) { 136 DPRINT((dfile, "unable to get default port number\n")); 137 #if DEBUG 138 if (errno == ERANGE) { 139 DPRINT((dfile, "low on buffer\n")); 140 } 141 #endif 142 return (AUDITD_INVALID); 143 } 144 *port_default = ntohs(serventry.s_port); 145 DPRINT((dfile, "default port: %d\n", *port_default)); 146 147 return (AUDITD_SUCCESS); 148 } 149 150 /* 151 * trim_me() - trims the white space characters around the specified string. 152 * Inputs - pointer to the beginning of the string (str_ptr); returns - pointer 153 * to the trimmed string. Function returns NULL pointer in case of received 154 * empty string, NULL pointer or in case the pointed string consists of white 155 * space characters only. 156 */ 157 static char * 158 trim_me(char *str_ptr) { 159 160 char *str_end; 161 162 if (str_ptr == NULL || *str_ptr == '\0') { 163 return (NULL); 164 } 165 166 while (isspace(*str_ptr)) { 167 str_ptr++; 168 } 169 if (*str_ptr == '\0') { 170 return (NULL); 171 } 172 173 str_end = str_ptr + strlen(str_ptr); 174 175 while (str_end > str_ptr && isspace(str_end[-1])) { 176 str_end--; 177 } 178 *str_end = '\0'; 179 180 return (str_ptr); 181 } 182 183 184 /* 185 * parsehosts() end parses the host string (hosts_str) 186 */ 187 static auditd_rc_t 188 parsehosts(char *hosts_str, char **error) 189 { 190 char *hostportmech, *hpm; 191 char *hostname; 192 char *port_str; 193 char *mech_str; 194 int port; 195 int port_default = -1; 196 gss_OID mech_oid; 197 char *lasts_hpm; 198 hostlist_t *lasthost = NULL; 199 hostlist_t *newhost; 200 struct hostent *hostentry; 201 int error_num; 202 int rc; 203 #if DEBUG 204 char addr_buf[INET6_ADDRSTRLEN]; 205 int num_of_hosts = 0; 206 #endif 207 208 hosts = lasthost; 209 210 DPRINT((dfile, "parsing %s\n", hosts_str)); 211 while ((hostportmech = strtok_r(hosts_str, ",", &lasts_hpm)) != NULL) { 212 213 hosts_str = NULL; 214 hostname = NULL; 215 port_str = NULL; 216 port = port_default; 217 mech_str = NULL; 218 mech_oid = GSS_C_NO_OID; 219 220 DPRINT((dfile, "parsing host:port:mech %s\n", hostportmech)); 221 222 if (strncmp(hostportmech, ":", 1 == 0)) { /* ":port:" case */ 223 *error = strdup(gettext("no hostname specified")); 224 return (AUDITD_INVALID); 225 } 226 227 /* parse single host:port:mech target */ 228 while ((hpm = strsep(&hostportmech, ":")) != NULL) { 229 230 if (hostname == NULL) { 231 hostname = hpm; 232 continue; 233 } 234 if (port_str == NULL) { 235 port_str = hpm; 236 continue; 237 } 238 if (mech_str == NULL) { 239 mech_str = hpm; 240 continue; 241 } 242 243 /* too many colons in the hostportmech string */ 244 *error = strdup(gettext("invalid host:port:mech " 245 "specification")); 246 return (AUDITD_INVALID); 247 } 248 249 if (hostname == NULL || *hostname == '\0') { 250 *error = strdup(gettext("invalid hostname " 251 "specification")); 252 return (AUDITD_INVALID); 253 } 254 255 /* trim hostname */ 256 hostname = trim_me(hostname); 257 if (hostname == NULL || *hostname == '\0') { 258 *error = strdup(gettext("empty hostname " 259 "specification")); 260 return (AUDITD_INVALID); 261 } 262 263 DPRINT((dfile, "resolving address for %s\n", hostname)); 264 265 hostentry = getipnodebyname(hostname, AF_INET6, 0, &error_num); 266 if (!hostentry) { 267 hostentry = getipnodebyname(hostname, AF_INET, 0, 268 &error_num); 269 } 270 if (!hostentry) { 271 if (error_num == TRY_AGAIN) { 272 *error = strdup(gettext("host not found, " 273 "try later")); 274 return (AUDITD_RETRY); 275 } else { 276 *error = strdup(gettext("host not found")); 277 return (AUDITD_INVALID); 278 } 279 } 280 DPRINT((dfile, "hostentry: h_name=%s, addr_len=%d, addr=%s\n", 281 hostentry->h_name, hostentry->h_length, 282 inet_ntop(hostentry->h_addrtype, 283 hostentry->h_addr_list[0], addr_buf, 284 INET6_ADDRSTRLEN))); 285 286 /* trim port */ 287 port_str = trim_me(port_str); 288 if (port_str == NULL || *port_str == '\0') { 289 if (port_default == -1 && 290 (rc = get_port_default(&port_default)) 291 != AUDITD_SUCCESS) { 292 *error = strdup(gettext( 293 "unable to get default port number")); 294 return (rc); 295 } 296 port = port_default; 297 DPRINT((dfile, "port: %d (default)\n", port)); 298 } else { 299 errno = 0; 300 port = atoi(port_str); 301 if (errno != 0 || port < 1 || port > USHRT_MAX) { 302 *error = strdup(gettext("invalid port number")); 303 return (AUDITD_INVALID); 304 } 305 DPRINT((dfile, "port: %d\n", port)); 306 } 307 308 /* trim mechanism */ 309 mech_str = trim_me(mech_str); 310 if (mech_str != NULL && *mech_str != '\0') { 311 if (rpc_gss_mech_to_oid(mech_str, &mech_oid) != TRUE) { 312 *error = strdup(gettext("unknown mechanism")); 313 return (AUDITD_INVALID); 314 } 315 DPRINT((dfile, "mechanism: %s\n", mech_str)); 316 #if DEBUG 317 } else { 318 DPRINT((dfile, "mechanism: null (default)\n")); 319 #endif 320 } 321 322 /* add this host to host list */ 323 newhost = malloc(sizeof (hostlist_t)); 324 if (newhost == NULL) { 325 *error = strdup(gettext("no memory")); 326 return (AUDITD_NO_MEMORY); 327 } 328 newhost->host = hostentry; 329 newhost->port = htons(port); 330 newhost->mech = mech_oid; 331 newhost->next_host = NULL; 332 if (lasthost != NULL) { 333 lasthost->next_host = newhost; 334 lasthost = lasthost->next_host; 335 } else { 336 lasthost = newhost; 337 hosts = newhost; 338 } 339 #if DEBUG 340 num_of_hosts++; 341 #endif 342 } 343 344 current_host = hosts; 345 DPRINT((dfile, "Configured %d hosts.\n", num_of_hosts)); 346 347 return (AUDITD_SUCCESS); 348 } 349 350 351 /* 352 * Frees host list 353 */ 354 static void 355 freehostlist() 356 { 357 hostlist_t *h, *n; 358 359 (void) pthread_mutex_lock(&plugin_mutex); 360 h = hosts; 361 while (h) { 362 n = h->next_host; 363 freehostent(h->host); 364 free(h); 365 h = n; 366 } 367 current_host = NULL; 368 hosts = NULL; 369 (void) pthread_mutex_unlock(&plugin_mutex); 370 } 371 372 #if DEBUG 373 static char * 374 auditd_message(auditd_rc_t msg_code) { 375 char *rc_msg; 376 377 switch (msg_code) { 378 case AUDITD_SUCCESS: 379 rc_msg = strdup("ok"); 380 break; 381 case AUDITD_RETRY: 382 rc_msg = strdup("retry after a delay"); 383 break; 384 case AUDITD_NO_MEMORY: 385 rc_msg = strdup("can't allocate memory"); 386 break; 387 case AUDITD_INVALID: 388 rc_msg = strdup("bad input"); 389 break; 390 case AUDITD_COMM_FAIL: 391 rc_msg = strdup("communications failure"); 392 break; 393 case AUDITD_FATAL: 394 rc_msg = strdup("other error"); 395 break; 396 case AUDITD_FAIL: 397 rc_msg = strdup("other non-fatal error"); 398 break; 399 } 400 return (rc_msg); 401 } 402 #endif 403 404 /* 405 * rsn_to_msg() - translation of the reason of closure identifier to the more 406 * human readable/understandable form. 407 */ 408 static char * 409 rsn_to_msg(close_rsn_t reason) 410 { 411 char *rc_msg; 412 413 switch (reason) { 414 case RSN_UNDEFINED: 415 rc_msg = strdup(gettext("not defined reason of failure")); 416 break; 417 case RSN_INIT_POLL: 418 rc_msg = strdup(gettext("poll() initialization failed")); 419 break; 420 case RSN_TOK_RECV_FAILED: 421 rc_msg = strdup(gettext("token receiving failed")); 422 break; 423 case RSN_TOK_TOO_BIG: 424 rc_msg = strdup(gettext("unacceptable token size")); 425 break; 426 case RSN_TOK_UNVERIFIABLE: 427 rc_msg = strdup(gettext("received unverifiable token")); 428 break; 429 case RSN_SOCKET_CLOSE: 430 rc_msg = strdup(gettext("closed socket")); 431 break; 432 case RSN_SOCKET_CREATE: 433 rc_msg = strdup(gettext("socket creation failed")); 434 break; 435 case RSN_CONNECTION_CREATE: 436 rc_msg = strdup(gettext("connection creation failed")); 437 break; 438 case RSN_PROTOCOL_NEGOTIATE: 439 rc_msg = strdup(gettext("protocol negotiation failed")); 440 break; 441 case RSN_GSS_CTX_ESTABLISH: 442 rc_msg = strdup(gettext("context establishing failed")); 443 break; 444 case RSN_GSS_CTX_EXP: 445 rc_msg = strdup(gettext("context expired")); 446 break; 447 case RSN_UNKNOWN_AF: 448 rc_msg = strdup(gettext("unknown address family")); 449 break; 450 case RSN_MEMORY_ALLOCATE: 451 rc_msg = strdup(gettext("memory allocation failed")); 452 break; 453 default: /* RSN_OTHER_ERR */ 454 rc_msg = strdup(gettext("other, not classified error")); 455 break; 456 } 457 return (rc_msg); 458 } 459 460 /* 461 * set_fdfl() - based on set_fl (FL_SET/FL_UNSET) un/sets the fl flag associated 462 * with fd file descriptor. 463 */ 464 static boolean_t 465 set_fdfl(int fd, int fl, boolean_t set_fl) 466 { 467 int flags; 468 469 /* power of two test - only single bit flags are allowed */ 470 if (!fl || (fl & (fl-1))) { 471 DPRINT((dfile, "incorrect flag - %d isn't power of two\n", fl)); 472 return (B_FALSE); 473 } 474 475 if ((flags = fcntl(fd, F_GETFL, 0)) < 0) { 476 DPRINT((dfile, "cannot get file descriptor flags\n")); 477 return (B_FALSE); 478 } 479 480 if (set_fl) { /* set the fl flag */ 481 if (flags & fl) { 482 return (B_TRUE); 483 } 484 485 flags |= fl; 486 487 } else { /* unset the fl flag */ 488 if (~flags & fl) { 489 return (B_TRUE); 490 } 491 492 flags &= ~fl; 493 } 494 495 if (fcntl(fd, F_SETFL, flags) == -1) { 496 DPRINT((dfile, "cannot %s file descriptor flags\n", 497 (set_fl ? "set" : "unset"))); 498 return (B_FALSE); 499 } 500 501 DPRINT((dfile, "fd: %d - flag: 0%o was %s\n", fd, fl, 502 (set_fl ? "set" : "unset"))); 503 return (B_TRUE); 504 } 505 506 507 /* 508 * create_notify_pipe() - creates the notification pipe. Function returns 509 * B_TRUE/B_FALSE on success/failure. 510 */ 511 static boolean_t 512 create_notify_pipe(int *notify_pipe, char **error) 513 { 514 515 if (pipe(notify_pipe) < 0) { 516 DPRINT((dfile, "Cannot create notify pipe: %s\n", 517 strerror(errno))); 518 *error = strdup(gettext("failed to create notification pipe")); 519 return (B_FALSE); 520 } else { 521 DPRINT((dfile, "Pipe created in:%d out:%d\n", notify_pipe[0], 522 notify_pipe[1])); 523 /* make (only) the pipe "in" end nonblocking */ 524 if (!set_fdfl(notify_pipe[0], O_NONBLOCK, FL_UNSET) || 525 !set_fdfl(notify_pipe[1], O_NONBLOCK, FL_SET)) { 526 DPRINT((dfile, "Cannot prepare blocking scheme on top " 527 "of the notification pipe: %s\n", strerror(errno))); 528 (void) close(notify_pipe[0]); 529 (void) close(notify_pipe[1]); 530 531 *error = strdup(gettext("failed to prepare blocking " 532 "scheme on top of the notification pipe")); 533 return (B_FALSE); 534 } 535 } 536 537 return (B_TRUE); 538 } 539 540 541 /* 542 * auditd_plugin() sends a record via a tcp connection. 543 * 544 * Operation: 545 * - 1 tcp connection opened at a time, referenced by current_host->sockfd 546 * - tries to (open and) send a record to the current_host where its address 547 * is taken from the first hostent h_addr_list entry 548 * - if connection times out, tries second host 549 * - if all hosts where tried tries again for retries number of times 550 * - if everything fails, it bails out with AUDITD_RETRY 551 * 552 * Note, that space on stack allocated for any error message returned along 553 * with AUDITD_RETRY is subsequently freed by auditd. 554 * 555 */ 556 auditd_rc_t 557 auditd_plugin(const char *input, size_t in_len, uint64_t sequence, char **error) 558 { 559 int rc = AUDITD_FAIL; 560 int send_record_rc = SEND_RECORD_FAIL; 561 hostlist_t *start_host; 562 int attempts = 0; 563 char *ext_error; /* extended error string */ 564 close_rsn_t err_rsn = RSN_UNDEFINED; 565 char *rsn_msg; 566 567 #if DEBUG 568 char *rc_msg; 569 static uint64_t last_sequence = 0; 570 571 if ((last_sequence > 0) && (sequence != last_sequence + 1)) { 572 DPRINT((dfile, "audit_remote: buffer sequence=%llu " 573 "but prev=%llu\n", sequence, last_sequence)); 574 } 575 last_sequence = sequence; 576 577 DPRINT((dfile, "audit_remote: input seq=%llu, len=%d\n", 578 sequence, in_len)); 579 #endif 580 581 (void) pthread_mutex_lock(&transq_lock); 582 583 if (transq_hdr.count == transq_count_max) { 584 DPRINT((dfile, "Transmission queue is full (%ld)\n", 585 transq_hdr.count)); 586 (void) pthread_mutex_unlock(&transq_lock); 587 *error = strdup(gettext("retransmission queue is full")); 588 return (AUDITD_RETRY); 589 } 590 (void) pthread_mutex_unlock(&transq_lock); 591 592 593 (void) pthread_mutex_lock(&plugin_mutex); 594 595 /* cycle over the hosts and possibly deliver the record */ 596 start_host = current_host; 597 while (rc != AUDITD_SUCCESS) { 598 DPRINT((dfile, "Trying to send record to %s [attempt:%d/%d]\n", 599 current_host->host->h_name, attempts + 1, retries)); 600 601 send_record_rc = send_record(current_host, input, in_len, 602 sequence, &err_rsn); 603 DPRINT((dfile, "send_record() returned %d - ", send_record_rc)); 604 605 switch (send_record_rc) { 606 case SEND_RECORD_SUCCESS: 607 DPRINT((dfile, "success\n")); 608 nosuccess_cnt = 0; 609 rc = AUDITD_SUCCESS; 610 break; 611 case SEND_RECORD_NEXT: 612 DPRINT((dfile, "retry the same host: %s (penalty)\n", 613 current_host->host->h_name)); 614 attempts++; 615 break; 616 case SEND_RECORD_RETRY: 617 DPRINT((dfile, "retry the same host: %s (no penalty)\n", 618 current_host->host->h_name)); 619 break; 620 } 621 622 623 if (send_record_rc == SEND_RECORD_NEXT) { 624 625 /* warn about unsuccessful auditd record delivery */ 626 rsn_msg = rsn_to_msg(err_rsn); 627 (void) asprintf(&ext_error, 628 "retry %d connection %s:%d %s", attempts + 1, 629 current_host->host->h_name, 630 ntohs(current_host->port), rsn_msg); 631 if (ext_error == NULL) { 632 free(rsn_msg); 633 *error = strdup(gettext("no memory")); 634 rc = AUDITD_NO_MEMORY; 635 break; 636 } 637 __audit_dowarn2("plugin", "audit_remote.so", "retry", 638 ext_error, attempts + 1); 639 free(rsn_msg); 640 free(ext_error); 641 642 643 if (attempts < retries) { 644 /* semi-exponential timeout back off */ 645 timeout = BOFF_TIMEOUT(attempts, timeout); 646 DPRINT((dfile, "New timeout=%d\n", timeout)); 647 } else { 648 /* get next host */ 649 current_host = current_host->next_host; 650 if (current_host == NULL) { 651 current_host = hosts; 652 } 653 timeout = RST_TIMEOUT(timeout_p_timeout); 654 DPRINT((dfile, "New timeout=%d\n", timeout)); 655 attempts = 0; 656 } 657 658 659 /* one cycle finished */ 660 if (current_host == start_host && attempts == 0) { 661 nosuccess_cnt++; 662 (void) asprintf(&ext_error, "all hosts defined " 663 "as p_hosts were tried to deliver " 664 "the audit record to with no success " 665 "- sleeping for %d seconds", 666 NOSUCCESS_DELAY); 667 if (ext_error == NULL) { 668 *error = strdup(gettext("no memory")); 669 rc = AUDITD_NO_MEMORY; 670 break; 671 } 672 __audit_dowarn2("plugin", "audit_remote.so", 673 "retry", ext_error, nosuccess_cnt); 674 free(ext_error); 675 (void) sleep(NOSUCCESS_DELAY); 676 } 677 678 } /* if (send_record_rc == SEND_RECORD_NEXT) */ 679 680 err_rsn = RSN_UNDEFINED; 681 682 } /* while (rc != AUDITD_SUCCESS) */ 683 684 (void) pthread_mutex_unlock(&plugin_mutex); 685 686 #if DEBUG 687 rc_msg = auditd_message(rc); 688 DPRINT((dfile, "audit_remote: returning: %s\n", rc_msg)); 689 free(rc_msg); 690 #endif 691 692 return (rc); 693 } 694 695 /* 696 * auditd_plugin_open() may be called multiple times; on initial open or 697 * `audit -s`, then kvlist != NULL; on `audit -n`, then kvlist == NULL. 698 * For more information see audit(1M). 699 * 700 * Note, that space on stack allocated for any error message returned along 701 * with AUDITD_RETRY is subsequently freed by auditd. 702 * 703 */ 704 auditd_rc_t 705 auditd_plugin_open(const kva_t *kvlist, char **ret_list, char **error) 706 { 707 kva_t *kv; 708 char *val_str; 709 int val; 710 long val_l; 711 int rc = 0; 712 713 *error = NULL; 714 *ret_list = NULL; 715 kv = (kva_t *)kvlist; 716 717 #if DEBUG 718 dfile = __auditd_debug_file_open(); 719 #endif 720 721 /* initial open or audit -s */ 722 if (kvlist != NULL) { 723 DPRINT((dfile, "Action: initial open or `audit -s`\n")); 724 val_str = kva_match(kv, "p_timeout"); 725 if (val_str != NULL) { 726 DPRINT((dfile, "val_str=%s\n", val_str)); 727 errno = 0; 728 val = atoi(val_str); 729 if (errno == 0 && val >= 1) { 730 timeout_p_timeout = val; 731 timeout = val; 732 } 733 } 734 735 val_str = kva_match(kv, "p_retries"); 736 if (val_str != NULL) { 737 DPRINT((dfile, "val_str=%s\n", val_str)); 738 errno = 0; 739 val = atoi(val_str); 740 if (errno == 0 && val >= 0) { 741 retries = val; 742 } 743 } 744 745 val_str = kva_match(kv, "qsize"); 746 if (val_str != NULL) { 747 DPRINT((dfile, "qsize=%s\n", val_str)); 748 errno = 0; 749 val_l = atol(val_str); 750 if (errno == 0 && val_l > 0) { 751 transq_count_max = val_l; 752 } 753 754 } else { 755 DPRINT((dfile, "qsize not in kvlist\n")); 756 if ((rc = set_transq_count_max()) != AUDITD_SUCCESS) { 757 *error = strdup(gettext("cannot get kernel " 758 "auditd queue high water mark\n")); 759 return (rc); 760 } 761 } 762 DPRINT((dfile, "timeout=%d, retries=%d, transq_count_max=%ld\n", 763 timeout, retries, transq_count_max)); 764 765 val_str = kva_match(kv, "p_hosts"); 766 if (val_str == NULL) { 767 *error = strdup(gettext("no hosts configured")); 768 return (AUDITD_RETRY); 769 } 770 if ((rc = parsehosts(val_str, error)) != AUDITD_SUCCESS) { 771 return (rc); 772 } 773 774 /* create the notification pipe towards the receiving thread */ 775 if (!notify_pipe_ready) { 776 if (create_notify_pipe(notify_pipe, error)) { 777 notify_pipe_ready = B_TRUE; 778 } else { 779 return (AUDITD_RETRY); 780 } 781 } 782 783 #if DEBUG 784 } else { /* audit -n */ 785 DPRINT((dfile, "Action: `audit -n`\n")); 786 #endif 787 } 788 789 return (AUDITD_SUCCESS); 790 } 791 792 /* 793 * auditd_plugin_close() performs shutdown operations. The return values are 794 * used by auditd to output warnings via the audit_warn(1M) script and the 795 * string returned via "error_text", is passed to audit_warn. 796 * 797 * Note, that space on stack allocated for any error message returned along 798 * with AUDITD_RETRY is subsequently freed by auditd. 799 * 800 */ 801 auditd_rc_t 802 auditd_plugin_close(char **error) 803 { 804 reset_transport(DO_EXIT, DO_SYNC); 805 if (pthread_join(recv_tid, NULL) != 0) { 806 *error = strdup(gettext("unable to close receiving thread")); 807 return (AUDITD_RETRY); 808 } 809 810 freehostlist(); 811 *error = NULL; 812 return (AUDITD_SUCCESS); 813 } 814