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
report_err(char * msg)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
report_gss_err(char * msg,OM_uint32 maj_stat,OM_uint32 min_stat)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
prot_ver_negotiate()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
sock_prepare(int * sockfdptr,struct hostent * host,close_rsn_t * err_rsn)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
establish_context()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
delete_context()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
send_token(int * fdptr,gss_buffer_t tok)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
recv_token(int fd,gss_buffer_t tok)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
connect_timeout(int sockfd,struct sockaddr * name,int namelen)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
send_timeout(int fd,const char * buf,size_t len)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
recv_timeout(int fd,char * buf,size_t len)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
read_fd(int fd,char * buf,size_t len)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
write_fd(int fd,char * buf,size_t len)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
send_record(struct hostlist_s * hostlptr,const char * input,size_t in_len,uint64_t sequence,close_rsn_t * err_rsn)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
init_recv_record()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
recv_record()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
init_poll(int fd)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
reset_transport(boolean_t do_close,boolean_t sync_on_return)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
do_reset(int * fds_cnt,struct pollfd * recv_fd,boolean_t do_signal)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
do_cleanup(int * fds_cnt,struct pollfd * recv_fd,boolean_t do_signal)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
transq_dequeue(transq_node_t * node_ptr)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
transq_enqueue(transq_node_t ** node_ptr,gss_buffer_t in_seqtoken_ptr,uint64_t sequence)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
transq_retransmit()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