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 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
25 * transport layer for audit_remote (handles connection establishment, gss
26 * context initialization, message encryption and verification)
31 #include <audit_plugin.h>
34 #include <gssapi/gssapi.h>
38 #include <netinet/in.h>
39 #include <netinet/tcp.h>
45 #include <sys/types.h>
46 #include <sys/socket.h>
51 #include "audit_remote.h"
54 static int sockfd
= -1;
55 static struct hostent
*current_host
;
56 static gss_OID
*current_mech_oid
;
57 static in_port_t current_port
;
58 static boolean_t flush_transq
;
60 static char *ver_str
= "01"; /* supported protocol version */
61 static char *ver_str_concat
; /* concat serv/client version */
63 static gss_ctx_id_t gss_ctx
;
64 static boolean_t gss_ctx_initialized
;
66 pthread_t recv_tid
; /* receiving thread */
67 static pthread_once_t recv_once_control
= PTHREAD_ONCE_INIT
;
69 extern int timeout
; /* connection timeout */
71 extern pthread_mutex_t plugin_mutex
;
72 transq_hdr_t transq_hdr
;
75 * The three locks synchronize the simultaneous actions on top of transmission
76 * queue, socket, gss_context.
78 pthread_mutex_t transq_lock
= PTHREAD_MUTEX_INITIALIZER
;
79 pthread_mutex_t sock_lock
= PTHREAD_MUTEX_INITIALIZER
;
80 pthread_mutex_t gss_ctx_lock
= PTHREAD_MUTEX_INITIALIZER
;
82 /* reset routine synchronization - required by the sending thread */
83 pthread_mutex_t reset_lock
= PTHREAD_MUTEX_INITIALIZER
;
84 static boolean_t reset_in_progress
; /* reset routine in progress */
86 #define NP_CLOSE -1 /* notification pipe - close message */
87 #define NP_EXIT -2 /* notification pipe - exit message */
88 boolean_t notify_pipe_ready
;
89 int notify_pipe
[2]; /* notif. pipe - receiving thread */
91 pthread_cond_t reset_cv
= PTHREAD_COND_INITIALIZER
;
92 static close_rsn_t recv_closure_rsn
;
94 #define MAX_TOK_LEN (128 * 1000) /* max token length we accept (B) */
96 /* transmission queue helpers */
97 static void transq_dequeue(transq_node_t
*);
98 static boolean_t
transq_enqueue(transq_node_t
**, gss_buffer_t
,
100 static int transq_retransmit(void);
102 static boolean_t
init_poll(int);
103 static void do_reset(int *, struct pollfd
*, boolean_t
);
104 static void do_cleanup(int *, struct pollfd
*, boolean_t
);
106 static void init_recv_record(void);
107 static void recv_record();
108 static int connect_timeout(int, struct sockaddr
*, int);
109 static int send_timeout(int, const char *, size_t);
110 static int recv_timeout(int, char *, size_t);
111 static int send_token(int *, gss_buffer_t
);
112 static int recv_token(int, gss_buffer_t
);
116 * report_err() - wrapper, mainly due to enhance the code readability - report
117 * error to syslog via call to __audit_syslog().
120 report_err(char *msg
)
122 __audit_syslog("audit_remote.so", LOG_CONS
| LOG_NDELAY
, LOG_DAEMON
,
129 * report_gss_err() - GSS API error reporting
132 report_gss_err(char *msg
, OM_uint32 maj_stat
, OM_uint32 min_stat
)
134 gss_buffer_desc msg_buf
;
135 OM_uint32 _min
, msg_ctx
;
141 (void) gss_display_status(&_min
, maj_stat
, GSS_C_GSS_CODE
,
142 *current_mech_oid
, &msg_ctx
, &msg_buf
);
143 (void) asprintf(&err_msg
,
144 gettext("GSS API error - %s(%u): %.*s\n"), msg
, maj_stat
,
145 msg_buf
.length
, (char *)msg_buf
.value
);
146 if (err_msg
!= NULL
) {
150 (void) gss_release_buffer(&_min
, &msg_buf
);
156 (void) gss_display_status(&_min
, min_stat
, GSS_C_MECH_CODE
,
157 *current_mech_oid
, &msg_ctx
, &msg_buf
);
158 (void) asprintf(&err_msg
,
159 gettext("GSS mech error - %s(%u): %.*s\n"), msg
, min_stat
,
160 msg_buf
.length
, (char *)msg_buf
.value
);
161 if (err_msg
!= NULL
) {
165 (void) gss_release_buffer(&_min
, &msg_buf
);
170 * prot_ver_negotiate() - negotiate/acknowledge the protocol version. Currently,
171 * there is only one version supported by the plugin - "01".
172 * Note: connection must be initiated prior version negotiation
177 gss_buffer_desc out_buf
, in_buf
;
178 size_t ver_str_concat_sz
;
181 * Set the version proposal string - once we support more than
182 * version "01" this part should be extended to solve the concatenation
183 * of supported version identifiers.
185 out_buf
.value
= (void *)ver_str
;
186 out_buf
.length
= strlen((char *)out_buf
.value
);
187 DPRINT((dfile
, "Protocol version proposal (size=%d): %.*s\n",
188 out_buf
.length
, out_buf
.length
, (char *)out_buf
.value
));
190 if (send_token(&sockfd
, &out_buf
) < 0) {
191 DPRINT((dfile
, "Sending protocol version token failed\n"));
195 if (recv_token(sockfd
, &in_buf
) < 0) {
196 DPRINT((dfile
, "Receiving protocol version token failed\n"));
201 * Verify the sent/received string - memcmp() is sufficient here
202 * because we support only one version and it is represented by
203 * the "01" string. The received version has to be "01" string as well.
205 if (out_buf
.length
!= in_buf
.length
||
206 memcmp(out_buf
.value
, in_buf
.value
, out_buf
.length
) != 0) {
207 DPRINT((dfile
, "Verification of the protocol version strings "
208 "failed [%d:%s][%d:%s]\n", out_buf
.length
,
209 (char *)out_buf
.value
, in_buf
.length
,
210 (char *)in_buf
.value
));
216 * Prepare the concatenated client/server version strings later used
217 * as an application_data field in the gss_channel_bindings_struct
220 ver_str_concat_sz
= out_buf
.length
+ in_buf
.length
+ 1;
221 ver_str_concat
= (char *)calloc(1, ver_str_concat_sz
);
222 if (ver_str_concat
== NULL
) {
223 report_err(gettext("Memory allocation failed"));
224 DPRINT((dfile
, "Memory allocation failed: %s\n",
229 (void) memcpy(ver_str_concat
, out_buf
.value
, out_buf
.length
);
230 (void) memcpy(ver_str_concat
+ out_buf
.length
, in_buf
.value
,
232 DPRINT((dfile
, "Concatenated version strings: %s\n", ver_str_concat
));
234 DPRINT((dfile
, "Protocol version agreed.\n"));
240 * sock_prepare() - creates and connects socket. Function returns
241 * B_FALSE/B_TRUE on failure/success and sets the err_rsn accordingly to the
245 sock_prepare(int *sockfdptr
, struct hostent
*host
, close_rsn_t
*err_rsn
)
247 struct sockaddr_storage addr
;
248 struct sockaddr_in
*sin
;
249 struct sockaddr_in6
*sin6
;
253 DPRINT((dfile
, "Creating socket for %s\n", host
->h_name
));
254 bzero(&addr
, sizeof (addr
));
255 addr
.ss_family
= host
->h_addrtype
;
256 switch (host
->h_addrtype
) {
258 sin
= (struct sockaddr_in
*)&addr
;
259 addr_len
= sizeof (struct sockaddr_in
);
260 bcopy(host
->h_addr_list
[0],
261 &(sin
->sin_addr
), sizeof (struct in_addr
));
262 sin
->sin_port
= current_port
;
265 sin6
= (struct sockaddr_in6
*)&addr
;
266 addr_len
= sizeof (struct sockaddr_in6
);
267 bcopy(host
->h_addr_list
[0],
268 &(sin6
->sin6_addr
), sizeof (struct in6_addr
));
269 sin6
->sin6_port
= current_port
;
272 /* unknown address family */
273 *err_rsn
= RSN_UNKNOWN_AF
;
276 if ((sock
= socket(addr
.ss_family
, SOCK_STREAM
, 0)) == -1) {
277 *err_rsn
= RSN_SOCKET_CREATE
;
280 DPRINT((dfile
, "Socket created, fd=%d, connecting..\n", sock
));
282 if (connect_timeout(sock
, (struct sockaddr
*)&addr
, addr_len
)) {
284 *err_rsn
= RSN_CONNECTION_CREATE
;
288 DPRINT((dfile
, "Connected to %s via fd=%d\n", host
->h_name
,
295 * establish_context() - establish the client/server GSS context.
297 * Note: connection must be established and version negotiated (in plain text)
298 * prior to establishing context.
303 gss_buffer_desc send_tok
, recv_tok
, *token_ptr
;
304 OM_uint32 maj_stat
, min_stat
;
305 OM_uint32 init_sec_min_stat
, ret_flags
;
307 char *gss_svc_name
= "audit";
309 struct gss_channel_bindings_struct input_chan_bindings
;
311 /* GSS service name = gss_svc_name + "@" + remote hostname (fqdn) */
312 (void) asprintf(&svc_name
, "%s@%s", gss_svc_name
, current_host
->h_name
);
313 if (svc_name
== NULL
) {
314 report_err(gettext("Cannot allocate service name\n"));
315 DPRINT((dfile
, "Memory allocation failed: %s\n",
319 DPRINT((dfile
, "Service name: %s\n", svc_name
));
321 send_tok
.value
= svc_name
;
322 send_tok
.length
= strlen(svc_name
);
323 maj_stat
= gss_import_name(&min_stat
, &send_tok
,
324 (gss_OID
)GSS_C_NT_HOSTBASED_SERVICE
, &gss_name
);
325 if (maj_stat
!= GSS_S_COMPLETE
) {
326 report_gss_err(gettext("initializing context"), maj_stat
,
331 token_ptr
= GSS_C_NO_BUFFER
;
332 gss_ctx
= GSS_C_NO_CONTEXT
;
334 /* initialize channel binding */
335 bzero(&input_chan_bindings
, sizeof (input_chan_bindings
));
336 input_chan_bindings
.initiator_addrtype
= GSS_C_AF_NULLADDR
;
337 input_chan_bindings
.acceptor_addrtype
= GSS_C_AF_NULLADDR
;
338 input_chan_bindings
.application_data
.length
= strlen(ver_str_concat
);
339 input_chan_bindings
.application_data
.value
= ver_str_concat
;
341 (void) pthread_mutex_lock(&gss_ctx_lock
);
343 maj_stat
= gss_init_sec_context(&init_sec_min_stat
,
344 GSS_C_NO_CREDENTIAL
, &gss_ctx
, gss_name
, *current_mech_oid
,
345 GSS_C_MUTUAL_FLAG
| GSS_C_REPLAY_FLAG
| GSS_C_SEQUENCE_FLAG
346 | GSS_C_CONF_FLAG
, 0, &input_chan_bindings
, token_ptr
,
347 NULL
, &send_tok
, &ret_flags
, NULL
);
349 if (token_ptr
!= GSS_C_NO_BUFFER
) {
350 (void) gss_release_buffer(&min_stat
, &recv_tok
);
353 if (send_tok
.length
!= 0) {
355 "Sending init_sec_context token (size=%d)\n",
357 if (send_token(&sockfd
, &send_tok
) < 0) {
359 (void) gss_release_name(&min_stat
, &gss_name
);
360 (void) pthread_mutex_unlock(&gss_ctx_lock
);
364 if (send_tok
.value
!= NULL
) {
365 free(send_tok
.value
); /* freeing svc_name */
366 send_tok
.value
= NULL
;
370 if (maj_stat
!= GSS_S_COMPLETE
&&
371 maj_stat
!= GSS_S_CONTINUE_NEEDED
) {
372 report_gss_err(gettext("initializing context"),
373 maj_stat
, init_sec_min_stat
);
374 if (gss_ctx
== GSS_C_NO_CONTEXT
) {
375 (void) gss_delete_sec_context(&min_stat
,
376 &gss_ctx
, GSS_C_NO_BUFFER
);
378 (void) gss_release_name(&min_stat
, &gss_name
);
379 (void) pthread_mutex_unlock(&gss_ctx_lock
);
383 if (maj_stat
== GSS_S_CONTINUE_NEEDED
) {
384 DPRINT((dfile
, "continue needed... "));
385 if (recv_token(sockfd
, &recv_tok
) < 0) {
386 (void) gss_release_name(&min_stat
, &gss_name
);
387 (void) pthread_mutex_unlock(&gss_ctx_lock
);
390 token_ptr
= &recv_tok
;
392 } while (maj_stat
== GSS_S_CONTINUE_NEEDED
);
393 (void) gss_release_name(&min_stat
, &gss_name
);
395 DPRINT((dfile
, "context established\n"));
396 (void) pthread_mutex_unlock(&gss_ctx_lock
);
401 * delete_context() - release GSS context.
408 (void) gss_delete_sec_context(&min_stat
, &gss_ctx
, GSS_C_NO_BUFFER
);
409 DPRINT((dfile
, "context deleted\n"));
413 * send_token() - send GSS token over the wire.
416 send_token(int *fdptr
, gss_buffer_t tok
)
423 (void) pthread_mutex_lock(&sock_lock
);
425 (void) pthread_mutex_unlock(&sock_lock
);
426 DPRINT((dfile
, "Socket detected as closed.\n"));
431 len
= htonl(tok
->length
);
432 lensz
= sizeof (len
);
434 out_buf
= (char *)malloc((size_t)(lensz
+ tok
->length
));
435 if (out_buf
== NULL
) {
436 (void) pthread_mutex_unlock(&sock_lock
);
437 report_err(gettext("Memory allocation failed"));
438 DPRINT((dfile
, "Memory allocation failed: %s\n",
442 (void) memcpy((void *)out_buf
, (void *)&len
, lensz
);
443 (void) memcpy((void *)(out_buf
+ lensz
), (void *)tok
->value
,
446 if (send_timeout(fd
, out_buf
, (lensz
+ tok
->length
))) {
447 (void) pthread_mutex_unlock(&sock_lock
);
452 (void) pthread_mutex_unlock(&sock_lock
);
459 * recv_token() - receive GSS token over the wire.
462 recv_token(int fd
, gss_buffer_t tok
)
466 if (recv_timeout(fd
, (char *)&len
, sizeof (len
))) {
471 /* simple DOS prevention mechanism */
472 if (len
> MAX_TOK_LEN
) {
473 report_err(gettext("Indicated invalid token length"));
474 DPRINT((dfile
, "Indicated token length > %dB\n", MAX_TOK_LEN
));
478 tok
->value
= (char *)malloc(len
);
479 if (tok
->value
== NULL
) {
480 report_err(gettext("Memory allocation failed"));
481 DPRINT((dfile
, "Memory allocation failed: %s\n",
487 if (recv_timeout(fd
, tok
->value
, len
)) {
504 * connect_timeout() - sets nonblocking I/O on a socket and timeout-connects
507 connect_timeout(int sockfd
, struct sockaddr
*name
, int namelen
)
512 struct sockaddr_storage addr
;
513 socklen_t addr_len
= sizeof (addr
);
516 flags
= fcntl(sockfd
, F_GETFL
, 0);
517 if (fcntl(sockfd
, F_SETFL
, flags
| O_NONBLOCK
) == -1) {
520 if (connect(sockfd
, name
, namelen
)) {
521 if (!(errno
== EINTR
|| errno
== EINPROGRESS
||
522 errno
== EWOULDBLOCK
)) {
527 fds
.events
= POLLOUT
;
530 rc
= poll(&fds
, 1, timeout
* 1000);
531 if (rc
== 0) { /* timeout */
534 if (errno
== EINTR
|| errno
== EAGAIN
) {
541 if (getpeername(sockfd
, (struct sockaddr
*)&addr
,
552 * send_timeout() - send data (in chunks if needed, each chunk in timeout secs).
555 send_timeout(int fd
, const char *buf
, size_t len
)
562 fds
.events
= POLLOUT
;
566 rc
= poll(&fds
, 1, timeout
* 1000);
567 if (rc
== 0) { /* timeout */
570 if (errno
== EINTR
|| errno
== EAGAIN
) {
580 bytes
= write(fd
, buf
, len
);
582 if (errno
== EINTR
) {
587 } else if (bytes
== 0) { /* eof */
599 * recv_timeout() - receive data (in chunks if needed, each chunk in timeout
600 * secs). In case the function is called from receiving thread, the function
601 * cycles the poll() call in timeout seconds (waits for input from server).
604 recv_timeout(int fd
, char *buf
, size_t len
)
615 rc
= poll(&fds
, 1, timeout
* 1000);
616 if (rc
== 0) { /* timeout */
619 if (errno
== EINTR
|| errno
== EAGAIN
) {
630 bytes
= read(fd
, buf
, len
);
632 if (errno
== EINTR
) {
637 } else if (bytes
== 0) { /* eof */
649 * read_fd() - reads data of length len from the given file descriptor fd to the
650 * buffer buf, in chunks if needed. Function returns B_FALSE on failure,
651 * otherwise B_TRUE. Function preserves errno, if it was set by the read(2).
654 read_fd(int fd
, char *buf
, size_t len
)
662 bytes
= read(fd
, buf
, len
);
663 if (bytes
< 0) { /* err */
664 if (errno
== EINTR
|| errno
== EAGAIN
) {
669 } else if (bytes
== 0) { /* eof */
677 DPRINT((dfile
, "read_fd: Read %d bytes.\n", len_o
- len
));
682 * write_fd() - writes buf of length len to the opened file descriptor fd, in
683 * chunks if needed. The data from the pipe are processed in the receiving
684 * thread. Function returns B_FALSE on failure, otherwise B_TRUE. Function
685 * preserves errno, if it was set by the write(2).
688 write_fd(int fd
, char *buf
, size_t len
)
696 bytes
= write(fd
, buf
, len
);
697 if (bytes
== -1) { /* err */
698 if (errno
== EINTR
|| errno
== EAGAIN
) {
709 DPRINT((dfile
, "write_fd: Wrote %d bytes.\n", len_o
- len
));
714 * Plug-in entry point
718 * send_record() - send an audit record to a host opening a connection,
719 * negotiate version and establish context if necessary.
722 send_record(struct hostlist_s
*hostlptr
, const char *input
, size_t in_len
,
723 uint64_t sequence
, close_rsn_t
*err_rsn
)
725 gss_buffer_desc in_buf
, out_buf
;
726 OM_uint32 maj_stat
, min_stat
;
729 transq_node_t
*node_ptr
;
730 uint64_t seq_n
; /* sequence in the network byte order */
731 boolean_t init_sock_poll
= B_FALSE
;
734 * We need to grab the reset_lock here, to prevent eventual
735 * unsynchronized cleanup calls within the reset routine (reset caused
736 * by the receiving thread) and the initialization calls in the
737 * send_record() code path.
739 (void) pthread_mutex_lock(&reset_lock
);
742 * Check whether the socket was closed by the recv thread prior to call
743 * send_record() and behave accordingly to the reason of the closure.
745 if (recv_closure_rsn
!= RSN_UNDEFINED
) {
746 *err_rsn
= recv_closure_rsn
;
747 if (recv_closure_rsn
== RSN_GSS_CTX_EXP
) {
748 rc
= SEND_RECORD_RETRY
;
750 rc
= SEND_RECORD_NEXT
;
752 recv_closure_rsn
= RSN_UNDEFINED
;
753 (void) pthread_mutex_unlock(&reset_lock
);
758 * Send request to other then previously used host.
760 if (current_host
!= hostlptr
->host
) {
761 DPRINT((dfile
, "Set new host: %s\n", hostlptr
->host
->h_name
));
763 (void) pthread_mutex_unlock(&reset_lock
);
764 reset_transport(DO_CLOSE
, DO_SYNC
);
765 return (SEND_RECORD_RETRY
);
767 current_host
= (struct hostent
*)hostlptr
->host
;
768 current_mech_oid
= &hostlptr
->mech
;
769 current_port
= hostlptr
->port
;
772 /* initiate the receiving thread */
773 (void) pthread_once(&recv_once_control
, init_recv_record
);
775 /* create and connect() socket, negotiate the protocol version */
777 /* socket operations */
778 DPRINT((dfile
, "Socket creation and connect\n"));
779 if (!sock_prepare(&sockfd
, current_host
, err_rsn
)) {
780 /* we believe the err_rsn set by sock_prepare() */
781 (void) pthread_mutex_unlock(&reset_lock
);
782 return (SEND_RECORD_NEXT
);
785 /* protocol version negotiation */
786 DPRINT((dfile
, "Protocol version negotiation\n"));
787 if (prot_ver_negotiate() != 0) {
789 "Protocol version negotiation failed\n"));
790 (void) pthread_mutex_unlock(&reset_lock
);
791 reset_transport(DO_CLOSE
, DO_SYNC
);
792 *err_rsn
= RSN_PROTOCOL_NEGOTIATE
;
793 return (SEND_RECORD_NEXT
);
796 /* let the socket be initiated for poll() */
797 init_sock_poll
= B_TRUE
;
800 if (!gss_ctx_initialized
) {
801 DPRINT((dfile
, "Establishing context..\n"));
802 if (establish_context() != 0) {
803 (void) pthread_mutex_unlock(&reset_lock
);
804 reset_transport(DO_CLOSE
, DO_SYNC
);
805 *err_rsn
= RSN_GSS_CTX_ESTABLISH
;
806 return (SEND_RECORD_NEXT
);
808 gss_ctx_initialized
= B_TRUE
;
811 /* let the recv thread poll() on the sockfd */
812 if (init_sock_poll
) {
813 init_sock_poll
= B_FALSE
;
814 if (!init_poll(sockfd
)) {
815 *err_rsn
= RSN_INIT_POLL
;
816 (void) pthread_mutex_unlock(&reset_lock
);
817 return (SEND_RECORD_RETRY
);
821 (void) pthread_mutex_unlock(&reset_lock
);
823 /* if not empty, retransmit contents of the transmission queue */
825 DPRINT((dfile
, "Retransmitting remaining (%ld) tokens from "
826 "the transmission queue\n", transq_hdr
.count
));
827 if ((rc
= transq_retransmit()) == 2) { /* gss context exp */
828 reset_transport(DO_CLOSE
, DO_SYNC
);
829 *err_rsn
= RSN_GSS_CTX_EXP
;
830 return (SEND_RECORD_RETRY
);
831 } else if (rc
== 1) {
832 reset_transport(DO_CLOSE
, DO_SYNC
);
833 *err_rsn
= RSN_OTHER_ERR
;
834 return (SEND_RECORD_NEXT
);
836 flush_transq
= B_FALSE
;
840 * Concatenate sequence number and the new record. Note, that the
841 * pointer to the chunk of memory allocated for the concatenated values
842 * is later passed to the transq_enqueu() function which stores the
843 * pointer in the transmission queue; subsequently called
844 * transq_dequeue() frees the allocated memory once the MIC is verified
845 * by the recv_record() function.
847 * If we return earlier than the transq_enqueue() is called, it's
848 * necessary to free the in_buf.value explicitly prior to return.
851 in_buf
.length
= in_len
+ sizeof (sequence
);
852 in_buf
.value
= malloc(in_buf
.length
);
853 if (in_buf
.value
== NULL
) {
854 report_err(gettext("Memory allocation failed"));
855 DPRINT((dfile
, "Memory allocation failed: %s\n",
857 reset_transport(DO_CLOSE
, DO_SYNC
);
858 *err_rsn
= RSN_MEMORY_ALLOCATE
;
859 return (SEND_RECORD_FAIL
);
861 seq_n
= htonll(sequence
);
862 (void) memcpy(in_buf
.value
, &seq_n
, sizeof (seq_n
));
863 (void) memcpy((char *)in_buf
.value
+ sizeof (seq_n
), input
, in_len
);
865 /* wrap sequence number and the new record to the per-message token */
866 (void) pthread_mutex_lock(&gss_ctx_lock
);
867 if (gss_ctx
!= NULL
) {
868 maj_stat
= gss_wrap(&min_stat
, gss_ctx
, 1, GSS_C_QOP_DEFAULT
,
869 &in_buf
, &conf_state
, &out_buf
);
870 (void) pthread_mutex_unlock(&gss_ctx_lock
);
874 case GSS_S_CONTEXT_EXPIRED
:
875 reset_transport(DO_CLOSE
, DO_SYNC
);
877 *err_rsn
= RSN_GSS_CTX_EXP
;
878 return (SEND_RECORD_RETRY
);
880 report_gss_err(gettext("gss_wrap message"), maj_stat
,
882 reset_transport(DO_CLOSE
, DO_SYNC
);
884 *err_rsn
= RSN_OTHER_ERR
;
885 return (SEND_RECORD_NEXT
);
887 } else { /* GSS context deleted by the recv thread */
888 (void) pthread_mutex_unlock(&gss_ctx_lock
);
889 reset_transport(DO_CLOSE
, DO_SYNC
);
891 *err_rsn
= RSN_OTHER_ERR
;
892 return (SEND_RECORD_NEXT
);
896 /* enqueue the to-be-sent token into transmission queue */
897 (void) pthread_mutex_lock(&transq_lock
);
898 if (!transq_enqueue(&node_ptr
, &in_buf
, sequence
)) {
899 (void) pthread_mutex_unlock(&transq_lock
);
900 reset_transport(DO_CLOSE
, DO_SYNC
);
902 (void) gss_release_buffer(&min_stat
, &out_buf
);
903 *err_rsn
= RSN_OTHER_ERR
;
904 return (SEND_RECORD_RETRY
);
906 DPRINT((dfile
, "Token enqueued for later verification\n"));
907 (void) pthread_mutex_unlock(&transq_lock
);
910 if (send_token(&sockfd
, &out_buf
) < 0) {
911 DPRINT((dfile
, "Token sending failed\n"));
912 reset_transport(DO_CLOSE
, DO_SYNC
);
913 (void) gss_release_buffer(&min_stat
, &out_buf
);
915 (void) pthread_mutex_lock(&transq_lock
);
916 transq_dequeue(node_ptr
);
917 (void) pthread_mutex_unlock(&transq_lock
);
919 *err_rsn
= RSN_OTHER_ERR
;
920 return (SEND_RECORD_NEXT
);
922 DPRINT((dfile
, "Token sent (transq size = %ld)\n", transq_hdr
.count
));
924 (void) gss_release_buffer(&min_stat
, &out_buf
);
926 return (SEND_RECORD_SUCCESS
);
930 * init_recv_record() - initialize the receiver thread
935 DPRINT((dfile
, "Initiating the recv thread\n"));
936 (void) pthread_create(&recv_tid
, NULL
, (void *(*)(void *))recv_record
,
943 * recv_record() - the receiver thread routine
948 OM_uint32 maj_stat
, min_stat
;
950 gss_buffer_desc in_buf
= GSS_C_EMPTY_BUFFER
;
951 gss_buffer_desc in_buf_mic
= GSS_C_EMPTY_BUFFER
;
952 transq_node_t
*cur_node
;
953 uint64_t r_seq_num
; /* received sequence number */
954 boolean_t token_verified
;
955 boolean_t break_flag
;
956 struct pollfd fds
[2];
958 struct pollfd
*pipe_fd
= &fds
[0];
959 struct pollfd
*recv_fd
= &fds
[1];
964 DPRINT((dfile
, "Receiver thread initiated\n"));
967 * Fill in the information in the vector of file descriptors passed
968 * later on to the poll() function. In the initial state, there is only
969 * one struct pollfd in the vector which contains file descriptor of the
970 * notification pipe - notify_pipe[1]. There might be up to two file
971 * descriptors (struct pollfd) in the vector - notify_pipe[1] which
972 * resides in the vector during the entire life of the receiving thread,
973 * and the own file descriptor from which we read data sent by the
974 * remote server application.
976 pipe_fd
->fd
= notify_pipe
[1];
977 pipe_fd
->events
= POLLIN
;
979 recv_fd
->events
= POLLIN
;
983 * In the endless loop, try to grab some data from the socket or
988 pipe_fd
->revents
= 0;
989 recv_fd
->revents
= 0;
990 recv_closure_rsn
= RSN_UNDEFINED
;
992 /* block on poll, thus rc != 0 */
993 rc
= poll(fds
, fds_cnt
, -1);
995 if (errno
== EAGAIN
|| errno
== EINTR
) {
996 /* silently continue on EAGAIN || EINTR */
999 /* log the debug message in any other case */
1000 DPRINT((dfile
, "poll() failed: %s\n",
1002 report_err(gettext("poll() failed.\n"));
1008 * Receive a message from the notification pipe. Information
1009 * from the notification pipe takes precedence over the received
1010 * data from the remote server application.
1012 * Notification pipe message format - message accepted
1013 * from the notify pipe comprises of two parts (int ||
1014 * boolean_t), where if the first part (sizeof (int)) equals
1015 * NP_CLOSE, then the second part (sizeof (boolean_t)) signals
1016 * the necessity of broadcasting (DO_SYNC/DO_NOT_SYNC) the end
1017 * of the reset routine.
1019 if (pipe_fd
->revents
& POLLIN
) {
1020 DPRINT((dfile
, "An event on notify pipe detected\n"));
1021 if (!read_fd(pipe_fd
->fd
, (char *)&np_data
,
1022 sizeof (np_data
))) {
1023 DPRINT((dfile
, "Reading notify pipe failed: "
1024 "%s\n", strerror(errno
)));
1025 report_err(gettext("Reading notify pipe "
1028 switch (np_data
.sock_num
) {
1029 case NP_EXIT
: /* exit receiving thread */
1030 do_cleanup(&fds_cnt
, recv_fd
,
1032 pthread_exit((void *)NULL
);
1034 case NP_CLOSE
: /* close and remove recv_fd */
1035 do_reset(&fds_cnt
, recv_fd
,
1038 default: /* add rc_pipe to the fds */
1039 recv_fd
->fd
= np_data
.sock_num
;
1045 /* Receive a token from the remote server application */
1046 if (recv_fd
->revents
& POLLIN
) {
1047 DPRINT((dfile
, "An event on fd detected\n"));
1048 if (!read_fd(recv_fd
->fd
, (char *)&len
, sizeof (len
))) {
1049 DPRINT((dfile
, "Token length recv failed\n"));
1050 recv_closure_rsn
= RSN_TOK_RECV_FAILED
;
1051 reset_transport(DO_CLOSE
, DO_NOT_SYNC
);
1056 /* simple DOS prevention mechanism */
1057 if (len
> MAX_TOK_LEN
) {
1058 report_err(gettext("Indicated invalid token "
1060 DPRINT((dfile
, "Indicated token length > %dB\n",
1062 recv_closure_rsn
= RSN_TOK_TOO_BIG
;
1063 reset_transport(DO_CLOSE
, DO_NOT_SYNC
);
1067 in_buf
.value
= (char *)malloc(len
);
1068 if (in_buf
.value
== NULL
) {
1069 report_err(gettext("Memory allocation failed"));
1070 DPRINT((dfile
, "Memory allocation failed: %s\n",
1072 recv_closure_rsn
= RSN_MEMORY_ALLOCATE
;
1073 reset_transport(DO_CLOSE
, DO_NOT_SYNC
);
1076 if (!read_fd(recv_fd
->fd
, (char *)in_buf
.value
, len
)) {
1077 DPRINT((dfile
, "Token value recv failed\n"));
1079 recv_closure_rsn
= RSN_TOK_RECV_FAILED
;
1080 reset_transport(DO_CLOSE
, DO_NOT_SYNC
);
1084 in_buf
.length
= len
;
1088 * Extract the sequence number and the MIC from
1089 * the per-message token
1091 (void) memcpy(&r_seq_num
, in_buf
.value
, sizeof (r_seq_num
));
1092 r_seq_num
= ntohll(r_seq_num
);
1093 in_buf_mic
.length
= in_buf
.length
- sizeof (r_seq_num
);
1094 in_buf_mic
.value
= (char *)in_buf
.value
+ sizeof (r_seq_num
);
1097 * seq_num/r_seq_num - the sequence number does not need to
1098 * be unique in the transmission queue. Any token in the
1099 * transmission queue with the same seq_num as the acknowledge
1100 * token received from the server is tested. This is due to the
1101 * fact that the plugin cannot influence (in the current
1102 * implementation) sequence numbers generated by the kernel (we
1103 * are reusing record sequence numbers as a transmission queue
1104 * sequence numbers). The probability of having two or more
1105 * tokens in the transmission queue is low and at the same time
1106 * the performance gain due to using sequence numbers is quite
1109 * In case a harder condition with regard to duplicate sequence
1110 * numbers in the transmission queue will be desired over time,
1111 * the break_flag behavior used below should be
1112 * removed/changed_accordingly.
1114 break_flag
= B_FALSE
;
1115 token_verified
= B_FALSE
;
1116 (void) pthread_mutex_lock(&transq_lock
);
1117 cur_node
= transq_hdr
.head
;
1118 while (cur_node
!= NULL
&& !break_flag
) {
1119 if (cur_node
->seq_num
!= r_seq_num
) {
1120 cur_node
= cur_node
->next
;
1124 (void) pthread_mutex_lock(&gss_ctx_lock
);
1125 maj_stat
= gss_verify_mic(&min_stat
, gss_ctx
,
1126 &(cur_node
->seq_token
), &in_buf_mic
,
1128 (void) pthread_mutex_unlock(&gss_ctx_lock
);
1130 if (!GSS_ERROR(maj_stat
)) { /* the success case */
1133 * All the GSS_S_OLD_TOKEN, GSS_S_UNSEQ_TOKEN,
1134 * GSS_S_GAP_TOKEN are perceived as correct
1135 * behavior of the server side. The plugin
1136 * implementation is resistant to any of the
1137 * above mention cases of returned status codes.
1140 case GSS_S_OLD_TOKEN
:
1141 case GSS_S_UNSEQ_TOKEN
:
1142 case GSS_S_GAP_TOKEN
:
1143 case GSS_S_COMPLETE
:
1145 * remove the verified record/node from
1146 * the transmission queue
1148 transq_dequeue(cur_node
);
1149 DPRINT((dfile
, "Recv thread verified "
1150 "the token (transq len = %ld)\n",
1153 token_verified
= B_TRUE
;
1154 break_flag
= B_TRUE
;
1158 * Both the default case as well as
1159 * GSS_S_DUPLICATE_TOKEN case should never
1160 * occur. It's been left here for the sake of
1162 * If any of the two cases occur, it is
1163 * subsequently cought because we don't set
1164 * the token_verified flag.
1167 case GSS_S_DUPLICATE_TOKEN
:
1169 break_flag
= B_TRUE
;
1171 } /* switch (maj_stat) */
1173 } else { /* the failure case */
1175 gettext("signature verification of the "
1176 "received token failed"),
1177 maj_stat
, min_stat
);
1180 case GSS_S_CONTEXT_EXPIRED
:
1181 /* retransmission necessary */
1182 recv_closure_rsn
= RSN_GSS_CTX_EXP
;
1183 break_flag
= B_TRUE
;
1184 DPRINT((dfile
, "Recv thread detected "
1185 "the GSS context expiration\n"));
1188 DPRINT((dfile
, "Bad signature "
1189 "detected (seq_num = %lld)\n",
1190 cur_node
->seq_num
));
1191 cur_node
= cur_node
->next
;
1195 gettext("signature verification"),
1196 maj_stat
, min_stat
);
1197 break_flag
= B_TRUE
;
1203 (void) pthread_mutex_unlock(&transq_lock
);
1205 if (in_buf
.value
!= NULL
) {
1207 in_buf
.value
= NULL
;
1211 if (!token_verified
) {
1213 * Received, but unverifiable token is perceived as
1214 * the protocol flow corruption with the penalty of
1215 * reinitializing the client/server connection.
1217 DPRINT((dfile
, "received unverifiable token\n"));
1218 report_err(gettext("received unverifiable token\n"));
1219 if (recv_closure_rsn
== RSN_UNDEFINED
) {
1220 recv_closure_rsn
= RSN_TOK_UNVERIFIABLE
;
1222 reset_transport(DO_CLOSE
, DO_NOT_SYNC
);
1232 * init_poll() - initiates the polling in the receiving thread via sending the
1233 * appropriate message over the notify pipe. Message format = (int ||
1234 * booleant_t), where the first part (sizeof (int)) contains the
1235 * newly_opened/to_be_polled socket file descriptor. The contents of the second
1236 * part (sizeof (boolean_t)) of the message works only as a padding here and no
1237 * action (no recv/send thread synchronisation) is made in the receiving thread
1238 * based on its value.
1244 int pipe_in
= notify_pipe
[0];
1246 np_data
.sock_num
= fd
;
1247 np_data
.sync
= B_FALSE
; /* padding only */
1249 if (!write_fd(pipe_in
, (char *)&np_data
, sizeof (np_data
))) {
1250 DPRINT((dfile
, "Cannot write to the notify pipe\n"));
1251 report_err(gettext("writing to the notify pipe failed"));
1260 * reset_transport() - locked by the reset_lock initiates the reset of socket,
1261 * GSS security context and (possibly) flags the transq for retransmission; for
1262 * more detailed information see do_reset(). The reset_transport() also allows
1263 * the synchronization - waiting for the reset to be finished.
1265 * do_close: DO_SYNC, DO_NOT_SYNC
1266 * sync_on_return: DO_EXIT (DO_NOT_CLOSE), DO_CLOSE (DO_NOT_EXIT)
1270 reset_transport(boolean_t do_close
, boolean_t sync_on_return
)
1272 int pipe_in
= notify_pipe
[0];
1276 * Check if the reset routine is in progress or whether it was already
1277 * executed by some other thread.
1279 (void) pthread_mutex_lock(&reset_lock
);
1280 if (reset_in_progress
) {
1281 (void) pthread_mutex_unlock(&reset_lock
);
1284 reset_in_progress
= B_TRUE
;
1286 np_data
.sock_num
= (do_close
? NP_CLOSE
: NP_EXIT
);
1287 np_data
.sync
= sync_on_return
;
1288 (void) write_fd(pipe_in
, (char *)&np_data
, sizeof (np_data
));
1290 if (sync_on_return
) {
1291 while (reset_in_progress
) {
1292 (void) pthread_cond_wait(&reset_cv
, &reset_lock
);
1293 DPRINT((dfile
, "Wait for sync\n"));
1295 DPRINT((dfile
, "Synced\n"));
1297 (void) pthread_mutex_unlock(&reset_lock
);
1303 * do_reset() - the own reseting routine called from the recv thread. If the
1304 * synchronization was requested, signal the finish via conditional variable.
1307 do_reset(int *fds_cnt
, struct pollfd
*recv_fd
, boolean_t do_signal
)
1310 (void) pthread_mutex_lock(&reset_lock
);
1313 (void) pthread_mutex_lock(&sock_lock
);
1315 DPRINT((dfile
, "socket already closed\n"));
1316 (void) pthread_mutex_unlock(&sock_lock
);
1319 (void) close(sockfd
);
1322 (void) pthread_mutex_unlock(&sock_lock
);
1327 if (gss_ctx_initialized
) {
1330 gss_ctx_initialized
= B_FALSE
;
1333 /* mark transq to be flushed */
1334 (void) pthread_mutex_lock(&transq_lock
);
1335 if (transq_hdr
.count
> 0) {
1336 flush_transq
= B_TRUE
;
1338 (void) pthread_mutex_unlock(&transq_lock
);
1341 reset_in_progress
= B_FALSE
;
1343 (void) pthread_cond_broadcast(&reset_cv
);
1346 (void) pthread_mutex_unlock(&reset_lock
);
1350 * do_cleanup() - removes all the preallocated space by the plugin; prepares the
1351 * plugin/application to be gracefully finished. Even thought the function
1352 * allows execution without signalling the successful finish, it's recommended
1353 * to use it (we usually want to wait for cleanup before exiting).
1356 do_cleanup(int *fds_cnt
, struct pollfd
*recv_fd
, boolean_t do_signal
)
1359 (void) pthread_mutex_lock(&reset_lock
);
1363 * note: keeping locking for safety, thought it shouldn't be necessary
1364 * in current implementation - we get here only in case the sending code
1365 * path calls auditd_plugin_close() (thus no socket manipulation) and
1366 * the recv thread is doing the own socket closure.
1368 (void) pthread_mutex_lock(&sock_lock
);
1370 DPRINT((dfile
, "Closing socket: %d\n", sockfd
));
1371 (void) close(sockfd
);
1376 (void) pthread_mutex_unlock(&sock_lock
);
1379 if (gss_ctx_initialized
) {
1380 DPRINT((dfile
, "Deleting context: "));
1383 gss_ctx_initialized
= B_FALSE
;
1386 /* transmission queue */
1387 (void) pthread_mutex_lock(&transq_lock
);
1388 if (transq_hdr
.count
> 0) {
1389 DPRINT((dfile
, "Deallocating the transmission queue "
1390 "(len = %ld)\n", transq_hdr
.count
));
1391 while (transq_hdr
.count
> 0) {
1392 transq_dequeue(transq_hdr
.head
);
1395 (void) pthread_mutex_unlock(&transq_lock
);
1397 /* notification pipe */
1398 if (notify_pipe_ready
) {
1399 (void) close(notify_pipe
[0]);
1400 (void) close(notify_pipe
[1]);
1401 notify_pipe_ready
= B_FALSE
;
1404 reset_in_progress
= B_FALSE
;
1406 (void) pthread_cond_broadcast(&reset_cv
);
1408 (void) pthread_mutex_unlock(&reset_lock
);
1413 * transq_dequeue() - dequeues given node pointed by the node_ptr from the
1414 * transmission queue. Transmission queue should be locked prior to use of this
1418 transq_dequeue(transq_node_t
*node_ptr
)
1421 if (node_ptr
== NULL
) {
1422 DPRINT((dfile
, "transq_dequeue(): called with NULL pointer\n"));
1426 free(node_ptr
->seq_token
.value
);
1428 if (node_ptr
->prev
!= NULL
) {
1429 node_ptr
->prev
->next
= node_ptr
->next
;
1431 if (node_ptr
->next
!= NULL
) {
1432 node_ptr
->next
->prev
= node_ptr
->prev
;
1436 /* update the transq_hdr */
1437 if (node_ptr
->next
== NULL
) {
1438 transq_hdr
.end
= node_ptr
->prev
;
1440 if (node_ptr
->prev
== NULL
) {
1441 transq_hdr
.head
= node_ptr
->next
;
1451 * transq_enqueue() - creates new node in (at the end of) the transmission
1452 * queue. in_ptoken_ptr is a pointer to the plain token in a form of
1453 * gss_buffer_desc. Function returns 0 on success and updates the *node_ptr to
1454 * point to a newly added transmission queue node. In case of any failure
1455 * function returns 1 and sets the *node_ptr to NULL.
1456 * Transmission queue should be locked prior to use of this function.
1459 transq_enqueue(transq_node_t
**node_ptr
, gss_buffer_t in_seqtoken_ptr
,
1463 *node_ptr
= calloc(1, sizeof (transq_node_t
));
1464 if (*node_ptr
== NULL
) {
1465 report_err(gettext("Memory allocation failed"));
1466 DPRINT((dfile
, "Memory allocation failed: %s\n",
1471 /* value of the seq_token.value = (sequence number || plain token) */
1472 (*node_ptr
)->seq_num
= sequence
;
1473 (*node_ptr
)->seq_token
.length
= in_seqtoken_ptr
->length
;
1474 (*node_ptr
)->seq_token
.value
= in_seqtoken_ptr
->value
;
1476 /* update the transq_hdr */
1477 if (transq_hdr
.head
== NULL
) {
1478 transq_hdr
.head
= *node_ptr
;
1480 if (transq_hdr
.end
!= NULL
) {
1481 (transq_hdr
.end
)->next
= *node_ptr
;
1482 (*node_ptr
)->prev
= transq_hdr
.end
;
1484 transq_hdr
.end
= *node_ptr
;
1491 if (*node_ptr
!= NULL
) {
1492 if ((*node_ptr
)->seq_token
.value
!= NULL
) {
1493 free((*node_ptr
)->seq_token
.value
);
1503 * transq_retransmit() - traverse the transmission queue and try to, 1 by 1,
1504 * re-wrap the tokens with the recent context information and retransmit the
1505 * tokens from the transmission queue.
1506 * Function returns 2 on GSS context expiration, 1 on any other error, 0 on
1507 * successfully resent transmission queue.
1513 OM_uint32 maj_stat
, min_stat
;
1514 transq_node_t
*cur_node
= transq_hdr
.head
;
1515 gss_buffer_desc out_buf
;
1518 DPRINT((dfile
, "Retransmission of the remainder in the transqueue\n"));
1520 while (cur_node
!= NULL
) {
1522 (void) pthread_mutex_lock(&transq_lock
);
1523 (void) pthread_mutex_lock(&gss_ctx_lock
);
1524 maj_stat
= gss_wrap(&min_stat
, gss_ctx
, 1, GSS_C_QOP_DEFAULT
,
1525 &(cur_node
->seq_token
), &conf_state
, &out_buf
);
1526 (void) pthread_mutex_unlock(&gss_ctx_lock
);
1529 case GSS_S_COMPLETE
:
1531 case GSS_S_CONTEXT_EXPIRED
:
1532 DPRINT((dfile
, "Context expired.\n"));
1533 report_gss_err(gettext("gss_wrap message"), maj_stat
,
1535 (void) pthread_mutex_unlock(&transq_lock
);
1538 report_gss_err(gettext("gss_wrap message"), maj_stat
,
1540 (void) pthread_mutex_unlock(&transq_lock
);
1544 DPRINT((dfile
, "Sending transmission queue token (seq=%lld, "
1545 "size=%d, transq len=%ld)\n", cur_node
->seq_num
,
1546 out_buf
.length
, transq_hdr
.count
));
1547 if (send_token(&sockfd
, &out_buf
) < 0) {
1548 (void) gss_release_buffer(&min_stat
, &out_buf
);
1549 (void) pthread_mutex_unlock(&transq_lock
);
1552 (void) gss_release_buffer(&min_stat
, &out_buf
);
1554 cur_node
= cur_node
->next
;
1555 (void) pthread_mutex_unlock(&transq_lock
);