xref: /onnv-gate/usr/src/lib/auditd_plugins/remote/transport.c (revision 10926:0f7be8ada1ff)
110188SJan.Friedel@Sun.COM /*
210188SJan.Friedel@Sun.COM  * CDDL HEADER START
310188SJan.Friedel@Sun.COM  *
410188SJan.Friedel@Sun.COM  * The contents of this file are subject to the terms of the
510188SJan.Friedel@Sun.COM  * Common Development and Distribution License (the "License").
610188SJan.Friedel@Sun.COM  * You may not use this file except in compliance with the License.
710188SJan.Friedel@Sun.COM  *
810188SJan.Friedel@Sun.COM  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
910188SJan.Friedel@Sun.COM  * or http://www.opensolaris.org/os/licensing.
1010188SJan.Friedel@Sun.COM  * See the License for the specific language governing permissions
1110188SJan.Friedel@Sun.COM  * and limitations under the License.
1210188SJan.Friedel@Sun.COM  *
1310188SJan.Friedel@Sun.COM  * When distributing Covered Code, include this CDDL HEADER in each
1410188SJan.Friedel@Sun.COM  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1510188SJan.Friedel@Sun.COM  * If applicable, add the following below this CDDL HEADER, with the
1610188SJan.Friedel@Sun.COM  * fields enclosed by brackets "[]" replaced with your own identifying
1710188SJan.Friedel@Sun.COM  * information: Portions Copyright [yyyy] [name of copyright owner]
1810188SJan.Friedel@Sun.COM  *
1910188SJan.Friedel@Sun.COM  * CDDL HEADER END
2010188SJan.Friedel@Sun.COM  */
2110188SJan.Friedel@Sun.COM /*
2210188SJan.Friedel@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
2310188SJan.Friedel@Sun.COM  * Use is subject to license terms.
2410188SJan.Friedel@Sun.COM  *
2510188SJan.Friedel@Sun.COM  * transport layer for audit_remote (handles connection establishment, gss
2610188SJan.Friedel@Sun.COM  * context initialization, message encryption and verification)
2710188SJan.Friedel@Sun.COM  *
2810188SJan.Friedel@Sun.COM  */
2910188SJan.Friedel@Sun.COM 
3010188SJan.Friedel@Sun.COM #include <assert.h>
3110188SJan.Friedel@Sun.COM #include <audit_plugin.h>
3210188SJan.Friedel@Sun.COM #include <errno.h>
3310188SJan.Friedel@Sun.COM #include <fcntl.h>
3410188SJan.Friedel@Sun.COM #include <gssapi/gssapi.h>
3510188SJan.Friedel@Sun.COM #include <libintl.h>
3610188SJan.Friedel@Sun.COM #include <mtmalloc.h>
3710188SJan.Friedel@Sun.COM #include <netdb.h>
3810188SJan.Friedel@Sun.COM #include <netinet/in.h>
3910188SJan.Friedel@Sun.COM #include <netinet/tcp.h>
4010188SJan.Friedel@Sun.COM #include <stdio.h>
4110188SJan.Friedel@Sun.COM #include <stdlib.h>
4210188SJan.Friedel@Sun.COM #include <string.h>
4310188SJan.Friedel@Sun.COM #include <strings.h>
4410188SJan.Friedel@Sun.COM #include <syslog.h>
4510188SJan.Friedel@Sun.COM #include <sys/types.h>
4610188SJan.Friedel@Sun.COM #include <sys/socket.h>
4710188SJan.Friedel@Sun.COM #include <unistd.h>
4810188SJan.Friedel@Sun.COM #include <poll.h>
4910188SJan.Friedel@Sun.COM #include <pthread.h>
5010188SJan.Friedel@Sun.COM 
5110188SJan.Friedel@Sun.COM #include "audit_remote.h"
5210188SJan.Friedel@Sun.COM 
5310188SJan.Friedel@Sun.COM 
5410188SJan.Friedel@Sun.COM static int 		sockfd = -1;
5510188SJan.Friedel@Sun.COM static struct hostent	*current_host;
5610188SJan.Friedel@Sun.COM static gss_OID		*current_mech_oid;
5710188SJan.Friedel@Sun.COM static in_port_t 	current_port;
5810188SJan.Friedel@Sun.COM static boolean_t	flush_transq;
5910188SJan.Friedel@Sun.COM 
6010188SJan.Friedel@Sun.COM static char		*ver_str = "01";	/* supported protocol version */
6110188SJan.Friedel@Sun.COM static char		*ver_str_concat;	/* concat serv/client version */
6210188SJan.Friedel@Sun.COM 
6310188SJan.Friedel@Sun.COM static gss_ctx_id_t	gss_ctx;
6410188SJan.Friedel@Sun.COM static boolean_t	gss_ctx_initialized;
6510188SJan.Friedel@Sun.COM 
6610188SJan.Friedel@Sun.COM pthread_t		recv_tid;		/* receiving thread */
6710188SJan.Friedel@Sun.COM static pthread_once_t	recv_once_control = PTHREAD_ONCE_INIT;
6810188SJan.Friedel@Sun.COM 
6910188SJan.Friedel@Sun.COM extern int		timeout;		/* connection timeout */
7010188SJan.Friedel@Sun.COM 
7110188SJan.Friedel@Sun.COM extern pthread_mutex_t	plugin_mutex;
7210188SJan.Friedel@Sun.COM transq_hdr_t		transq_hdr;
7310188SJan.Friedel@Sun.COM 
7410188SJan.Friedel@Sun.COM /*
7510188SJan.Friedel@Sun.COM  * The three locks synchronize the simultaneous actions on top of transmission
7610188SJan.Friedel@Sun.COM  * queue, socket, gss_context.
7710188SJan.Friedel@Sun.COM  */
7810188SJan.Friedel@Sun.COM pthread_mutex_t		transq_lock = PTHREAD_MUTEX_INITIALIZER;
7910188SJan.Friedel@Sun.COM pthread_mutex_t		sock_lock = PTHREAD_MUTEX_INITIALIZER;
8010188SJan.Friedel@Sun.COM pthread_mutex_t		gss_ctx_lock = PTHREAD_MUTEX_INITIALIZER;
8110188SJan.Friedel@Sun.COM 
8210188SJan.Friedel@Sun.COM /* reset routine synchronization - required by the sending thread */
8310188SJan.Friedel@Sun.COM pthread_mutex_t		reset_lock = PTHREAD_MUTEX_INITIALIZER;
8410188SJan.Friedel@Sun.COM static boolean_t	reset_in_progress;	/* reset routine in progress */
8510188SJan.Friedel@Sun.COM 
8610188SJan.Friedel@Sun.COM #define	NP_CLOSE	-1		/* notification pipe - close message */
8710188SJan.Friedel@Sun.COM #define	NP_EXIT		-2		/* notification pipe - exit message */
8810188SJan.Friedel@Sun.COM boolean_t		notify_pipe_ready;
8910188SJan.Friedel@Sun.COM int			notify_pipe[2]; /* notif. pipe - receiving thread */
9010188SJan.Friedel@Sun.COM 
9110188SJan.Friedel@Sun.COM pthread_cond_t		reset_cv = PTHREAD_COND_INITIALIZER;
9210188SJan.Friedel@Sun.COM static close_rsn_t	recv_closure_rsn;
9310188SJan.Friedel@Sun.COM 
9410188SJan.Friedel@Sun.COM #define	MAX_TOK_LEN	(128 * 1000)	/* max token length we accept (B) */
9510188SJan.Friedel@Sun.COM 
9610188SJan.Friedel@Sun.COM /* transmission queue helpers */
9710188SJan.Friedel@Sun.COM static void		transq_dequeue(transq_node_t *);
9810188SJan.Friedel@Sun.COM static boolean_t	transq_enqueue(transq_node_t **, gss_buffer_t,
9910188SJan.Friedel@Sun.COM     uint64_t);
10010188SJan.Friedel@Sun.COM static int		transq_retransmit(void);
10110188SJan.Friedel@Sun.COM 
10210188SJan.Friedel@Sun.COM static boolean_t	init_poll(int);
10310188SJan.Friedel@Sun.COM static void 		do_reset(int *, struct pollfd *, boolean_t);
10410188SJan.Friedel@Sun.COM static void 		do_cleanup(int *, struct pollfd *, boolean_t);
10510188SJan.Friedel@Sun.COM 
10610188SJan.Friedel@Sun.COM static void		init_recv_record(void);
10710188SJan.Friedel@Sun.COM static void		recv_record();
10810188SJan.Friedel@Sun.COM static int		connect_timeout(int, struct sockaddr *, int);
10910188SJan.Friedel@Sun.COM static int		send_timeout(int, const char *, size_t);
11010188SJan.Friedel@Sun.COM static int		recv_timeout(int, char *, size_t);
11110188SJan.Friedel@Sun.COM static int		send_token(int *, gss_buffer_t);
11210188SJan.Friedel@Sun.COM static int		recv_token(int, gss_buffer_t);
11310188SJan.Friedel@Sun.COM 
11410188SJan.Friedel@Sun.COM 
11510188SJan.Friedel@Sun.COM /*
11610188SJan.Friedel@Sun.COM  * report_err() - wrapper, mainly due to enhance the code readability - report
11710188SJan.Friedel@Sun.COM  * error to syslog via call to __audit_syslog().
11810188SJan.Friedel@Sun.COM  */
11910188SJan.Friedel@Sun.COM static void
report_err(char * msg)12010188SJan.Friedel@Sun.COM report_err(char *msg)
12110188SJan.Friedel@Sun.COM {
12210188SJan.Friedel@Sun.COM 	__audit_syslog("audit_remote.so", LOG_CONS | LOG_NDELAY, LOG_DAEMON,
12310188SJan.Friedel@Sun.COM 	    LOG_ERR, msg);
12410188SJan.Friedel@Sun.COM 
12510188SJan.Friedel@Sun.COM }
12610188SJan.Friedel@Sun.COM 
12710188SJan.Friedel@Sun.COM 
12810188SJan.Friedel@Sun.COM /*
12910188SJan.Friedel@Sun.COM  * report_gss_err() - GSS API error reporting
13010188SJan.Friedel@Sun.COM  */
13110188SJan.Friedel@Sun.COM static void
report_gss_err(char * msg,OM_uint32 maj_stat,OM_uint32 min_stat)13210188SJan.Friedel@Sun.COM report_gss_err(char *msg, OM_uint32 maj_stat, OM_uint32 min_stat)
13310188SJan.Friedel@Sun.COM {
13410188SJan.Friedel@Sun.COM 	gss_buffer_desc	msg_buf;
13510188SJan.Friedel@Sun.COM 	OM_uint32	_min, msg_ctx;
13610188SJan.Friedel@Sun.COM 	char		*err_msg;
13710188SJan.Friedel@Sun.COM 
13810188SJan.Friedel@Sun.COM 	/* major stat */
13910188SJan.Friedel@Sun.COM 	msg_ctx = 0;
14010188SJan.Friedel@Sun.COM 	do {
14110188SJan.Friedel@Sun.COM 		(void) gss_display_status(&_min, maj_stat, GSS_C_GSS_CODE,
14210188SJan.Friedel@Sun.COM 		    *current_mech_oid, &msg_ctx, &msg_buf);
14310188SJan.Friedel@Sun.COM 		(void) asprintf(&err_msg,
14410188SJan.Friedel@Sun.COM 		    gettext("GSS API error - %s(%u): %.*s\n"), msg, maj_stat,
14510188SJan.Friedel@Sun.COM 		    msg_buf.length, (char *)msg_buf.value);
14610188SJan.Friedel@Sun.COM 		if (err_msg != NULL) {
14710188SJan.Friedel@Sun.COM 			report_err(err_msg);
14810188SJan.Friedel@Sun.COM 			free(err_msg);
14910188SJan.Friedel@Sun.COM 		}
15010188SJan.Friedel@Sun.COM 		(void) gss_release_buffer(&_min, &msg_buf);
15110188SJan.Friedel@Sun.COM 	} while (msg_ctx);
15210188SJan.Friedel@Sun.COM 
15310188SJan.Friedel@Sun.COM 	/* minor stat */
15410188SJan.Friedel@Sun.COM 	msg_ctx = 0;
15510188SJan.Friedel@Sun.COM 	do {
15610188SJan.Friedel@Sun.COM 		(void) gss_display_status(&_min, min_stat, GSS_C_MECH_CODE,
15710188SJan.Friedel@Sun.COM 		    *current_mech_oid, &msg_ctx, &msg_buf);
15810188SJan.Friedel@Sun.COM 		(void) asprintf(&err_msg,
15910188SJan.Friedel@Sun.COM 		    gettext("GSS mech error - %s(%u): %.*s\n"), msg, min_stat,
16010188SJan.Friedel@Sun.COM 		    msg_buf.length, (char *)msg_buf.value);
16110188SJan.Friedel@Sun.COM 		if (err_msg != NULL) {
16210188SJan.Friedel@Sun.COM 			report_err(err_msg);
16310188SJan.Friedel@Sun.COM 			free(err_msg);
16410188SJan.Friedel@Sun.COM 		}
16510188SJan.Friedel@Sun.COM 		(void) gss_release_buffer(&_min, &msg_buf);
16610188SJan.Friedel@Sun.COM 	} while (msg_ctx);
16710188SJan.Friedel@Sun.COM }
16810188SJan.Friedel@Sun.COM 
16910188SJan.Friedel@Sun.COM /*
17010188SJan.Friedel@Sun.COM  * prot_ver_negotiate() - negotiate/acknowledge the protocol version. Currently,
17110188SJan.Friedel@Sun.COM  * there is only one version supported by the plugin - "01".
17210188SJan.Friedel@Sun.COM  * Note: connection must be initiated prior version negotiation
17310188SJan.Friedel@Sun.COM  */
17410188SJan.Friedel@Sun.COM static int
prot_ver_negotiate()17510188SJan.Friedel@Sun.COM prot_ver_negotiate()
17610188SJan.Friedel@Sun.COM {
17710188SJan.Friedel@Sun.COM 	gss_buffer_desc	out_buf, in_buf;
17810188SJan.Friedel@Sun.COM 	size_t		ver_str_concat_sz;
17910188SJan.Friedel@Sun.COM 
18010188SJan.Friedel@Sun.COM 	/*
18110188SJan.Friedel@Sun.COM 	 * Set the version proposal string - once we support more than
18210188SJan.Friedel@Sun.COM 	 * version "01" this part should be extended to solve the concatenation
18310188SJan.Friedel@Sun.COM 	 * of supported version identifiers.
18410188SJan.Friedel@Sun.COM 	 */
18510188SJan.Friedel@Sun.COM 	out_buf.value = (void *)ver_str;
18610188SJan.Friedel@Sun.COM 	out_buf.length = strlen((char *)out_buf.value);
18710188SJan.Friedel@Sun.COM 	DPRINT((dfile, "Protocol version proposal (size=%d): %.*s\n",
18810188SJan.Friedel@Sun.COM 	    out_buf.length, out_buf.length, (char *)out_buf.value));
18910188SJan.Friedel@Sun.COM 
19010188SJan.Friedel@Sun.COM 	if (send_token(&sockfd, &out_buf) < 0) {
19110188SJan.Friedel@Sun.COM 		DPRINT((dfile, "Sending protocol version token failed\n"));
19210188SJan.Friedel@Sun.COM 		return (-1);
19310188SJan.Friedel@Sun.COM 	}
19410188SJan.Friedel@Sun.COM 
19510188SJan.Friedel@Sun.COM 	if (recv_token(sockfd, &in_buf) < 0) {
19610188SJan.Friedel@Sun.COM 		DPRINT((dfile, "Receiving protocol version token failed\n"));
19710188SJan.Friedel@Sun.COM 		return (-1);
19810188SJan.Friedel@Sun.COM 	}
19910188SJan.Friedel@Sun.COM 
20010188SJan.Friedel@Sun.COM 	/*
20110188SJan.Friedel@Sun.COM 	 * Verify the sent/received string - memcmp() is sufficient here
20210188SJan.Friedel@Sun.COM 	 * because we support only one version and it is represented by
20310188SJan.Friedel@Sun.COM 	 * the "01" string. The received version has to be "01" string as well.
20410188SJan.Friedel@Sun.COM 	 */
20510188SJan.Friedel@Sun.COM 	if (out_buf.length != in_buf.length ||
20610188SJan.Friedel@Sun.COM 	    memcmp(out_buf.value, in_buf.value, out_buf.length) != 0) {
20710188SJan.Friedel@Sun.COM 		DPRINT((dfile, "Verification of the protocol version strings "
20810188SJan.Friedel@Sun.COM 		    "failed [%d:%s][%d:%s]\n", out_buf.length,
20910188SJan.Friedel@Sun.COM 		    (char *)out_buf.value, in_buf.length,
21010188SJan.Friedel@Sun.COM 		    (char *)in_buf.value));
21110188SJan.Friedel@Sun.COM 		free(in_buf.value);
21210188SJan.Friedel@Sun.COM 		return (-1);
21310188SJan.Friedel@Sun.COM 	}
21410188SJan.Friedel@Sun.COM 
21510188SJan.Friedel@Sun.COM 	/*
21610188SJan.Friedel@Sun.COM 	 * Prepare the concatenated client/server version strings later used
21710188SJan.Friedel@Sun.COM 	 * as an application_data field in the gss_channel_bindings_struct
21810188SJan.Friedel@Sun.COM 	 * structure.
21910188SJan.Friedel@Sun.COM 	 */
22010188SJan.Friedel@Sun.COM 	ver_str_concat_sz = out_buf.length + in_buf.length + 1;
22110188SJan.Friedel@Sun.COM 	ver_str_concat = (char *)calloc(1, ver_str_concat_sz);
22210188SJan.Friedel@Sun.COM 	if (ver_str_concat == NULL) {
22310188SJan.Friedel@Sun.COM 		report_err(gettext("Memory allocation failed"));
22410188SJan.Friedel@Sun.COM 		DPRINT((dfile, "Memory allocation failed: %s\n",
22510188SJan.Friedel@Sun.COM 		    strerror(errno)));
22610188SJan.Friedel@Sun.COM 		free(in_buf.value);
22710188SJan.Friedel@Sun.COM 		return (-1);
22810188SJan.Friedel@Sun.COM 	}
22910188SJan.Friedel@Sun.COM 	(void) memcpy(ver_str_concat, out_buf.value, out_buf.length);
23010188SJan.Friedel@Sun.COM 	(void) memcpy(ver_str_concat + out_buf.length, in_buf.value,
23110188SJan.Friedel@Sun.COM 	    in_buf.length);
23210188SJan.Friedel@Sun.COM 	DPRINT((dfile, "Concatenated version strings: %s\n", ver_str_concat));
23310188SJan.Friedel@Sun.COM 
23410188SJan.Friedel@Sun.COM 	DPRINT((dfile, "Protocol version agreed.\n"));
23510188SJan.Friedel@Sun.COM 	free(in_buf.value);
23610188SJan.Friedel@Sun.COM 	return (0);
23710188SJan.Friedel@Sun.COM }
23810188SJan.Friedel@Sun.COM 
23910188SJan.Friedel@Sun.COM /*
24010188SJan.Friedel@Sun.COM  * sock_prepare() - creates and connects socket. Function returns
24110188SJan.Friedel@Sun.COM  * B_FALSE/B_TRUE on failure/success and sets the err_rsn accordingly to the
24210188SJan.Friedel@Sun.COM  * reason of failure.
24310188SJan.Friedel@Sun.COM  */
24410188SJan.Friedel@Sun.COM static boolean_t
sock_prepare(int * sockfdptr,struct hostent * host,close_rsn_t * err_rsn)24510188SJan.Friedel@Sun.COM sock_prepare(int *sockfdptr, struct hostent *host, close_rsn_t *err_rsn)
24610188SJan.Friedel@Sun.COM {
24710188SJan.Friedel@Sun.COM 	struct sockaddr_storage	addr;
24810188SJan.Friedel@Sun.COM 	struct sockaddr_in	*sin;
24910188SJan.Friedel@Sun.COM 	struct sockaddr_in6	*sin6;
25010188SJan.Friedel@Sun.COM 	size_t			addr_len;
25110188SJan.Friedel@Sun.COM 	int			sock;
25210188SJan.Friedel@Sun.COM 
25310188SJan.Friedel@Sun.COM 	DPRINT((dfile, "Creating socket for %s\n", host->h_name));
25410188SJan.Friedel@Sun.COM 	bzero(&addr, sizeof (addr));
25510188SJan.Friedel@Sun.COM 	addr.ss_family = host->h_addrtype;
25610188SJan.Friedel@Sun.COM 	switch (host->h_addrtype) {
25710188SJan.Friedel@Sun.COM 	case AF_INET:
25810188SJan.Friedel@Sun.COM 		sin = (struct sockaddr_in *)&addr;
25910188SJan.Friedel@Sun.COM 		addr_len = sizeof (struct sockaddr_in);
26010188SJan.Friedel@Sun.COM 		bcopy(host->h_addr_list[0],
26110188SJan.Friedel@Sun.COM 		    &(sin->sin_addr), sizeof (struct in_addr));
26210188SJan.Friedel@Sun.COM 		sin->sin_port = current_port;
26310188SJan.Friedel@Sun.COM 		break;
26410188SJan.Friedel@Sun.COM 	case AF_INET6:
26510188SJan.Friedel@Sun.COM 		sin6 = (struct sockaddr_in6 *)&addr;
26610188SJan.Friedel@Sun.COM 		addr_len = sizeof (struct sockaddr_in6);
26710188SJan.Friedel@Sun.COM 		bcopy(host->h_addr_list[0],
26810188SJan.Friedel@Sun.COM 		    &(sin6->sin6_addr), sizeof (struct in6_addr));
26910188SJan.Friedel@Sun.COM 		sin6->sin6_port = current_port;
27010188SJan.Friedel@Sun.COM 		break;
27110188SJan.Friedel@Sun.COM 	default:
27210188SJan.Friedel@Sun.COM 		/* unknown address family */
27310188SJan.Friedel@Sun.COM 		*err_rsn = RSN_UNKNOWN_AF;
27410188SJan.Friedel@Sun.COM 		return (B_FALSE);
27510188SJan.Friedel@Sun.COM 	}
27610188SJan.Friedel@Sun.COM 	if ((sock = socket(addr.ss_family, SOCK_STREAM, 0)) == -1) {
27710188SJan.Friedel@Sun.COM 		*err_rsn = RSN_SOCKET_CREATE;
27810188SJan.Friedel@Sun.COM 		return (B_FALSE);
27910188SJan.Friedel@Sun.COM 	}
28010188SJan.Friedel@Sun.COM 	DPRINT((dfile, "Socket created, fd=%d, connecting..\n", sock));
28110188SJan.Friedel@Sun.COM 
28210188SJan.Friedel@Sun.COM 	if (connect_timeout(sock, (struct sockaddr *)&addr, addr_len)) {
28310188SJan.Friedel@Sun.COM 		(void) close(sock);
28410188SJan.Friedel@Sun.COM 		*err_rsn = RSN_CONNECTION_CREATE;
28510188SJan.Friedel@Sun.COM 		return (B_FALSE);
28610188SJan.Friedel@Sun.COM 	}
28710188SJan.Friedel@Sun.COM 	*sockfdptr = sock;
28810188SJan.Friedel@Sun.COM 	DPRINT((dfile, "Connected to %s via fd=%d\n", host->h_name,
28910188SJan.Friedel@Sun.COM 	    *sockfdptr));
29010188SJan.Friedel@Sun.COM 
29110188SJan.Friedel@Sun.COM 	return (B_TRUE);
29210188SJan.Friedel@Sun.COM }
29310188SJan.Friedel@Sun.COM 
29410188SJan.Friedel@Sun.COM /*
29510188SJan.Friedel@Sun.COM  * establish_context() - establish the client/server GSS context.
29610188SJan.Friedel@Sun.COM  *
29710188SJan.Friedel@Sun.COM  * Note: connection must be established and version negotiated (in plain text)
29810188SJan.Friedel@Sun.COM  * prior to establishing context.
29910188SJan.Friedel@Sun.COM  */
30010188SJan.Friedel@Sun.COM static int
establish_context()30110188SJan.Friedel@Sun.COM establish_context()
30210188SJan.Friedel@Sun.COM {
30310188SJan.Friedel@Sun.COM 	gss_buffer_desc				send_tok, recv_tok, *token_ptr;
30410188SJan.Friedel@Sun.COM 	OM_uint32				maj_stat, min_stat;
30510188SJan.Friedel@Sun.COM 	OM_uint32				init_sec_min_stat, ret_flags;
30610188SJan.Friedel@Sun.COM 	gss_name_t				gss_name;
30710188SJan.Friedel@Sun.COM 	char					*gss_svc_name = "audit";
30810188SJan.Friedel@Sun.COM 	char					*svc_name;
30910188SJan.Friedel@Sun.COM 	struct gss_channel_bindings_struct	input_chan_bindings;
31010188SJan.Friedel@Sun.COM 
31110188SJan.Friedel@Sun.COM 	/* GSS service name = gss_svc_name + "@" + remote hostname (fqdn) */
31210188SJan.Friedel@Sun.COM 	(void) asprintf(&svc_name, "%s@%s", gss_svc_name, current_host->h_name);
31310188SJan.Friedel@Sun.COM 	if (svc_name == NULL) {
31410188SJan.Friedel@Sun.COM 		report_err(gettext("Cannot allocate service name\n"));
31510188SJan.Friedel@Sun.COM 		DPRINT((dfile, "Memory allocation failed: %s\n",
31610188SJan.Friedel@Sun.COM 		    strerror(errno)));
31710188SJan.Friedel@Sun.COM 		return (-1);
31810188SJan.Friedel@Sun.COM 	}
31910188SJan.Friedel@Sun.COM 	DPRINT((dfile, "Service name: %s\n", svc_name));
32010188SJan.Friedel@Sun.COM 
32110188SJan.Friedel@Sun.COM 	send_tok.value = svc_name;
32210188SJan.Friedel@Sun.COM 	send_tok.length = strlen(svc_name);
32310188SJan.Friedel@Sun.COM 	maj_stat = gss_import_name(&min_stat, &send_tok,
32410188SJan.Friedel@Sun.COM 	    (gss_OID)GSS_C_NT_HOSTBASED_SERVICE, &gss_name);
32510188SJan.Friedel@Sun.COM 	if (maj_stat != GSS_S_COMPLETE) {
32610188SJan.Friedel@Sun.COM 		report_gss_err(gettext("initializing context"), maj_stat,
32710188SJan.Friedel@Sun.COM 		    min_stat);
32810188SJan.Friedel@Sun.COM 		free(svc_name);
32910188SJan.Friedel@Sun.COM 		return (-1);
33010188SJan.Friedel@Sun.COM 	}
33110188SJan.Friedel@Sun.COM 	token_ptr = GSS_C_NO_BUFFER;
33210188SJan.Friedel@Sun.COM 	gss_ctx = GSS_C_NO_CONTEXT;
33310188SJan.Friedel@Sun.COM 
33410188SJan.Friedel@Sun.COM 	/* initialize channel binding */
33510188SJan.Friedel@Sun.COM 	bzero(&input_chan_bindings, sizeof (input_chan_bindings));
33610925SJan.Friedel@Sun.COM 	input_chan_bindings.initiator_addrtype = GSS_C_AF_NULLADDR;
33710925SJan.Friedel@Sun.COM 	input_chan_bindings.acceptor_addrtype = GSS_C_AF_NULLADDR;
338*10926SJan.Friedel@Sun.COM 	input_chan_bindings.application_data.length = strlen(ver_str_concat);
33910188SJan.Friedel@Sun.COM 	input_chan_bindings.application_data.value = ver_str_concat;
34010188SJan.Friedel@Sun.COM 
34110188SJan.Friedel@Sun.COM 	(void) pthread_mutex_lock(&gss_ctx_lock);
34210188SJan.Friedel@Sun.COM 	do {
34310188SJan.Friedel@Sun.COM 		maj_stat = gss_init_sec_context(&init_sec_min_stat,
34410188SJan.Friedel@Sun.COM 		    GSS_C_NO_CREDENTIAL, &gss_ctx, gss_name, *current_mech_oid,
34510188SJan.Friedel@Sun.COM 		    GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG
34610188SJan.Friedel@Sun.COM 		    | GSS_C_CONF_FLAG, 0, &input_chan_bindings, token_ptr,
34710188SJan.Friedel@Sun.COM 		    NULL, &send_tok, &ret_flags, NULL);
34810188SJan.Friedel@Sun.COM 
34910188SJan.Friedel@Sun.COM 		if (token_ptr != GSS_C_NO_BUFFER) {
35010188SJan.Friedel@Sun.COM 			(void) gss_release_buffer(&min_stat, &recv_tok);
35110188SJan.Friedel@Sun.COM 		}
35210188SJan.Friedel@Sun.COM 
35310188SJan.Friedel@Sun.COM 		if (send_tok.length != 0) {
35410188SJan.Friedel@Sun.COM 			DPRINT((dfile,
35510188SJan.Friedel@Sun.COM 			    "Sending init_sec_context token (size=%d)\n",
35610188SJan.Friedel@Sun.COM 			    send_tok.length));
35710188SJan.Friedel@Sun.COM 			if (send_token(&sockfd, &send_tok) < 0) {
35810188SJan.Friedel@Sun.COM 				free(svc_name);
35910188SJan.Friedel@Sun.COM 				(void) gss_release_name(&min_stat, &gss_name);
36010188SJan.Friedel@Sun.COM 				(void) pthread_mutex_unlock(&gss_ctx_lock);
36110188SJan.Friedel@Sun.COM 				return (-1);
36210188SJan.Friedel@Sun.COM 			}
36310188SJan.Friedel@Sun.COM 		}
36410188SJan.Friedel@Sun.COM 		if (send_tok.value != NULL) {
36510188SJan.Friedel@Sun.COM 			free(send_tok.value);	/* freeing svc_name */
36610188SJan.Friedel@Sun.COM 			send_tok.value = NULL;
36710188SJan.Friedel@Sun.COM 			send_tok.length = 0;
36810188SJan.Friedel@Sun.COM 		}
36910188SJan.Friedel@Sun.COM 
37010188SJan.Friedel@Sun.COM 		if (maj_stat != GSS_S_COMPLETE &&
37110188SJan.Friedel@Sun.COM 		    maj_stat != GSS_S_CONTINUE_NEEDED) {
37210188SJan.Friedel@Sun.COM 			report_gss_err(gettext("initializing context"),
37310188SJan.Friedel@Sun.COM 			    maj_stat, init_sec_min_stat);
37410188SJan.Friedel@Sun.COM 			if (gss_ctx == GSS_C_NO_CONTEXT) {
37510188SJan.Friedel@Sun.COM 				(void) gss_delete_sec_context(&min_stat,
37610188SJan.Friedel@Sun.COM 				    &gss_ctx, GSS_C_NO_BUFFER);
37710188SJan.Friedel@Sun.COM 			}
37810188SJan.Friedel@Sun.COM 			(void) gss_release_name(&min_stat, &gss_name);
37910188SJan.Friedel@Sun.COM 			(void) pthread_mutex_unlock(&gss_ctx_lock);
38010188SJan.Friedel@Sun.COM 			return (-1);
38110188SJan.Friedel@Sun.COM 		}
38210188SJan.Friedel@Sun.COM 
38310188SJan.Friedel@Sun.COM 		if (maj_stat == GSS_S_CONTINUE_NEEDED) {
38410188SJan.Friedel@Sun.COM 			DPRINT((dfile, "continue needed... "));
38510188SJan.Friedel@Sun.COM 			if (recv_token(sockfd, &recv_tok) < 0) {
38610188SJan.Friedel@Sun.COM 				(void) gss_release_name(&min_stat, &gss_name);
38710188SJan.Friedel@Sun.COM 				(void) pthread_mutex_unlock(&gss_ctx_lock);
38810188SJan.Friedel@Sun.COM 				return (-1);
38910188SJan.Friedel@Sun.COM 			}
39010188SJan.Friedel@Sun.COM 			token_ptr = &recv_tok;
39110188SJan.Friedel@Sun.COM 		}
39210188SJan.Friedel@Sun.COM 	} while (maj_stat == GSS_S_CONTINUE_NEEDED);
39310188SJan.Friedel@Sun.COM 	(void) gss_release_name(&min_stat, &gss_name);
39410188SJan.Friedel@Sun.COM 
39510188SJan.Friedel@Sun.COM 	DPRINT((dfile, "context established\n"));
39610188SJan.Friedel@Sun.COM 	(void) pthread_mutex_unlock(&gss_ctx_lock);
39710188SJan.Friedel@Sun.COM 	return (0);
39810188SJan.Friedel@Sun.COM }
39910188SJan.Friedel@Sun.COM 
40010188SJan.Friedel@Sun.COM /*
40110188SJan.Friedel@Sun.COM  * delete_context() - release GSS context.
40210188SJan.Friedel@Sun.COM  */
40310188SJan.Friedel@Sun.COM static void
delete_context()40410188SJan.Friedel@Sun.COM delete_context()
40510188SJan.Friedel@Sun.COM {
40610188SJan.Friedel@Sun.COM 	OM_uint32	min_stat;
40710188SJan.Friedel@Sun.COM 
40810188SJan.Friedel@Sun.COM 	(void) gss_delete_sec_context(&min_stat, &gss_ctx, GSS_C_NO_BUFFER);
40910188SJan.Friedel@Sun.COM 	DPRINT((dfile, "context deleted\n"));
41010188SJan.Friedel@Sun.COM }
41110188SJan.Friedel@Sun.COM 
41210188SJan.Friedel@Sun.COM /*
41310188SJan.Friedel@Sun.COM  * send_token() - send GSS token over the wire.
41410188SJan.Friedel@Sun.COM  */
41510188SJan.Friedel@Sun.COM static int
send_token(int * fdptr,gss_buffer_t tok)41610188SJan.Friedel@Sun.COM send_token(int *fdptr, gss_buffer_t tok)
41710188SJan.Friedel@Sun.COM {
41810188SJan.Friedel@Sun.COM 	uint32_t	len;
41910188SJan.Friedel@Sun.COM 	uint32_t	lensz;
42010188SJan.Friedel@Sun.COM 	char		*out_buf;
42110188SJan.Friedel@Sun.COM 	int		fd;
42210188SJan.Friedel@Sun.COM 
42310188SJan.Friedel@Sun.COM 	(void) pthread_mutex_lock(&sock_lock);
42410188SJan.Friedel@Sun.COM 	if (*fdptr == -1) {
42510188SJan.Friedel@Sun.COM 		(void) pthread_mutex_unlock(&sock_lock);
42610188SJan.Friedel@Sun.COM 		DPRINT((dfile, "Socket detected as closed.\n"));
42710188SJan.Friedel@Sun.COM 		return (-1);
42810188SJan.Friedel@Sun.COM 	}
42910188SJan.Friedel@Sun.COM 	fd = *fdptr;
43010188SJan.Friedel@Sun.COM 
43110188SJan.Friedel@Sun.COM 	len = htonl(tok->length);
43210188SJan.Friedel@Sun.COM 	lensz = sizeof (len);
43310188SJan.Friedel@Sun.COM 
43410188SJan.Friedel@Sun.COM 	out_buf = (char *)malloc((size_t)(lensz + tok->length));
43510188SJan.Friedel@Sun.COM 	if (out_buf == NULL) {
43610188SJan.Friedel@Sun.COM 		(void) pthread_mutex_unlock(&sock_lock);
43710188SJan.Friedel@Sun.COM 		report_err(gettext("Memory allocation failed"));
43810188SJan.Friedel@Sun.COM 		DPRINT((dfile, "Memory allocation failed: %s\n",
43910188SJan.Friedel@Sun.COM 		    strerror(errno)));
44010188SJan.Friedel@Sun.COM 		return (-1);
44110188SJan.Friedel@Sun.COM 	}
44210188SJan.Friedel@Sun.COM 	(void) memcpy((void *)out_buf, (void *)&len, lensz);
44310188SJan.Friedel@Sun.COM 	(void) memcpy((void *)(out_buf + lensz), (void *)tok->value,
44410188SJan.Friedel@Sun.COM 	    tok->length);
44510188SJan.Friedel@Sun.COM 
44610188SJan.Friedel@Sun.COM 	if (send_timeout(fd, out_buf, (lensz + tok->length))) {
44710188SJan.Friedel@Sun.COM 		(void) pthread_mutex_unlock(&sock_lock);
44810188SJan.Friedel@Sun.COM 		free(out_buf);
44910188SJan.Friedel@Sun.COM 		return (-1);
45010188SJan.Friedel@Sun.COM 	}
45110188SJan.Friedel@Sun.COM 
45210188SJan.Friedel@Sun.COM 	(void) pthread_mutex_unlock(&sock_lock);
45310188SJan.Friedel@Sun.COM 	free(out_buf);
45410188SJan.Friedel@Sun.COM 	return (0);
45510188SJan.Friedel@Sun.COM }
45610188SJan.Friedel@Sun.COM 
45710188SJan.Friedel@Sun.COM 
45810188SJan.Friedel@Sun.COM /*
45910188SJan.Friedel@Sun.COM  * recv_token() - receive GSS token over the wire.
46010188SJan.Friedel@Sun.COM  */
46110188SJan.Friedel@Sun.COM static int
recv_token(int fd,gss_buffer_t tok)46210188SJan.Friedel@Sun.COM recv_token(int fd, gss_buffer_t tok)
46310188SJan.Friedel@Sun.COM {
46410188SJan.Friedel@Sun.COM 	uint32_t	len;
46510188SJan.Friedel@Sun.COM 
46610188SJan.Friedel@Sun.COM 	if (recv_timeout(fd, (char *)&len, sizeof (len))) {
46710188SJan.Friedel@Sun.COM 		return (-1);
46810188SJan.Friedel@Sun.COM 	}
46910188SJan.Friedel@Sun.COM 	len = ntohl(len);
47010188SJan.Friedel@Sun.COM 
47110188SJan.Friedel@Sun.COM 	/* simple DOS prevention mechanism */
47210188SJan.Friedel@Sun.COM 	if (len > MAX_TOK_LEN) {
47310188SJan.Friedel@Sun.COM 		report_err(gettext("Indicated invalid token length"));
47410188SJan.Friedel@Sun.COM 		DPRINT((dfile, "Indicated token length > %dB\n", MAX_TOK_LEN));
47510188SJan.Friedel@Sun.COM 		return (-1);
47610188SJan.Friedel@Sun.COM 	}
47710188SJan.Friedel@Sun.COM 
47810188SJan.Friedel@Sun.COM 	tok->value = (char *)malloc(len);
47910188SJan.Friedel@Sun.COM 	if (tok->value == NULL) {
48010188SJan.Friedel@Sun.COM 		report_err(gettext("Memory allocation failed"));
48110188SJan.Friedel@Sun.COM 		DPRINT((dfile, "Memory allocation failed: %s\n",
48210188SJan.Friedel@Sun.COM 		    strerror(errno)));
48310188SJan.Friedel@Sun.COM 		tok->length = 0;
48410188SJan.Friedel@Sun.COM 		return (-1);
48510188SJan.Friedel@Sun.COM 	}
48610188SJan.Friedel@Sun.COM 
48710188SJan.Friedel@Sun.COM 	if (recv_timeout(fd, tok->value, len)) {
48810188SJan.Friedel@Sun.COM 		free(tok->value);
48910188SJan.Friedel@Sun.COM 		tok->value = NULL;
49010188SJan.Friedel@Sun.COM 		tok->length = 0;
49110188SJan.Friedel@Sun.COM 		return (-1);
49210188SJan.Friedel@Sun.COM 	}
49310188SJan.Friedel@Sun.COM 
49410188SJan.Friedel@Sun.COM 	tok->length = len;
49510188SJan.Friedel@Sun.COM 	return (0);
49610188SJan.Friedel@Sun.COM }
49710188SJan.Friedel@Sun.COM 
49810188SJan.Friedel@Sun.COM 
49910188SJan.Friedel@Sun.COM /*
50010188SJan.Friedel@Sun.COM  * I/O functions
50110188SJan.Friedel@Sun.COM  */
50210188SJan.Friedel@Sun.COM 
50310188SJan.Friedel@Sun.COM /*
50410188SJan.Friedel@Sun.COM  * connect_timeout() - sets nonblocking I/O on a socket and timeout-connects
50510188SJan.Friedel@Sun.COM  */
50610188SJan.Friedel@Sun.COM static int
connect_timeout(int sockfd,struct sockaddr * name,int namelen)50710188SJan.Friedel@Sun.COM connect_timeout(int sockfd, struct sockaddr *name, int namelen)
50810188SJan.Friedel@Sun.COM {
50910188SJan.Friedel@Sun.COM 	int			flags;
51010188SJan.Friedel@Sun.COM 	struct pollfd		fds;
51110188SJan.Friedel@Sun.COM 	int			rc;
51210188SJan.Friedel@Sun.COM 	struct sockaddr_storage	addr;
51310188SJan.Friedel@Sun.COM 	socklen_t		addr_len = sizeof (addr);
51410188SJan.Friedel@Sun.COM 
51510188SJan.Friedel@Sun.COM 
51610188SJan.Friedel@Sun.COM 	flags = fcntl(sockfd, F_GETFL, 0);
51710188SJan.Friedel@Sun.COM 	if (fcntl(sockfd, F_SETFL, flags | O_NONBLOCK) == -1) {
51810188SJan.Friedel@Sun.COM 		return (-1);
51910188SJan.Friedel@Sun.COM 	}
52010188SJan.Friedel@Sun.COM 	if (connect(sockfd, name, namelen)) {
52110188SJan.Friedel@Sun.COM 		if (!(errno == EINTR || errno == EINPROGRESS ||
52210188SJan.Friedel@Sun.COM 		    errno == EWOULDBLOCK)) {
52310188SJan.Friedel@Sun.COM 			return (-1);
52410188SJan.Friedel@Sun.COM 		}
52510188SJan.Friedel@Sun.COM 	}
52610188SJan.Friedel@Sun.COM 	fds.fd = sockfd;
52710188SJan.Friedel@Sun.COM 	fds.events = POLLOUT;
52810188SJan.Friedel@Sun.COM 	for (;;) {
52910188SJan.Friedel@Sun.COM 		fds.revents = 0;
53010188SJan.Friedel@Sun.COM 		rc = poll(&fds, 1, timeout * 1000);
53110188SJan.Friedel@Sun.COM 		if (rc == 0) {	/* timeout */
53210188SJan.Friedel@Sun.COM 			return (-1);
53310188SJan.Friedel@Sun.COM 		} else if (rc < 0) {
53410188SJan.Friedel@Sun.COM 			if (errno == EINTR || errno == EAGAIN) {
53510188SJan.Friedel@Sun.COM 				continue;
53610188SJan.Friedel@Sun.COM 			} else {
53710188SJan.Friedel@Sun.COM 				return (-1);
53810188SJan.Friedel@Sun.COM 			}
53910188SJan.Friedel@Sun.COM 		}
54010188SJan.Friedel@Sun.COM 		if (fds.revents) {
54110188SJan.Friedel@Sun.COM 			if (getpeername(sockfd, (struct sockaddr *)&addr,
54210188SJan.Friedel@Sun.COM 			    &addr_len))
54310188SJan.Friedel@Sun.COM 				return (-1);
54410188SJan.Friedel@Sun.COM 		} else {
54510188SJan.Friedel@Sun.COM 			return (-1);
54610188SJan.Friedel@Sun.COM 		}
54710188SJan.Friedel@Sun.COM 		return (0);
54810188SJan.Friedel@Sun.COM 	}
54910188SJan.Friedel@Sun.COM }
55010188SJan.Friedel@Sun.COM 
55110188SJan.Friedel@Sun.COM /*
55210188SJan.Friedel@Sun.COM  * send_timeout() - send data (in chunks if needed, each chunk in timeout secs).
55310188SJan.Friedel@Sun.COM  */
55410188SJan.Friedel@Sun.COM static int
send_timeout(int fd,const char * buf,size_t len)55510188SJan.Friedel@Sun.COM send_timeout(int fd, const char *buf, size_t len)
55610188SJan.Friedel@Sun.COM {
55710188SJan.Friedel@Sun.COM 	int		bytes;
55810188SJan.Friedel@Sun.COM 	struct pollfd	fds;
55910188SJan.Friedel@Sun.COM 	int		rc;
56010188SJan.Friedel@Sun.COM 
56110188SJan.Friedel@Sun.COM 	fds.fd = fd;
56210188SJan.Friedel@Sun.COM 	fds.events = POLLOUT;
56310188SJan.Friedel@Sun.COM 
56410188SJan.Friedel@Sun.COM 	while (len) {
56510188SJan.Friedel@Sun.COM 		fds.revents = 0;
56610188SJan.Friedel@Sun.COM 		rc = poll(&fds, 1, timeout * 1000);
56710188SJan.Friedel@Sun.COM 		if (rc == 0) {	/* timeout */
56810188SJan.Friedel@Sun.COM 			return (-1);
56910188SJan.Friedel@Sun.COM 		} else if (rc < 0) {
57010188SJan.Friedel@Sun.COM 			if (errno == EINTR || errno == EAGAIN) {
57110188SJan.Friedel@Sun.COM 				continue;
57210188SJan.Friedel@Sun.COM 			} else {
57310188SJan.Friedel@Sun.COM 				return (-1);
57410188SJan.Friedel@Sun.COM 			}
57510188SJan.Friedel@Sun.COM 		}
57610188SJan.Friedel@Sun.COM 		if (!fds.revents) {
57710188SJan.Friedel@Sun.COM 			return (-1);
57810188SJan.Friedel@Sun.COM 		}
57910188SJan.Friedel@Sun.COM 
58010188SJan.Friedel@Sun.COM 		bytes = write(fd, buf, len);
58110188SJan.Friedel@Sun.COM 		if (bytes < 0) {
58210188SJan.Friedel@Sun.COM 			if (errno == EINTR) {
58310188SJan.Friedel@Sun.COM 				continue;
58410188SJan.Friedel@Sun.COM 			} else {
58510188SJan.Friedel@Sun.COM 				return (-1);
58610188SJan.Friedel@Sun.COM 			}
58710188SJan.Friedel@Sun.COM 		} else if (bytes == 0) {	/* eof */
58810188SJan.Friedel@Sun.COM 			return (-1);
58910188SJan.Friedel@Sun.COM 		}
59010188SJan.Friedel@Sun.COM 
59110188SJan.Friedel@Sun.COM 		len -= bytes;
59210188SJan.Friedel@Sun.COM 		buf += bytes;
59310188SJan.Friedel@Sun.COM 	}
59410188SJan.Friedel@Sun.COM 
59510188SJan.Friedel@Sun.COM 	return (0);
59610188SJan.Friedel@Sun.COM }
59710188SJan.Friedel@Sun.COM 
59810188SJan.Friedel@Sun.COM /*
59910188SJan.Friedel@Sun.COM  * recv_timeout() - receive data (in chunks if needed, each chunk in timeout
60010188SJan.Friedel@Sun.COM  * secs). In case the function is called from receiving thread, the function
60110188SJan.Friedel@Sun.COM  * cycles the poll() call in timeout seconds (waits for input from server).
60210188SJan.Friedel@Sun.COM  */
60310188SJan.Friedel@Sun.COM static int
recv_timeout(int fd,char * buf,size_t len)60410188SJan.Friedel@Sun.COM recv_timeout(int fd, char *buf, size_t len)
60510188SJan.Friedel@Sun.COM {
60610188SJan.Friedel@Sun.COM 	int		bytes;
60710188SJan.Friedel@Sun.COM 	struct pollfd	fds;
60810188SJan.Friedel@Sun.COM 	int		rc;
60910188SJan.Friedel@Sun.COM 
61010188SJan.Friedel@Sun.COM 	fds.fd = fd;
61110188SJan.Friedel@Sun.COM 	fds.events = POLLIN;
61210188SJan.Friedel@Sun.COM 
61310188SJan.Friedel@Sun.COM 	while (len) {
61410188SJan.Friedel@Sun.COM 		fds.revents = 0;
61510188SJan.Friedel@Sun.COM 		rc = poll(&fds, 1, timeout * 1000);
61610188SJan.Friedel@Sun.COM 		if (rc == 0) {			/* timeout */
61710188SJan.Friedel@Sun.COM 			return (-1);
61810188SJan.Friedel@Sun.COM 		} else if (rc < 0) {
61910188SJan.Friedel@Sun.COM 			if (errno == EINTR || errno == EAGAIN) {
62010188SJan.Friedel@Sun.COM 				continue;
62110188SJan.Friedel@Sun.COM 			} else {
62210188SJan.Friedel@Sun.COM 				return (-1);
62310188SJan.Friedel@Sun.COM 			}
62410188SJan.Friedel@Sun.COM 		}
62510188SJan.Friedel@Sun.COM 
62610188SJan.Friedel@Sun.COM 		if (!fds.revents) {
62710188SJan.Friedel@Sun.COM 			return (-1);
62810188SJan.Friedel@Sun.COM 		}
62910188SJan.Friedel@Sun.COM 
63010188SJan.Friedel@Sun.COM 		bytes = read(fd, buf, len);
63110188SJan.Friedel@Sun.COM 		if (bytes < 0) {
63210188SJan.Friedel@Sun.COM 			if (errno == EINTR) {
63310188SJan.Friedel@Sun.COM 				continue;
63410188SJan.Friedel@Sun.COM 			} else {
63510188SJan.Friedel@Sun.COM 				return (-1);
63610188SJan.Friedel@Sun.COM 			}
63710188SJan.Friedel@Sun.COM 		} else if (bytes == 0) {	/* eof */
63810188SJan.Friedel@Sun.COM 			return (-1);
63910188SJan.Friedel@Sun.COM 		}
64010188SJan.Friedel@Sun.COM 
64110188SJan.Friedel@Sun.COM 		len -= bytes;
64210188SJan.Friedel@Sun.COM 		buf += bytes;
64310188SJan.Friedel@Sun.COM 	}
64410188SJan.Friedel@Sun.COM 
64510188SJan.Friedel@Sun.COM 	return (0);
64610188SJan.Friedel@Sun.COM }
64710188SJan.Friedel@Sun.COM 
64810188SJan.Friedel@Sun.COM /*
64910188SJan.Friedel@Sun.COM  * read_fd() - reads data of length len from the given file descriptor fd to the
65010188SJan.Friedel@Sun.COM  * buffer buf, in chunks if needed. Function returns B_FALSE on failure,
65110188SJan.Friedel@Sun.COM  * otherwise B_TRUE. Function preserves errno, if it was set by the read(2).
65210188SJan.Friedel@Sun.COM  */
65310188SJan.Friedel@Sun.COM static boolean_t
read_fd(int fd,char * buf,size_t len)65410188SJan.Friedel@Sun.COM read_fd(int fd, char *buf, size_t len)
65510188SJan.Friedel@Sun.COM {
65610188SJan.Friedel@Sun.COM 	int		bytes;
65710188SJan.Friedel@Sun.COM #ifdef DEBUG
65810188SJan.Friedel@Sun.COM 	size_t		len_o = len;
65910188SJan.Friedel@Sun.COM #endif
66010188SJan.Friedel@Sun.COM 
66110188SJan.Friedel@Sun.COM 	while (len) {
66210188SJan.Friedel@Sun.COM 		bytes = read(fd, buf, len);
66310188SJan.Friedel@Sun.COM 		if (bytes < 0) {		/* err */
66410188SJan.Friedel@Sun.COM 			if (errno == EINTR || errno == EAGAIN) {
66510188SJan.Friedel@Sun.COM 				continue;
66610188SJan.Friedel@Sun.COM 			} else {
66710188SJan.Friedel@Sun.COM 				return (B_FALSE);
66810188SJan.Friedel@Sun.COM 			}
66910188SJan.Friedel@Sun.COM 		} else if (bytes == 0) {	/* eof */
67010188SJan.Friedel@Sun.COM 			return (B_FALSE);
67110188SJan.Friedel@Sun.COM 		}
67210188SJan.Friedel@Sun.COM 
67310188SJan.Friedel@Sun.COM 		len -= bytes;
67410188SJan.Friedel@Sun.COM 		buf += bytes;
67510188SJan.Friedel@Sun.COM 	}
67610188SJan.Friedel@Sun.COM 
67710188SJan.Friedel@Sun.COM 	DPRINT((dfile, "read_fd: Read %d bytes.\n", len_o - len));
67810188SJan.Friedel@Sun.COM 	return (B_TRUE);
67910188SJan.Friedel@Sun.COM }
68010188SJan.Friedel@Sun.COM 
68110188SJan.Friedel@Sun.COM /*
68210188SJan.Friedel@Sun.COM  * write_fd() - writes buf of length len to the opened file descriptor fd, in
68310188SJan.Friedel@Sun.COM  * chunks if needed. The data from the pipe are processed in the receiving
68410188SJan.Friedel@Sun.COM  * thread. Function returns B_FALSE on failure, otherwise B_TRUE. Function
68510188SJan.Friedel@Sun.COM  * preserves errno, if it was set by the write(2).
68610188SJan.Friedel@Sun.COM  */
68710188SJan.Friedel@Sun.COM static boolean_t
write_fd(int fd,char * buf,size_t len)68810188SJan.Friedel@Sun.COM write_fd(int fd, char *buf, size_t len)
68910188SJan.Friedel@Sun.COM {
69010188SJan.Friedel@Sun.COM 	int		bytes;
69110188SJan.Friedel@Sun.COM #ifdef DEBUG
69210188SJan.Friedel@Sun.COM 	size_t		len_o = len;
69310188SJan.Friedel@Sun.COM #endif
69410188SJan.Friedel@Sun.COM 
69510188SJan.Friedel@Sun.COM 	while (len) {
69610188SJan.Friedel@Sun.COM 		bytes = write(fd, buf, len);
69710188SJan.Friedel@Sun.COM 		if (bytes == -1) {		/* err */
69810188SJan.Friedel@Sun.COM 			if (errno == EINTR || errno == EAGAIN) {
69910188SJan.Friedel@Sun.COM 				continue;
70010188SJan.Friedel@Sun.COM 			} else {
70110188SJan.Friedel@Sun.COM 				return (B_FALSE);
70210188SJan.Friedel@Sun.COM 			}
70310188SJan.Friedel@Sun.COM 		}
70410188SJan.Friedel@Sun.COM 
70510188SJan.Friedel@Sun.COM 		len -= bytes;
70610188SJan.Friedel@Sun.COM 		buf += bytes;
70710188SJan.Friedel@Sun.COM 	}
70810188SJan.Friedel@Sun.COM 
70910188SJan.Friedel@Sun.COM 	DPRINT((dfile, "write_fd: Wrote %d bytes.\n", len_o - len));
71010188SJan.Friedel@Sun.COM 	return (B_TRUE);
71110188SJan.Friedel@Sun.COM }
71210188SJan.Friedel@Sun.COM 
71310188SJan.Friedel@Sun.COM /*
71410188SJan.Friedel@Sun.COM  * Plug-in entry point
71510188SJan.Friedel@Sun.COM  */
71610188SJan.Friedel@Sun.COM 
71710188SJan.Friedel@Sun.COM /*
71810188SJan.Friedel@Sun.COM  * send_record() - send an audit record to a host opening a connection,
71910188SJan.Friedel@Sun.COM  * negotiate version and establish context if necessary.
72010188SJan.Friedel@Sun.COM  */
72110188SJan.Friedel@Sun.COM send_record_rc_t
send_record(struct hostlist_s * hostlptr,const char * input,size_t in_len,uint64_t sequence,close_rsn_t * err_rsn)72210188SJan.Friedel@Sun.COM send_record(struct hostlist_s *hostlptr, const char *input, size_t in_len,
72310188SJan.Friedel@Sun.COM     uint64_t sequence, close_rsn_t *err_rsn)
72410188SJan.Friedel@Sun.COM {
72510188SJan.Friedel@Sun.COM 	gss_buffer_desc		in_buf, out_buf;
72610188SJan.Friedel@Sun.COM 	OM_uint32		maj_stat, min_stat;
72710188SJan.Friedel@Sun.COM 	int			conf_state;
72810188SJan.Friedel@Sun.COM 	int			rc;
72910188SJan.Friedel@Sun.COM 	transq_node_t		*node_ptr;
73010188SJan.Friedel@Sun.COM 	uint64_t		seq_n;	/* sequence in the network byte order */
73110188SJan.Friedel@Sun.COM 	boolean_t		init_sock_poll = B_FALSE;
73210188SJan.Friedel@Sun.COM 
73310188SJan.Friedel@Sun.COM 	/*
73410188SJan.Friedel@Sun.COM 	 * We need to grab the reset_lock here, to prevent eventual
73510188SJan.Friedel@Sun.COM 	 * unsynchronized cleanup calls within the reset routine (reset caused
73610188SJan.Friedel@Sun.COM 	 * by the receiving thread) and the initialization calls in the
73710188SJan.Friedel@Sun.COM 	 * send_record() code path.
73810188SJan.Friedel@Sun.COM 	 */
73910188SJan.Friedel@Sun.COM 	(void) pthread_mutex_lock(&reset_lock);
74010188SJan.Friedel@Sun.COM 
74110188SJan.Friedel@Sun.COM 	/*
74210188SJan.Friedel@Sun.COM 	 * Check whether the socket was closed by the recv thread prior to call
74310188SJan.Friedel@Sun.COM 	 * send_record() and behave accordingly to the reason of the closure.
74410188SJan.Friedel@Sun.COM 	 */
74510188SJan.Friedel@Sun.COM 	if (recv_closure_rsn != RSN_UNDEFINED) {
74610188SJan.Friedel@Sun.COM 		*err_rsn = recv_closure_rsn;
74710188SJan.Friedel@Sun.COM 		if (recv_closure_rsn == RSN_GSS_CTX_EXP) {
74810188SJan.Friedel@Sun.COM 			rc = SEND_RECORD_RETRY;
74910188SJan.Friedel@Sun.COM 		} else {
75010188SJan.Friedel@Sun.COM 			rc = SEND_RECORD_NEXT;
75110188SJan.Friedel@Sun.COM 		}
75210188SJan.Friedel@Sun.COM 		recv_closure_rsn = RSN_UNDEFINED;
75310188SJan.Friedel@Sun.COM 		(void) pthread_mutex_unlock(&reset_lock);
75410188SJan.Friedel@Sun.COM 		return (rc);
75510188SJan.Friedel@Sun.COM 	}
75610188SJan.Friedel@Sun.COM 
75710188SJan.Friedel@Sun.COM 	/*
75810188SJan.Friedel@Sun.COM 	 * Send request to other then previously used host.
75910188SJan.Friedel@Sun.COM 	 */
76010188SJan.Friedel@Sun.COM 	if (current_host != hostlptr->host) {
76110188SJan.Friedel@Sun.COM 		DPRINT((dfile, "Set new host: %s\n", hostlptr->host->h_name));
76210188SJan.Friedel@Sun.COM 		if (sockfd != -1) {
76310188SJan.Friedel@Sun.COM 			(void) pthread_mutex_unlock(&reset_lock);
76410188SJan.Friedel@Sun.COM 			reset_transport(DO_CLOSE, DO_SYNC);
76510188SJan.Friedel@Sun.COM 			return (SEND_RECORD_RETRY);
76610188SJan.Friedel@Sun.COM 		}
76710188SJan.Friedel@Sun.COM 		current_host = (struct hostent *)hostlptr->host;
76810188SJan.Friedel@Sun.COM 		current_mech_oid = &hostlptr->mech;
76910188SJan.Friedel@Sun.COM 		current_port = hostlptr->port;
77010188SJan.Friedel@Sun.COM 	}
77110188SJan.Friedel@Sun.COM 
77210188SJan.Friedel@Sun.COM 	/* initiate the receiving thread */
77310188SJan.Friedel@Sun.COM 	(void) pthread_once(&recv_once_control, init_recv_record);
77410188SJan.Friedel@Sun.COM 
77510188SJan.Friedel@Sun.COM 	/* create and connect() socket, negotiate the protocol version */
77610188SJan.Friedel@Sun.COM 	if (sockfd == -1) {
77710188SJan.Friedel@Sun.COM 		/* socket operations */
77810188SJan.Friedel@Sun.COM 		DPRINT((dfile, "Socket creation and connect\n"));
77910188SJan.Friedel@Sun.COM 		if (!sock_prepare(&sockfd, current_host, err_rsn)) {
78010188SJan.Friedel@Sun.COM 			/* we believe the err_rsn set by sock_prepare() */
78110188SJan.Friedel@Sun.COM 			(void) pthread_mutex_unlock(&reset_lock);
78210188SJan.Friedel@Sun.COM 			return (SEND_RECORD_NEXT);
78310188SJan.Friedel@Sun.COM 		}
78410188SJan.Friedel@Sun.COM 
78510188SJan.Friedel@Sun.COM 		/* protocol version negotiation */
78610188SJan.Friedel@Sun.COM 		DPRINT((dfile, "Protocol version negotiation\n"));
78710188SJan.Friedel@Sun.COM 		if (prot_ver_negotiate() != 0) {
78810188SJan.Friedel@Sun.COM 			DPRINT((dfile,
78910188SJan.Friedel@Sun.COM 			    "Protocol version negotiation failed\n"));
79010188SJan.Friedel@Sun.COM 			(void) pthread_mutex_unlock(&reset_lock);
79110188SJan.Friedel@Sun.COM 			reset_transport(DO_CLOSE, DO_SYNC);
79210188SJan.Friedel@Sun.COM 			*err_rsn = RSN_PROTOCOL_NEGOTIATE;
79310188SJan.Friedel@Sun.COM 			return (SEND_RECORD_NEXT);
79410188SJan.Friedel@Sun.COM 		}
79510188SJan.Friedel@Sun.COM 
79610188SJan.Friedel@Sun.COM 		/* let the socket be initiated for poll() */
79710188SJan.Friedel@Sun.COM 		init_sock_poll = B_TRUE;
79810188SJan.Friedel@Sun.COM 	}
79910188SJan.Friedel@Sun.COM 
80010188SJan.Friedel@Sun.COM 	if (!gss_ctx_initialized) {
80110188SJan.Friedel@Sun.COM 		DPRINT((dfile, "Establishing context..\n"));
80210188SJan.Friedel@Sun.COM 		if (establish_context() != 0) {
80310188SJan.Friedel@Sun.COM 			(void) pthread_mutex_unlock(&reset_lock);
80410188SJan.Friedel@Sun.COM 			reset_transport(DO_CLOSE, DO_SYNC);
80510188SJan.Friedel@Sun.COM 			*err_rsn = RSN_GSS_CTX_ESTABLISH;
80610188SJan.Friedel@Sun.COM 			return (SEND_RECORD_NEXT);
80710188SJan.Friedel@Sun.COM 		}
80810188SJan.Friedel@Sun.COM 		gss_ctx_initialized = B_TRUE;
80910188SJan.Friedel@Sun.COM 	}
81010188SJan.Friedel@Sun.COM 
81110188SJan.Friedel@Sun.COM 	/* let the recv thread poll() on the sockfd */
81210188SJan.Friedel@Sun.COM 	if (init_sock_poll) {
81310188SJan.Friedel@Sun.COM 		init_sock_poll = B_FALSE;
81410188SJan.Friedel@Sun.COM 		if (!init_poll(sockfd)) {
81510188SJan.Friedel@Sun.COM 			*err_rsn = RSN_INIT_POLL;
81610188SJan.Friedel@Sun.COM 			(void) pthread_mutex_unlock(&reset_lock);
81710188SJan.Friedel@Sun.COM 			return (SEND_RECORD_RETRY);
81810188SJan.Friedel@Sun.COM 		}
81910188SJan.Friedel@Sun.COM 	}
82010188SJan.Friedel@Sun.COM 
82110188SJan.Friedel@Sun.COM 	(void) pthread_mutex_unlock(&reset_lock);
82210188SJan.Friedel@Sun.COM 
82310188SJan.Friedel@Sun.COM 	/* if not empty, retransmit contents of the transmission queue */
82410188SJan.Friedel@Sun.COM 	if (flush_transq) {
82510188SJan.Friedel@Sun.COM 		DPRINT((dfile, "Retransmitting remaining (%ld) tokens from "
82610188SJan.Friedel@Sun.COM 		    "the transmission queue\n", transq_hdr.count));
82710188SJan.Friedel@Sun.COM 		if ((rc = transq_retransmit()) == 2) { /* gss context exp */
82810188SJan.Friedel@Sun.COM 			reset_transport(DO_CLOSE, DO_SYNC);
82910188SJan.Friedel@Sun.COM 			*err_rsn = RSN_GSS_CTX_EXP;
83010188SJan.Friedel@Sun.COM 			return (SEND_RECORD_RETRY);
83110188SJan.Friedel@Sun.COM 		} else if (rc == 1) {
83210188SJan.Friedel@Sun.COM 			reset_transport(DO_CLOSE, DO_SYNC);
83310188SJan.Friedel@Sun.COM 			*err_rsn = RSN_OTHER_ERR;
83410188SJan.Friedel@Sun.COM 			return (SEND_RECORD_NEXT);
83510188SJan.Friedel@Sun.COM 		}
83610188SJan.Friedel@Sun.COM 		flush_transq = B_FALSE;
83710188SJan.Friedel@Sun.COM 	}
83810188SJan.Friedel@Sun.COM 
83910188SJan.Friedel@Sun.COM 	/*
84010188SJan.Friedel@Sun.COM 	 * Concatenate sequence number and the new record. Note, that the
84110188SJan.Friedel@Sun.COM 	 * pointer to the chunk of memory allocated for the concatenated values
84210188SJan.Friedel@Sun.COM 	 * is later passed to the transq_enqueu() function which stores the
84310188SJan.Friedel@Sun.COM 	 * pointer in the transmission queue; subsequently called
84410188SJan.Friedel@Sun.COM 	 * transq_dequeue() frees the allocated memory once the MIC is verified
84510188SJan.Friedel@Sun.COM 	 * by the recv_record() function.
84610188SJan.Friedel@Sun.COM 	 *
84710188SJan.Friedel@Sun.COM 	 * If we return earlier than the transq_enqueue() is called, it's
84810188SJan.Friedel@Sun.COM 	 * necessary to free the in_buf.value explicitly prior to return.
84910188SJan.Friedel@Sun.COM 	 *
85010188SJan.Friedel@Sun.COM 	 */
85110188SJan.Friedel@Sun.COM 	in_buf.length = in_len + sizeof (sequence);
85210188SJan.Friedel@Sun.COM 	in_buf.value = malloc(in_buf.length);
85310188SJan.Friedel@Sun.COM 	if (in_buf.value == NULL) {
85410188SJan.Friedel@Sun.COM 			report_err(gettext("Memory allocation failed"));
85510188SJan.Friedel@Sun.COM 			DPRINT((dfile, "Memory allocation failed: %s\n",
85610188SJan.Friedel@Sun.COM 			    strerror(errno)));
85710188SJan.Friedel@Sun.COM 			reset_transport(DO_CLOSE, DO_SYNC);
85810188SJan.Friedel@Sun.COM 			*err_rsn = RSN_MEMORY_ALLOCATE;
85910188SJan.Friedel@Sun.COM 			return (SEND_RECORD_FAIL);
86010188SJan.Friedel@Sun.COM 	}
86110188SJan.Friedel@Sun.COM 	seq_n = htonll(sequence);
86210188SJan.Friedel@Sun.COM 	(void) memcpy(in_buf.value, &seq_n, sizeof (seq_n));
86310188SJan.Friedel@Sun.COM 	(void) memcpy((char *)in_buf.value + sizeof (seq_n), input, in_len);
86410188SJan.Friedel@Sun.COM 
86510188SJan.Friedel@Sun.COM 	/* wrap sequence number and the new record to the per-message token */
86610188SJan.Friedel@Sun.COM 	(void) pthread_mutex_lock(&gss_ctx_lock);
86710188SJan.Friedel@Sun.COM 	if (gss_ctx != NULL) {
86810188SJan.Friedel@Sun.COM 		maj_stat = gss_wrap(&min_stat, gss_ctx, 1, GSS_C_QOP_DEFAULT,
86910188SJan.Friedel@Sun.COM 		    &in_buf, &conf_state, &out_buf);
87010188SJan.Friedel@Sun.COM 		(void) pthread_mutex_unlock(&gss_ctx_lock);
87110188SJan.Friedel@Sun.COM 		switch (maj_stat) {
87210188SJan.Friedel@Sun.COM 		case GSS_S_COMPLETE:
87310188SJan.Friedel@Sun.COM 			break;
87410188SJan.Friedel@Sun.COM 		case GSS_S_CONTEXT_EXPIRED:
87510188SJan.Friedel@Sun.COM 			reset_transport(DO_CLOSE, DO_SYNC);
87610188SJan.Friedel@Sun.COM 			free(in_buf.value);
87710188SJan.Friedel@Sun.COM 			*err_rsn = RSN_GSS_CTX_EXP;
87810188SJan.Friedel@Sun.COM 			return (SEND_RECORD_RETRY);
87910188SJan.Friedel@Sun.COM 		default:
88010188SJan.Friedel@Sun.COM 			report_gss_err(gettext("gss_wrap message"), maj_stat,
88110188SJan.Friedel@Sun.COM 			    min_stat);
88210188SJan.Friedel@Sun.COM 			reset_transport(DO_CLOSE, DO_SYNC);
88310188SJan.Friedel@Sun.COM 			free(in_buf.value);
88410188SJan.Friedel@Sun.COM 			*err_rsn = RSN_OTHER_ERR;
88510188SJan.Friedel@Sun.COM 			return (SEND_RECORD_NEXT);
88610188SJan.Friedel@Sun.COM 		}
88710188SJan.Friedel@Sun.COM 	} else {	/* GSS context deleted by the recv thread */
88810188SJan.Friedel@Sun.COM 		(void) pthread_mutex_unlock(&gss_ctx_lock);
88910188SJan.Friedel@Sun.COM 		reset_transport(DO_CLOSE, DO_SYNC);
89010188SJan.Friedel@Sun.COM 		free(in_buf.value);
89110188SJan.Friedel@Sun.COM 		*err_rsn = RSN_OTHER_ERR;
89210188SJan.Friedel@Sun.COM 		return (SEND_RECORD_NEXT);
89310188SJan.Friedel@Sun.COM 	}
89410188SJan.Friedel@Sun.COM 
89510188SJan.Friedel@Sun.COM 
89610188SJan.Friedel@Sun.COM 	/* enqueue the to-be-sent token into transmission queue */
89710188SJan.Friedel@Sun.COM 	(void) pthread_mutex_lock(&transq_lock);
89810188SJan.Friedel@Sun.COM 	if (!transq_enqueue(&node_ptr, &in_buf, sequence)) {
89910188SJan.Friedel@Sun.COM 		(void) pthread_mutex_unlock(&transq_lock);
90010188SJan.Friedel@Sun.COM 		reset_transport(DO_CLOSE, DO_SYNC);
90110188SJan.Friedel@Sun.COM 		free(in_buf.value);
90210188SJan.Friedel@Sun.COM 		(void) gss_release_buffer(&min_stat, &out_buf);
90310188SJan.Friedel@Sun.COM 		*err_rsn = RSN_OTHER_ERR;
90410188SJan.Friedel@Sun.COM 		return (SEND_RECORD_RETRY);
90510188SJan.Friedel@Sun.COM 	}
90610188SJan.Friedel@Sun.COM 	DPRINT((dfile, "Token enqueued for later verification\n"));
90710188SJan.Friedel@Sun.COM 	(void) pthread_mutex_unlock(&transq_lock);
90810188SJan.Friedel@Sun.COM 
90910188SJan.Friedel@Sun.COM 	/* send token */
91010188SJan.Friedel@Sun.COM 	if (send_token(&sockfd, &out_buf) < 0) {
91110188SJan.Friedel@Sun.COM 		DPRINT((dfile, "Token sending failed\n"));
91210188SJan.Friedel@Sun.COM 		reset_transport(DO_CLOSE, DO_SYNC);
91310188SJan.Friedel@Sun.COM 		(void) gss_release_buffer(&min_stat, &out_buf);
91410188SJan.Friedel@Sun.COM 
91510188SJan.Friedel@Sun.COM 		(void) pthread_mutex_lock(&transq_lock);
91610188SJan.Friedel@Sun.COM 		transq_dequeue(node_ptr);
91710188SJan.Friedel@Sun.COM 		(void) pthread_mutex_unlock(&transq_lock);
91810188SJan.Friedel@Sun.COM 
91910188SJan.Friedel@Sun.COM 		*err_rsn = RSN_OTHER_ERR;
92010188SJan.Friedel@Sun.COM 		return (SEND_RECORD_NEXT);
92110188SJan.Friedel@Sun.COM 	}
92210188SJan.Friedel@Sun.COM 	DPRINT((dfile, "Token sent (transq size = %ld)\n", transq_hdr.count));
92310188SJan.Friedel@Sun.COM 
92410188SJan.Friedel@Sun.COM 	(void) gss_release_buffer(&min_stat, &out_buf);
92510188SJan.Friedel@Sun.COM 
92610188SJan.Friedel@Sun.COM 	return (SEND_RECORD_SUCCESS);
92710188SJan.Friedel@Sun.COM }
92810188SJan.Friedel@Sun.COM 
92910188SJan.Friedel@Sun.COM /*
93010188SJan.Friedel@Sun.COM  * init_recv_record() - initialize the receiver thread
93110188SJan.Friedel@Sun.COM  */
93210188SJan.Friedel@Sun.COM static void
init_recv_record()93310188SJan.Friedel@Sun.COM init_recv_record()
93410188SJan.Friedel@Sun.COM {
93510188SJan.Friedel@Sun.COM 	DPRINT((dfile, "Initiating the recv thread\n"));
93610188SJan.Friedel@Sun.COM 	(void) pthread_create(&recv_tid, NULL, (void *(*)(void *))recv_record,
93710188SJan.Friedel@Sun.COM 	    (void *)NULL);
93810188SJan.Friedel@Sun.COM 
93910188SJan.Friedel@Sun.COM }
94010188SJan.Friedel@Sun.COM 
94110188SJan.Friedel@Sun.COM 
94210188SJan.Friedel@Sun.COM /*
94310188SJan.Friedel@Sun.COM  * recv_record() - the receiver thread routine
94410188SJan.Friedel@Sun.COM  */
94510188SJan.Friedel@Sun.COM static void
recv_record()94610188SJan.Friedel@Sun.COM recv_record()
94710188SJan.Friedel@Sun.COM {
94810188SJan.Friedel@Sun.COM 	OM_uint32		maj_stat, min_stat;
94910188SJan.Friedel@Sun.COM 	gss_qop_t		qop_state;
95010188SJan.Friedel@Sun.COM 	gss_buffer_desc		in_buf = GSS_C_EMPTY_BUFFER;
95110188SJan.Friedel@Sun.COM 	gss_buffer_desc		in_buf_mic = GSS_C_EMPTY_BUFFER;
95210188SJan.Friedel@Sun.COM 	transq_node_t		*cur_node;
95310188SJan.Friedel@Sun.COM 	uint64_t		r_seq_num;	/* received sequence number */
95410188SJan.Friedel@Sun.COM 	boolean_t		token_verified;
95510188SJan.Friedel@Sun.COM 	boolean_t		break_flag;
95610188SJan.Friedel@Sun.COM 	struct pollfd		fds[2];
95710188SJan.Friedel@Sun.COM 	int			fds_cnt;
95810188SJan.Friedel@Sun.COM 	struct pollfd		*pipe_fd = &fds[0];
95910188SJan.Friedel@Sun.COM 	struct pollfd		*recv_fd = &fds[1];
96010188SJan.Friedel@Sun.COM 	uint32_t		len;
96110188SJan.Friedel@Sun.COM 	int			rc;
96210188SJan.Friedel@Sun.COM 	pipe_msg_t		np_data;
96310188SJan.Friedel@Sun.COM 
96410188SJan.Friedel@Sun.COM 	DPRINT((dfile, "Receiver thread initiated\n"));
96510188SJan.Friedel@Sun.COM 
96610188SJan.Friedel@Sun.COM 	/*
96710188SJan.Friedel@Sun.COM 	 * Fill in the information in the vector of file descriptors passed
96810188SJan.Friedel@Sun.COM 	 * later on to the poll() function. In the initial state, there is only
96910188SJan.Friedel@Sun.COM 	 * one struct pollfd in the vector which contains file descriptor of the
97010188SJan.Friedel@Sun.COM 	 * notification pipe - notify_pipe[1]. There might be up to two file
97110188SJan.Friedel@Sun.COM 	 * descriptors (struct pollfd) in the vector - notify_pipe[1] which
97210188SJan.Friedel@Sun.COM 	 * resides in the vector during the entire life of the receiving thread,
97310188SJan.Friedel@Sun.COM 	 * and the own file descriptor from which we read data sent by the
97410188SJan.Friedel@Sun.COM 	 * remote server application.
97510188SJan.Friedel@Sun.COM 	 */
97610188SJan.Friedel@Sun.COM 	pipe_fd->fd = notify_pipe[1];
97710188SJan.Friedel@Sun.COM 	pipe_fd->events = POLLIN;
97810188SJan.Friedel@Sun.COM 	recv_fd->fd = -1;
97910188SJan.Friedel@Sun.COM 	recv_fd->events = POLLIN;
98010188SJan.Friedel@Sun.COM 	fds_cnt = 1;
98110188SJan.Friedel@Sun.COM 
98210188SJan.Friedel@Sun.COM 	/*
98310188SJan.Friedel@Sun.COM 	 * In the endless loop, try to grab some data from the socket or
98410188SJan.Friedel@Sun.COM 	 * notify_pipe[1].
98510188SJan.Friedel@Sun.COM 	 */
98610188SJan.Friedel@Sun.COM 	for (;;) {
98710188SJan.Friedel@Sun.COM 
98810188SJan.Friedel@Sun.COM 		pipe_fd->revents = 0;
98910188SJan.Friedel@Sun.COM 		recv_fd->revents = 0;
99010188SJan.Friedel@Sun.COM 		recv_closure_rsn = RSN_UNDEFINED;
99110188SJan.Friedel@Sun.COM 
99210188SJan.Friedel@Sun.COM 		/* block on poll, thus rc != 0 */
99310188SJan.Friedel@Sun.COM 		rc = poll(fds, fds_cnt, -1);
99410188SJan.Friedel@Sun.COM 		if (rc == -1) {
99510188SJan.Friedel@Sun.COM 			if (errno == EAGAIN || errno == EINTR) {
99610188SJan.Friedel@Sun.COM 				/* silently continue on EAGAIN || EINTR */
99710188SJan.Friedel@Sun.COM 				continue;
99810188SJan.Friedel@Sun.COM 			} else {
99910188SJan.Friedel@Sun.COM 				/* log the debug message in any other case */
100010188SJan.Friedel@Sun.COM 				DPRINT((dfile, "poll() failed: %s\n",
100110188SJan.Friedel@Sun.COM 				    strerror(errno)));
100210188SJan.Friedel@Sun.COM 				report_err(gettext("poll() failed.\n"));
100310188SJan.Friedel@Sun.COM 				continue;
100410188SJan.Friedel@Sun.COM 			}
100510188SJan.Friedel@Sun.COM 		}
100610188SJan.Friedel@Sun.COM 
100710188SJan.Friedel@Sun.COM 		/*
100810188SJan.Friedel@Sun.COM 		 * Receive a message from the notification pipe. Information
100910188SJan.Friedel@Sun.COM 		 * from the notification pipe takes precedence over the received
101010188SJan.Friedel@Sun.COM 		 * data from the remote server application.
101110188SJan.Friedel@Sun.COM 		 *
101210188SJan.Friedel@Sun.COM 		 * Notification pipe message format - message accepted
101310188SJan.Friedel@Sun.COM 		 * from the notify pipe comprises of two parts (int ||
101410188SJan.Friedel@Sun.COM 		 * boolean_t), where if the first part (sizeof (int)) equals
101510188SJan.Friedel@Sun.COM 		 * NP_CLOSE, then the second part (sizeof (boolean_t)) signals
101610188SJan.Friedel@Sun.COM 		 * the necessity of broadcasting (DO_SYNC/DO_NOT_SYNC) the end
101710188SJan.Friedel@Sun.COM 		 * of the reset routine.
101810188SJan.Friedel@Sun.COM 		 */
101910188SJan.Friedel@Sun.COM 		if (pipe_fd->revents & POLLIN) {
102010188SJan.Friedel@Sun.COM 			DPRINT((dfile, "An event on notify pipe detected\n"));
102110188SJan.Friedel@Sun.COM 			if (!read_fd(pipe_fd->fd, (char *)&np_data,
102210188SJan.Friedel@Sun.COM 			    sizeof (np_data))) {
102310188SJan.Friedel@Sun.COM 				DPRINT((dfile, "Reading notify pipe failed: "
102410188SJan.Friedel@Sun.COM 				    "%s\n", strerror(errno)));
102510188SJan.Friedel@Sun.COM 				report_err(gettext("Reading notify pipe "
102610188SJan.Friedel@Sun.COM 				    "failed"));
102710188SJan.Friedel@Sun.COM 			} else {
102810188SJan.Friedel@Sun.COM 				switch (np_data.sock_num) {
102910188SJan.Friedel@Sun.COM 				case NP_EXIT:	/* exit receiving thread */
103010188SJan.Friedel@Sun.COM 					do_cleanup(&fds_cnt, recv_fd,
103110188SJan.Friedel@Sun.COM 					    np_data.sync);
103210188SJan.Friedel@Sun.COM 					pthread_exit((void *)NULL);
103310188SJan.Friedel@Sun.COM 					break;
103410188SJan.Friedel@Sun.COM 				case NP_CLOSE:	/* close and remove recv_fd */
103510188SJan.Friedel@Sun.COM 					do_reset(&fds_cnt, recv_fd,
103610188SJan.Friedel@Sun.COM 					    np_data.sync);
103710188SJan.Friedel@Sun.COM 					continue;
103810188SJan.Friedel@Sun.COM 				default:	/* add rc_pipe to the fds */
103910188SJan.Friedel@Sun.COM 					recv_fd->fd = np_data.sock_num;
104010188SJan.Friedel@Sun.COM 					fds_cnt = 2;
104110188SJan.Friedel@Sun.COM 					continue;
104210188SJan.Friedel@Sun.COM 				}
104310188SJan.Friedel@Sun.COM 			}
104410188SJan.Friedel@Sun.COM 		}
104510188SJan.Friedel@Sun.COM 		/* Receive a token from the remote server application */
104610188SJan.Friedel@Sun.COM 		if (recv_fd->revents & POLLIN) {
104710188SJan.Friedel@Sun.COM 			DPRINT((dfile, "An event on fd detected\n"));
104810188SJan.Friedel@Sun.COM 			if (!read_fd(recv_fd->fd, (char *)&len, sizeof (len))) {
104910188SJan.Friedel@Sun.COM 				DPRINT((dfile, "Token length recv failed\n"));
105010188SJan.Friedel@Sun.COM 				recv_closure_rsn = RSN_TOK_RECV_FAILED;
105110188SJan.Friedel@Sun.COM 				reset_transport(DO_CLOSE, DO_NOT_SYNC);
105210188SJan.Friedel@Sun.COM 				continue;
105310188SJan.Friedel@Sun.COM 			}
105410188SJan.Friedel@Sun.COM 			len = ntohl(len);
105510188SJan.Friedel@Sun.COM 
105610188SJan.Friedel@Sun.COM 			/* simple DOS prevention mechanism */
105710188SJan.Friedel@Sun.COM 			if (len > MAX_TOK_LEN) {
105810188SJan.Friedel@Sun.COM 				report_err(gettext("Indicated invalid token "
105910188SJan.Friedel@Sun.COM 				    "length"));
106010188SJan.Friedel@Sun.COM 				DPRINT((dfile, "Indicated token length > %dB\n",
106110188SJan.Friedel@Sun.COM 				    MAX_TOK_LEN));
106210188SJan.Friedel@Sun.COM 				recv_closure_rsn = RSN_TOK_TOO_BIG;
106310188SJan.Friedel@Sun.COM 				reset_transport(DO_CLOSE, DO_NOT_SYNC);
106410188SJan.Friedel@Sun.COM 				continue;
106510188SJan.Friedel@Sun.COM 			}
106610188SJan.Friedel@Sun.COM 
106710188SJan.Friedel@Sun.COM 			in_buf.value = (char *)malloc(len);
106810188SJan.Friedel@Sun.COM 			if (in_buf.value == NULL) {
106910188SJan.Friedel@Sun.COM 				report_err(gettext("Memory allocation failed"));
107010188SJan.Friedel@Sun.COM 				DPRINT((dfile, "Memory allocation failed: %s\n",
107110188SJan.Friedel@Sun.COM 				    strerror(errno)));
107210188SJan.Friedel@Sun.COM 				recv_closure_rsn = RSN_MEMORY_ALLOCATE;
107310188SJan.Friedel@Sun.COM 				reset_transport(DO_CLOSE, DO_NOT_SYNC);
107410188SJan.Friedel@Sun.COM 				continue;
107510188SJan.Friedel@Sun.COM 			}
107610188SJan.Friedel@Sun.COM 			if (!read_fd(recv_fd->fd, (char *)in_buf.value, len)) {
107710188SJan.Friedel@Sun.COM 				DPRINT((dfile, "Token value recv failed\n"));
107810188SJan.Friedel@Sun.COM 				free(in_buf.value);
107910188SJan.Friedel@Sun.COM 				recv_closure_rsn = RSN_TOK_RECV_FAILED;
108010188SJan.Friedel@Sun.COM 				reset_transport(DO_CLOSE, DO_NOT_SYNC);
108110188SJan.Friedel@Sun.COM 				continue;
108210188SJan.Friedel@Sun.COM 			}
108310188SJan.Friedel@Sun.COM 
108410188SJan.Friedel@Sun.COM 			in_buf.length = len;
108510188SJan.Friedel@Sun.COM 		}
108610188SJan.Friedel@Sun.COM 
108710188SJan.Friedel@Sun.COM 		/*
108810188SJan.Friedel@Sun.COM 		 * Extract the sequence number and the MIC from
108910188SJan.Friedel@Sun.COM 		 * the per-message token
109010188SJan.Friedel@Sun.COM 		 */
109110188SJan.Friedel@Sun.COM 		(void) memcpy(&r_seq_num, in_buf.value, sizeof (r_seq_num));
109210188SJan.Friedel@Sun.COM 		r_seq_num = ntohll(r_seq_num);
109310188SJan.Friedel@Sun.COM 		in_buf_mic.length = in_buf.length - sizeof (r_seq_num);
109410188SJan.Friedel@Sun.COM 		in_buf_mic.value = (char *)in_buf.value + sizeof (r_seq_num);
109510188SJan.Friedel@Sun.COM 
109610188SJan.Friedel@Sun.COM 		/*
109710188SJan.Friedel@Sun.COM 		 * seq_num/r_seq_num - the sequence number does not need to
109810188SJan.Friedel@Sun.COM 		 * be unique in the transmission queue. Any token in the
109910188SJan.Friedel@Sun.COM 		 * transmission queue with the same seq_num as the acknowledge
110010188SJan.Friedel@Sun.COM 		 * token received from the server is tested. This is due to the
110110188SJan.Friedel@Sun.COM 		 * fact that the plugin cannot influence (in the current
110210188SJan.Friedel@Sun.COM 		 * implementation) sequence numbers generated by the kernel (we
110310188SJan.Friedel@Sun.COM 		 * are reusing record sequence numbers as a transmission queue
110410188SJan.Friedel@Sun.COM 		 * sequence numbers). The probability of having two or more
110510188SJan.Friedel@Sun.COM 		 * tokens in the transmission queue is low and at the same time
110610188SJan.Friedel@Sun.COM 		 * the performance gain due to using sequence numbers is quite
110710188SJan.Friedel@Sun.COM 		 * high.
110810188SJan.Friedel@Sun.COM 		 *
110910188SJan.Friedel@Sun.COM 		 * In case a harder condition with regard to duplicate sequence
111010188SJan.Friedel@Sun.COM 		 * numbers in the transmission queue will be desired over time,
111110188SJan.Friedel@Sun.COM 		 * the break_flag behavior used below should be
111210188SJan.Friedel@Sun.COM 		 * removed/changed_accordingly.
111310188SJan.Friedel@Sun.COM 		 */
111410188SJan.Friedel@Sun.COM 		break_flag = B_FALSE;
111510188SJan.Friedel@Sun.COM 		token_verified = B_FALSE;
111610188SJan.Friedel@Sun.COM 		(void) pthread_mutex_lock(&transq_lock);
111710188SJan.Friedel@Sun.COM 		cur_node = transq_hdr.head;
111810188SJan.Friedel@Sun.COM 		while (cur_node != NULL && !break_flag) {
111910188SJan.Friedel@Sun.COM 			if (cur_node->seq_num != r_seq_num) {
112010188SJan.Friedel@Sun.COM 				cur_node = cur_node->next;
112110188SJan.Friedel@Sun.COM 				continue;
112210188SJan.Friedel@Sun.COM 			}
112310188SJan.Friedel@Sun.COM 
112410188SJan.Friedel@Sun.COM 			(void) pthread_mutex_lock(&gss_ctx_lock);
112510188SJan.Friedel@Sun.COM 			maj_stat = gss_verify_mic(&min_stat, gss_ctx,
112610188SJan.Friedel@Sun.COM 			    &(cur_node->seq_token), &in_buf_mic,
112710188SJan.Friedel@Sun.COM 			    &qop_state);
112810188SJan.Friedel@Sun.COM 			(void) pthread_mutex_unlock(&gss_ctx_lock);
112910188SJan.Friedel@Sun.COM 
113010188SJan.Friedel@Sun.COM 			if (!GSS_ERROR(maj_stat)) { /* the success case */
113110188SJan.Friedel@Sun.COM 				switch (maj_stat) {
113210188SJan.Friedel@Sun.COM 				/*
113310188SJan.Friedel@Sun.COM 				 * All the GSS_S_OLD_TOKEN, GSS_S_UNSEQ_TOKEN,
113410188SJan.Friedel@Sun.COM 				 * GSS_S_GAP_TOKEN are perceived as correct
113510188SJan.Friedel@Sun.COM 				 * behavior of the server side. The plugin
113610188SJan.Friedel@Sun.COM 				 * implementation is resistant to any of the
113710188SJan.Friedel@Sun.COM 				 * above mention cases of returned status codes.
113810188SJan.Friedel@Sun.COM 				 */
113910188SJan.Friedel@Sun.COM 				/*FALLTHRU*/
114010188SJan.Friedel@Sun.COM 				case GSS_S_OLD_TOKEN:
114110188SJan.Friedel@Sun.COM 				case GSS_S_UNSEQ_TOKEN:
114210188SJan.Friedel@Sun.COM 				case GSS_S_GAP_TOKEN:
114310188SJan.Friedel@Sun.COM 				case GSS_S_COMPLETE:
114410188SJan.Friedel@Sun.COM 					/*
114510188SJan.Friedel@Sun.COM 					 * remove the verified record/node from
114610188SJan.Friedel@Sun.COM 					 * the transmission queue
114710188SJan.Friedel@Sun.COM 					 */
114810188SJan.Friedel@Sun.COM 					transq_dequeue(cur_node);
114910188SJan.Friedel@Sun.COM 					DPRINT((dfile, "Recv thread verified "
115010188SJan.Friedel@Sun.COM 					    "the token (transq len = %ld)\n",
115110188SJan.Friedel@Sun.COM 					    transq_hdr.count));
115210188SJan.Friedel@Sun.COM 
115310188SJan.Friedel@Sun.COM 					token_verified = B_TRUE;
115410188SJan.Friedel@Sun.COM 					break_flag = B_TRUE;
115510188SJan.Friedel@Sun.COM 					break;
115610188SJan.Friedel@Sun.COM 
115710188SJan.Friedel@Sun.COM 				/*
115810188SJan.Friedel@Sun.COM 				 * Both the default case as well as
115910188SJan.Friedel@Sun.COM 				 * GSS_S_DUPLICATE_TOKEN case should never
116010188SJan.Friedel@Sun.COM 				 * occur. It's been left here for the sake of
116110188SJan.Friedel@Sun.COM 				 * completeness.
116210188SJan.Friedel@Sun.COM 				 * If any of the two cases occur, it is
116310188SJan.Friedel@Sun.COM 				 * subsequently cought because we don't set
116410188SJan.Friedel@Sun.COM 				 * the token_verified flag.
116510188SJan.Friedel@Sun.COM 				 */
116610188SJan.Friedel@Sun.COM 				/*FALLTHRU*/
116710188SJan.Friedel@Sun.COM 				case GSS_S_DUPLICATE_TOKEN:
116810188SJan.Friedel@Sun.COM 				default:
116910188SJan.Friedel@Sun.COM 					break_flag = B_TRUE;
117010188SJan.Friedel@Sun.COM 					break;
117110188SJan.Friedel@Sun.COM 				} /* switch (maj_stat) */
117210188SJan.Friedel@Sun.COM 
117310188SJan.Friedel@Sun.COM 			} else { 	/* the failure case */
117410188SJan.Friedel@Sun.COM 				report_gss_err(
117510188SJan.Friedel@Sun.COM 				    gettext("signature verification of the "
117610188SJan.Friedel@Sun.COM 				    "received token failed"),
117710188SJan.Friedel@Sun.COM 				    maj_stat, min_stat);
117810188SJan.Friedel@Sun.COM 
117910188SJan.Friedel@Sun.COM 				switch (maj_stat) {
118010188SJan.Friedel@Sun.COM 				case GSS_S_CONTEXT_EXPIRED:
118110188SJan.Friedel@Sun.COM 					/* retransmission necessary */
118210188SJan.Friedel@Sun.COM 					recv_closure_rsn = RSN_GSS_CTX_EXP;
118310188SJan.Friedel@Sun.COM 					break_flag = B_TRUE;
118410188SJan.Friedel@Sun.COM 					DPRINT((dfile, "Recv thread detected "
118510188SJan.Friedel@Sun.COM 					    "the GSS context expiration\n"));
118610188SJan.Friedel@Sun.COM 					break;
118710188SJan.Friedel@Sun.COM 				case GSS_S_BAD_SIG:
118810188SJan.Friedel@Sun.COM 					DPRINT((dfile, "Bad signature "
118910188SJan.Friedel@Sun.COM 					    "detected (seq_num = %lld)\n",
119010188SJan.Friedel@Sun.COM 					    cur_node->seq_num));
119110188SJan.Friedel@Sun.COM 					cur_node = cur_node->next;
119210188SJan.Friedel@Sun.COM 					break;
119310188SJan.Friedel@Sun.COM 				default:
119410188SJan.Friedel@Sun.COM 					report_gss_err(
119510188SJan.Friedel@Sun.COM 					    gettext("signature verification"),
119610188SJan.Friedel@Sun.COM 					    maj_stat, min_stat);
119710188SJan.Friedel@Sun.COM 					break_flag = B_TRUE;
119810188SJan.Friedel@Sun.COM 					break;
119910188SJan.Friedel@Sun.COM 				}
120010188SJan.Friedel@Sun.COM 			}
120110188SJan.Friedel@Sun.COM 
120210188SJan.Friedel@Sun.COM 		} /* while */
120310188SJan.Friedel@Sun.COM 		(void) pthread_mutex_unlock(&transq_lock);
120410188SJan.Friedel@Sun.COM 
120510188SJan.Friedel@Sun.COM 		if (in_buf.value != NULL) {
120610188SJan.Friedel@Sun.COM 			free(in_buf.value);
120710188SJan.Friedel@Sun.COM 			in_buf.value = NULL;
120810188SJan.Friedel@Sun.COM 			in_buf.length = 0;
120910188SJan.Friedel@Sun.COM 		}
121010188SJan.Friedel@Sun.COM 
121110188SJan.Friedel@Sun.COM 		if (!token_verified) {
121210188SJan.Friedel@Sun.COM 			/*
121310188SJan.Friedel@Sun.COM 			 * Received, but unverifiable token is perceived as
121410188SJan.Friedel@Sun.COM 			 * the protocol flow corruption with the penalty of
121510188SJan.Friedel@Sun.COM 			 * reinitializing the client/server connection.
121610188SJan.Friedel@Sun.COM 			 */
121710188SJan.Friedel@Sun.COM 			DPRINT((dfile, "received unverifiable token\n"));
121810188SJan.Friedel@Sun.COM 			report_err(gettext("received unverifiable token\n"));
121910188SJan.Friedel@Sun.COM 			if (recv_closure_rsn == RSN_UNDEFINED) {
122010188SJan.Friedel@Sun.COM 				recv_closure_rsn = RSN_TOK_UNVERIFIABLE;
122110188SJan.Friedel@Sun.COM 			}
122210188SJan.Friedel@Sun.COM 			reset_transport(DO_CLOSE, DO_NOT_SYNC);
122310188SJan.Friedel@Sun.COM 		}
122410188SJan.Friedel@Sun.COM 
122510188SJan.Friedel@Sun.COM 	} /* for (;;) */
122610188SJan.Friedel@Sun.COM 
122710188SJan.Friedel@Sun.COM 
122810188SJan.Friedel@Sun.COM }
122910188SJan.Friedel@Sun.COM 
123010188SJan.Friedel@Sun.COM 
123110188SJan.Friedel@Sun.COM /*
123210188SJan.Friedel@Sun.COM  * init_poll() - initiates the polling in the receiving thread via sending the
123310188SJan.Friedel@Sun.COM  * appropriate message over the notify pipe. Message format = (int ||
123410188SJan.Friedel@Sun.COM  * booleant_t), where the first part (sizeof (int)) contains the
123510188SJan.Friedel@Sun.COM  * newly_opened/to_be_polled socket file descriptor. The contents of the second
123610188SJan.Friedel@Sun.COM  * part (sizeof (boolean_t)) of the message works only as a padding here and no
123710188SJan.Friedel@Sun.COM  * action (no recv/send thread synchronisation) is made in the receiving thread
123810188SJan.Friedel@Sun.COM  * based on its value.
123910188SJan.Friedel@Sun.COM  */
124010188SJan.Friedel@Sun.COM static boolean_t
init_poll(int fd)124110188SJan.Friedel@Sun.COM init_poll(int fd)
124210188SJan.Friedel@Sun.COM {
124310188SJan.Friedel@Sun.COM 	pipe_msg_t	np_data;
124410188SJan.Friedel@Sun.COM 	int		pipe_in = notify_pipe[0];
124510188SJan.Friedel@Sun.COM 
124610188SJan.Friedel@Sun.COM 	np_data.sock_num = fd;
124710188SJan.Friedel@Sun.COM 	np_data.sync = B_FALSE;	/* padding only */
124810188SJan.Friedel@Sun.COM 
124910188SJan.Friedel@Sun.COM 	if (!write_fd(pipe_in, (char *)&np_data, sizeof (np_data))) {
125010188SJan.Friedel@Sun.COM 		DPRINT((dfile, "Cannot write to the notify pipe\n"));
125110188SJan.Friedel@Sun.COM 		report_err(gettext("writing to the notify pipe failed"));
125210188SJan.Friedel@Sun.COM 		return (B_FALSE);
125310188SJan.Friedel@Sun.COM 	}
125410188SJan.Friedel@Sun.COM 
125510188SJan.Friedel@Sun.COM 	return (B_TRUE);
125610188SJan.Friedel@Sun.COM }
125710188SJan.Friedel@Sun.COM 
125810188SJan.Friedel@Sun.COM 
125910188SJan.Friedel@Sun.COM /*
126010188SJan.Friedel@Sun.COM  * reset_transport() - locked by the reset_lock initiates the reset of socket,
126110188SJan.Friedel@Sun.COM  * GSS security context and (possibly) flags the transq for retransmission; for
126210188SJan.Friedel@Sun.COM  * more detailed information see do_reset(). The reset_transport() also allows
126310188SJan.Friedel@Sun.COM  * the synchronization - waiting for the reset to be finished.
126410188SJan.Friedel@Sun.COM  *
126510188SJan.Friedel@Sun.COM  * do_close: DO_SYNC, DO_NOT_SYNC
126610188SJan.Friedel@Sun.COM  * sync_on_return: DO_EXIT (DO_NOT_CLOSE), DO_CLOSE (DO_NOT_EXIT)
126710188SJan.Friedel@Sun.COM  *
126810188SJan.Friedel@Sun.COM  */
126910188SJan.Friedel@Sun.COM void
reset_transport(boolean_t do_close,boolean_t sync_on_return)127010188SJan.Friedel@Sun.COM reset_transport(boolean_t do_close, boolean_t sync_on_return)
127110188SJan.Friedel@Sun.COM {
127210188SJan.Friedel@Sun.COM 	int		pipe_in = notify_pipe[0];
127310188SJan.Friedel@Sun.COM 	pipe_msg_t	np_data;
127410188SJan.Friedel@Sun.COM 
127510188SJan.Friedel@Sun.COM 	/*
127610188SJan.Friedel@Sun.COM 	 * Check if the reset routine is in progress or whether it was already
127710188SJan.Friedel@Sun.COM 	 * executed by some other thread.
127810188SJan.Friedel@Sun.COM 	 */
127910188SJan.Friedel@Sun.COM 	(void) pthread_mutex_lock(&reset_lock);
128010188SJan.Friedel@Sun.COM 	if (reset_in_progress) {
128110188SJan.Friedel@Sun.COM 		(void) pthread_mutex_unlock(&reset_lock);
128210188SJan.Friedel@Sun.COM 		return;
128310188SJan.Friedel@Sun.COM 	}
128410188SJan.Friedel@Sun.COM 	reset_in_progress = B_TRUE;
128510188SJan.Friedel@Sun.COM 
128610188SJan.Friedel@Sun.COM 	np_data.sock_num = (do_close ? NP_CLOSE : NP_EXIT);
128710188SJan.Friedel@Sun.COM 	np_data.sync = sync_on_return;
128810188SJan.Friedel@Sun.COM 	(void) write_fd(pipe_in, (char *)&np_data, sizeof (np_data));
128910188SJan.Friedel@Sun.COM 
129010188SJan.Friedel@Sun.COM 	if (sync_on_return) {
129110188SJan.Friedel@Sun.COM 		while (reset_in_progress) {
129210188SJan.Friedel@Sun.COM 			(void) pthread_cond_wait(&reset_cv, &reset_lock);
129310188SJan.Friedel@Sun.COM 			DPRINT((dfile, "Wait for sync\n"));
129410188SJan.Friedel@Sun.COM 		}
129510188SJan.Friedel@Sun.COM 		DPRINT((dfile, "Synced\n"));
129610188SJan.Friedel@Sun.COM 	}
129710188SJan.Friedel@Sun.COM 	(void) pthread_mutex_unlock(&reset_lock);
129810188SJan.Friedel@Sun.COM 
129910188SJan.Friedel@Sun.COM }
130010188SJan.Friedel@Sun.COM 
130110188SJan.Friedel@Sun.COM 
130210188SJan.Friedel@Sun.COM /*
130310188SJan.Friedel@Sun.COM  * do_reset() - the own reseting routine called from the recv thread. If the
130410188SJan.Friedel@Sun.COM  * synchronization was requested, signal the finish via conditional variable.
130510188SJan.Friedel@Sun.COM  */
130610188SJan.Friedel@Sun.COM static void
do_reset(int * fds_cnt,struct pollfd * recv_fd,boolean_t do_signal)130710188SJan.Friedel@Sun.COM do_reset(int *fds_cnt, struct pollfd *recv_fd, boolean_t do_signal)
130810188SJan.Friedel@Sun.COM {
130910188SJan.Friedel@Sun.COM 
131010188SJan.Friedel@Sun.COM 	(void) pthread_mutex_lock(&reset_lock);
131110188SJan.Friedel@Sun.COM 
131210188SJan.Friedel@Sun.COM 	/* socket */
131310188SJan.Friedel@Sun.COM 	(void) pthread_mutex_lock(&sock_lock);
131410188SJan.Friedel@Sun.COM 	if (sockfd == -1) {
131510188SJan.Friedel@Sun.COM 		DPRINT((dfile, "socket already closed\n"));
131610188SJan.Friedel@Sun.COM 		(void) pthread_mutex_unlock(&sock_lock);
131710188SJan.Friedel@Sun.COM 		goto out;
131810188SJan.Friedel@Sun.COM 	} else {
131910188SJan.Friedel@Sun.COM 		(void) close(sockfd);
132010188SJan.Friedel@Sun.COM 		sockfd = -1;
132110188SJan.Friedel@Sun.COM 		recv_fd->fd = -1;
132210188SJan.Friedel@Sun.COM 		(void) pthread_mutex_unlock(&sock_lock);
132310188SJan.Friedel@Sun.COM 	}
132410188SJan.Friedel@Sun.COM 	*fds_cnt = 1;
132510188SJan.Friedel@Sun.COM 
132610188SJan.Friedel@Sun.COM 	/* context */
132710188SJan.Friedel@Sun.COM 	if (gss_ctx_initialized) {
132810188SJan.Friedel@Sun.COM 		delete_context();
132910188SJan.Friedel@Sun.COM 	}
133010188SJan.Friedel@Sun.COM 	gss_ctx_initialized = B_FALSE;
133110188SJan.Friedel@Sun.COM 	gss_ctx = NULL;
133210188SJan.Friedel@Sun.COM 
133310188SJan.Friedel@Sun.COM 	/* mark transq to be flushed */
133410188SJan.Friedel@Sun.COM 	(void) pthread_mutex_lock(&transq_lock);
133510188SJan.Friedel@Sun.COM 	if (transq_hdr.count > 0) {
133610188SJan.Friedel@Sun.COM 		flush_transq = B_TRUE;
133710188SJan.Friedel@Sun.COM 	}
133810188SJan.Friedel@Sun.COM 	(void) pthread_mutex_unlock(&transq_lock);
133910188SJan.Friedel@Sun.COM 
134010188SJan.Friedel@Sun.COM out:
134110188SJan.Friedel@Sun.COM 	reset_in_progress = B_FALSE;
134210188SJan.Friedel@Sun.COM 	if (do_signal) {
134310188SJan.Friedel@Sun.COM 		(void) pthread_cond_broadcast(&reset_cv);
134410188SJan.Friedel@Sun.COM 	}
134510188SJan.Friedel@Sun.COM 
134610188SJan.Friedel@Sun.COM 	(void) pthread_mutex_unlock(&reset_lock);
134710188SJan.Friedel@Sun.COM }
134810188SJan.Friedel@Sun.COM 
134910188SJan.Friedel@Sun.COM /*
135010188SJan.Friedel@Sun.COM  * do_cleanup() - removes all the preallocated space by the plugin; prepares the
135110188SJan.Friedel@Sun.COM  * plugin/application to be gracefully finished. Even thought the function
135210188SJan.Friedel@Sun.COM  * allows execution without signalling the successful finish, it's recommended
135310188SJan.Friedel@Sun.COM  * to use it (we usually want to wait for cleanup before exiting).
135410188SJan.Friedel@Sun.COM  */
135510188SJan.Friedel@Sun.COM static void
do_cleanup(int * fds_cnt,struct pollfd * recv_fd,boolean_t do_signal)135610188SJan.Friedel@Sun.COM do_cleanup(int *fds_cnt, struct pollfd *recv_fd, boolean_t do_signal)
135710188SJan.Friedel@Sun.COM {
135810188SJan.Friedel@Sun.COM 
135910188SJan.Friedel@Sun.COM 	(void) pthread_mutex_lock(&reset_lock);
136010188SJan.Friedel@Sun.COM 
136110188SJan.Friedel@Sun.COM 	/*
136210188SJan.Friedel@Sun.COM 	 * socket
136310188SJan.Friedel@Sun.COM 	 * note: keeping locking for safety, thought it shouldn't be necessary
136410188SJan.Friedel@Sun.COM 	 * in current implementation - we get here only in case the sending code
136510188SJan.Friedel@Sun.COM 	 * path calls auditd_plugin_close() (thus no socket manipulation) and
136610188SJan.Friedel@Sun.COM 	 * the recv thread is doing the own socket closure.
136710188SJan.Friedel@Sun.COM 	 */
136810188SJan.Friedel@Sun.COM 	(void) pthread_mutex_lock(&sock_lock);
136910188SJan.Friedel@Sun.COM 	if (sockfd != -1) {
137010188SJan.Friedel@Sun.COM 		DPRINT((dfile, "Closing socket: %d\n", sockfd));
137110188SJan.Friedel@Sun.COM 		(void) close(sockfd);
137210188SJan.Friedel@Sun.COM 		sockfd = -1;
137310188SJan.Friedel@Sun.COM 		recv_fd->fd = -1;
137410188SJan.Friedel@Sun.COM 	}
137510188SJan.Friedel@Sun.COM 	*fds_cnt = 1;
137610188SJan.Friedel@Sun.COM 	(void) pthread_mutex_unlock(&sock_lock);
137710188SJan.Friedel@Sun.COM 
137810188SJan.Friedel@Sun.COM 	/* context */
137910188SJan.Friedel@Sun.COM 	if (gss_ctx_initialized) {
138010188SJan.Friedel@Sun.COM 		DPRINT((dfile, "Deleting context: "));
138110188SJan.Friedel@Sun.COM 		delete_context();
138210188SJan.Friedel@Sun.COM 	}
138310188SJan.Friedel@Sun.COM 	gss_ctx_initialized = B_FALSE;
138410188SJan.Friedel@Sun.COM 	gss_ctx = NULL;
138510188SJan.Friedel@Sun.COM 
138610188SJan.Friedel@Sun.COM 	/* transmission queue */
138710188SJan.Friedel@Sun.COM 	(void) pthread_mutex_lock(&transq_lock);
138810188SJan.Friedel@Sun.COM 	if (transq_hdr.count > 0) {
138910188SJan.Friedel@Sun.COM 		DPRINT((dfile, "Deallocating the transmission queue "
139010188SJan.Friedel@Sun.COM 		    "(len = %ld)\n", transq_hdr.count));
139110188SJan.Friedel@Sun.COM 		while (transq_hdr.count > 0) {
139210188SJan.Friedel@Sun.COM 			transq_dequeue(transq_hdr.head);
139310188SJan.Friedel@Sun.COM 		}
139410188SJan.Friedel@Sun.COM 	}
139510188SJan.Friedel@Sun.COM 	(void) pthread_mutex_unlock(&transq_lock);
139610188SJan.Friedel@Sun.COM 
139710188SJan.Friedel@Sun.COM 	/* notification pipe */
139810188SJan.Friedel@Sun.COM 	if (notify_pipe_ready) {
139910188SJan.Friedel@Sun.COM 		(void) close(notify_pipe[0]);
140010188SJan.Friedel@Sun.COM 		(void) close(notify_pipe[1]);
140110188SJan.Friedel@Sun.COM 		notify_pipe_ready = B_FALSE;
140210188SJan.Friedel@Sun.COM 	}
140310188SJan.Friedel@Sun.COM 
140410188SJan.Friedel@Sun.COM 	reset_in_progress = B_FALSE;
140510188SJan.Friedel@Sun.COM 	if (do_signal) {
140610188SJan.Friedel@Sun.COM 		(void) pthread_cond_broadcast(&reset_cv);
140710188SJan.Friedel@Sun.COM 	}
140810188SJan.Friedel@Sun.COM 	(void) pthread_mutex_unlock(&reset_lock);
140910188SJan.Friedel@Sun.COM }
141010188SJan.Friedel@Sun.COM 
141110188SJan.Friedel@Sun.COM 
141210188SJan.Friedel@Sun.COM /*
141310188SJan.Friedel@Sun.COM  * transq_dequeue() - dequeues given node pointed by the node_ptr from the
141410188SJan.Friedel@Sun.COM  * transmission queue. Transmission queue should be locked prior to use of this
141510188SJan.Friedel@Sun.COM  * function.
141610188SJan.Friedel@Sun.COM  */
141710188SJan.Friedel@Sun.COM static void
transq_dequeue(transq_node_t * node_ptr)141810188SJan.Friedel@Sun.COM transq_dequeue(transq_node_t *node_ptr)
141910188SJan.Friedel@Sun.COM {
142010188SJan.Friedel@Sun.COM 
142110188SJan.Friedel@Sun.COM 	if (node_ptr == NULL) {
142210188SJan.Friedel@Sun.COM 		DPRINT((dfile, "transq_dequeue(): called with NULL pointer\n"));
142310188SJan.Friedel@Sun.COM 		return;
142410188SJan.Friedel@Sun.COM 	}
142510188SJan.Friedel@Sun.COM 
142610188SJan.Friedel@Sun.COM 	free(node_ptr->seq_token.value);
142710188SJan.Friedel@Sun.COM 
142810188SJan.Friedel@Sun.COM 	if (node_ptr->prev != NULL) {
142910188SJan.Friedel@Sun.COM 		node_ptr->prev->next = node_ptr->next;
143010188SJan.Friedel@Sun.COM 	}
143110188SJan.Friedel@Sun.COM 	if (node_ptr->next != NULL) {
143210188SJan.Friedel@Sun.COM 		node_ptr->next->prev = node_ptr->prev;
143310188SJan.Friedel@Sun.COM 	}
143410188SJan.Friedel@Sun.COM 
143510188SJan.Friedel@Sun.COM 
143610188SJan.Friedel@Sun.COM 	/* update the transq_hdr */
143710188SJan.Friedel@Sun.COM 	if (node_ptr->next == NULL) {
143810188SJan.Friedel@Sun.COM 		transq_hdr.end = node_ptr->prev;
143910188SJan.Friedel@Sun.COM 	}
144010188SJan.Friedel@Sun.COM 	if (node_ptr->prev == NULL) {
144110188SJan.Friedel@Sun.COM 		transq_hdr.head = node_ptr->next;
144210188SJan.Friedel@Sun.COM 	}
144310188SJan.Friedel@Sun.COM 
144410188SJan.Friedel@Sun.COM 	transq_hdr.count--;
144510188SJan.Friedel@Sun.COM 
144610188SJan.Friedel@Sun.COM 	free(node_ptr);
144710188SJan.Friedel@Sun.COM }
144810188SJan.Friedel@Sun.COM 
144910188SJan.Friedel@Sun.COM 
145010188SJan.Friedel@Sun.COM /*
145110188SJan.Friedel@Sun.COM  * transq_enqueue() - creates new node in (at the end of) the transmission
145210188SJan.Friedel@Sun.COM  * queue. in_ptoken_ptr is a pointer to the plain token in a form of
145310188SJan.Friedel@Sun.COM  * gss_buffer_desc. Function returns 0 on success and updates the *node_ptr to
145410188SJan.Friedel@Sun.COM  * point to a newly added transmission queue node. In case of any failure
145510188SJan.Friedel@Sun.COM  * function returns 1 and sets the *node_ptr to NULL.
145610188SJan.Friedel@Sun.COM  * Transmission queue should be locked prior to use of this function.
145710188SJan.Friedel@Sun.COM  */
145810188SJan.Friedel@Sun.COM static boolean_t
transq_enqueue(transq_node_t ** node_ptr,gss_buffer_t in_seqtoken_ptr,uint64_t sequence)145910188SJan.Friedel@Sun.COM transq_enqueue(transq_node_t **node_ptr, gss_buffer_t in_seqtoken_ptr,
146010188SJan.Friedel@Sun.COM     uint64_t sequence)
146110188SJan.Friedel@Sun.COM {
146210188SJan.Friedel@Sun.COM 
146310188SJan.Friedel@Sun.COM 	*node_ptr = calloc(1, sizeof (transq_node_t));
146410188SJan.Friedel@Sun.COM 	if (*node_ptr == NULL) {
146510188SJan.Friedel@Sun.COM 		report_err(gettext("Memory allocation failed"));
146610188SJan.Friedel@Sun.COM 		DPRINT((dfile, "Memory allocation failed: %s\n",
146710188SJan.Friedel@Sun.COM 		    strerror(errno)));
146810188SJan.Friedel@Sun.COM 		goto errout;
146910188SJan.Friedel@Sun.COM 	}
147010188SJan.Friedel@Sun.COM 
147110188SJan.Friedel@Sun.COM 	/* value of the seq_token.value = (sequence number || plain token) */
147210188SJan.Friedel@Sun.COM 	(*node_ptr)->seq_num = sequence;
147310188SJan.Friedel@Sun.COM 	(*node_ptr)->seq_token.length = in_seqtoken_ptr->length;
147410188SJan.Friedel@Sun.COM 	(*node_ptr)->seq_token.value = in_seqtoken_ptr->value;
147510188SJan.Friedel@Sun.COM 
147610188SJan.Friedel@Sun.COM 	/* update the transq_hdr */
147710188SJan.Friedel@Sun.COM 	if (transq_hdr.head == NULL) {
147810188SJan.Friedel@Sun.COM 		transq_hdr.head = *node_ptr;
147910188SJan.Friedel@Sun.COM 	}
148010188SJan.Friedel@Sun.COM 	if (transq_hdr.end != NULL) {
148110188SJan.Friedel@Sun.COM 		(transq_hdr.end)->next = *node_ptr;
148210188SJan.Friedel@Sun.COM 		(*node_ptr)->prev = transq_hdr.end;
148310188SJan.Friedel@Sun.COM 	}
148410188SJan.Friedel@Sun.COM 	transq_hdr.end = *node_ptr;
148510188SJan.Friedel@Sun.COM 
148610188SJan.Friedel@Sun.COM 	transq_hdr.count++;
148710188SJan.Friedel@Sun.COM 
148810188SJan.Friedel@Sun.COM 	return (B_TRUE);
148910188SJan.Friedel@Sun.COM 
149010188SJan.Friedel@Sun.COM errout:
149110188SJan.Friedel@Sun.COM 	if (*node_ptr != NULL) {
149210188SJan.Friedel@Sun.COM 		if ((*node_ptr)->seq_token.value != NULL) {
149310188SJan.Friedel@Sun.COM 			free((*node_ptr)->seq_token.value);
149410188SJan.Friedel@Sun.COM 		}
149510188SJan.Friedel@Sun.COM 		free(*node_ptr);
149610188SJan.Friedel@Sun.COM 		*node_ptr = NULL;
149710188SJan.Friedel@Sun.COM 	}
149810188SJan.Friedel@Sun.COM 	return (B_FALSE);
149910188SJan.Friedel@Sun.COM }
150010188SJan.Friedel@Sun.COM 
150110188SJan.Friedel@Sun.COM 
150210188SJan.Friedel@Sun.COM /*
150310188SJan.Friedel@Sun.COM  * transq_retransmit() - traverse the transmission queue and try to, 1 by 1,
150410188SJan.Friedel@Sun.COM  * re-wrap the tokens with the recent context information and retransmit the
150510188SJan.Friedel@Sun.COM  * tokens from the transmission queue.
150610188SJan.Friedel@Sun.COM  * Function returns 2 on GSS context expiration, 1 on any other error, 0 on
150710188SJan.Friedel@Sun.COM  * successfully resent transmission queue.
150810188SJan.Friedel@Sun.COM  */
150910188SJan.Friedel@Sun.COM static int
transq_retransmit()151010188SJan.Friedel@Sun.COM transq_retransmit()
151110188SJan.Friedel@Sun.COM {
151210188SJan.Friedel@Sun.COM 
151310188SJan.Friedel@Sun.COM 	OM_uint32	maj_stat, min_stat;
151410188SJan.Friedel@Sun.COM 	transq_node_t	*cur_node = transq_hdr.head;
151510188SJan.Friedel@Sun.COM 	gss_buffer_desc	out_buf;
151610188SJan.Friedel@Sun.COM 	int		conf_state;
151710188SJan.Friedel@Sun.COM 
151810188SJan.Friedel@Sun.COM 	DPRINT((dfile, "Retransmission of the remainder in the transqueue\n"));
151910188SJan.Friedel@Sun.COM 
152010188SJan.Friedel@Sun.COM 	while (cur_node != NULL) {
152110188SJan.Friedel@Sun.COM 
152210188SJan.Friedel@Sun.COM 		(void) pthread_mutex_lock(&transq_lock);
152310188SJan.Friedel@Sun.COM 		(void) pthread_mutex_lock(&gss_ctx_lock);
152410188SJan.Friedel@Sun.COM 		maj_stat = gss_wrap(&min_stat, gss_ctx, 1, GSS_C_QOP_DEFAULT,
152510188SJan.Friedel@Sun.COM 		    &(cur_node->seq_token), &conf_state, &out_buf);
152610188SJan.Friedel@Sun.COM 		(void) pthread_mutex_unlock(&gss_ctx_lock);
152710188SJan.Friedel@Sun.COM 
152810188SJan.Friedel@Sun.COM 		switch (maj_stat) {
152910188SJan.Friedel@Sun.COM 		case GSS_S_COMPLETE:
153010188SJan.Friedel@Sun.COM 			break;
153110188SJan.Friedel@Sun.COM 		case GSS_S_CONTEXT_EXPIRED:
153210188SJan.Friedel@Sun.COM 			DPRINT((dfile, "Context expired.\n"));
153310188SJan.Friedel@Sun.COM 			report_gss_err(gettext("gss_wrap message"), maj_stat,
153410188SJan.Friedel@Sun.COM 			    min_stat);
153510188SJan.Friedel@Sun.COM 			(void) pthread_mutex_unlock(&transq_lock);
153610188SJan.Friedel@Sun.COM 			return (2);
153710188SJan.Friedel@Sun.COM 		default:
153810188SJan.Friedel@Sun.COM 			report_gss_err(gettext("gss_wrap message"), maj_stat,
153910188SJan.Friedel@Sun.COM 			    min_stat);
154010188SJan.Friedel@Sun.COM 			(void) pthread_mutex_unlock(&transq_lock);
154110188SJan.Friedel@Sun.COM 			return (1);
154210188SJan.Friedel@Sun.COM 		}
154310188SJan.Friedel@Sun.COM 
154410188SJan.Friedel@Sun.COM 		DPRINT((dfile, "Sending transmission queue token (seq=%lld, "
154510188SJan.Friedel@Sun.COM 		    "size=%d, transq len=%ld)\n", cur_node->seq_num,
154610188SJan.Friedel@Sun.COM 		    out_buf.length, transq_hdr.count));
154710188SJan.Friedel@Sun.COM 		if (send_token(&sockfd, &out_buf) < 0) {
154810188SJan.Friedel@Sun.COM 			(void) gss_release_buffer(&min_stat, &out_buf);
154910188SJan.Friedel@Sun.COM 			(void) pthread_mutex_unlock(&transq_lock);
155010188SJan.Friedel@Sun.COM 			return (1);
155110188SJan.Friedel@Sun.COM 		}
155210188SJan.Friedel@Sun.COM 		(void) gss_release_buffer(&min_stat, &out_buf);
155310188SJan.Friedel@Sun.COM 
155410188SJan.Friedel@Sun.COM 		cur_node = cur_node->next;
155510188SJan.Friedel@Sun.COM 		(void) pthread_mutex_unlock(&transq_lock);
155610188SJan.Friedel@Sun.COM 
155710188SJan.Friedel@Sun.COM 	} /* while */
155810188SJan.Friedel@Sun.COM 
155910188SJan.Friedel@Sun.COM 	return (0);
156010188SJan.Friedel@Sun.COM }
1561