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