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.
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.
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]
22 * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
24 * send audit records to remote host
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.
34 #include <arpa/inet.h>
36 #include <audit_plugin.h>
37 #include <bsm/audit.h>
38 #include <bsm/audit_record.h>
39 #include <bsm/libbsm.h>
42 #include <gssapi/gssapi.h>
46 #include <rpc/rpcsec_gss.h>
54 #include <sys/param.h>
55 #include <sys/socket.h>
56 #include <sys/types.h>
60 #include "audit_remote.h"
62 #define DEFAULT_TIMEOUT 5 /* default connection timeout (in secs) */
63 #define NOSUCCESS_DELAY 20 /* unsuccessful delivery to all p_hosts */
65 #define FL_SET B_TRUE /* set_fdfl(): set the flag */
66 #define FL_UNSET B_FALSE /* set_fdfl(): unset the flag */
68 static int nosuccess_cnt
; /* unsuccessful delivery counter */
70 static int retries
; /* connection retries */
71 int timeout
; /* connection timeout */
72 static int timeout_p_timeout
; /* p_timeout attr storage */
74 /* semi-exponential timeout back off; x .. attempts, y .. timeout */
75 #define BOFF_TIMEOUT(x, y) (x < 3 ? y * 2 * x : y * 8)
77 /* general plugin lock */
78 pthread_mutex_t plugin_mutex
= PTHREAD_MUTEX_INITIALIZER
;
80 static struct hostlist_s
*current_host
;
81 static struct hostlist_s
*hosts
;
82 static struct hostlist_s
*hosts_prev
;
84 extern struct transq_hdr_s transq_hdr
;
85 static long transq_count_max
;
86 extern pthread_mutex_t transq_lock
;
88 extern pthread_t recv_tid
;
90 extern boolean_t notify_pipe_ready
;
91 extern int notify_pipe
[2];
94 FILE *dfile
; /* debug file */
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(5)
101 * plugin configuration.
104 set_transq_count_max()
106 struct au_qctrl qctrl
;
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",
112 return (AUDITD_SUCCESS
);
115 DPRINT((dfile
, "Setting the transmission queue max length failed\n"));
116 return (AUDITD_RETRY
);
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.
125 get_port_default(int *port_default
)
128 struct servent serventry
;
129 char serventry_buf
[1024];
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"));
135 if (errno
== ERANGE
) {
136 DPRINT((dfile
, "low on buffer\n"));
139 return (AUDITD_INVALID
);
141 *port_default
= ntohs(serventry
.s_port
);
142 DPRINT((dfile
, "default port: %d\n", *port_default
));
144 return (AUDITD_SUCCESS
);
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.
155 trim_me(char *str_ptr
) {
159 if (str_ptr
== NULL
|| *str_ptr
== '\0') {
163 while (isspace(*str_ptr
)) {
166 if (*str_ptr
== '\0') {
170 str_end
= str_ptr
+ strlen(str_ptr
);
172 while (str_end
> str_ptr
&& isspace(str_end
[-1])) {
181 * Frees host list - should be called while keeping auditd_mutex.
184 freehostlist(hostlist_t
**hostlist_ptr
)
192 freehostent(h
->host
);
196 *hostlist_ptr
= NULL
;
200 * parsehosts() end parses the host string (hosts_str)
203 parsehosts(char *hosts_str
, char **error
)
205 char *hostportmech
, *hpm
;
210 int port_default
= -1;
213 hostlist_t
*lasthost
= NULL
;
214 hostlist_t
*hosts_new
= NULL
;
216 struct hostent
*hostentry
;
220 char addr_buf
[INET6_ADDRSTRLEN
];
221 int num_of_hosts
= 0;
224 DPRINT((dfile
, "parsing %s\n", hosts_str
));
225 while ((hostportmech
= strtok_r(hosts_str
, ",", &lasts_hpm
)) != NULL
) {
232 mech_oid
= GSS_C_NO_OID
;
234 DPRINT((dfile
, "parsing host:port:mech %s\n", hostportmech
));
236 if (strncmp(hostportmech
, ":", 1 == 0)) { /* ":port:" case */
237 *error
= strdup(gettext("no hostname specified"));
238 return (AUDITD_INVALID
);
241 /* parse single host:port:mech target */
242 while ((hpm
= strsep(&hostportmech
, ":")) != NULL
) {
244 if (hostname
== NULL
) {
248 if (port_str
== NULL
) {
252 if (mech_str
== NULL
) {
257 /* too many colons in the hostportmech string */
258 *error
= strdup(gettext("invalid host:port:mech "
260 return (AUDITD_INVALID
);
263 if (hostname
== NULL
|| *hostname
== '\0') {
264 *error
= strdup(gettext("invalid hostname "
266 return (AUDITD_INVALID
);
270 hostname
= trim_me(hostname
);
271 if (hostname
== NULL
|| *hostname
== '\0') {
272 *error
= strdup(gettext("empty hostname "
274 return (AUDITD_INVALID
);
277 DPRINT((dfile
, "resolving address for %s\n", hostname
));
279 hostentry
= getipnodebyname(hostname
, AF_INET6
, 0, &error_num
);
281 hostentry
= getipnodebyname(hostname
, AF_INET
, 0,
285 if (error_num
== TRY_AGAIN
) {
286 *error
= strdup(gettext("host not found, "
288 return (AUDITD_RETRY
);
290 *error
= strdup(gettext("host not found"));
291 return (AUDITD_INVALID
);
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
,
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
))
306 *error
= strdup(gettext(
307 "unable to get default port number"));
311 DPRINT((dfile
, "port: %d (default)\n", port
));
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
);
319 DPRINT((dfile
, "port: %d\n", port
));
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
);
329 DPRINT((dfile
, "mechanism: %s\n", mech_str
));
332 DPRINT((dfile
, "mechanism: null (default)\n"));
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
);
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
;
358 (void) pthread_mutex_lock(&plugin_mutex
);
359 if (hosts_prev
== NULL
) {
363 current_host
= hosts
;
364 (void) pthread_mutex_unlock(&plugin_mutex
);
366 DPRINT((dfile
, "Configured %d hosts.\n", num_of_hosts
));
368 return (AUDITD_SUCCESS
);
374 auditd_message(auditd_rc_t msg_code
) {
379 rc_msg
= strdup("ok");
382 rc_msg
= strdup("retry after a delay");
384 case AUDITD_NO_MEMORY
:
385 rc_msg
= strdup("can't allocate memory");
388 rc_msg
= strdup("bad input");
390 case AUDITD_COMM_FAIL
:
391 rc_msg
= strdup("communications failure");
394 rc_msg
= strdup("other error");
397 rc_msg
= strdup("other non-fatal error");
405 * rsn_to_msg() - translation of the reason of closure identifier to the more
406 * human readable/understandable form.
409 rsn_to_msg(close_rsn_t reason
)
415 rc_msg
= strdup(gettext("not defined reason of failure"));
418 rc_msg
= strdup(gettext("poll() initialization failed"));
420 case RSN_TOK_RECV_FAILED
:
421 rc_msg
= strdup(gettext("token receiving failed"));
423 case RSN_TOK_TOO_BIG
:
424 rc_msg
= strdup(gettext("unacceptable token size"));
426 case RSN_TOK_UNVERIFIABLE
:
427 rc_msg
= strdup(gettext("received unverifiable token"));
429 case RSN_SOCKET_CLOSE
:
430 rc_msg
= strdup(gettext("closed socket"));
432 case RSN_SOCKET_CREATE
:
433 rc_msg
= strdup(gettext("socket creation failed"));
435 case RSN_CONNECTION_CREATE
:
436 rc_msg
= strdup(gettext("connection creation failed"));
438 case RSN_PROTOCOL_NEGOTIATE
:
439 rc_msg
= strdup(gettext("protocol negotiation failed"));
441 case RSN_GSS_CTX_ESTABLISH
:
442 rc_msg
= strdup(gettext("context establishing failed"));
444 case RSN_GSS_CTX_EXP
:
445 rc_msg
= strdup(gettext("context expired"));
448 rc_msg
= strdup(gettext("unknown address family"));
450 case RSN_MEMORY_ALLOCATE
:
451 rc_msg
= strdup(gettext("memory allocation failed"));
453 default: /* RSN_OTHER_ERR */
454 rc_msg
= strdup(gettext("other, not classified error"));
461 * set_fdfl() - based on set_fl (FL_SET/FL_UNSET) un/sets the fl flag associated
462 * with fd file descriptor.
465 set_fdfl(int fd
, int fl
, boolean_t set_fl
)
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
));
475 if ((flags
= fcntl(fd
, F_GETFL
, 0)) < 0) {
476 DPRINT((dfile
, "cannot get file descriptor flags\n"));
480 if (set_fl
) { /* set the fl flag */
487 } else { /* unset the fl flag */
495 if (fcntl(fd
, F_SETFL
, flags
) == -1) {
496 DPRINT((dfile
, "cannot %s file descriptor flags\n",
497 (set_fl
? "set" : "unset")));
501 DPRINT((dfile
, "fd: %d - flag: 0%o was %s\n", fd
, fl
,
502 (set_fl
? "set" : "unset")));
508 * create_notify_pipe() - creates the notification pipe. Function returns
509 * B_TRUE/B_FALSE on success/failure.
512 create_notify_pipe(int *notify_pipe
, char **error
)
515 if (pipe(notify_pipe
) < 0) {
516 DPRINT((dfile
, "Cannot create notify pipe: %s\n",
518 *error
= strdup(gettext("failed to create notification pipe"));
521 DPRINT((dfile
, "Pipe created in:%d out:%d\n", notify_pipe
[0],
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]);
531 *error
= strdup(gettext("failed to prepare blocking "
532 "scheme on top of the notification pipe"));
542 * auditd_plugin() sends a record via a tcp connection.
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
552 * Note, that space on stack allocated for any error message returned along
553 * with AUDITD_RETRY is subsequently freed by auditd.
557 auditd_plugin(const char *input
, size_t in_len
, uint64_t sequence
, char **error
)
559 int rc
= AUDITD_FAIL
;
560 int send_record_rc
= SEND_RECORD_FAIL
;
561 hostlist_t
*start_host
;
563 char *ext_error
; /* extended error string */
564 close_rsn_t err_rsn
= RSN_UNDEFINED
;
569 static uint64_t last_sequence
= 0;
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
));
575 last_sequence
= sequence
;
577 DPRINT((dfile
, "audit_remote: input seq=%llu, len=%d\n",
581 (void) pthread_mutex_lock(&transq_lock
);
583 if (transq_hdr
.count
== transq_count_max
) {
584 DPRINT((dfile
, "Transmission queue is full (%ld)\n",
586 (void) pthread_mutex_unlock(&transq_lock
);
587 *error
= strdup(gettext("retransmission queue is full"));
588 return (AUDITD_RETRY
);
590 (void) pthread_mutex_unlock(&transq_lock
);
593 (void) pthread_mutex_lock(&plugin_mutex
);
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
));
601 send_record_rc
= send_record(current_host
, input
, in_len
,
603 DPRINT((dfile
, "send_record() returned %d - ", send_record_rc
));
605 switch (send_record_rc
) {
606 case SEND_RECORD_SUCCESS
:
607 DPRINT((dfile
, "success\n"));
610 if (hosts_prev
!= NULL
) {
611 freehostlist(&hosts_prev
);
612 DPRINT((dfile
, "stale host list freed\n"));
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
));
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
));
626 if (send_record_rc
== SEND_RECORD_NEXT
) {
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
) {
636 *error
= strdup(gettext("no memory"));
637 rc
= AUDITD_NO_MEMORY
;
640 __audit_dowarn2("plugin", "audit_remote.so", "retry",
641 ext_error
, attempts
+ 1);
645 if (attempts
< retries
) {
646 /* semi-exponential timeout back off */
647 timeout
= BOFF_TIMEOUT(attempts
, timeout
);
648 DPRINT((dfile
, "New timeout=%d\n", timeout
));
651 current_host
= current_host
->next_host
;
652 if (current_host
== NULL
) {
653 current_host
= hosts
;
655 timeout
= timeout_p_timeout
;
656 DPRINT((dfile
, "New timeout=%d\n", timeout
));
660 /* one cycle finished */
661 if (current_host
== start_host
&& attempts
== 0) {
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",
668 if (ext_error
== NULL
) {
669 *error
= strdup(gettext("no memory"));
670 rc
= AUDITD_NO_MEMORY
;
673 __audit_dowarn2("plugin", "audit_remote.so",
674 "retry", ext_error
, nosuccess_cnt
);
676 (void) sleep(NOSUCCESS_DELAY
);
679 } /* if (send_record_rc == SEND_RECORD_NEXT) */
681 err_rsn
= RSN_UNDEFINED
;
683 } /* while (rc != AUDITD_SUCCESS) */
685 (void) pthread_mutex_unlock(&plugin_mutex
);
688 rc_msg
= auditd_message(rc
);
689 DPRINT((dfile
, "audit_remote: returning: %s\n", rc_msg
));
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(1M).
701 * Note, that space on stack allocated for any error message returned along
702 * with AUDITD_RETRY is subsequently freed by auditd.
706 auditd_plugin_open(const kva_t
*kvlist
, char **ret_list
, char **error
)
716 kv
= (kva_t
*)kvlist
;
719 dfile
= __auditd_debug_file_open();
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
) {
728 gettext("p_timeout attribute not found"));
729 return (AUDITD_RETRY
);
731 DPRINT((dfile
, "val_str=%s\n", val_str
));
734 if (errno
== 0 && val
>= 1) {
735 timeout_p_timeout
= val
;
738 timeout_p_timeout
= DEFAULT_TIMEOUT
;
739 timeout
= timeout_p_timeout
;
740 DPRINT((dfile
, "p_timeout set to default value: %d\n",
744 val_str
= kva_match(kv
, "p_retries");
745 if (val_str
== NULL
) {
747 gettext("p_retries attribute not found"));
748 return (AUDITD_RETRY
);
750 DPRINT((dfile
, "val_str=%s\n", val_str
));
753 if (errno
== 0 && val
>= 0) {
757 val_str
= kva_match(kv
, "qsize");
758 if (val_str
== NULL
) {
759 *error
= strdup(gettext("qsize attribute not found"));
760 return (AUDITD_RETRY
);
762 DPRINT((dfile
, "qsize=%s\n", val_str
));
764 val_l
= atol(val_str
);
765 if (errno
== 0 && val_l
>= 0) {
766 transq_count_max
= val_l
;
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"));
774 DPRINT((dfile
, "timeout=%d, retries=%d, transq_count_max=%ld\n",
775 timeout
, retries
, transq_count_max
));
777 val_str
= kva_match(kv
, "p_hosts");
778 if (val_str
== NULL
) {
779 *error
= strdup(gettext("no hosts configured"));
780 return (AUDITD_RETRY
);
782 if ((rc
= parsehosts(val_str
, error
)) != AUDITD_SUCCESS
) {
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
;
791 return (AUDITD_RETRY
);
796 } else { /* audit -n */
797 DPRINT((dfile
, "Action: `audit -n`\n"));
801 return (AUDITD_SUCCESS
);
805 * auditd_plugin_close() performs shutdown operations. The return values are
806 * used by auditd to output warnings via the audit_warn(1M) script and the
807 * string returned via "error_text", is passed to audit_warn.
809 * Note, that space on stack allocated for any error message returned along
810 * with AUDITD_RETRY is subsequently freed by auditd.
814 auditd_plugin_close(char **error
)
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
);
822 (void) pthread_mutex_lock(&plugin_mutex
);
823 freehostlist(&hosts
);
824 freehostlist(&hosts_prev
);
825 (void) pthread_mutex_unlock(&plugin_mutex
);
828 return (AUDITD_SUCCESS
);