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_TIMEOUT 5 /* default connection timeout (in secs) */ 63 #define NOSUCCESS_DELAY 20 /* unsuccessful delivery to all p_hosts */ 64 65 #define FL_SET B_TRUE /* set_fdfl(): set the flag */ 66 #define FL_UNSET B_FALSE /* set_fdfl(): unset the flag */ 67 68 static int nosuccess_cnt; /* unsuccessful delivery counter */ 69 70 static int retries; /* connection retries */ 71 int timeout; /* connection timeout */ 72 static int timeout_p_timeout; /* p_timeout attr storage */ 73 74 /* semi-exponential timeout back off; x .. attempts, y .. timeout */ 75 #define BOFF_TIMEOUT(x, y) (x < 3 ? y * 2 * x : y * 8) 76 77 /* general plugin lock */ 78 pthread_mutex_t plugin_mutex = PTHREAD_MUTEX_INITIALIZER; 79 80 static struct hostlist_s *current_host; 81 static struct hostlist_s *hosts; 82 static struct hostlist_s *hosts_prev; 83 84 extern struct transq_hdr_s transq_hdr; 85 static long transq_count_max; 86 extern pthread_mutex_t transq_lock; 87 88 extern pthread_t recv_tid; 89 90 extern boolean_t notify_pipe_ready; 91 extern int notify_pipe[2]; 92 93 #if DEBUG 94 FILE *dfile; /* debug file */ 95 #endif 96 97 /* 98 * set_transq_count_max() - sets the transq_count_max value based on kernel 99 * audit queue high water mark. This is backup solution for a case, when the 100 * the default qsize zero value is (intentionally) set in the audit_remote(7) 101 * plugin configuration. 102 */ 103 static auditd_rc_t 104 set_transq_count_max() 105 { 106 struct au_qctrl qctrl; 107 108 if (auditon(A_GETQCTRL, (caddr_t)&qctrl, 0) != -1) { 109 transq_count_max = qctrl.aq_hiwater; 110 DPRINT((dfile, "Transmission queue max length set to %ld\n", 111 transq_count_max)); 112 return (AUDITD_SUCCESS); 113 } 114 115 DPRINT((dfile, "Setting the transmission queue max length failed\n")); 116 return (AUDITD_RETRY); 117 } 118 119 /* 120 * get_port_default() - set the default port number; note, that "solaris-audit" 121 * used below in the code is the IANA assigned service name for the secure 122 * remote solaris audit logging. 123 */ 124 static auditd_rc_t 125 get_port_default(int *port_default) 126 { 127 128 struct servent serventry; 129 char serventry_buf[1024]; 130 131 if (getservbyname_r("solaris-audit", "tcp", &serventry, 132 (char *)&serventry_buf, sizeof (serventry_buf)) == NULL) { 133 DPRINT((dfile, "unable to get default port number\n")); 134 #if DEBUG 135 if (errno == ERANGE) { 136 DPRINT((dfile, "low on buffer\n")); 137 } 138 #endif 139 return (AUDITD_INVALID); 140 } 141 *port_default = ntohs(serventry.s_port); 142 DPRINT((dfile, "default port: %d\n", *port_default)); 143 144 return (AUDITD_SUCCESS); 145 } 146 147 /* 148 * trim_me() - trims the white space characters around the specified string. 149 * Inputs - pointer to the beginning of the string (str_ptr); returns - pointer 150 * to the trimmed string. Function returns NULL pointer in case of received 151 * empty string, NULL pointer or in case the pointed string consists of white 152 * space characters only. 153 */ 154 static char * 155 trim_me(char *str_ptr) { 156 157 char *str_end; 158 159 if (str_ptr == NULL || *str_ptr == '\0') { 160 return (NULL); 161 } 162 163 while (isspace(*str_ptr)) { 164 str_ptr++; 165 } 166 if (*str_ptr == '\0') { 167 return (NULL); 168 } 169 170 str_end = str_ptr + strlen(str_ptr); 171 172 while (str_end > str_ptr && isspace(str_end[-1])) { 173 str_end--; 174 } 175 *str_end = '\0'; 176 177 return (str_ptr); 178 } 179 180 /* 181 * Frees host list - should be called while keeping auditd_mutex. 182 */ 183 static void 184 freehostlist(hostlist_t **hostlist_ptr) 185 { 186 hostlist_t *h, *n; 187 188 h = *hostlist_ptr; 189 190 while (h != NULL) { 191 n = h->next_host; 192 freehostent(h->host); 193 free(h); 194 h = n; 195 } 196 *hostlist_ptr = NULL; 197 } 198 199 /* 200 * parsehosts() end parses the host string (hosts_str) 201 */ 202 static auditd_rc_t 203 parsehosts(char *hosts_str, char **error) 204 { 205 char *hostportmech, *hpm; 206 char *hostname; 207 char *port_str; 208 char *mech_str; 209 int port; 210 int port_default = -1; 211 gss_OID mech_oid; 212 char *lasts_hpm; 213 hostlist_t *lasthost = NULL; 214 hostlist_t *hosts_new = NULL; 215 hostlist_t *newhost; 216 struct hostent *hostentry; 217 int error_num; 218 int rc; 219 #if DEBUG 220 char addr_buf[INET6_ADDRSTRLEN]; 221 int num_of_hosts = 0; 222 #endif 223 224 DPRINT((dfile, "parsing %s\n", hosts_str)); 225 while ((hostportmech = strtok_r(hosts_str, ",", &lasts_hpm)) != NULL) { 226 227 hosts_str = NULL; 228 hostname = NULL; 229 port_str = NULL; 230 port = port_default; 231 mech_str = NULL; 232 mech_oid = GSS_C_NO_OID; 233 234 DPRINT((dfile, "parsing host:port:mech %s\n", hostportmech)); 235 236 if (strncmp(hostportmech, ":", 1 == 0)) { /* ":port:" case */ 237 *error = strdup(gettext("no hostname specified")); 238 return (AUDITD_INVALID); 239 } 240 241 /* parse single host:port:mech target */ 242 while ((hpm = strsep(&hostportmech, ":")) != NULL) { 243 244 if (hostname == NULL) { 245 hostname = hpm; 246 continue; 247 } 248 if (port_str == NULL) { 249 port_str = hpm; 250 continue; 251 } 252 if (mech_str == NULL) { 253 mech_str = hpm; 254 continue; 255 } 256 257 /* too many colons in the hostportmech string */ 258 *error = strdup(gettext("invalid host:port:mech " 259 "specification")); 260 return (AUDITD_INVALID); 261 } 262 263 if (hostname == NULL || *hostname == '\0') { 264 *error = strdup(gettext("invalid hostname " 265 "specification")); 266 return (AUDITD_INVALID); 267 } 268 269 /* trim hostname */ 270 hostname = trim_me(hostname); 271 if (hostname == NULL || *hostname == '\0') { 272 *error = strdup(gettext("empty hostname " 273 "specification")); 274 return (AUDITD_INVALID); 275 } 276 277 DPRINT((dfile, "resolving address for %s\n", hostname)); 278 279 hostentry = getipnodebyname(hostname, AF_INET6, 0, &error_num); 280 if (!hostentry) { 281 hostentry = getipnodebyname(hostname, AF_INET, 0, 282 &error_num); 283 } 284 if (!hostentry) { 285 if (error_num == TRY_AGAIN) { 286 *error = strdup(gettext("host not found, " 287 "try later")); 288 return (AUDITD_RETRY); 289 } else { 290 *error = strdup(gettext("host not found")); 291 return (AUDITD_INVALID); 292 } 293 } 294 DPRINT((dfile, "hostentry: h_name=%s, addr_len=%d, addr=%s\n", 295 hostentry->h_name, hostentry->h_length, 296 inet_ntop(hostentry->h_addrtype, 297 hostentry->h_addr_list[0], addr_buf, 298 INET6_ADDRSTRLEN))); 299 300 /* trim port */ 301 port_str = trim_me(port_str); 302 if (port_str == NULL || *port_str == '\0') { 303 if (port_default == -1 && 304 (rc = get_port_default(&port_default)) 305 != AUDITD_SUCCESS) { 306 *error = strdup(gettext( 307 "unable to get default port number")); 308 return (rc); 309 } 310 port = port_default; 311 DPRINT((dfile, "port: %d (default)\n", port)); 312 } else { 313 errno = 0; 314 port = atoi(port_str); 315 if (errno != 0 || port < 1 || port > USHRT_MAX) { 316 *error = strdup(gettext("invalid port number")); 317 return (AUDITD_INVALID); 318 } 319 DPRINT((dfile, "port: %d\n", port)); 320 } 321 322 /* trim mechanism */ 323 mech_str = trim_me(mech_str); 324 if (mech_str != NULL && *mech_str != '\0') { 325 if (rpc_gss_mech_to_oid(mech_str, &mech_oid) != TRUE) { 326 *error = strdup(gettext("unknown mechanism")); 327 return (AUDITD_INVALID); 328 } 329 DPRINT((dfile, "mechanism: %s\n", mech_str)); 330 #if DEBUG 331 } else { 332 DPRINT((dfile, "mechanism: null (default)\n")); 333 #endif 334 } 335 336 /* add this host to host list */ 337 newhost = malloc(sizeof (hostlist_t)); 338 if (newhost == NULL) { 339 *error = strdup(gettext("no memory")); 340 return (AUDITD_NO_MEMORY); 341 } 342 newhost->host = hostentry; 343 newhost->port = htons(port); 344 newhost->mech = mech_oid; 345 newhost->next_host = NULL; 346 if (lasthost != NULL) { 347 lasthost->next_host = newhost; 348 lasthost = lasthost->next_host; 349 } else { 350 lasthost = newhost; 351 hosts_new = newhost; 352 } 353 #if DEBUG 354 num_of_hosts++; 355 #endif 356 } 357 358 (void) pthread_mutex_lock(&plugin_mutex); 359 if (hosts_prev == NULL) { 360 hosts_prev = hosts; 361 } 362 hosts = hosts_new; 363 current_host = hosts; 364 (void) pthread_mutex_unlock(&plugin_mutex); 365 366 DPRINT((dfile, "Configured %d hosts.\n", num_of_hosts)); 367 368 return (AUDITD_SUCCESS); 369 } 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 if (hosts_prev != NULL) { 611 freehostlist(&hosts_prev); 612 DPRINT((dfile, "stale host list freed\n")); 613 } 614 break; 615 case SEND_RECORD_NEXT: 616 DPRINT((dfile, "retry the same host: %s (penalty) " 617 "rsn:%d\n", current_host->host->h_name, err_rsn)); 618 attempts++; 619 break; 620 case SEND_RECORD_RETRY: 621 DPRINT((dfile, "retry the same host: %s (no penalty) " 622 "rsn:%d\n", current_host->host->h_name, err_rsn)); 623 break; 624 } 625 626 if (send_record_rc == SEND_RECORD_NEXT) { 627 628 /* warn about unsuccessful auditd record delivery */ 629 rsn_msg = rsn_to_msg(err_rsn); 630 (void) asprintf(&ext_error, 631 "retry %d connection %s:%d %s", attempts + 1, 632 current_host->host->h_name, 633 ntohs(current_host->port), rsn_msg); 634 if (ext_error == NULL) { 635 free(rsn_msg); 636 *error = strdup(gettext("no memory")); 637 rc = AUDITD_NO_MEMORY; 638 break; 639 } 640 __audit_dowarn2("plugin", "audit_remote.so", "retry", 641 ext_error, attempts + 1); 642 free(rsn_msg); 643 free(ext_error); 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 = timeout_p_timeout; 656 DPRINT((dfile, "New timeout=%d\n", timeout)); 657 attempts = 0; 658 } 659 660 /* one cycle finished */ 661 if (current_host == start_host && attempts == 0) { 662 nosuccess_cnt++; 663 (void) asprintf(&ext_error, "all hosts defined " 664 "as p_hosts were tried to deliver " 665 "the audit record to with no success " 666 "- sleeping for %d seconds", 667 NOSUCCESS_DELAY); 668 if (ext_error == NULL) { 669 *error = strdup(gettext("no memory")); 670 rc = AUDITD_NO_MEMORY; 671 break; 672 } 673 __audit_dowarn2("plugin", "audit_remote.so", 674 "retry", ext_error, nosuccess_cnt); 675 free(ext_error); 676 (void) sleep(NOSUCCESS_DELAY); 677 } 678 679 } /* if (send_record_rc == SEND_RECORD_NEXT) */ 680 681 err_rsn = RSN_UNDEFINED; 682 683 } /* while (rc != AUDITD_SUCCESS) */ 684 685 (void) pthread_mutex_unlock(&plugin_mutex); 686 687 #if DEBUG 688 rc_msg = auditd_message(rc); 689 DPRINT((dfile, "audit_remote: returning: %s\n", rc_msg)); 690 free(rc_msg); 691 #endif 692 693 return (rc); 694 } 695 696 /* 697 * auditd_plugin_open() may be called multiple times; on initial open or 698 * `audit -s`, then kvlist != NULL; on `audit -n`, then kvlist == NULL. 699 * For more information see audit(8). 700 * 701 * Note, that space on stack allocated for any error message returned along 702 * with AUDITD_RETRY is subsequently freed by auditd. 703 * 704 */ 705 auditd_rc_t 706 auditd_plugin_open(const kva_t *kvlist, char **ret_list, char **error) 707 { 708 kva_t *kv; 709 char *val_str; 710 int val; 711 long val_l; 712 int rc = 0; 713 714 *error = NULL; 715 *ret_list = NULL; 716 kv = (kva_t *)kvlist; 717 718 #if DEBUG 719 dfile = __auditd_debug_file_open(); 720 #endif 721 722 /* initial open or audit -s */ 723 if (kvlist != NULL) { 724 DPRINT((dfile, "Action: initial open or `audit -s`\n")); 725 val_str = kva_match(kv, "p_timeout"); 726 if (val_str == NULL) { 727 *error = strdup( 728 gettext("p_timeout attribute not found")); 729 return (AUDITD_RETRY); 730 } 731 DPRINT((dfile, "val_str=%s\n", val_str)); 732 errno = 0; 733 val = atoi(val_str); 734 if (errno == 0 && val >= 1) { 735 timeout_p_timeout = val; 736 timeout = val; 737 } else { 738 timeout_p_timeout = DEFAULT_TIMEOUT; 739 timeout = timeout_p_timeout; 740 DPRINT((dfile, "p_timeout set to default value: %d\n", 741 timeout)); 742 } 743 744 val_str = kva_match(kv, "p_retries"); 745 if (val_str == NULL) { 746 *error = strdup( 747 gettext("p_retries attribute not found")); 748 return (AUDITD_RETRY); 749 } 750 DPRINT((dfile, "val_str=%s\n", val_str)); 751 errno = 0; 752 val = atoi(val_str); 753 if (errno == 0 && val >= 0) { 754 retries = val; 755 } 756 757 val_str = kva_match(kv, "qsize"); 758 if (val_str == NULL) { 759 *error = strdup(gettext("qsize attribute not found")); 760 return (AUDITD_RETRY); 761 } 762 DPRINT((dfile, "qsize=%s\n", val_str)); 763 errno = 0; 764 val_l = atol(val_str); 765 if (errno == 0 && val_l >= 0) { 766 transq_count_max = val_l; 767 } 768 if (transq_count_max == 0 && 769 (rc = set_transq_count_max()) != AUDITD_SUCCESS) { 770 *error = strdup(gettext("cannot get kernel " 771 "auditd queue high water mark\n")); 772 return (rc); 773 } 774 DPRINT((dfile, "timeout=%d, retries=%d, transq_count_max=%ld\n", 775 timeout, retries, transq_count_max)); 776 777 val_str = kva_match(kv, "p_hosts"); 778 if (val_str == NULL) { 779 *error = strdup(gettext("no hosts configured")); 780 return (AUDITD_RETRY); 781 } 782 if ((rc = parsehosts(val_str, error)) != AUDITD_SUCCESS) { 783 return (rc); 784 } 785 786 /* create the notification pipe towards the receiving thread */ 787 if (!notify_pipe_ready) { 788 if (create_notify_pipe(notify_pipe, error)) { 789 notify_pipe_ready = B_TRUE; 790 } else { 791 return (AUDITD_RETRY); 792 } 793 } 794 795 #if DEBUG 796 } else { /* audit -n */ 797 DPRINT((dfile, "Action: `audit -n`\n")); 798 #endif 799 } 800 801 return (AUDITD_SUCCESS); 802 } 803 804 /* 805 * auditd_plugin_close() performs shutdown operations. The return values are 806 * used by auditd to output warnings via the audit_warn(8) script and the 807 * string returned via "error_text", is passed to audit_warn. 808 * 809 * Note, that space on stack allocated for any error message returned along 810 * with AUDITD_RETRY is subsequently freed by auditd. 811 * 812 */ 813 auditd_rc_t 814 auditd_plugin_close(char **error) 815 { 816 reset_transport(DO_EXIT, DO_SYNC); 817 if (pthread_join(recv_tid, NULL) != 0) { 818 *error = strdup(gettext("unable to close receiving thread")); 819 return (AUDITD_RETRY); 820 } 821 822 (void) pthread_mutex_lock(&plugin_mutex); 823 freehostlist(&hosts); 824 freehostlist(&hosts_prev); 825 (void) pthread_mutex_unlock(&plugin_mutex); 826 current_host = NULL; 827 *error = NULL; 828 return (AUDITD_SUCCESS); 829 } 830