xref: /titanic_52/usr/src/lib/auditd_plugins/remote/transport.c (revision c7bef3b16d3d2a0b09ff75fbbd724283ef1ee7e7)
1657a8c20SJan Friedel /*
2657a8c20SJan Friedel  * CDDL HEADER START
3657a8c20SJan Friedel  *
4657a8c20SJan Friedel  * The contents of this file are subject to the terms of the
5657a8c20SJan Friedel  * Common Development and Distribution License (the "License").
6657a8c20SJan Friedel  * You may not use this file except in compliance with the License.
7657a8c20SJan Friedel  *
8657a8c20SJan Friedel  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9657a8c20SJan Friedel  * or http://www.opensolaris.org/os/licensing.
10657a8c20SJan Friedel  * See the License for the specific language governing permissions
11657a8c20SJan Friedel  * and limitations under the License.
12657a8c20SJan Friedel  *
13657a8c20SJan Friedel  * When distributing Covered Code, include this CDDL HEADER in each
14657a8c20SJan Friedel  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15657a8c20SJan Friedel  * If applicable, add the following below this CDDL HEADER, with the
16657a8c20SJan Friedel  * fields enclosed by brackets "[]" replaced with your own identifying
17657a8c20SJan Friedel  * information: Portions Copyright [yyyy] [name of copyright owner]
18657a8c20SJan Friedel  *
19657a8c20SJan Friedel  * CDDL HEADER END
20657a8c20SJan Friedel  */
21657a8c20SJan Friedel /*
22657a8c20SJan Friedel  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23657a8c20SJan Friedel  * Use is subject to license terms.
24657a8c20SJan Friedel  *
25657a8c20SJan Friedel  * transport layer for audit_remote (handles connection establishment, gss
26657a8c20SJan Friedel  * context initialization, message encryption and verification)
27657a8c20SJan Friedel  *
28657a8c20SJan Friedel  */
29657a8c20SJan Friedel 
30657a8c20SJan Friedel #include <assert.h>
31657a8c20SJan Friedel #include <audit_plugin.h>
32657a8c20SJan Friedel #include <errno.h>
33657a8c20SJan Friedel #include <fcntl.h>
34657a8c20SJan Friedel #include <gssapi/gssapi.h>
35657a8c20SJan Friedel #include <libintl.h>
36657a8c20SJan Friedel #include <mtmalloc.h>
37657a8c20SJan Friedel #include <netdb.h>
38657a8c20SJan Friedel #include <netinet/in.h>
39657a8c20SJan Friedel #include <netinet/tcp.h>
40657a8c20SJan Friedel #include <stdio.h>
41657a8c20SJan Friedel #include <stdlib.h>
42657a8c20SJan Friedel #include <string.h>
43657a8c20SJan Friedel #include <strings.h>
44657a8c20SJan Friedel #include <syslog.h>
45657a8c20SJan Friedel #include <sys/types.h>
46657a8c20SJan Friedel #include <sys/socket.h>
47657a8c20SJan Friedel #include <unistd.h>
48657a8c20SJan Friedel #include <poll.h>
49657a8c20SJan Friedel #include <pthread.h>
50657a8c20SJan Friedel 
51657a8c20SJan Friedel #include "audit_remote.h"
52657a8c20SJan Friedel 
53657a8c20SJan Friedel 
54657a8c20SJan Friedel static int 		sockfd = -1;
55657a8c20SJan Friedel static struct hostent	*current_host;
56657a8c20SJan Friedel static gss_OID		*current_mech_oid;
57657a8c20SJan Friedel static in_port_t 	current_port;
58657a8c20SJan Friedel static boolean_t	flush_transq;
59657a8c20SJan Friedel 
60657a8c20SJan Friedel static char		*ver_str = "01";	/* supported protocol version */
61657a8c20SJan Friedel static char		*ver_str_concat;	/* concat serv/client version */
62657a8c20SJan Friedel 
63657a8c20SJan Friedel static gss_ctx_id_t	gss_ctx;
64657a8c20SJan Friedel static boolean_t	gss_ctx_initialized;
65657a8c20SJan Friedel 
66657a8c20SJan Friedel pthread_t		recv_tid;		/* receiving thread */
67657a8c20SJan Friedel static pthread_once_t	recv_once_control = PTHREAD_ONCE_INIT;
68657a8c20SJan Friedel 
69657a8c20SJan Friedel extern int		timeout;		/* connection timeout */
70657a8c20SJan Friedel 
71657a8c20SJan Friedel extern pthread_mutex_t	plugin_mutex;
72657a8c20SJan Friedel transq_hdr_t		transq_hdr;
73657a8c20SJan Friedel 
74657a8c20SJan Friedel /*
75657a8c20SJan Friedel  * The three locks synchronize the simultaneous actions on top of transmission
76657a8c20SJan Friedel  * queue, socket, gss_context.
77657a8c20SJan Friedel  */
78657a8c20SJan Friedel pthread_mutex_t		transq_lock = PTHREAD_MUTEX_INITIALIZER;
79657a8c20SJan Friedel pthread_mutex_t		sock_lock = PTHREAD_MUTEX_INITIALIZER;
80657a8c20SJan Friedel pthread_mutex_t		gss_ctx_lock = PTHREAD_MUTEX_INITIALIZER;
81657a8c20SJan Friedel 
82657a8c20SJan Friedel /* reset routine synchronization - required by the sending thread */
83657a8c20SJan Friedel pthread_mutex_t		reset_lock = PTHREAD_MUTEX_INITIALIZER;
84657a8c20SJan Friedel static boolean_t	reset_in_progress;	/* reset routine in progress */
85657a8c20SJan Friedel 
86657a8c20SJan Friedel #define	NP_CLOSE	-1		/* notification pipe - close message */
87657a8c20SJan Friedel #define	NP_EXIT		-2		/* notification pipe - exit message */
88657a8c20SJan Friedel boolean_t		notify_pipe_ready;
89657a8c20SJan Friedel int			notify_pipe[2]; /* notif. pipe - receiving thread */
90657a8c20SJan Friedel 
91657a8c20SJan Friedel pthread_cond_t		reset_cv = PTHREAD_COND_INITIALIZER;
92657a8c20SJan Friedel static close_rsn_t	recv_closure_rsn;
93657a8c20SJan Friedel 
94657a8c20SJan Friedel #define	MAX_TOK_LEN	(128 * 1000)	/* max token length we accept (B) */
95657a8c20SJan Friedel 
96657a8c20SJan Friedel /* transmission queue helpers */
97657a8c20SJan Friedel static void		transq_dequeue(transq_node_t *);
98657a8c20SJan Friedel static boolean_t	transq_enqueue(transq_node_t **, gss_buffer_t,
99657a8c20SJan Friedel     uint64_t);
100657a8c20SJan Friedel static int		transq_retransmit(void);
101657a8c20SJan Friedel 
102657a8c20SJan Friedel static boolean_t	init_poll(int);
103657a8c20SJan Friedel static void 		do_reset(int *, struct pollfd *, boolean_t);
104657a8c20SJan Friedel static void 		do_cleanup(int *, struct pollfd *, boolean_t);
105657a8c20SJan Friedel 
106657a8c20SJan Friedel static void		init_recv_record(void);
107657a8c20SJan Friedel static void		recv_record();
108657a8c20SJan Friedel static int		connect_timeout(int, struct sockaddr *, int);
109657a8c20SJan Friedel static int		send_timeout(int, const char *, size_t);
110657a8c20SJan Friedel static int		recv_timeout(int, char *, size_t);
111657a8c20SJan Friedel static int		send_token(int *, gss_buffer_t);
112657a8c20SJan Friedel static int		recv_token(int, gss_buffer_t);
113657a8c20SJan Friedel 
114657a8c20SJan Friedel 
115657a8c20SJan Friedel /*
116657a8c20SJan Friedel  * report_err() - wrapper, mainly due to enhance the code readability - report
117657a8c20SJan Friedel  * error to syslog via call to __audit_syslog().
118657a8c20SJan Friedel  */
119657a8c20SJan Friedel static void
120657a8c20SJan Friedel report_err(char *msg)
121657a8c20SJan Friedel {
122657a8c20SJan Friedel 	__audit_syslog("audit_remote.so", LOG_CONS | LOG_NDELAY, LOG_DAEMON,
123657a8c20SJan Friedel 	    LOG_ERR, msg);
124657a8c20SJan Friedel 
125657a8c20SJan Friedel }
126657a8c20SJan Friedel 
127657a8c20SJan Friedel 
128657a8c20SJan Friedel /*
129657a8c20SJan Friedel  * report_gss_err() - GSS API error reporting
130657a8c20SJan Friedel  */
131657a8c20SJan Friedel static void
132657a8c20SJan Friedel report_gss_err(char *msg, OM_uint32 maj_stat, OM_uint32 min_stat)
133657a8c20SJan Friedel {
134657a8c20SJan Friedel 	gss_buffer_desc	msg_buf;
135657a8c20SJan Friedel 	OM_uint32	_min, msg_ctx;
136657a8c20SJan Friedel 	char		*err_msg;
137657a8c20SJan Friedel 
138657a8c20SJan Friedel 	/* major stat */
139657a8c20SJan Friedel 	msg_ctx = 0;
140657a8c20SJan Friedel 	do {
141657a8c20SJan Friedel 		(void) gss_display_status(&_min, maj_stat, GSS_C_GSS_CODE,
142657a8c20SJan Friedel 		    *current_mech_oid, &msg_ctx, &msg_buf);
143657a8c20SJan Friedel 		(void) asprintf(&err_msg,
144657a8c20SJan Friedel 		    gettext("GSS API error - %s(%u): %.*s\n"), msg, maj_stat,
145657a8c20SJan Friedel 		    msg_buf.length, (char *)msg_buf.value);
146657a8c20SJan Friedel 		if (err_msg != NULL) {
147657a8c20SJan Friedel 			report_err(err_msg);
148657a8c20SJan Friedel 			free(err_msg);
149657a8c20SJan Friedel 		}
150657a8c20SJan Friedel 		(void) gss_release_buffer(&_min, &msg_buf);
151657a8c20SJan Friedel 	} while (msg_ctx);
152657a8c20SJan Friedel 
153657a8c20SJan Friedel 	/* minor stat */
154657a8c20SJan Friedel 	msg_ctx = 0;
155657a8c20SJan Friedel 	do {
156657a8c20SJan Friedel 		(void) gss_display_status(&_min, min_stat, GSS_C_MECH_CODE,
157657a8c20SJan Friedel 		    *current_mech_oid, &msg_ctx, &msg_buf);
158657a8c20SJan Friedel 		(void) asprintf(&err_msg,
159657a8c20SJan Friedel 		    gettext("GSS mech error - %s(%u): %.*s\n"), msg, min_stat,
160657a8c20SJan Friedel 		    msg_buf.length, (char *)msg_buf.value);
161657a8c20SJan Friedel 		if (err_msg != NULL) {
162657a8c20SJan Friedel 			report_err(err_msg);
163657a8c20SJan Friedel 			free(err_msg);
164657a8c20SJan Friedel 		}
165657a8c20SJan Friedel 		(void) gss_release_buffer(&_min, &msg_buf);
166657a8c20SJan Friedel 	} while (msg_ctx);
167657a8c20SJan Friedel }
168657a8c20SJan Friedel 
169657a8c20SJan Friedel /*
170657a8c20SJan Friedel  * prot_ver_negotiate() - negotiate/acknowledge the protocol version. Currently,
171657a8c20SJan Friedel  * there is only one version supported by the plugin - "01".
172657a8c20SJan Friedel  * Note: connection must be initiated prior version negotiation
173657a8c20SJan Friedel  */
174657a8c20SJan Friedel static int
175657a8c20SJan Friedel prot_ver_negotiate()
176657a8c20SJan Friedel {
177657a8c20SJan Friedel 	gss_buffer_desc	out_buf, in_buf;
178657a8c20SJan Friedel 	size_t		ver_str_concat_sz;
179657a8c20SJan Friedel 
180657a8c20SJan Friedel 	/*
181657a8c20SJan Friedel 	 * Set the version proposal string - once we support more than
182657a8c20SJan Friedel 	 * version "01" this part should be extended to solve the concatenation
183657a8c20SJan Friedel 	 * of supported version identifiers.
184657a8c20SJan Friedel 	 */
185657a8c20SJan Friedel 	out_buf.value = (void *)ver_str;
186657a8c20SJan Friedel 	out_buf.length = strlen((char *)out_buf.value);
187657a8c20SJan Friedel 	DPRINT((dfile, "Protocol version proposal (size=%d): %.*s\n",
188657a8c20SJan Friedel 	    out_buf.length, out_buf.length, (char *)out_buf.value));
189657a8c20SJan Friedel 
190657a8c20SJan Friedel 	if (send_token(&sockfd, &out_buf) < 0) {
191657a8c20SJan Friedel 		DPRINT((dfile, "Sending protocol version token failed\n"));
192657a8c20SJan Friedel 		return (-1);
193657a8c20SJan Friedel 	}
194657a8c20SJan Friedel 
195657a8c20SJan Friedel 	if (recv_token(sockfd, &in_buf) < 0) {
196657a8c20SJan Friedel 		DPRINT((dfile, "Receiving protocol version token failed\n"));
197657a8c20SJan Friedel 		return (-1);
198657a8c20SJan Friedel 	}
199657a8c20SJan Friedel 
200657a8c20SJan Friedel 	/*
201657a8c20SJan Friedel 	 * Verify the sent/received string - memcmp() is sufficient here
202657a8c20SJan Friedel 	 * because we support only one version and it is represented by
203657a8c20SJan Friedel 	 * the "01" string. The received version has to be "01" string as well.
204657a8c20SJan Friedel 	 */
205657a8c20SJan Friedel 	if (out_buf.length != in_buf.length ||
206657a8c20SJan Friedel 	    memcmp(out_buf.value, in_buf.value, out_buf.length) != 0) {
207657a8c20SJan Friedel 		DPRINT((dfile, "Verification of the protocol version strings "
208657a8c20SJan Friedel 		    "failed [%d:%s][%d:%s]\n", out_buf.length,
209657a8c20SJan Friedel 		    (char *)out_buf.value, in_buf.length,
210657a8c20SJan Friedel 		    (char *)in_buf.value));
211657a8c20SJan Friedel 		free(in_buf.value);
212657a8c20SJan Friedel 		return (-1);
213657a8c20SJan Friedel 	}
214657a8c20SJan Friedel 
215657a8c20SJan Friedel 	/*
216657a8c20SJan Friedel 	 * Prepare the concatenated client/server version strings later used
217657a8c20SJan Friedel 	 * as an application_data field in the gss_channel_bindings_struct
218657a8c20SJan Friedel 	 * structure.
219657a8c20SJan Friedel 	 */
220657a8c20SJan Friedel 	ver_str_concat_sz = out_buf.length + in_buf.length + 1;
221657a8c20SJan Friedel 	ver_str_concat = (char *)calloc(1, ver_str_concat_sz);
222657a8c20SJan Friedel 	if (ver_str_concat == NULL) {
223657a8c20SJan Friedel 		report_err(gettext("Memory allocation failed"));
224657a8c20SJan Friedel 		DPRINT((dfile, "Memory allocation failed: %s\n",
225657a8c20SJan Friedel 		    strerror(errno)));
226657a8c20SJan Friedel 		free(in_buf.value);
227657a8c20SJan Friedel 		return (-1);
228657a8c20SJan Friedel 	}
229657a8c20SJan Friedel 	(void) memcpy(ver_str_concat, out_buf.value, out_buf.length);
230657a8c20SJan Friedel 	(void) memcpy(ver_str_concat + out_buf.length, in_buf.value,
231657a8c20SJan Friedel 	    in_buf.length);
232657a8c20SJan Friedel 	DPRINT((dfile, "Concatenated version strings: %s\n", ver_str_concat));
233657a8c20SJan Friedel 
234657a8c20SJan Friedel 	DPRINT((dfile, "Protocol version agreed.\n"));
235657a8c20SJan Friedel 	free(in_buf.value);
236657a8c20SJan Friedel 	return (0);
237657a8c20SJan Friedel }
238657a8c20SJan Friedel 
239657a8c20SJan Friedel /*
240657a8c20SJan Friedel  * sock_prepare() - creates and connects socket. Function returns
241657a8c20SJan Friedel  * B_FALSE/B_TRUE on failure/success and sets the err_rsn accordingly to the
242657a8c20SJan Friedel  * reason of failure.
243657a8c20SJan Friedel  */
244657a8c20SJan Friedel static boolean_t
245657a8c20SJan Friedel sock_prepare(int *sockfdptr, struct hostent *host, close_rsn_t *err_rsn)
246657a8c20SJan Friedel {
247657a8c20SJan Friedel 	struct sockaddr_storage	addr;
248657a8c20SJan Friedel 	struct sockaddr_in	*sin;
249657a8c20SJan Friedel 	struct sockaddr_in6	*sin6;
250657a8c20SJan Friedel 	size_t			addr_len;
251657a8c20SJan Friedel 	int			sock;
252657a8c20SJan Friedel 
253657a8c20SJan Friedel 	DPRINT((dfile, "Creating socket for %s\n", host->h_name));
254657a8c20SJan Friedel 	bzero(&addr, sizeof (addr));
255657a8c20SJan Friedel 	addr.ss_family = host->h_addrtype;
256657a8c20SJan Friedel 	switch (host->h_addrtype) {
257657a8c20SJan Friedel 	case AF_INET:
258657a8c20SJan Friedel 		sin = (struct sockaddr_in *)&addr;
259657a8c20SJan Friedel 		addr_len = sizeof (struct sockaddr_in);
260657a8c20SJan Friedel 		bcopy(host->h_addr_list[0],
261657a8c20SJan Friedel 		    &(sin->sin_addr), sizeof (struct in_addr));
262657a8c20SJan Friedel 		sin->sin_port = current_port;
263657a8c20SJan Friedel 		break;
264657a8c20SJan Friedel 	case AF_INET6:
265657a8c20SJan Friedel 		sin6 = (struct sockaddr_in6 *)&addr;
266657a8c20SJan Friedel 		addr_len = sizeof (struct sockaddr_in6);
267657a8c20SJan Friedel 		bcopy(host->h_addr_list[0],
268657a8c20SJan Friedel 		    &(sin6->sin6_addr), sizeof (struct in6_addr));
269657a8c20SJan Friedel 		sin6->sin6_port = current_port;
270657a8c20SJan Friedel 		break;
271657a8c20SJan Friedel 	default:
272657a8c20SJan Friedel 		/* unknown address family */
273657a8c20SJan Friedel 		*err_rsn = RSN_UNKNOWN_AF;
274657a8c20SJan Friedel 		return (B_FALSE);
275657a8c20SJan Friedel 	}
276657a8c20SJan Friedel 	if ((sock = socket(addr.ss_family, SOCK_STREAM, 0)) == -1) {
277657a8c20SJan Friedel 		*err_rsn = RSN_SOCKET_CREATE;
278657a8c20SJan Friedel 		return (B_FALSE);
279657a8c20SJan Friedel 	}
280657a8c20SJan Friedel 	DPRINT((dfile, "Socket created, fd=%d, connecting..\n", sock));
281657a8c20SJan Friedel 
282657a8c20SJan Friedel 	if (connect_timeout(sock, (struct sockaddr *)&addr, addr_len)) {
283657a8c20SJan Friedel 		(void) close(sock);
284657a8c20SJan Friedel 		*err_rsn = RSN_CONNECTION_CREATE;
285657a8c20SJan Friedel 		return (B_FALSE);
286657a8c20SJan Friedel 	}
287657a8c20SJan Friedel 	*sockfdptr = sock;
288657a8c20SJan Friedel 	DPRINT((dfile, "Connected to %s via fd=%d\n", host->h_name,
289657a8c20SJan Friedel 	    *sockfdptr));
290657a8c20SJan Friedel 
291657a8c20SJan Friedel 	return (B_TRUE);
292657a8c20SJan Friedel }
293657a8c20SJan Friedel 
294657a8c20SJan Friedel /*
295657a8c20SJan Friedel  * establish_context() - establish the client/server GSS context.
296657a8c20SJan Friedel  *
297657a8c20SJan Friedel  * Note: connection must be established and version negotiated (in plain text)
298657a8c20SJan Friedel  * prior to establishing context.
299657a8c20SJan Friedel  */
300657a8c20SJan Friedel static int
301657a8c20SJan Friedel establish_context()
302657a8c20SJan Friedel {
303657a8c20SJan Friedel 	gss_buffer_desc				send_tok, recv_tok, *token_ptr;
304657a8c20SJan Friedel 	OM_uint32				maj_stat, min_stat;
305657a8c20SJan Friedel 	OM_uint32				init_sec_min_stat, ret_flags;
306657a8c20SJan Friedel 	gss_name_t				gss_name;
307657a8c20SJan Friedel 	char					*gss_svc_name = "audit";
308657a8c20SJan Friedel 	char					*svc_name;
309657a8c20SJan Friedel 	struct gss_channel_bindings_struct	input_chan_bindings;
310657a8c20SJan Friedel 
311657a8c20SJan Friedel 	/* GSS service name = gss_svc_name + "@" + remote hostname (fqdn) */
312657a8c20SJan Friedel 	(void) asprintf(&svc_name, "%s@%s", gss_svc_name, current_host->h_name);
313657a8c20SJan Friedel 	if (svc_name == NULL) {
314657a8c20SJan Friedel 		report_err(gettext("Cannot allocate service name\n"));
315657a8c20SJan Friedel 		DPRINT((dfile, "Memory allocation failed: %s\n",
316657a8c20SJan Friedel 		    strerror(errno)));
317657a8c20SJan Friedel 		return (-1);
318657a8c20SJan Friedel 	}
319657a8c20SJan Friedel 	DPRINT((dfile, "Service name: %s\n", svc_name));
320657a8c20SJan Friedel 
321657a8c20SJan Friedel 	send_tok.value = svc_name;
322657a8c20SJan Friedel 	send_tok.length = strlen(svc_name);
323657a8c20SJan Friedel 	maj_stat = gss_import_name(&min_stat, &send_tok,
324657a8c20SJan Friedel 	    (gss_OID)GSS_C_NT_HOSTBASED_SERVICE, &gss_name);
325657a8c20SJan Friedel 	if (maj_stat != GSS_S_COMPLETE) {
326657a8c20SJan Friedel 		report_gss_err(gettext("initializing context"), maj_stat,
327657a8c20SJan Friedel 		    min_stat);
328657a8c20SJan Friedel 		free(svc_name);
329657a8c20SJan Friedel 		return (-1);
330657a8c20SJan Friedel 	}
331657a8c20SJan Friedel 	token_ptr = GSS_C_NO_BUFFER;
332657a8c20SJan Friedel 	gss_ctx = GSS_C_NO_CONTEXT;
333657a8c20SJan Friedel 
334657a8c20SJan Friedel 	/* initialize channel binding */
335657a8c20SJan Friedel 	bzero(&input_chan_bindings, sizeof (input_chan_bindings));
336bf515db2SJan Friedel 	input_chan_bindings.initiator_addrtype = GSS_C_AF_NULLADDR;
337bf515db2SJan Friedel 	input_chan_bindings.acceptor_addrtype = GSS_C_AF_NULLADDR;
338*c7bef3b1SJan Friedel 	input_chan_bindings.application_data.length = strlen(ver_str_concat);
339657a8c20SJan Friedel 	input_chan_bindings.application_data.value = ver_str_concat;
340657a8c20SJan Friedel 
341657a8c20SJan Friedel 	(void) pthread_mutex_lock(&gss_ctx_lock);
342657a8c20SJan Friedel 	do {
343657a8c20SJan Friedel 		maj_stat = gss_init_sec_context(&init_sec_min_stat,
344657a8c20SJan Friedel 		    GSS_C_NO_CREDENTIAL, &gss_ctx, gss_name, *current_mech_oid,
345657a8c20SJan Friedel 		    GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG
346657a8c20SJan Friedel 		    | GSS_C_CONF_FLAG, 0, &input_chan_bindings, token_ptr,
347657a8c20SJan Friedel 		    NULL, &send_tok, &ret_flags, NULL);
348657a8c20SJan Friedel 
349657a8c20SJan Friedel 		if (token_ptr != GSS_C_NO_BUFFER) {
350657a8c20SJan Friedel 			(void) gss_release_buffer(&min_stat, &recv_tok);
351657a8c20SJan Friedel 		}
352657a8c20SJan Friedel 
353657a8c20SJan Friedel 		if (send_tok.length != 0) {
354657a8c20SJan Friedel 			DPRINT((dfile,
355657a8c20SJan Friedel 			    "Sending init_sec_context token (size=%d)\n",
356657a8c20SJan Friedel 			    send_tok.length));
357657a8c20SJan Friedel 			if (send_token(&sockfd, &send_tok) < 0) {
358657a8c20SJan Friedel 				free(svc_name);
359657a8c20SJan Friedel 				(void) gss_release_name(&min_stat, &gss_name);
360657a8c20SJan Friedel 				(void) pthread_mutex_unlock(&gss_ctx_lock);
361657a8c20SJan Friedel 				return (-1);
362657a8c20SJan Friedel 			}
363657a8c20SJan Friedel 		}
364657a8c20SJan Friedel 		if (send_tok.value != NULL) {
365657a8c20SJan Friedel 			free(send_tok.value);	/* freeing svc_name */
366657a8c20SJan Friedel 			send_tok.value = NULL;
367657a8c20SJan Friedel 			send_tok.length = 0;
368657a8c20SJan Friedel 		}
369657a8c20SJan Friedel 
370657a8c20SJan Friedel 		if (maj_stat != GSS_S_COMPLETE &&
371657a8c20SJan Friedel 		    maj_stat != GSS_S_CONTINUE_NEEDED) {
372657a8c20SJan Friedel 			report_gss_err(gettext("initializing context"),
373657a8c20SJan Friedel 			    maj_stat, init_sec_min_stat);
374657a8c20SJan Friedel 			if (gss_ctx == GSS_C_NO_CONTEXT) {
375657a8c20SJan Friedel 				(void) gss_delete_sec_context(&min_stat,
376657a8c20SJan Friedel 				    &gss_ctx, GSS_C_NO_BUFFER);
377657a8c20SJan Friedel 			}
378657a8c20SJan Friedel 			(void) gss_release_name(&min_stat, &gss_name);
379657a8c20SJan Friedel 			(void) pthread_mutex_unlock(&gss_ctx_lock);
380657a8c20SJan Friedel 			return (-1);
381657a8c20SJan Friedel 		}
382657a8c20SJan Friedel 
383657a8c20SJan Friedel 		if (maj_stat == GSS_S_CONTINUE_NEEDED) {
384657a8c20SJan Friedel 			DPRINT((dfile, "continue needed... "));
385657a8c20SJan Friedel 			if (recv_token(sockfd, &recv_tok) < 0) {
386657a8c20SJan Friedel 				(void) gss_release_name(&min_stat, &gss_name);
387657a8c20SJan Friedel 				(void) pthread_mutex_unlock(&gss_ctx_lock);
388657a8c20SJan Friedel 				return (-1);
389657a8c20SJan Friedel 			}
390657a8c20SJan Friedel 			token_ptr = &recv_tok;
391657a8c20SJan Friedel 		}
392657a8c20SJan Friedel 	} while (maj_stat == GSS_S_CONTINUE_NEEDED);
393657a8c20SJan Friedel 	(void) gss_release_name(&min_stat, &gss_name);
394657a8c20SJan Friedel 
395657a8c20SJan Friedel 	DPRINT((dfile, "context established\n"));
396657a8c20SJan Friedel 	(void) pthread_mutex_unlock(&gss_ctx_lock);
397657a8c20SJan Friedel 	return (0);
398657a8c20SJan Friedel }
399657a8c20SJan Friedel 
400657a8c20SJan Friedel /*
401657a8c20SJan Friedel  * delete_context() - release GSS context.
402657a8c20SJan Friedel  */
403657a8c20SJan Friedel static void
404657a8c20SJan Friedel delete_context()
405657a8c20SJan Friedel {
406657a8c20SJan Friedel 	OM_uint32	min_stat;
407657a8c20SJan Friedel 
408657a8c20SJan Friedel 	(void) gss_delete_sec_context(&min_stat, &gss_ctx, GSS_C_NO_BUFFER);
409657a8c20SJan Friedel 	DPRINT((dfile, "context deleted\n"));
410657a8c20SJan Friedel }
411657a8c20SJan Friedel 
412657a8c20SJan Friedel /*
413657a8c20SJan Friedel  * send_token() - send GSS token over the wire.
414657a8c20SJan Friedel  */
415657a8c20SJan Friedel static int
416657a8c20SJan Friedel send_token(int *fdptr, gss_buffer_t tok)
417657a8c20SJan Friedel {
418657a8c20SJan Friedel 	uint32_t	len;
419657a8c20SJan Friedel 	uint32_t	lensz;
420657a8c20SJan Friedel 	char		*out_buf;
421657a8c20SJan Friedel 	int		fd;
422657a8c20SJan Friedel 
423657a8c20SJan Friedel 	(void) pthread_mutex_lock(&sock_lock);
424657a8c20SJan Friedel 	if (*fdptr == -1) {
425657a8c20SJan Friedel 		(void) pthread_mutex_unlock(&sock_lock);
426657a8c20SJan Friedel 		DPRINT((dfile, "Socket detected as closed.\n"));
427657a8c20SJan Friedel 		return (-1);
428657a8c20SJan Friedel 	}
429657a8c20SJan Friedel 	fd = *fdptr;
430657a8c20SJan Friedel 
431657a8c20SJan Friedel 	len = htonl(tok->length);
432657a8c20SJan Friedel 	lensz = sizeof (len);
433657a8c20SJan Friedel 
434657a8c20SJan Friedel 	out_buf = (char *)malloc((size_t)(lensz + tok->length));
435657a8c20SJan Friedel 	if (out_buf == NULL) {
436657a8c20SJan Friedel 		(void) pthread_mutex_unlock(&sock_lock);
437657a8c20SJan Friedel 		report_err(gettext("Memory allocation failed"));
438657a8c20SJan Friedel 		DPRINT((dfile, "Memory allocation failed: %s\n",
439657a8c20SJan Friedel 		    strerror(errno)));
440657a8c20SJan Friedel 		return (-1);
441657a8c20SJan Friedel 	}
442657a8c20SJan Friedel 	(void) memcpy((void *)out_buf, (void *)&len, lensz);
443657a8c20SJan Friedel 	(void) memcpy((void *)(out_buf + lensz), (void *)tok->value,
444657a8c20SJan Friedel 	    tok->length);
445657a8c20SJan Friedel 
446657a8c20SJan Friedel 	if (send_timeout(fd, out_buf, (lensz + tok->length))) {
447657a8c20SJan Friedel 		(void) pthread_mutex_unlock(&sock_lock);
448657a8c20SJan Friedel 		free(out_buf);
449657a8c20SJan Friedel 		return (-1);
450657a8c20SJan Friedel 	}
451657a8c20SJan Friedel 
452657a8c20SJan Friedel 	(void) pthread_mutex_unlock(&sock_lock);
453657a8c20SJan Friedel 	free(out_buf);
454657a8c20SJan Friedel 	return (0);
455657a8c20SJan Friedel }
456657a8c20SJan Friedel 
457657a8c20SJan Friedel 
458657a8c20SJan Friedel /*
459657a8c20SJan Friedel  * recv_token() - receive GSS token over the wire.
460657a8c20SJan Friedel  */
461657a8c20SJan Friedel static int
462657a8c20SJan Friedel recv_token(int fd, gss_buffer_t tok)
463657a8c20SJan Friedel {
464657a8c20SJan Friedel 	uint32_t	len;
465657a8c20SJan Friedel 
466657a8c20SJan Friedel 	if (recv_timeout(fd, (char *)&len, sizeof (len))) {
467657a8c20SJan Friedel 		return (-1);
468657a8c20SJan Friedel 	}
469657a8c20SJan Friedel 	len = ntohl(len);
470657a8c20SJan Friedel 
471657a8c20SJan Friedel 	/* simple DOS prevention mechanism */
472657a8c20SJan Friedel 	if (len > MAX_TOK_LEN) {
473657a8c20SJan Friedel 		report_err(gettext("Indicated invalid token length"));
474657a8c20SJan Friedel 		DPRINT((dfile, "Indicated token length > %dB\n", MAX_TOK_LEN));
475657a8c20SJan Friedel 		return (-1);
476657a8c20SJan Friedel 	}
477657a8c20SJan Friedel 
478657a8c20SJan Friedel 	tok->value = (char *)malloc(len);
479657a8c20SJan Friedel 	if (tok->value == NULL) {
480657a8c20SJan Friedel 		report_err(gettext("Memory allocation failed"));
481657a8c20SJan Friedel 		DPRINT((dfile, "Memory allocation failed: %s\n",
482657a8c20SJan Friedel 		    strerror(errno)));
483657a8c20SJan Friedel 		tok->length = 0;
484657a8c20SJan Friedel 		return (-1);
485657a8c20SJan Friedel 	}
486657a8c20SJan Friedel 
487657a8c20SJan Friedel 	if (recv_timeout(fd, tok->value, len)) {
488657a8c20SJan Friedel 		free(tok->value);
489657a8c20SJan Friedel 		tok->value = NULL;
490657a8c20SJan Friedel 		tok->length = 0;
491657a8c20SJan Friedel 		return (-1);
492657a8c20SJan Friedel 	}
493657a8c20SJan Friedel 
494657a8c20SJan Friedel 	tok->length = len;
495657a8c20SJan Friedel 	return (0);
496657a8c20SJan Friedel }
497657a8c20SJan Friedel 
498657a8c20SJan Friedel 
499657a8c20SJan Friedel /*
500657a8c20SJan Friedel  * I/O functions
501657a8c20SJan Friedel  */
502657a8c20SJan Friedel 
503657a8c20SJan Friedel /*
504657a8c20SJan Friedel  * connect_timeout() - sets nonblocking I/O on a socket and timeout-connects
505657a8c20SJan Friedel  */
506657a8c20SJan Friedel static int
507657a8c20SJan Friedel connect_timeout(int sockfd, struct sockaddr *name, int namelen)
508657a8c20SJan Friedel {
509657a8c20SJan Friedel 	int			flags;
510657a8c20SJan Friedel 	struct pollfd		fds;
511657a8c20SJan Friedel 	int			rc;
512657a8c20SJan Friedel 	struct sockaddr_storage	addr;
513657a8c20SJan Friedel 	socklen_t		addr_len = sizeof (addr);
514657a8c20SJan Friedel 
515657a8c20SJan Friedel 
516657a8c20SJan Friedel 	flags = fcntl(sockfd, F_GETFL, 0);
517657a8c20SJan Friedel 	if (fcntl(sockfd, F_SETFL, flags | O_NONBLOCK) == -1) {
518657a8c20SJan Friedel 		return (-1);
519657a8c20SJan Friedel 	}
520657a8c20SJan Friedel 	if (connect(sockfd, name, namelen)) {
521657a8c20SJan Friedel 		if (!(errno == EINTR || errno == EINPROGRESS ||
522657a8c20SJan Friedel 		    errno == EWOULDBLOCK)) {
523657a8c20SJan Friedel 			return (-1);
524657a8c20SJan Friedel 		}
525657a8c20SJan Friedel 	}
526657a8c20SJan Friedel 	fds.fd = sockfd;
527657a8c20SJan Friedel 	fds.events = POLLOUT;
528657a8c20SJan Friedel 	for (;;) {
529657a8c20SJan Friedel 		fds.revents = 0;
530657a8c20SJan Friedel 		rc = poll(&fds, 1, timeout * 1000);
531657a8c20SJan Friedel 		if (rc == 0) {	/* timeout */
532657a8c20SJan Friedel 			return (-1);
533657a8c20SJan Friedel 		} else if (rc < 0) {
534657a8c20SJan Friedel 			if (errno == EINTR || errno == EAGAIN) {
535657a8c20SJan Friedel 				continue;
536657a8c20SJan Friedel 			} else {
537657a8c20SJan Friedel 				return (-1);
538657a8c20SJan Friedel 			}
539657a8c20SJan Friedel 		}
540657a8c20SJan Friedel 		if (fds.revents) {
541657a8c20SJan Friedel 			if (getpeername(sockfd, (struct sockaddr *)&addr,
542657a8c20SJan Friedel 			    &addr_len))
543657a8c20SJan Friedel 				return (-1);
544657a8c20SJan Friedel 		} else {
545657a8c20SJan Friedel 			return (-1);
546657a8c20SJan Friedel 		}
547657a8c20SJan Friedel 		return (0);
548657a8c20SJan Friedel 	}
549657a8c20SJan Friedel }
550657a8c20SJan Friedel 
551657a8c20SJan Friedel /*
552657a8c20SJan Friedel  * send_timeout() - send data (in chunks if needed, each chunk in timeout secs).
553657a8c20SJan Friedel  */
554657a8c20SJan Friedel static int
555657a8c20SJan Friedel send_timeout(int fd, const char *buf, size_t len)
556657a8c20SJan Friedel {
557657a8c20SJan Friedel 	int		bytes;
558657a8c20SJan Friedel 	struct pollfd	fds;
559657a8c20SJan Friedel 	int		rc;
560657a8c20SJan Friedel 
561657a8c20SJan Friedel 	fds.fd = fd;
562657a8c20SJan Friedel 	fds.events = POLLOUT;
563657a8c20SJan Friedel 
564657a8c20SJan Friedel 	while (len) {
565657a8c20SJan Friedel 		fds.revents = 0;
566657a8c20SJan Friedel 		rc = poll(&fds, 1, timeout * 1000);
567657a8c20SJan Friedel 		if (rc == 0) {	/* timeout */
568657a8c20SJan Friedel 			return (-1);
569657a8c20SJan Friedel 		} else if (rc < 0) {
570657a8c20SJan Friedel 			if (errno == EINTR || errno == EAGAIN) {
571657a8c20SJan Friedel 				continue;
572657a8c20SJan Friedel 			} else {
573657a8c20SJan Friedel 				return (-1);
574657a8c20SJan Friedel 			}
575657a8c20SJan Friedel 		}
576657a8c20SJan Friedel 		if (!fds.revents) {
577657a8c20SJan Friedel 			return (-1);
578657a8c20SJan Friedel 		}
579657a8c20SJan Friedel 
580657a8c20SJan Friedel 		bytes = write(fd, buf, len);
581657a8c20SJan Friedel 		if (bytes < 0) {
582657a8c20SJan Friedel 			if (errno == EINTR) {
583657a8c20SJan Friedel 				continue;
584657a8c20SJan Friedel 			} else {
585657a8c20SJan Friedel 				return (-1);
586657a8c20SJan Friedel 			}
587657a8c20SJan Friedel 		} else if (bytes == 0) {	/* eof */
588657a8c20SJan Friedel 			return (-1);
589657a8c20SJan Friedel 		}
590657a8c20SJan Friedel 
591657a8c20SJan Friedel 		len -= bytes;
592657a8c20SJan Friedel 		buf += bytes;
593657a8c20SJan Friedel 	}
594657a8c20SJan Friedel 
595657a8c20SJan Friedel 	return (0);
596657a8c20SJan Friedel }
597657a8c20SJan Friedel 
598657a8c20SJan Friedel /*
599657a8c20SJan Friedel  * recv_timeout() - receive data (in chunks if needed, each chunk in timeout
600657a8c20SJan Friedel  * secs). In case the function is called from receiving thread, the function
601657a8c20SJan Friedel  * cycles the poll() call in timeout seconds (waits for input from server).
602657a8c20SJan Friedel  */
603657a8c20SJan Friedel static int
604657a8c20SJan Friedel recv_timeout(int fd, char *buf, size_t len)
605657a8c20SJan Friedel {
606657a8c20SJan Friedel 	int		bytes;
607657a8c20SJan Friedel 	struct pollfd	fds;
608657a8c20SJan Friedel 	int		rc;
609657a8c20SJan Friedel 
610657a8c20SJan Friedel 	fds.fd = fd;
611657a8c20SJan Friedel 	fds.events = POLLIN;
612657a8c20SJan Friedel 
613657a8c20SJan Friedel 	while (len) {
614657a8c20SJan Friedel 		fds.revents = 0;
615657a8c20SJan Friedel 		rc = poll(&fds, 1, timeout * 1000);
616657a8c20SJan Friedel 		if (rc == 0) {			/* timeout */
617657a8c20SJan Friedel 			return (-1);
618657a8c20SJan Friedel 		} else if (rc < 0) {
619657a8c20SJan Friedel 			if (errno == EINTR || errno == EAGAIN) {
620657a8c20SJan Friedel 				continue;
621657a8c20SJan Friedel 			} else {
622657a8c20SJan Friedel 				return (-1);
623657a8c20SJan Friedel 			}
624657a8c20SJan Friedel 		}
625657a8c20SJan Friedel 
626657a8c20SJan Friedel 		if (!fds.revents) {
627657a8c20SJan Friedel 			return (-1);
628657a8c20SJan Friedel 		}
629657a8c20SJan Friedel 
630657a8c20SJan Friedel 		bytes = read(fd, buf, len);
631657a8c20SJan Friedel 		if (bytes < 0) {
632657a8c20SJan Friedel 			if (errno == EINTR) {
633657a8c20SJan Friedel 				continue;
634657a8c20SJan Friedel 			} else {
635657a8c20SJan Friedel 				return (-1);
636657a8c20SJan Friedel 			}
637657a8c20SJan Friedel 		} else if (bytes == 0) {	/* eof */
638657a8c20SJan Friedel 			return (-1);
639657a8c20SJan Friedel 		}
640657a8c20SJan Friedel 
641657a8c20SJan Friedel 		len -= bytes;
642657a8c20SJan Friedel 		buf += bytes;
643657a8c20SJan Friedel 	}
644657a8c20SJan Friedel 
645657a8c20SJan Friedel 	return (0);
646657a8c20SJan Friedel }
647657a8c20SJan Friedel 
648657a8c20SJan Friedel /*
649657a8c20SJan Friedel  * read_fd() - reads data of length len from the given file descriptor fd to the
650657a8c20SJan Friedel  * buffer buf, in chunks if needed. Function returns B_FALSE on failure,
651657a8c20SJan Friedel  * otherwise B_TRUE. Function preserves errno, if it was set by the read(2).
652657a8c20SJan Friedel  */
653657a8c20SJan Friedel static boolean_t
654657a8c20SJan Friedel read_fd(int fd, char *buf, size_t len)
655657a8c20SJan Friedel {
656657a8c20SJan Friedel 	int		bytes;
657657a8c20SJan Friedel #ifdef DEBUG
658657a8c20SJan Friedel 	size_t		len_o = len;
659657a8c20SJan Friedel #endif
660657a8c20SJan Friedel 
661657a8c20SJan Friedel 	while (len) {
662657a8c20SJan Friedel 		bytes = read(fd, buf, len);
663657a8c20SJan Friedel 		if (bytes < 0) {		/* err */
664657a8c20SJan Friedel 			if (errno == EINTR || errno == EAGAIN) {
665657a8c20SJan Friedel 				continue;
666657a8c20SJan Friedel 			} else {
667657a8c20SJan Friedel 				return (B_FALSE);
668657a8c20SJan Friedel 			}
669657a8c20SJan Friedel 		} else if (bytes == 0) {	/* eof */
670657a8c20SJan Friedel 			return (B_FALSE);
671657a8c20SJan Friedel 		}
672657a8c20SJan Friedel 
673657a8c20SJan Friedel 		len -= bytes;
674657a8c20SJan Friedel 		buf += bytes;
675657a8c20SJan Friedel 	}
676657a8c20SJan Friedel 
677657a8c20SJan Friedel 	DPRINT((dfile, "read_fd: Read %d bytes.\n", len_o - len));
678657a8c20SJan Friedel 	return (B_TRUE);
679657a8c20SJan Friedel }
680657a8c20SJan Friedel 
681657a8c20SJan Friedel /*
682657a8c20SJan Friedel  * write_fd() - writes buf of length len to the opened file descriptor fd, in
683657a8c20SJan Friedel  * chunks if needed. The data from the pipe are processed in the receiving
684657a8c20SJan Friedel  * thread. Function returns B_FALSE on failure, otherwise B_TRUE. Function
685657a8c20SJan Friedel  * preserves errno, if it was set by the write(2).
686657a8c20SJan Friedel  */
687657a8c20SJan Friedel static boolean_t
688657a8c20SJan Friedel write_fd(int fd, char *buf, size_t len)
689657a8c20SJan Friedel {
690657a8c20SJan Friedel 	int		bytes;
691657a8c20SJan Friedel #ifdef DEBUG
692657a8c20SJan Friedel 	size_t		len_o = len;
693657a8c20SJan Friedel #endif
694657a8c20SJan Friedel 
695657a8c20SJan Friedel 	while (len) {
696657a8c20SJan Friedel 		bytes = write(fd, buf, len);
697657a8c20SJan Friedel 		if (bytes == -1) {		/* err */
698657a8c20SJan Friedel 			if (errno == EINTR || errno == EAGAIN) {
699657a8c20SJan Friedel 				continue;
700657a8c20SJan Friedel 			} else {
701657a8c20SJan Friedel 				return (B_FALSE);
702657a8c20SJan Friedel 			}
703657a8c20SJan Friedel 		}
704657a8c20SJan Friedel 
705657a8c20SJan Friedel 		len -= bytes;
706657a8c20SJan Friedel 		buf += bytes;
707657a8c20SJan Friedel 	}
708657a8c20SJan Friedel 
709657a8c20SJan Friedel 	DPRINT((dfile, "write_fd: Wrote %d bytes.\n", len_o - len));
710657a8c20SJan Friedel 	return (B_TRUE);
711657a8c20SJan Friedel }
712657a8c20SJan Friedel 
713657a8c20SJan Friedel /*
714657a8c20SJan Friedel  * Plug-in entry point
715657a8c20SJan Friedel  */
716657a8c20SJan Friedel 
717657a8c20SJan Friedel /*
718657a8c20SJan Friedel  * send_record() - send an audit record to a host opening a connection,
719657a8c20SJan Friedel  * negotiate version and establish context if necessary.
720657a8c20SJan Friedel  */
721657a8c20SJan Friedel send_record_rc_t
722657a8c20SJan Friedel send_record(struct hostlist_s *hostlptr, const char *input, size_t in_len,
723657a8c20SJan Friedel     uint64_t sequence, close_rsn_t *err_rsn)
724657a8c20SJan Friedel {
725657a8c20SJan Friedel 	gss_buffer_desc		in_buf, out_buf;
726657a8c20SJan Friedel 	OM_uint32		maj_stat, min_stat;
727657a8c20SJan Friedel 	int			conf_state;
728657a8c20SJan Friedel 	int			rc;
729657a8c20SJan Friedel 	transq_node_t		*node_ptr;
730657a8c20SJan Friedel 	uint64_t		seq_n;	/* sequence in the network byte order */
731657a8c20SJan Friedel 	boolean_t		init_sock_poll = B_FALSE;
732657a8c20SJan Friedel 
733657a8c20SJan Friedel 	/*
734657a8c20SJan Friedel 	 * We need to grab the reset_lock here, to prevent eventual
735657a8c20SJan Friedel 	 * unsynchronized cleanup calls within the reset routine (reset caused
736657a8c20SJan Friedel 	 * by the receiving thread) and the initialization calls in the
737657a8c20SJan Friedel 	 * send_record() code path.
738657a8c20SJan Friedel 	 */
739657a8c20SJan Friedel 	(void) pthread_mutex_lock(&reset_lock);
740657a8c20SJan Friedel 
741657a8c20SJan Friedel 	/*
742657a8c20SJan Friedel 	 * Check whether the socket was closed by the recv thread prior to call
743657a8c20SJan Friedel 	 * send_record() and behave accordingly to the reason of the closure.
744657a8c20SJan Friedel 	 */
745657a8c20SJan Friedel 	if (recv_closure_rsn != RSN_UNDEFINED) {
746657a8c20SJan Friedel 		*err_rsn = recv_closure_rsn;
747657a8c20SJan Friedel 		if (recv_closure_rsn == RSN_GSS_CTX_EXP) {
748657a8c20SJan Friedel 			rc = SEND_RECORD_RETRY;
749657a8c20SJan Friedel 		} else {
750657a8c20SJan Friedel 			rc = SEND_RECORD_NEXT;
751657a8c20SJan Friedel 		}
752657a8c20SJan Friedel 		recv_closure_rsn = RSN_UNDEFINED;
753657a8c20SJan Friedel 		(void) pthread_mutex_unlock(&reset_lock);
754657a8c20SJan Friedel 		return (rc);
755657a8c20SJan Friedel 	}
756657a8c20SJan Friedel 
757657a8c20SJan Friedel 	/*
758657a8c20SJan Friedel 	 * Send request to other then previously used host.
759657a8c20SJan Friedel 	 */
760657a8c20SJan Friedel 	if (current_host != hostlptr->host) {
761657a8c20SJan Friedel 		DPRINT((dfile, "Set new host: %s\n", hostlptr->host->h_name));
762657a8c20SJan Friedel 		if (sockfd != -1) {
763657a8c20SJan Friedel 			(void) pthread_mutex_unlock(&reset_lock);
764657a8c20SJan Friedel 			reset_transport(DO_CLOSE, DO_SYNC);
765657a8c20SJan Friedel 			return (SEND_RECORD_RETRY);
766657a8c20SJan Friedel 		}
767657a8c20SJan Friedel 		current_host = (struct hostent *)hostlptr->host;
768657a8c20SJan Friedel 		current_mech_oid = &hostlptr->mech;
769657a8c20SJan Friedel 		current_port = hostlptr->port;
770657a8c20SJan Friedel 	}
771657a8c20SJan Friedel 
772657a8c20SJan Friedel 	/* initiate the receiving thread */
773657a8c20SJan Friedel 	(void) pthread_once(&recv_once_control, init_recv_record);
774657a8c20SJan Friedel 
775657a8c20SJan Friedel 	/* create and connect() socket, negotiate the protocol version */
776657a8c20SJan Friedel 	if (sockfd == -1) {
777657a8c20SJan Friedel 		/* socket operations */
778657a8c20SJan Friedel 		DPRINT((dfile, "Socket creation and connect\n"));
779657a8c20SJan Friedel 		if (!sock_prepare(&sockfd, current_host, err_rsn)) {
780657a8c20SJan Friedel 			/* we believe the err_rsn set by sock_prepare() */
781657a8c20SJan Friedel 			(void) pthread_mutex_unlock(&reset_lock);
782657a8c20SJan Friedel 			return (SEND_RECORD_NEXT);
783657a8c20SJan Friedel 		}
784657a8c20SJan Friedel 
785657a8c20SJan Friedel 		/* protocol version negotiation */
786657a8c20SJan Friedel 		DPRINT((dfile, "Protocol version negotiation\n"));
787657a8c20SJan Friedel 		if (prot_ver_negotiate() != 0) {
788657a8c20SJan Friedel 			DPRINT((dfile,
789657a8c20SJan Friedel 			    "Protocol version negotiation failed\n"));
790657a8c20SJan Friedel 			(void) pthread_mutex_unlock(&reset_lock);
791657a8c20SJan Friedel 			reset_transport(DO_CLOSE, DO_SYNC);
792657a8c20SJan Friedel 			*err_rsn = RSN_PROTOCOL_NEGOTIATE;
793657a8c20SJan Friedel 			return (SEND_RECORD_NEXT);
794657a8c20SJan Friedel 		}
795657a8c20SJan Friedel 
796657a8c20SJan Friedel 		/* let the socket be initiated for poll() */
797657a8c20SJan Friedel 		init_sock_poll = B_TRUE;
798657a8c20SJan Friedel 	}
799657a8c20SJan Friedel 
800657a8c20SJan Friedel 	if (!gss_ctx_initialized) {
801657a8c20SJan Friedel 		DPRINT((dfile, "Establishing context..\n"));
802657a8c20SJan Friedel 		if (establish_context() != 0) {
803657a8c20SJan Friedel 			(void) pthread_mutex_unlock(&reset_lock);
804657a8c20SJan Friedel 			reset_transport(DO_CLOSE, DO_SYNC);
805657a8c20SJan Friedel 			*err_rsn = RSN_GSS_CTX_ESTABLISH;
806657a8c20SJan Friedel 			return (SEND_RECORD_NEXT);
807657a8c20SJan Friedel 		}
808657a8c20SJan Friedel 		gss_ctx_initialized = B_TRUE;
809657a8c20SJan Friedel 	}
810657a8c20SJan Friedel 
811657a8c20SJan Friedel 	/* let the recv thread poll() on the sockfd */
812657a8c20SJan Friedel 	if (init_sock_poll) {
813657a8c20SJan Friedel 		init_sock_poll = B_FALSE;
814657a8c20SJan Friedel 		if (!init_poll(sockfd)) {
815657a8c20SJan Friedel 			*err_rsn = RSN_INIT_POLL;
816657a8c20SJan Friedel 			(void) pthread_mutex_unlock(&reset_lock);
817657a8c20SJan Friedel 			return (SEND_RECORD_RETRY);
818657a8c20SJan Friedel 		}
819657a8c20SJan Friedel 	}
820657a8c20SJan Friedel 
821657a8c20SJan Friedel 	(void) pthread_mutex_unlock(&reset_lock);
822657a8c20SJan Friedel 
823657a8c20SJan Friedel 	/* if not empty, retransmit contents of the transmission queue */
824657a8c20SJan Friedel 	if (flush_transq) {
825657a8c20SJan Friedel 		DPRINT((dfile, "Retransmitting remaining (%ld) tokens from "
826657a8c20SJan Friedel 		    "the transmission queue\n", transq_hdr.count));
827657a8c20SJan Friedel 		if ((rc = transq_retransmit()) == 2) { /* gss context exp */
828657a8c20SJan Friedel 			reset_transport(DO_CLOSE, DO_SYNC);
829657a8c20SJan Friedel 			*err_rsn = RSN_GSS_CTX_EXP;
830657a8c20SJan Friedel 			return (SEND_RECORD_RETRY);
831657a8c20SJan Friedel 		} else if (rc == 1) {
832657a8c20SJan Friedel 			reset_transport(DO_CLOSE, DO_SYNC);
833657a8c20SJan Friedel 			*err_rsn = RSN_OTHER_ERR;
834657a8c20SJan Friedel 			return (SEND_RECORD_NEXT);
835657a8c20SJan Friedel 		}
836657a8c20SJan Friedel 		flush_transq = B_FALSE;
837657a8c20SJan Friedel 	}
838657a8c20SJan Friedel 
839657a8c20SJan Friedel 	/*
840657a8c20SJan Friedel 	 * Concatenate sequence number and the new record. Note, that the
841657a8c20SJan Friedel 	 * pointer to the chunk of memory allocated for the concatenated values
842657a8c20SJan Friedel 	 * is later passed to the transq_enqueu() function which stores the
843657a8c20SJan Friedel 	 * pointer in the transmission queue; subsequently called
844657a8c20SJan Friedel 	 * transq_dequeue() frees the allocated memory once the MIC is verified
845657a8c20SJan Friedel 	 * by the recv_record() function.
846657a8c20SJan Friedel 	 *
847657a8c20SJan Friedel 	 * If we return earlier than the transq_enqueue() is called, it's
848657a8c20SJan Friedel 	 * necessary to free the in_buf.value explicitly prior to return.
849657a8c20SJan Friedel 	 *
850657a8c20SJan Friedel 	 */
851657a8c20SJan Friedel 	in_buf.length = in_len + sizeof (sequence);
852657a8c20SJan Friedel 	in_buf.value = malloc(in_buf.length);
853657a8c20SJan Friedel 	if (in_buf.value == NULL) {
854657a8c20SJan Friedel 			report_err(gettext("Memory allocation failed"));
855657a8c20SJan Friedel 			DPRINT((dfile, "Memory allocation failed: %s\n",
856657a8c20SJan Friedel 			    strerror(errno)));
857657a8c20SJan Friedel 			reset_transport(DO_CLOSE, DO_SYNC);
858657a8c20SJan Friedel 			*err_rsn = RSN_MEMORY_ALLOCATE;
859657a8c20SJan Friedel 			return (SEND_RECORD_FAIL);
860657a8c20SJan Friedel 	}
861657a8c20SJan Friedel 	seq_n = htonll(sequence);
862657a8c20SJan Friedel 	(void) memcpy(in_buf.value, &seq_n, sizeof (seq_n));
863657a8c20SJan Friedel 	(void) memcpy((char *)in_buf.value + sizeof (seq_n), input, in_len);
864657a8c20SJan Friedel 
865657a8c20SJan Friedel 	/* wrap sequence number and the new record to the per-message token */
866657a8c20SJan Friedel 	(void) pthread_mutex_lock(&gss_ctx_lock);
867657a8c20SJan Friedel 	if (gss_ctx != NULL) {
868657a8c20SJan Friedel 		maj_stat = gss_wrap(&min_stat, gss_ctx, 1, GSS_C_QOP_DEFAULT,
869657a8c20SJan Friedel 		    &in_buf, &conf_state, &out_buf);
870657a8c20SJan Friedel 		(void) pthread_mutex_unlock(&gss_ctx_lock);
871657a8c20SJan Friedel 		switch (maj_stat) {
872657a8c20SJan Friedel 		case GSS_S_COMPLETE:
873657a8c20SJan Friedel 			break;
874657a8c20SJan Friedel 		case GSS_S_CONTEXT_EXPIRED:
875657a8c20SJan Friedel 			reset_transport(DO_CLOSE, DO_SYNC);
876657a8c20SJan Friedel 			free(in_buf.value);
877657a8c20SJan Friedel 			*err_rsn = RSN_GSS_CTX_EXP;
878657a8c20SJan Friedel 			return (SEND_RECORD_RETRY);
879657a8c20SJan Friedel 		default:
880657a8c20SJan Friedel 			report_gss_err(gettext("gss_wrap message"), maj_stat,
881657a8c20SJan Friedel 			    min_stat);
882657a8c20SJan Friedel 			reset_transport(DO_CLOSE, DO_SYNC);
883657a8c20SJan Friedel 			free(in_buf.value);
884657a8c20SJan Friedel 			*err_rsn = RSN_OTHER_ERR;
885657a8c20SJan Friedel 			return (SEND_RECORD_NEXT);
886657a8c20SJan Friedel 		}
887657a8c20SJan Friedel 	} else {	/* GSS context deleted by the recv thread */
888657a8c20SJan Friedel 		(void) pthread_mutex_unlock(&gss_ctx_lock);
889657a8c20SJan Friedel 		reset_transport(DO_CLOSE, DO_SYNC);
890657a8c20SJan Friedel 		free(in_buf.value);
891657a8c20SJan Friedel 		*err_rsn = RSN_OTHER_ERR;
892657a8c20SJan Friedel 		return (SEND_RECORD_NEXT);
893657a8c20SJan Friedel 	}
894657a8c20SJan Friedel 
895657a8c20SJan Friedel 
896657a8c20SJan Friedel 	/* enqueue the to-be-sent token into transmission queue */
897657a8c20SJan Friedel 	(void) pthread_mutex_lock(&transq_lock);
898657a8c20SJan Friedel 	if (!transq_enqueue(&node_ptr, &in_buf, sequence)) {
899657a8c20SJan Friedel 		(void) pthread_mutex_unlock(&transq_lock);
900657a8c20SJan Friedel 		reset_transport(DO_CLOSE, DO_SYNC);
901657a8c20SJan Friedel 		free(in_buf.value);
902657a8c20SJan Friedel 		(void) gss_release_buffer(&min_stat, &out_buf);
903657a8c20SJan Friedel 		*err_rsn = RSN_OTHER_ERR;
904657a8c20SJan Friedel 		return (SEND_RECORD_RETRY);
905657a8c20SJan Friedel 	}
906657a8c20SJan Friedel 	DPRINT((dfile, "Token enqueued for later verification\n"));
907657a8c20SJan Friedel 	(void) pthread_mutex_unlock(&transq_lock);
908657a8c20SJan Friedel 
909657a8c20SJan Friedel 	/* send token */
910657a8c20SJan Friedel 	if (send_token(&sockfd, &out_buf) < 0) {
911657a8c20SJan Friedel 		DPRINT((dfile, "Token sending failed\n"));
912657a8c20SJan Friedel 		reset_transport(DO_CLOSE, DO_SYNC);
913657a8c20SJan Friedel 		(void) gss_release_buffer(&min_stat, &out_buf);
914657a8c20SJan Friedel 
915657a8c20SJan Friedel 		(void) pthread_mutex_lock(&transq_lock);
916657a8c20SJan Friedel 		transq_dequeue(node_ptr);
917657a8c20SJan Friedel 		(void) pthread_mutex_unlock(&transq_lock);
918657a8c20SJan Friedel 
919657a8c20SJan Friedel 		*err_rsn = RSN_OTHER_ERR;
920657a8c20SJan Friedel 		return (SEND_RECORD_NEXT);
921657a8c20SJan Friedel 	}
922657a8c20SJan Friedel 	DPRINT((dfile, "Token sent (transq size = %ld)\n", transq_hdr.count));
923657a8c20SJan Friedel 
924657a8c20SJan Friedel 	(void) gss_release_buffer(&min_stat, &out_buf);
925657a8c20SJan Friedel 
926657a8c20SJan Friedel 	return (SEND_RECORD_SUCCESS);
927657a8c20SJan Friedel }
928657a8c20SJan Friedel 
929657a8c20SJan Friedel /*
930657a8c20SJan Friedel  * init_recv_record() - initialize the receiver thread
931657a8c20SJan Friedel  */
932657a8c20SJan Friedel static void
933657a8c20SJan Friedel init_recv_record()
934657a8c20SJan Friedel {
935657a8c20SJan Friedel 	DPRINT((dfile, "Initiating the recv thread\n"));
936657a8c20SJan Friedel 	(void) pthread_create(&recv_tid, NULL, (void *(*)(void *))recv_record,
937657a8c20SJan Friedel 	    (void *)NULL);
938657a8c20SJan Friedel 
939657a8c20SJan Friedel }
940657a8c20SJan Friedel 
941657a8c20SJan Friedel 
942657a8c20SJan Friedel /*
943657a8c20SJan Friedel  * recv_record() - the receiver thread routine
944657a8c20SJan Friedel  */
945657a8c20SJan Friedel static void
946657a8c20SJan Friedel recv_record()
947657a8c20SJan Friedel {
948657a8c20SJan Friedel 	OM_uint32		maj_stat, min_stat;
949657a8c20SJan Friedel 	gss_qop_t		qop_state;
950657a8c20SJan Friedel 	gss_buffer_desc		in_buf = GSS_C_EMPTY_BUFFER;
951657a8c20SJan Friedel 	gss_buffer_desc		in_buf_mic = GSS_C_EMPTY_BUFFER;
952657a8c20SJan Friedel 	transq_node_t		*cur_node;
953657a8c20SJan Friedel 	uint64_t		r_seq_num;	/* received sequence number */
954657a8c20SJan Friedel 	boolean_t		token_verified;
955657a8c20SJan Friedel 	boolean_t		break_flag;
956657a8c20SJan Friedel 	struct pollfd		fds[2];
957657a8c20SJan Friedel 	int			fds_cnt;
958657a8c20SJan Friedel 	struct pollfd		*pipe_fd = &fds[0];
959657a8c20SJan Friedel 	struct pollfd		*recv_fd = &fds[1];
960657a8c20SJan Friedel 	uint32_t		len;
961657a8c20SJan Friedel 	int			rc;
962657a8c20SJan Friedel 	pipe_msg_t		np_data;
963657a8c20SJan Friedel 
964657a8c20SJan Friedel 	DPRINT((dfile, "Receiver thread initiated\n"));
965657a8c20SJan Friedel 
966657a8c20SJan Friedel 	/*
967657a8c20SJan Friedel 	 * Fill in the information in the vector of file descriptors passed
968657a8c20SJan Friedel 	 * later on to the poll() function. In the initial state, there is only
969657a8c20SJan Friedel 	 * one struct pollfd in the vector which contains file descriptor of the
970657a8c20SJan Friedel 	 * notification pipe - notify_pipe[1]. There might be up to two file
971657a8c20SJan Friedel 	 * descriptors (struct pollfd) in the vector - notify_pipe[1] which
972657a8c20SJan Friedel 	 * resides in the vector during the entire life of the receiving thread,
973657a8c20SJan Friedel 	 * and the own file descriptor from which we read data sent by the
974657a8c20SJan Friedel 	 * remote server application.
975657a8c20SJan Friedel 	 */
976657a8c20SJan Friedel 	pipe_fd->fd = notify_pipe[1];
977657a8c20SJan Friedel 	pipe_fd->events = POLLIN;
978657a8c20SJan Friedel 	recv_fd->fd = -1;
979657a8c20SJan Friedel 	recv_fd->events = POLLIN;
980657a8c20SJan Friedel 	fds_cnt = 1;
981657a8c20SJan Friedel 
982657a8c20SJan Friedel 	/*
983657a8c20SJan Friedel 	 * In the endless loop, try to grab some data from the socket or
984657a8c20SJan Friedel 	 * notify_pipe[1].
985657a8c20SJan Friedel 	 */
986657a8c20SJan Friedel 	for (;;) {
987657a8c20SJan Friedel 
988657a8c20SJan Friedel 		pipe_fd->revents = 0;
989657a8c20SJan Friedel 		recv_fd->revents = 0;
990657a8c20SJan Friedel 		recv_closure_rsn = RSN_UNDEFINED;
991657a8c20SJan Friedel 
992657a8c20SJan Friedel 		/* block on poll, thus rc != 0 */
993657a8c20SJan Friedel 		rc = poll(fds, fds_cnt, -1);
994657a8c20SJan Friedel 		if (rc == -1) {
995657a8c20SJan Friedel 			if (errno == EAGAIN || errno == EINTR) {
996657a8c20SJan Friedel 				/* silently continue on EAGAIN || EINTR */
997657a8c20SJan Friedel 				continue;
998657a8c20SJan Friedel 			} else {
999657a8c20SJan Friedel 				/* log the debug message in any other case */
1000657a8c20SJan Friedel 				DPRINT((dfile, "poll() failed: %s\n",
1001657a8c20SJan Friedel 				    strerror(errno)));
1002657a8c20SJan Friedel 				report_err(gettext("poll() failed.\n"));
1003657a8c20SJan Friedel 				continue;
1004657a8c20SJan Friedel 			}
1005657a8c20SJan Friedel 		}
1006657a8c20SJan Friedel 
1007657a8c20SJan Friedel 		/*
1008657a8c20SJan Friedel 		 * Receive a message from the notification pipe. Information
1009657a8c20SJan Friedel 		 * from the notification pipe takes precedence over the received
1010657a8c20SJan Friedel 		 * data from the remote server application.
1011657a8c20SJan Friedel 		 *
1012657a8c20SJan Friedel 		 * Notification pipe message format - message accepted
1013657a8c20SJan Friedel 		 * from the notify pipe comprises of two parts (int ||
1014657a8c20SJan Friedel 		 * boolean_t), where if the first part (sizeof (int)) equals
1015657a8c20SJan Friedel 		 * NP_CLOSE, then the second part (sizeof (boolean_t)) signals
1016657a8c20SJan Friedel 		 * the necessity of broadcasting (DO_SYNC/DO_NOT_SYNC) the end
1017657a8c20SJan Friedel 		 * of the reset routine.
1018657a8c20SJan Friedel 		 */
1019657a8c20SJan Friedel 		if (pipe_fd->revents & POLLIN) {
1020657a8c20SJan Friedel 			DPRINT((dfile, "An event on notify pipe detected\n"));
1021657a8c20SJan Friedel 			if (!read_fd(pipe_fd->fd, (char *)&np_data,
1022657a8c20SJan Friedel 			    sizeof (np_data))) {
1023657a8c20SJan Friedel 				DPRINT((dfile, "Reading notify pipe failed: "
1024657a8c20SJan Friedel 				    "%s\n", strerror(errno)));
1025657a8c20SJan Friedel 				report_err(gettext("Reading notify pipe "
1026657a8c20SJan Friedel 				    "failed"));
1027657a8c20SJan Friedel 			} else {
1028657a8c20SJan Friedel 				switch (np_data.sock_num) {
1029657a8c20SJan Friedel 				case NP_EXIT:	/* exit receiving thread */
1030657a8c20SJan Friedel 					do_cleanup(&fds_cnt, recv_fd,
1031657a8c20SJan Friedel 					    np_data.sync);
1032657a8c20SJan Friedel 					pthread_exit((void *)NULL);
1033657a8c20SJan Friedel 					break;
1034657a8c20SJan Friedel 				case NP_CLOSE:	/* close and remove recv_fd */
1035657a8c20SJan Friedel 					do_reset(&fds_cnt, recv_fd,
1036657a8c20SJan Friedel 					    np_data.sync);
1037657a8c20SJan Friedel 					continue;
1038657a8c20SJan Friedel 				default:	/* add rc_pipe to the fds */
1039657a8c20SJan Friedel 					recv_fd->fd = np_data.sock_num;
1040657a8c20SJan Friedel 					fds_cnt = 2;
1041657a8c20SJan Friedel 					continue;
1042657a8c20SJan Friedel 				}
1043657a8c20SJan Friedel 			}
1044657a8c20SJan Friedel 		}
1045657a8c20SJan Friedel 		/* Receive a token from the remote server application */
1046657a8c20SJan Friedel 		if (recv_fd->revents & POLLIN) {
1047657a8c20SJan Friedel 			DPRINT((dfile, "An event on fd detected\n"));
1048657a8c20SJan Friedel 			if (!read_fd(recv_fd->fd, (char *)&len, sizeof (len))) {
1049657a8c20SJan Friedel 				DPRINT((dfile, "Token length recv failed\n"));
1050657a8c20SJan Friedel 				recv_closure_rsn = RSN_TOK_RECV_FAILED;
1051657a8c20SJan Friedel 				reset_transport(DO_CLOSE, DO_NOT_SYNC);
1052657a8c20SJan Friedel 				continue;
1053657a8c20SJan Friedel 			}
1054657a8c20SJan Friedel 			len = ntohl(len);
1055657a8c20SJan Friedel 
1056657a8c20SJan Friedel 			/* simple DOS prevention mechanism */
1057657a8c20SJan Friedel 			if (len > MAX_TOK_LEN) {
1058657a8c20SJan Friedel 				report_err(gettext("Indicated invalid token "
1059657a8c20SJan Friedel 				    "length"));
1060657a8c20SJan Friedel 				DPRINT((dfile, "Indicated token length > %dB\n",
1061657a8c20SJan Friedel 				    MAX_TOK_LEN));
1062657a8c20SJan Friedel 				recv_closure_rsn = RSN_TOK_TOO_BIG;
1063657a8c20SJan Friedel 				reset_transport(DO_CLOSE, DO_NOT_SYNC);
1064657a8c20SJan Friedel 				continue;
1065657a8c20SJan Friedel 			}
1066657a8c20SJan Friedel 
1067657a8c20SJan Friedel 			in_buf.value = (char *)malloc(len);
1068657a8c20SJan Friedel 			if (in_buf.value == NULL) {
1069657a8c20SJan Friedel 				report_err(gettext("Memory allocation failed"));
1070657a8c20SJan Friedel 				DPRINT((dfile, "Memory allocation failed: %s\n",
1071657a8c20SJan Friedel 				    strerror(errno)));
1072657a8c20SJan Friedel 				recv_closure_rsn = RSN_MEMORY_ALLOCATE;
1073657a8c20SJan Friedel 				reset_transport(DO_CLOSE, DO_NOT_SYNC);
1074657a8c20SJan Friedel 				continue;
1075657a8c20SJan Friedel 			}
1076657a8c20SJan Friedel 			if (!read_fd(recv_fd->fd, (char *)in_buf.value, len)) {
1077657a8c20SJan Friedel 				DPRINT((dfile, "Token value recv failed\n"));
1078657a8c20SJan Friedel 				free(in_buf.value);
1079657a8c20SJan Friedel 				recv_closure_rsn = RSN_TOK_RECV_FAILED;
1080657a8c20SJan Friedel 				reset_transport(DO_CLOSE, DO_NOT_SYNC);
1081657a8c20SJan Friedel 				continue;
1082657a8c20SJan Friedel 			}
1083657a8c20SJan Friedel 
1084657a8c20SJan Friedel 			in_buf.length = len;
1085657a8c20SJan Friedel 		}
1086657a8c20SJan Friedel 
1087657a8c20SJan Friedel 		/*
1088657a8c20SJan Friedel 		 * Extract the sequence number and the MIC from
1089657a8c20SJan Friedel 		 * the per-message token
1090657a8c20SJan Friedel 		 */
1091657a8c20SJan Friedel 		(void) memcpy(&r_seq_num, in_buf.value, sizeof (r_seq_num));
1092657a8c20SJan Friedel 		r_seq_num = ntohll(r_seq_num);
1093657a8c20SJan Friedel 		in_buf_mic.length = in_buf.length - sizeof (r_seq_num);
1094657a8c20SJan Friedel 		in_buf_mic.value = (char *)in_buf.value + sizeof (r_seq_num);
1095657a8c20SJan Friedel 
1096657a8c20SJan Friedel 		/*
1097657a8c20SJan Friedel 		 * seq_num/r_seq_num - the sequence number does not need to
1098657a8c20SJan Friedel 		 * be unique in the transmission queue. Any token in the
1099657a8c20SJan Friedel 		 * transmission queue with the same seq_num as the acknowledge
1100657a8c20SJan Friedel 		 * token received from the server is tested. This is due to the
1101657a8c20SJan Friedel 		 * fact that the plugin cannot influence (in the current
1102657a8c20SJan Friedel 		 * implementation) sequence numbers generated by the kernel (we
1103657a8c20SJan Friedel 		 * are reusing record sequence numbers as a transmission queue
1104657a8c20SJan Friedel 		 * sequence numbers). The probability of having two or more
1105657a8c20SJan Friedel 		 * tokens in the transmission queue is low and at the same time
1106657a8c20SJan Friedel 		 * the performance gain due to using sequence numbers is quite
1107657a8c20SJan Friedel 		 * high.
1108657a8c20SJan Friedel 		 *
1109657a8c20SJan Friedel 		 * In case a harder condition with regard to duplicate sequence
1110657a8c20SJan Friedel 		 * numbers in the transmission queue will be desired over time,
1111657a8c20SJan Friedel 		 * the break_flag behavior used below should be
1112657a8c20SJan Friedel 		 * removed/changed_accordingly.
1113657a8c20SJan Friedel 		 */
1114657a8c20SJan Friedel 		break_flag = B_FALSE;
1115657a8c20SJan Friedel 		token_verified = B_FALSE;
1116657a8c20SJan Friedel 		(void) pthread_mutex_lock(&transq_lock);
1117657a8c20SJan Friedel 		cur_node = transq_hdr.head;
1118657a8c20SJan Friedel 		while (cur_node != NULL && !break_flag) {
1119657a8c20SJan Friedel 			if (cur_node->seq_num != r_seq_num) {
1120657a8c20SJan Friedel 				cur_node = cur_node->next;
1121657a8c20SJan Friedel 				continue;
1122657a8c20SJan Friedel 			}
1123657a8c20SJan Friedel 
1124657a8c20SJan Friedel 			(void) pthread_mutex_lock(&gss_ctx_lock);
1125657a8c20SJan Friedel 			maj_stat = gss_verify_mic(&min_stat, gss_ctx,
1126657a8c20SJan Friedel 			    &(cur_node->seq_token), &in_buf_mic,
1127657a8c20SJan Friedel 			    &qop_state);
1128657a8c20SJan Friedel 			(void) pthread_mutex_unlock(&gss_ctx_lock);
1129657a8c20SJan Friedel 
1130657a8c20SJan Friedel 			if (!GSS_ERROR(maj_stat)) { /* the success case */
1131657a8c20SJan Friedel 				switch (maj_stat) {
1132657a8c20SJan Friedel 				/*
1133657a8c20SJan Friedel 				 * All the GSS_S_OLD_TOKEN, GSS_S_UNSEQ_TOKEN,
1134657a8c20SJan Friedel 				 * GSS_S_GAP_TOKEN are perceived as correct
1135657a8c20SJan Friedel 				 * behavior of the server side. The plugin
1136657a8c20SJan Friedel 				 * implementation is resistant to any of the
1137657a8c20SJan Friedel 				 * above mention cases of returned status codes.
1138657a8c20SJan Friedel 				 */
1139657a8c20SJan Friedel 				/*FALLTHRU*/
1140657a8c20SJan Friedel 				case GSS_S_OLD_TOKEN:
1141657a8c20SJan Friedel 				case GSS_S_UNSEQ_TOKEN:
1142657a8c20SJan Friedel 				case GSS_S_GAP_TOKEN:
1143657a8c20SJan Friedel 				case GSS_S_COMPLETE:
1144657a8c20SJan Friedel 					/*
1145657a8c20SJan Friedel 					 * remove the verified record/node from
1146657a8c20SJan Friedel 					 * the transmission queue
1147657a8c20SJan Friedel 					 */
1148657a8c20SJan Friedel 					transq_dequeue(cur_node);
1149657a8c20SJan Friedel 					DPRINT((dfile, "Recv thread verified "
1150657a8c20SJan Friedel 					    "the token (transq len = %ld)\n",
1151657a8c20SJan Friedel 					    transq_hdr.count));
1152657a8c20SJan Friedel 
1153657a8c20SJan Friedel 					token_verified = B_TRUE;
1154657a8c20SJan Friedel 					break_flag = B_TRUE;
1155657a8c20SJan Friedel 					break;
1156657a8c20SJan Friedel 
1157657a8c20SJan Friedel 				/*
1158657a8c20SJan Friedel 				 * Both the default case as well as
1159657a8c20SJan Friedel 				 * GSS_S_DUPLICATE_TOKEN case should never
1160657a8c20SJan Friedel 				 * occur. It's been left here for the sake of
1161657a8c20SJan Friedel 				 * completeness.
1162657a8c20SJan Friedel 				 * If any of the two cases occur, it is
1163657a8c20SJan Friedel 				 * subsequently cought because we don't set
1164657a8c20SJan Friedel 				 * the token_verified flag.
1165657a8c20SJan Friedel 				 */
1166657a8c20SJan Friedel 				/*FALLTHRU*/
1167657a8c20SJan Friedel 				case GSS_S_DUPLICATE_TOKEN:
1168657a8c20SJan Friedel 				default:
1169657a8c20SJan Friedel 					break_flag = B_TRUE;
1170657a8c20SJan Friedel 					break;
1171657a8c20SJan Friedel 				} /* switch (maj_stat) */
1172657a8c20SJan Friedel 
1173657a8c20SJan Friedel 			} else { 	/* the failure case */
1174657a8c20SJan Friedel 				report_gss_err(
1175657a8c20SJan Friedel 				    gettext("signature verification of the "
1176657a8c20SJan Friedel 				    "received token failed"),
1177657a8c20SJan Friedel 				    maj_stat, min_stat);
1178657a8c20SJan Friedel 
1179657a8c20SJan Friedel 				switch (maj_stat) {
1180657a8c20SJan Friedel 				case GSS_S_CONTEXT_EXPIRED:
1181657a8c20SJan Friedel 					/* retransmission necessary */
1182657a8c20SJan Friedel 					recv_closure_rsn = RSN_GSS_CTX_EXP;
1183657a8c20SJan Friedel 					break_flag = B_TRUE;
1184657a8c20SJan Friedel 					DPRINT((dfile, "Recv thread detected "
1185657a8c20SJan Friedel 					    "the GSS context expiration\n"));
1186657a8c20SJan Friedel 					break;
1187657a8c20SJan Friedel 				case GSS_S_BAD_SIG:
1188657a8c20SJan Friedel 					DPRINT((dfile, "Bad signature "
1189657a8c20SJan Friedel 					    "detected (seq_num = %lld)\n",
1190657a8c20SJan Friedel 					    cur_node->seq_num));
1191657a8c20SJan Friedel 					cur_node = cur_node->next;
1192657a8c20SJan Friedel 					break;
1193657a8c20SJan Friedel 				default:
1194657a8c20SJan Friedel 					report_gss_err(
1195657a8c20SJan Friedel 					    gettext("signature verification"),
1196657a8c20SJan Friedel 					    maj_stat, min_stat);
1197657a8c20SJan Friedel 					break_flag = B_TRUE;
1198657a8c20SJan Friedel 					break;
1199657a8c20SJan Friedel 				}
1200657a8c20SJan Friedel 			}
1201657a8c20SJan Friedel 
1202657a8c20SJan Friedel 		} /* while */
1203657a8c20SJan Friedel 		(void) pthread_mutex_unlock(&transq_lock);
1204657a8c20SJan Friedel 
1205657a8c20SJan Friedel 		if (in_buf.value != NULL) {
1206657a8c20SJan Friedel 			free(in_buf.value);
1207657a8c20SJan Friedel 			in_buf.value = NULL;
1208657a8c20SJan Friedel 			in_buf.length = 0;
1209657a8c20SJan Friedel 		}
1210657a8c20SJan Friedel 
1211657a8c20SJan Friedel 		if (!token_verified) {
1212657a8c20SJan Friedel 			/*
1213657a8c20SJan Friedel 			 * Received, but unverifiable token is perceived as
1214657a8c20SJan Friedel 			 * the protocol flow corruption with the penalty of
1215657a8c20SJan Friedel 			 * reinitializing the client/server connection.
1216657a8c20SJan Friedel 			 */
1217657a8c20SJan Friedel 			DPRINT((dfile, "received unverifiable token\n"));
1218657a8c20SJan Friedel 			report_err(gettext("received unverifiable token\n"));
1219657a8c20SJan Friedel 			if (recv_closure_rsn == RSN_UNDEFINED) {
1220657a8c20SJan Friedel 				recv_closure_rsn = RSN_TOK_UNVERIFIABLE;
1221657a8c20SJan Friedel 			}
1222657a8c20SJan Friedel 			reset_transport(DO_CLOSE, DO_NOT_SYNC);
1223657a8c20SJan Friedel 		}
1224657a8c20SJan Friedel 
1225657a8c20SJan Friedel 	} /* for (;;) */
1226657a8c20SJan Friedel 
1227657a8c20SJan Friedel 
1228657a8c20SJan Friedel }
1229657a8c20SJan Friedel 
1230657a8c20SJan Friedel 
1231657a8c20SJan Friedel /*
1232657a8c20SJan Friedel  * init_poll() - initiates the polling in the receiving thread via sending the
1233657a8c20SJan Friedel  * appropriate message over the notify pipe. Message format = (int ||
1234657a8c20SJan Friedel  * booleant_t), where the first part (sizeof (int)) contains the
1235657a8c20SJan Friedel  * newly_opened/to_be_polled socket file descriptor. The contents of the second
1236657a8c20SJan Friedel  * part (sizeof (boolean_t)) of the message works only as a padding here and no
1237657a8c20SJan Friedel  * action (no recv/send thread synchronisation) is made in the receiving thread
1238657a8c20SJan Friedel  * based on its value.
1239657a8c20SJan Friedel  */
1240657a8c20SJan Friedel static boolean_t
1241657a8c20SJan Friedel init_poll(int fd)
1242657a8c20SJan Friedel {
1243657a8c20SJan Friedel 	pipe_msg_t	np_data;
1244657a8c20SJan Friedel 	int		pipe_in = notify_pipe[0];
1245657a8c20SJan Friedel 
1246657a8c20SJan Friedel 	np_data.sock_num = fd;
1247657a8c20SJan Friedel 	np_data.sync = B_FALSE;	/* padding only */
1248657a8c20SJan Friedel 
1249657a8c20SJan Friedel 	if (!write_fd(pipe_in, (char *)&np_data, sizeof (np_data))) {
1250657a8c20SJan Friedel 		DPRINT((dfile, "Cannot write to the notify pipe\n"));
1251657a8c20SJan Friedel 		report_err(gettext("writing to the notify pipe failed"));
1252657a8c20SJan Friedel 		return (B_FALSE);
1253657a8c20SJan Friedel 	}
1254657a8c20SJan Friedel 
1255657a8c20SJan Friedel 	return (B_TRUE);
1256657a8c20SJan Friedel }
1257657a8c20SJan Friedel 
1258657a8c20SJan Friedel 
1259657a8c20SJan Friedel /*
1260657a8c20SJan Friedel  * reset_transport() - locked by the reset_lock initiates the reset of socket,
1261657a8c20SJan Friedel  * GSS security context and (possibly) flags the transq for retransmission; for
1262657a8c20SJan Friedel  * more detailed information see do_reset(). The reset_transport() also allows
1263657a8c20SJan Friedel  * the synchronization - waiting for the reset to be finished.
1264657a8c20SJan Friedel  *
1265657a8c20SJan Friedel  * do_close: DO_SYNC, DO_NOT_SYNC
1266657a8c20SJan Friedel  * sync_on_return: DO_EXIT (DO_NOT_CLOSE), DO_CLOSE (DO_NOT_EXIT)
1267657a8c20SJan Friedel  *
1268657a8c20SJan Friedel  */
1269657a8c20SJan Friedel void
1270657a8c20SJan Friedel reset_transport(boolean_t do_close, boolean_t sync_on_return)
1271657a8c20SJan Friedel {
1272657a8c20SJan Friedel 	int		pipe_in = notify_pipe[0];
1273657a8c20SJan Friedel 	pipe_msg_t	np_data;
1274657a8c20SJan Friedel 
1275657a8c20SJan Friedel 	/*
1276657a8c20SJan Friedel 	 * Check if the reset routine is in progress or whether it was already
1277657a8c20SJan Friedel 	 * executed by some other thread.
1278657a8c20SJan Friedel 	 */
1279657a8c20SJan Friedel 	(void) pthread_mutex_lock(&reset_lock);
1280657a8c20SJan Friedel 	if (reset_in_progress) {
1281657a8c20SJan Friedel 		(void) pthread_mutex_unlock(&reset_lock);
1282657a8c20SJan Friedel 		return;
1283657a8c20SJan Friedel 	}
1284657a8c20SJan Friedel 	reset_in_progress = B_TRUE;
1285657a8c20SJan Friedel 
1286657a8c20SJan Friedel 	np_data.sock_num = (do_close ? NP_CLOSE : NP_EXIT);
1287657a8c20SJan Friedel 	np_data.sync = sync_on_return;
1288657a8c20SJan Friedel 	(void) write_fd(pipe_in, (char *)&np_data, sizeof (np_data));
1289657a8c20SJan Friedel 
1290657a8c20SJan Friedel 	if (sync_on_return) {
1291657a8c20SJan Friedel 		while (reset_in_progress) {
1292657a8c20SJan Friedel 			(void) pthread_cond_wait(&reset_cv, &reset_lock);
1293657a8c20SJan Friedel 			DPRINT((dfile, "Wait for sync\n"));
1294657a8c20SJan Friedel 		}
1295657a8c20SJan Friedel 		DPRINT((dfile, "Synced\n"));
1296657a8c20SJan Friedel 	}
1297657a8c20SJan Friedel 	(void) pthread_mutex_unlock(&reset_lock);
1298657a8c20SJan Friedel 
1299657a8c20SJan Friedel }
1300657a8c20SJan Friedel 
1301657a8c20SJan Friedel 
1302657a8c20SJan Friedel /*
1303657a8c20SJan Friedel  * do_reset() - the own reseting routine called from the recv thread. If the
1304657a8c20SJan Friedel  * synchronization was requested, signal the finish via conditional variable.
1305657a8c20SJan Friedel  */
1306657a8c20SJan Friedel static void
1307657a8c20SJan Friedel do_reset(int *fds_cnt, struct pollfd *recv_fd, boolean_t do_signal)
1308657a8c20SJan Friedel {
1309657a8c20SJan Friedel 
1310657a8c20SJan Friedel 	(void) pthread_mutex_lock(&reset_lock);
1311657a8c20SJan Friedel 
1312657a8c20SJan Friedel 	/* socket */
1313657a8c20SJan Friedel 	(void) pthread_mutex_lock(&sock_lock);
1314657a8c20SJan Friedel 	if (sockfd == -1) {
1315657a8c20SJan Friedel 		DPRINT((dfile, "socket already closed\n"));
1316657a8c20SJan Friedel 		(void) pthread_mutex_unlock(&sock_lock);
1317657a8c20SJan Friedel 		goto out;
1318657a8c20SJan Friedel 	} else {
1319657a8c20SJan Friedel 		(void) close(sockfd);
1320657a8c20SJan Friedel 		sockfd = -1;
1321657a8c20SJan Friedel 		recv_fd->fd = -1;
1322657a8c20SJan Friedel 		(void) pthread_mutex_unlock(&sock_lock);
1323657a8c20SJan Friedel 	}
1324657a8c20SJan Friedel 	*fds_cnt = 1;
1325657a8c20SJan Friedel 
1326657a8c20SJan Friedel 	/* context */
1327657a8c20SJan Friedel 	if (gss_ctx_initialized) {
1328657a8c20SJan Friedel 		delete_context();
1329657a8c20SJan Friedel 	}
1330657a8c20SJan Friedel 	gss_ctx_initialized = B_FALSE;
1331657a8c20SJan Friedel 	gss_ctx = NULL;
1332657a8c20SJan Friedel 
1333657a8c20SJan Friedel 	/* mark transq to be flushed */
1334657a8c20SJan Friedel 	(void) pthread_mutex_lock(&transq_lock);
1335657a8c20SJan Friedel 	if (transq_hdr.count > 0) {
1336657a8c20SJan Friedel 		flush_transq = B_TRUE;
1337657a8c20SJan Friedel 	}
1338657a8c20SJan Friedel 	(void) pthread_mutex_unlock(&transq_lock);
1339657a8c20SJan Friedel 
1340657a8c20SJan Friedel out:
1341657a8c20SJan Friedel 	reset_in_progress = B_FALSE;
1342657a8c20SJan Friedel 	if (do_signal) {
1343657a8c20SJan Friedel 		(void) pthread_cond_broadcast(&reset_cv);
1344657a8c20SJan Friedel 	}
1345657a8c20SJan Friedel 
1346657a8c20SJan Friedel 	(void) pthread_mutex_unlock(&reset_lock);
1347657a8c20SJan Friedel }
1348657a8c20SJan Friedel 
1349657a8c20SJan Friedel /*
1350657a8c20SJan Friedel  * do_cleanup() - removes all the preallocated space by the plugin; prepares the
1351657a8c20SJan Friedel  * plugin/application to be gracefully finished. Even thought the function
1352657a8c20SJan Friedel  * allows execution without signalling the successful finish, it's recommended
1353657a8c20SJan Friedel  * to use it (we usually want to wait for cleanup before exiting).
1354657a8c20SJan Friedel  */
1355657a8c20SJan Friedel static void
1356657a8c20SJan Friedel do_cleanup(int *fds_cnt, struct pollfd *recv_fd, boolean_t do_signal)
1357657a8c20SJan Friedel {
1358657a8c20SJan Friedel 
1359657a8c20SJan Friedel 	(void) pthread_mutex_lock(&reset_lock);
1360657a8c20SJan Friedel 
1361657a8c20SJan Friedel 	/*
1362657a8c20SJan Friedel 	 * socket
1363657a8c20SJan Friedel 	 * note: keeping locking for safety, thought it shouldn't be necessary
1364657a8c20SJan Friedel 	 * in current implementation - we get here only in case the sending code
1365657a8c20SJan Friedel 	 * path calls auditd_plugin_close() (thus no socket manipulation) and
1366657a8c20SJan Friedel 	 * the recv thread is doing the own socket closure.
1367657a8c20SJan Friedel 	 */
1368657a8c20SJan Friedel 	(void) pthread_mutex_lock(&sock_lock);
1369657a8c20SJan Friedel 	if (sockfd != -1) {
1370657a8c20SJan Friedel 		DPRINT((dfile, "Closing socket: %d\n", sockfd));
1371657a8c20SJan Friedel 		(void) close(sockfd);
1372657a8c20SJan Friedel 		sockfd = -1;
1373657a8c20SJan Friedel 		recv_fd->fd = -1;
1374657a8c20SJan Friedel 	}
1375657a8c20SJan Friedel 	*fds_cnt = 1;
1376657a8c20SJan Friedel 	(void) pthread_mutex_unlock(&sock_lock);
1377657a8c20SJan Friedel 
1378657a8c20SJan Friedel 	/* context */
1379657a8c20SJan Friedel 	if (gss_ctx_initialized) {
1380657a8c20SJan Friedel 		DPRINT((dfile, "Deleting context: "));
1381657a8c20SJan Friedel 		delete_context();
1382657a8c20SJan Friedel 	}
1383657a8c20SJan Friedel 	gss_ctx_initialized = B_FALSE;
1384657a8c20SJan Friedel 	gss_ctx = NULL;
1385657a8c20SJan Friedel 
1386657a8c20SJan Friedel 	/* transmission queue */
1387657a8c20SJan Friedel 	(void) pthread_mutex_lock(&transq_lock);
1388657a8c20SJan Friedel 	if (transq_hdr.count > 0) {
1389657a8c20SJan Friedel 		DPRINT((dfile, "Deallocating the transmission queue "
1390657a8c20SJan Friedel 		    "(len = %ld)\n", transq_hdr.count));
1391657a8c20SJan Friedel 		while (transq_hdr.count > 0) {
1392657a8c20SJan Friedel 			transq_dequeue(transq_hdr.head);
1393657a8c20SJan Friedel 		}
1394657a8c20SJan Friedel 	}
1395657a8c20SJan Friedel 	(void) pthread_mutex_unlock(&transq_lock);
1396657a8c20SJan Friedel 
1397657a8c20SJan Friedel 	/* notification pipe */
1398657a8c20SJan Friedel 	if (notify_pipe_ready) {
1399657a8c20SJan Friedel 		(void) close(notify_pipe[0]);
1400657a8c20SJan Friedel 		(void) close(notify_pipe[1]);
1401657a8c20SJan Friedel 		notify_pipe_ready = B_FALSE;
1402657a8c20SJan Friedel 	}
1403657a8c20SJan Friedel 
1404657a8c20SJan Friedel 	reset_in_progress = B_FALSE;
1405657a8c20SJan Friedel 	if (do_signal) {
1406657a8c20SJan Friedel 		(void) pthread_cond_broadcast(&reset_cv);
1407657a8c20SJan Friedel 	}
1408657a8c20SJan Friedel 	(void) pthread_mutex_unlock(&reset_lock);
1409657a8c20SJan Friedel }
1410657a8c20SJan Friedel 
1411657a8c20SJan Friedel 
1412657a8c20SJan Friedel /*
1413657a8c20SJan Friedel  * transq_dequeue() - dequeues given node pointed by the node_ptr from the
1414657a8c20SJan Friedel  * transmission queue. Transmission queue should be locked prior to use of this
1415657a8c20SJan Friedel  * function.
1416657a8c20SJan Friedel  */
1417657a8c20SJan Friedel static void
1418657a8c20SJan Friedel transq_dequeue(transq_node_t *node_ptr)
1419657a8c20SJan Friedel {
1420657a8c20SJan Friedel 
1421657a8c20SJan Friedel 	if (node_ptr == NULL) {
1422657a8c20SJan Friedel 		DPRINT((dfile, "transq_dequeue(): called with NULL pointer\n"));
1423657a8c20SJan Friedel 		return;
1424657a8c20SJan Friedel 	}
1425657a8c20SJan Friedel 
1426657a8c20SJan Friedel 	free(node_ptr->seq_token.value);
1427657a8c20SJan Friedel 
1428657a8c20SJan Friedel 	if (node_ptr->prev != NULL) {
1429657a8c20SJan Friedel 		node_ptr->prev->next = node_ptr->next;
1430657a8c20SJan Friedel 	}
1431657a8c20SJan Friedel 	if (node_ptr->next != NULL) {
1432657a8c20SJan Friedel 		node_ptr->next->prev = node_ptr->prev;
1433657a8c20SJan Friedel 	}
1434657a8c20SJan Friedel 
1435657a8c20SJan Friedel 
1436657a8c20SJan Friedel 	/* update the transq_hdr */
1437657a8c20SJan Friedel 	if (node_ptr->next == NULL) {
1438657a8c20SJan Friedel 		transq_hdr.end = node_ptr->prev;
1439657a8c20SJan Friedel 	}
1440657a8c20SJan Friedel 	if (node_ptr->prev == NULL) {
1441657a8c20SJan Friedel 		transq_hdr.head = node_ptr->next;
1442657a8c20SJan Friedel 	}
1443657a8c20SJan Friedel 
1444657a8c20SJan Friedel 	transq_hdr.count--;
1445657a8c20SJan Friedel 
1446657a8c20SJan Friedel 	free(node_ptr);
1447657a8c20SJan Friedel }
1448657a8c20SJan Friedel 
1449657a8c20SJan Friedel 
1450657a8c20SJan Friedel /*
1451657a8c20SJan Friedel  * transq_enqueue() - creates new node in (at the end of) the transmission
1452657a8c20SJan Friedel  * queue. in_ptoken_ptr is a pointer to the plain token in a form of
1453657a8c20SJan Friedel  * gss_buffer_desc. Function returns 0 on success and updates the *node_ptr to
1454657a8c20SJan Friedel  * point to a newly added transmission queue node. In case of any failure
1455657a8c20SJan Friedel  * function returns 1 and sets the *node_ptr to NULL.
1456657a8c20SJan Friedel  * Transmission queue should be locked prior to use of this function.
1457657a8c20SJan Friedel  */
1458657a8c20SJan Friedel static boolean_t
1459657a8c20SJan Friedel transq_enqueue(transq_node_t **node_ptr, gss_buffer_t in_seqtoken_ptr,
1460657a8c20SJan Friedel     uint64_t sequence)
1461657a8c20SJan Friedel {
1462657a8c20SJan Friedel 
1463657a8c20SJan Friedel 	*node_ptr = calloc(1, sizeof (transq_node_t));
1464657a8c20SJan Friedel 	if (*node_ptr == NULL) {
1465657a8c20SJan Friedel 		report_err(gettext("Memory allocation failed"));
1466657a8c20SJan Friedel 		DPRINT((dfile, "Memory allocation failed: %s\n",
1467657a8c20SJan Friedel 		    strerror(errno)));
1468657a8c20SJan Friedel 		goto errout;
1469657a8c20SJan Friedel 	}
1470657a8c20SJan Friedel 
1471657a8c20SJan Friedel 	/* value of the seq_token.value = (sequence number || plain token) */
1472657a8c20SJan Friedel 	(*node_ptr)->seq_num = sequence;
1473657a8c20SJan Friedel 	(*node_ptr)->seq_token.length = in_seqtoken_ptr->length;
1474657a8c20SJan Friedel 	(*node_ptr)->seq_token.value = in_seqtoken_ptr->value;
1475657a8c20SJan Friedel 
1476657a8c20SJan Friedel 	/* update the transq_hdr */
1477657a8c20SJan Friedel 	if (transq_hdr.head == NULL) {
1478657a8c20SJan Friedel 		transq_hdr.head = *node_ptr;
1479657a8c20SJan Friedel 	}
1480657a8c20SJan Friedel 	if (transq_hdr.end != NULL) {
1481657a8c20SJan Friedel 		(transq_hdr.end)->next = *node_ptr;
1482657a8c20SJan Friedel 		(*node_ptr)->prev = transq_hdr.end;
1483657a8c20SJan Friedel 	}
1484657a8c20SJan Friedel 	transq_hdr.end = *node_ptr;
1485657a8c20SJan Friedel 
1486657a8c20SJan Friedel 	transq_hdr.count++;
1487657a8c20SJan Friedel 
1488657a8c20SJan Friedel 	return (B_TRUE);
1489657a8c20SJan Friedel 
1490657a8c20SJan Friedel errout:
1491657a8c20SJan Friedel 	if (*node_ptr != NULL) {
1492657a8c20SJan Friedel 		if ((*node_ptr)->seq_token.value != NULL) {
1493657a8c20SJan Friedel 			free((*node_ptr)->seq_token.value);
1494657a8c20SJan Friedel 		}
1495657a8c20SJan Friedel 		free(*node_ptr);
1496657a8c20SJan Friedel 		*node_ptr = NULL;
1497657a8c20SJan Friedel 	}
1498657a8c20SJan Friedel 	return (B_FALSE);
1499657a8c20SJan Friedel }
1500657a8c20SJan Friedel 
1501657a8c20SJan Friedel 
1502657a8c20SJan Friedel /*
1503657a8c20SJan Friedel  * transq_retransmit() - traverse the transmission queue and try to, 1 by 1,
1504657a8c20SJan Friedel  * re-wrap the tokens with the recent context information and retransmit the
1505657a8c20SJan Friedel  * tokens from the transmission queue.
1506657a8c20SJan Friedel  * Function returns 2 on GSS context expiration, 1 on any other error, 0 on
1507657a8c20SJan Friedel  * successfully resent transmission queue.
1508657a8c20SJan Friedel  */
1509657a8c20SJan Friedel static int
1510657a8c20SJan Friedel transq_retransmit()
1511657a8c20SJan Friedel {
1512657a8c20SJan Friedel 
1513657a8c20SJan Friedel 	OM_uint32	maj_stat, min_stat;
1514657a8c20SJan Friedel 	transq_node_t	*cur_node = transq_hdr.head;
1515657a8c20SJan Friedel 	gss_buffer_desc	out_buf;
1516657a8c20SJan Friedel 	int		conf_state;
1517657a8c20SJan Friedel 
1518657a8c20SJan Friedel 	DPRINT((dfile, "Retransmission of the remainder in the transqueue\n"));
1519657a8c20SJan Friedel 
1520657a8c20SJan Friedel 	while (cur_node != NULL) {
1521657a8c20SJan Friedel 
1522657a8c20SJan Friedel 		(void) pthread_mutex_lock(&transq_lock);
1523657a8c20SJan Friedel 		(void) pthread_mutex_lock(&gss_ctx_lock);
1524657a8c20SJan Friedel 		maj_stat = gss_wrap(&min_stat, gss_ctx, 1, GSS_C_QOP_DEFAULT,
1525657a8c20SJan Friedel 		    &(cur_node->seq_token), &conf_state, &out_buf);
1526657a8c20SJan Friedel 		(void) pthread_mutex_unlock(&gss_ctx_lock);
1527657a8c20SJan Friedel 
1528657a8c20SJan Friedel 		switch (maj_stat) {
1529657a8c20SJan Friedel 		case GSS_S_COMPLETE:
1530657a8c20SJan Friedel 			break;
1531657a8c20SJan Friedel 		case GSS_S_CONTEXT_EXPIRED:
1532657a8c20SJan Friedel 			DPRINT((dfile, "Context expired.\n"));
1533657a8c20SJan Friedel 			report_gss_err(gettext("gss_wrap message"), maj_stat,
1534657a8c20SJan Friedel 			    min_stat);
1535657a8c20SJan Friedel 			(void) pthread_mutex_unlock(&transq_lock);
1536657a8c20SJan Friedel 			return (2);
1537657a8c20SJan Friedel 		default:
1538657a8c20SJan Friedel 			report_gss_err(gettext("gss_wrap message"), maj_stat,
1539657a8c20SJan Friedel 			    min_stat);
1540657a8c20SJan Friedel 			(void) pthread_mutex_unlock(&transq_lock);
1541657a8c20SJan Friedel 			return (1);
1542657a8c20SJan Friedel 		}
1543657a8c20SJan Friedel 
1544657a8c20SJan Friedel 		DPRINT((dfile, "Sending transmission queue token (seq=%lld, "
1545657a8c20SJan Friedel 		    "size=%d, transq len=%ld)\n", cur_node->seq_num,
1546657a8c20SJan Friedel 		    out_buf.length, transq_hdr.count));
1547657a8c20SJan Friedel 		if (send_token(&sockfd, &out_buf) < 0) {
1548657a8c20SJan Friedel 			(void) gss_release_buffer(&min_stat, &out_buf);
1549657a8c20SJan Friedel 			(void) pthread_mutex_unlock(&transq_lock);
1550657a8c20SJan Friedel 			return (1);
1551657a8c20SJan Friedel 		}
1552657a8c20SJan Friedel 		(void) gss_release_buffer(&min_stat, &out_buf);
1553657a8c20SJan Friedel 
1554657a8c20SJan Friedel 		cur_node = cur_node->next;
1555657a8c20SJan Friedel 		(void) pthread_mutex_unlock(&transq_lock);
1556657a8c20SJan Friedel 
1557657a8c20SJan Friedel 	} /* while */
1558657a8c20SJan Friedel 
1559657a8c20SJan Friedel 	return (0);
1560657a8c20SJan Friedel }
1561