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