1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 * 25 * transport layer for audit_remote (handles connection establishment, gss 26 * context initialization, message encryption and verification) 27 * 28 */ 29 30 #include <assert.h> 31 #include <audit_plugin.h> 32 #include <errno.h> 33 #include <fcntl.h> 34 #include <gssapi/gssapi.h> 35 #include <libintl.h> 36 #include <mtmalloc.h> 37 #include <netdb.h> 38 #include <netinet/in.h> 39 #include <netinet/tcp.h> 40 #include <stdio.h> 41 #include <stdlib.h> 42 #include <string.h> 43 #include <strings.h> 44 #include <syslog.h> 45 #include <sys/types.h> 46 #include <sys/socket.h> 47 #include <unistd.h> 48 #include <poll.h> 49 #include <pthread.h> 50 51 #include "audit_remote.h" 52 53 54 static int sockfd = -1; 55 static struct hostent *current_host; 56 static gss_OID *current_mech_oid; 57 static in_port_t current_port; 58 static boolean_t flush_transq; 59 60 static char *ver_str = "01"; /* supported protocol version */ 61 static char *ver_str_concat; /* concat serv/client version */ 62 63 static gss_ctx_id_t gss_ctx; 64 static boolean_t gss_ctx_initialized; 65 66 pthread_t recv_tid; /* receiving thread */ 67 static pthread_once_t recv_once_control = PTHREAD_ONCE_INIT; 68 69 extern int timeout; /* connection timeout */ 70 71 extern pthread_mutex_t plugin_mutex; 72 transq_hdr_t transq_hdr; 73 74 /* 75 * The three locks synchronize the simultaneous actions on top of transmission 76 * queue, socket, gss_context. 77 */ 78 pthread_mutex_t transq_lock = PTHREAD_MUTEX_INITIALIZER; 79 pthread_mutex_t sock_lock = PTHREAD_MUTEX_INITIALIZER; 80 pthread_mutex_t gss_ctx_lock = PTHREAD_MUTEX_INITIALIZER; 81 82 /* reset routine synchronization - required by the sending thread */ 83 pthread_mutex_t reset_lock = PTHREAD_MUTEX_INITIALIZER; 84 static boolean_t reset_in_progress; /* reset routine in progress */ 85 86 #define NP_CLOSE -1 /* notification pipe - close message */ 87 #define NP_EXIT -2 /* notification pipe - exit message */ 88 boolean_t notify_pipe_ready; 89 int notify_pipe[2]; /* notif. pipe - receiving thread */ 90 91 pthread_cond_t reset_cv = PTHREAD_COND_INITIALIZER; 92 static close_rsn_t recv_closure_rsn; 93 94 #define MAX_TOK_LEN (128 * 1000) /* max token length we accept (B) */ 95 96 /* transmission queue helpers */ 97 static void transq_dequeue(transq_node_t *); 98 static boolean_t transq_enqueue(transq_node_t **, gss_buffer_t, 99 uint64_t); 100 static int transq_retransmit(void); 101 102 static boolean_t init_poll(int); 103 static void do_reset(int *, struct pollfd *, boolean_t); 104 static void do_cleanup(int *, struct pollfd *, boolean_t); 105 106 static void init_recv_record(void); 107 static void recv_record(); 108 static int connect_timeout(int, struct sockaddr *, int); 109 static int send_timeout(int, const char *, size_t); 110 static int recv_timeout(int, char *, size_t); 111 static int send_token(int *, gss_buffer_t); 112 static int recv_token(int, gss_buffer_t); 113 114 115 /* 116 * report_err() - wrapper, mainly due to enhance the code readability - report 117 * error to syslog via call to __audit_syslog(). 118 */ 119 static void 120 report_err(char *msg) 121 { 122 __audit_syslog("audit_remote.so", LOG_CONS | LOG_NDELAY, LOG_DAEMON, 123 LOG_ERR, msg); 124 125 } 126 127 128 /* 129 * report_gss_err() - GSS API error reporting 130 */ 131 static void 132 report_gss_err(char *msg, OM_uint32 maj_stat, OM_uint32 min_stat) 133 { 134 gss_buffer_desc msg_buf; 135 OM_uint32 _min, msg_ctx; 136 char *err_msg; 137 138 /* major stat */ 139 msg_ctx = 0; 140 do { 141 (void) gss_display_status(&_min, maj_stat, GSS_C_GSS_CODE, 142 *current_mech_oid, &msg_ctx, &msg_buf); 143 (void) asprintf(&err_msg, 144 gettext("GSS API error - %s(%u): %.*s\n"), msg, maj_stat, 145 msg_buf.length, (char *)msg_buf.value); 146 if (err_msg != NULL) { 147 report_err(err_msg); 148 free(err_msg); 149 } 150 (void) gss_release_buffer(&_min, &msg_buf); 151 } while (msg_ctx); 152 153 /* minor stat */ 154 msg_ctx = 0; 155 do { 156 (void) gss_display_status(&_min, min_stat, GSS_C_MECH_CODE, 157 *current_mech_oid, &msg_ctx, &msg_buf); 158 (void) asprintf(&err_msg, 159 gettext("GSS mech error - %s(%u): %.*s\n"), msg, min_stat, 160 msg_buf.length, (char *)msg_buf.value); 161 if (err_msg != NULL) { 162 report_err(err_msg); 163 free(err_msg); 164 } 165 (void) gss_release_buffer(&_min, &msg_buf); 166 } while (msg_ctx); 167 } 168 169 /* 170 * prot_ver_negotiate() - negotiate/acknowledge the protocol version. Currently, 171 * there is only one version supported by the plugin - "01". 172 * Note: connection must be initiated prior version negotiation 173 */ 174 static int 175 prot_ver_negotiate() 176 { 177 gss_buffer_desc out_buf, in_buf; 178 size_t ver_str_concat_sz; 179 180 /* 181 * Set the version proposal string - once we support more than 182 * version "01" this part should be extended to solve the concatenation 183 * of supported version identifiers. 184 */ 185 out_buf.value = (void *)ver_str; 186 out_buf.length = strlen((char *)out_buf.value); 187 DPRINT((dfile, "Protocol version proposal (size=%d): %.*s\n", 188 out_buf.length, out_buf.length, (char *)out_buf.value)); 189 190 if (send_token(&sockfd, &out_buf) < 0) { 191 DPRINT((dfile, "Sending protocol version token failed\n")); 192 return (-1); 193 } 194 195 if (recv_token(sockfd, &in_buf) < 0) { 196 DPRINT((dfile, "Receiving protocol version token failed\n")); 197 return (-1); 198 } 199 200 /* 201 * Verify the sent/received string - memcmp() is sufficient here 202 * because we support only one version and it is represented by 203 * the "01" string. The received version has to be "01" string as well. 204 */ 205 if (out_buf.length != in_buf.length || 206 memcmp(out_buf.value, in_buf.value, out_buf.length) != 0) { 207 DPRINT((dfile, "Verification of the protocol version strings " 208 "failed [%d:%s][%d:%s]\n", out_buf.length, 209 (char *)out_buf.value, in_buf.length, 210 (char *)in_buf.value)); 211 free(in_buf.value); 212 return (-1); 213 } 214 215 /* 216 * Prepare the concatenated client/server version strings later used 217 * as an application_data field in the gss_channel_bindings_struct 218 * structure. 219 */ 220 ver_str_concat_sz = out_buf.length + in_buf.length + 1; 221 ver_str_concat = (char *)calloc(1, ver_str_concat_sz); 222 if (ver_str_concat == NULL) { 223 report_err(gettext("Memory allocation failed")); 224 DPRINT((dfile, "Memory allocation failed: %s\n", 225 strerror(errno))); 226 free(in_buf.value); 227 return (-1); 228 } 229 (void) memcpy(ver_str_concat, out_buf.value, out_buf.length); 230 (void) memcpy(ver_str_concat + out_buf.length, in_buf.value, 231 in_buf.length); 232 DPRINT((dfile, "Concatenated version strings: %s\n", ver_str_concat)); 233 234 DPRINT((dfile, "Protocol version agreed.\n")); 235 free(in_buf.value); 236 return (0); 237 } 238 239 /* 240 * sock_prepare() - creates and connects socket. Function returns 241 * B_FALSE/B_TRUE on failure/success and sets the err_rsn accordingly to the 242 * reason of failure. 243 */ 244 static boolean_t 245 sock_prepare(int *sockfdptr, struct hostent *host, close_rsn_t *err_rsn) 246 { 247 struct sockaddr_storage addr; 248 struct sockaddr_in *sin; 249 struct sockaddr_in6 *sin6; 250 size_t addr_len; 251 int sock; 252 253 DPRINT((dfile, "Creating socket for %s\n", host->h_name)); 254 bzero(&addr, sizeof (addr)); 255 addr.ss_family = host->h_addrtype; 256 switch (host->h_addrtype) { 257 case AF_INET: 258 sin = (struct sockaddr_in *)&addr; 259 addr_len = sizeof (struct sockaddr_in); 260 bcopy(host->h_addr_list[0], 261 &(sin->sin_addr), sizeof (struct in_addr)); 262 sin->sin_port = current_port; 263 break; 264 case AF_INET6: 265 sin6 = (struct sockaddr_in6 *)&addr; 266 addr_len = sizeof (struct sockaddr_in6); 267 bcopy(host->h_addr_list[0], 268 &(sin6->sin6_addr), sizeof (struct in6_addr)); 269 sin6->sin6_port = current_port; 270 break; 271 default: 272 /* unknown address family */ 273 *err_rsn = RSN_UNKNOWN_AF; 274 return (B_FALSE); 275 } 276 if ((sock = socket(addr.ss_family, SOCK_STREAM, 0)) == -1) { 277 *err_rsn = RSN_SOCKET_CREATE; 278 return (B_FALSE); 279 } 280 DPRINT((dfile, "Socket created, fd=%d, connecting..\n", sock)); 281 282 if (connect_timeout(sock, (struct sockaddr *)&addr, addr_len)) { 283 (void) close(sock); 284 *err_rsn = RSN_CONNECTION_CREATE; 285 return (B_FALSE); 286 } 287 *sockfdptr = sock; 288 DPRINT((dfile, "Connected to %s via fd=%d\n", host->h_name, 289 *sockfdptr)); 290 291 return (B_TRUE); 292 } 293 294 /* 295 * establish_context() - establish the client/server GSS context. 296 * 297 * Note: connection must be established and version negotiated (in plain text) 298 * prior to establishing context. 299 */ 300 static int 301 establish_context() 302 { 303 gss_buffer_desc send_tok, recv_tok, *token_ptr; 304 OM_uint32 maj_stat, min_stat; 305 OM_uint32 init_sec_min_stat, ret_flags; 306 gss_name_t gss_name; 307 char *gss_svc_name = "audit"; 308 char *svc_name; 309 size_t ver_str_concat_len; 310 struct gss_channel_bindings_struct input_chan_bindings; 311 312 /* GSS service name = gss_svc_name + "@" + remote hostname (fqdn) */ 313 (void) asprintf(&svc_name, "%s@%s", gss_svc_name, current_host->h_name); 314 if (svc_name == NULL) { 315 report_err(gettext("Cannot allocate service name\n")); 316 DPRINT((dfile, "Memory allocation failed: %s\n", 317 strerror(errno))); 318 return (-1); 319 } 320 DPRINT((dfile, "Service name: %s\n", svc_name)); 321 322 send_tok.value = svc_name; 323 send_tok.length = strlen(svc_name); 324 maj_stat = gss_import_name(&min_stat, &send_tok, 325 (gss_OID)GSS_C_NT_HOSTBASED_SERVICE, &gss_name); 326 if (maj_stat != GSS_S_COMPLETE) { 327 report_gss_err(gettext("initializing context"), maj_stat, 328 min_stat); 329 free(svc_name); 330 return (-1); 331 } 332 token_ptr = GSS_C_NO_BUFFER; 333 gss_ctx = GSS_C_NO_CONTEXT; 334 335 /* initialize channel binding */ 336 bzero(&input_chan_bindings, sizeof (input_chan_bindings)); 337 input_chan_bindings.initiator_addrtype = GSS_C_AF_NULLADDR; 338 input_chan_bindings.acceptor_addrtype = GSS_C_AF_NULLADDR; 339 ver_str_concat_len = strlen(ver_str_concat) + 1; 340 input_chan_bindings.application_data.length = ver_str_concat_len; 341 input_chan_bindings.application_data.value = ver_str_concat; 342 343 (void) pthread_mutex_lock(&gss_ctx_lock); 344 do { 345 maj_stat = gss_init_sec_context(&init_sec_min_stat, 346 GSS_C_NO_CREDENTIAL, &gss_ctx, gss_name, *current_mech_oid, 347 GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG 348 | GSS_C_CONF_FLAG, 0, &input_chan_bindings, token_ptr, 349 NULL, &send_tok, &ret_flags, NULL); 350 351 if (token_ptr != GSS_C_NO_BUFFER) { 352 (void) gss_release_buffer(&min_stat, &recv_tok); 353 } 354 355 if (send_tok.length != 0) { 356 DPRINT((dfile, 357 "Sending init_sec_context token (size=%d)\n", 358 send_tok.length)); 359 if (send_token(&sockfd, &send_tok) < 0) { 360 free(svc_name); 361 (void) gss_release_name(&min_stat, &gss_name); 362 (void) pthread_mutex_unlock(&gss_ctx_lock); 363 return (-1); 364 } 365 } 366 if (send_tok.value != NULL) { 367 free(send_tok.value); /* freeing svc_name */ 368 send_tok.value = NULL; 369 send_tok.length = 0; 370 } 371 372 if (maj_stat != GSS_S_COMPLETE && 373 maj_stat != GSS_S_CONTINUE_NEEDED) { 374 report_gss_err(gettext("initializing context"), 375 maj_stat, init_sec_min_stat); 376 if (gss_ctx == GSS_C_NO_CONTEXT) { 377 (void) gss_delete_sec_context(&min_stat, 378 &gss_ctx, GSS_C_NO_BUFFER); 379 } 380 (void) gss_release_name(&min_stat, &gss_name); 381 (void) pthread_mutex_unlock(&gss_ctx_lock); 382 return (-1); 383 } 384 385 if (maj_stat == GSS_S_CONTINUE_NEEDED) { 386 DPRINT((dfile, "continue needed... ")); 387 if (recv_token(sockfd, &recv_tok) < 0) { 388 (void) gss_release_name(&min_stat, &gss_name); 389 (void) pthread_mutex_unlock(&gss_ctx_lock); 390 return (-1); 391 } 392 token_ptr = &recv_tok; 393 } 394 } while (maj_stat == GSS_S_CONTINUE_NEEDED); 395 (void) gss_release_name(&min_stat, &gss_name); 396 397 DPRINT((dfile, "context established\n")); 398 (void) pthread_mutex_unlock(&gss_ctx_lock); 399 return (0); 400 } 401 402 /* 403 * delete_context() - release GSS context. 404 */ 405 static void 406 delete_context() 407 { 408 OM_uint32 min_stat; 409 410 (void) gss_delete_sec_context(&min_stat, &gss_ctx, GSS_C_NO_BUFFER); 411 DPRINT((dfile, "context deleted\n")); 412 } 413 414 /* 415 * send_token() - send GSS token over the wire. 416 */ 417 static int 418 send_token(int *fdptr, gss_buffer_t tok) 419 { 420 uint32_t len; 421 uint32_t lensz; 422 char *out_buf; 423 int fd; 424 425 (void) pthread_mutex_lock(&sock_lock); 426 if (*fdptr == -1) { 427 (void) pthread_mutex_unlock(&sock_lock); 428 DPRINT((dfile, "Socket detected as closed.\n")); 429 return (-1); 430 } 431 fd = *fdptr; 432 433 len = htonl(tok->length); 434 lensz = sizeof (len); 435 436 out_buf = (char *)malloc((size_t)(lensz + tok->length)); 437 if (out_buf == NULL) { 438 (void) pthread_mutex_unlock(&sock_lock); 439 report_err(gettext("Memory allocation failed")); 440 DPRINT((dfile, "Memory allocation failed: %s\n", 441 strerror(errno))); 442 return (-1); 443 } 444 (void) memcpy((void *)out_buf, (void *)&len, lensz); 445 (void) memcpy((void *)(out_buf + lensz), (void *)tok->value, 446 tok->length); 447 448 if (send_timeout(fd, out_buf, (lensz + tok->length))) { 449 (void) pthread_mutex_unlock(&sock_lock); 450 free(out_buf); 451 return (-1); 452 } 453 454 (void) pthread_mutex_unlock(&sock_lock); 455 free(out_buf); 456 return (0); 457 } 458 459 460 /* 461 * recv_token() - receive GSS token over the wire. 462 */ 463 static int 464 recv_token(int fd, gss_buffer_t tok) 465 { 466 uint32_t len; 467 468 if (recv_timeout(fd, (char *)&len, sizeof (len))) { 469 return (-1); 470 } 471 len = ntohl(len); 472 473 /* simple DOS prevention mechanism */ 474 if (len > MAX_TOK_LEN) { 475 report_err(gettext("Indicated invalid token length")); 476 DPRINT((dfile, "Indicated token length > %dB\n", MAX_TOK_LEN)); 477 return (-1); 478 } 479 480 tok->value = (char *)malloc(len); 481 if (tok->value == NULL) { 482 report_err(gettext("Memory allocation failed")); 483 DPRINT((dfile, "Memory allocation failed: %s\n", 484 strerror(errno))); 485 tok->length = 0; 486 return (-1); 487 } 488 489 if (recv_timeout(fd, tok->value, len)) { 490 free(tok->value); 491 tok->value = NULL; 492 tok->length = 0; 493 return (-1); 494 } 495 496 tok->length = len; 497 return (0); 498 } 499 500 501 /* 502 * I/O functions 503 */ 504 505 /* 506 * connect_timeout() - sets nonblocking I/O on a socket and timeout-connects 507 */ 508 static int 509 connect_timeout(int sockfd, struct sockaddr *name, int namelen) 510 { 511 int flags; 512 struct pollfd fds; 513 int rc; 514 struct sockaddr_storage addr; 515 socklen_t addr_len = sizeof (addr); 516 517 518 flags = fcntl(sockfd, F_GETFL, 0); 519 if (fcntl(sockfd, F_SETFL, flags | O_NONBLOCK) == -1) { 520 return (-1); 521 } 522 if (connect(sockfd, name, namelen)) { 523 if (!(errno == EINTR || errno == EINPROGRESS || 524 errno == EWOULDBLOCK)) { 525 return (-1); 526 } 527 } 528 fds.fd = sockfd; 529 fds.events = POLLOUT; 530 for (;;) { 531 fds.revents = 0; 532 rc = poll(&fds, 1, timeout * 1000); 533 if (rc == 0) { /* timeout */ 534 return (-1); 535 } else if (rc < 0) { 536 if (errno == EINTR || errno == EAGAIN) { 537 continue; 538 } else { 539 return (-1); 540 } 541 } 542 if (fds.revents) { 543 if (getpeername(sockfd, (struct sockaddr *)&addr, 544 &addr_len)) 545 return (-1); 546 } else { 547 return (-1); 548 } 549 return (0); 550 } 551 } 552 553 /* 554 * send_timeout() - send data (in chunks if needed, each chunk in timeout secs). 555 */ 556 static int 557 send_timeout(int fd, const char *buf, size_t len) 558 { 559 int bytes; 560 struct pollfd fds; 561 int rc; 562 563 fds.fd = fd; 564 fds.events = POLLOUT; 565 566 while (len) { 567 fds.revents = 0; 568 rc = poll(&fds, 1, timeout * 1000); 569 if (rc == 0) { /* timeout */ 570 return (-1); 571 } else if (rc < 0) { 572 if (errno == EINTR || errno == EAGAIN) { 573 continue; 574 } else { 575 return (-1); 576 } 577 } 578 if (!fds.revents) { 579 return (-1); 580 } 581 582 bytes = write(fd, buf, len); 583 if (bytes < 0) { 584 if (errno == EINTR) { 585 continue; 586 } else { 587 return (-1); 588 } 589 } else if (bytes == 0) { /* eof */ 590 return (-1); 591 } 592 593 len -= bytes; 594 buf += bytes; 595 } 596 597 return (0); 598 } 599 600 /* 601 * recv_timeout() - receive data (in chunks if needed, each chunk in timeout 602 * secs). In case the function is called from receiving thread, the function 603 * cycles the poll() call in timeout seconds (waits for input from server). 604 */ 605 static int 606 recv_timeout(int fd, char *buf, size_t len) 607 { 608 int bytes; 609 struct pollfd fds; 610 int rc; 611 612 fds.fd = fd; 613 fds.events = POLLIN; 614 615 while (len) { 616 fds.revents = 0; 617 rc = poll(&fds, 1, timeout * 1000); 618 if (rc == 0) { /* timeout */ 619 return (-1); 620 } else if (rc < 0) { 621 if (errno == EINTR || errno == EAGAIN) { 622 continue; 623 } else { 624 return (-1); 625 } 626 } 627 628 if (!fds.revents) { 629 return (-1); 630 } 631 632 bytes = read(fd, buf, len); 633 if (bytes < 0) { 634 if (errno == EINTR) { 635 continue; 636 } else { 637 return (-1); 638 } 639 } else if (bytes == 0) { /* eof */ 640 return (-1); 641 } 642 643 len -= bytes; 644 buf += bytes; 645 } 646 647 return (0); 648 } 649 650 /* 651 * read_fd() - reads data of length len from the given file descriptor fd to the 652 * buffer buf, in chunks if needed. Function returns B_FALSE on failure, 653 * otherwise B_TRUE. Function preserves errno, if it was set by the read(2). 654 */ 655 static boolean_t 656 read_fd(int fd, char *buf, size_t len) 657 { 658 int bytes; 659 #ifdef DEBUG 660 size_t len_o = len; 661 #endif 662 663 while (len) { 664 bytes = read(fd, buf, len); 665 if (bytes < 0) { /* err */ 666 if (errno == EINTR || errno == EAGAIN) { 667 continue; 668 } else { 669 return (B_FALSE); 670 } 671 } else if (bytes == 0) { /* eof */ 672 return (B_FALSE); 673 } 674 675 len -= bytes; 676 buf += bytes; 677 } 678 679 DPRINT((dfile, "read_fd: Read %d bytes.\n", len_o - len)); 680 return (B_TRUE); 681 } 682 683 /* 684 * write_fd() - writes buf of length len to the opened file descriptor fd, in 685 * chunks if needed. The data from the pipe are processed in the receiving 686 * thread. Function returns B_FALSE on failure, otherwise B_TRUE. Function 687 * preserves errno, if it was set by the write(2). 688 */ 689 static boolean_t 690 write_fd(int fd, char *buf, size_t len) 691 { 692 int bytes; 693 #ifdef DEBUG 694 size_t len_o = len; 695 #endif 696 697 while (len) { 698 bytes = write(fd, buf, len); 699 if (bytes == -1) { /* err */ 700 if (errno == EINTR || errno == EAGAIN) { 701 continue; 702 } else { 703 return (B_FALSE); 704 } 705 } 706 707 len -= bytes; 708 buf += bytes; 709 } 710 711 DPRINT((dfile, "write_fd: Wrote %d bytes.\n", len_o - len)); 712 return (B_TRUE); 713 } 714 715 /* 716 * Plug-in entry point 717 */ 718 719 /* 720 * send_record() - send an audit record to a host opening a connection, 721 * negotiate version and establish context if necessary. 722 */ 723 send_record_rc_t 724 send_record(struct hostlist_s *hostlptr, const char *input, size_t in_len, 725 uint64_t sequence, close_rsn_t *err_rsn) 726 { 727 gss_buffer_desc in_buf, out_buf; 728 OM_uint32 maj_stat, min_stat; 729 int conf_state; 730 int rc; 731 transq_node_t *node_ptr; 732 uint64_t seq_n; /* sequence in the network byte order */ 733 boolean_t init_sock_poll = B_FALSE; 734 735 /* 736 * We need to grab the reset_lock here, to prevent eventual 737 * unsynchronized cleanup calls within the reset routine (reset caused 738 * by the receiving thread) and the initialization calls in the 739 * send_record() code path. 740 */ 741 (void) pthread_mutex_lock(&reset_lock); 742 743 /* 744 * Check whether the socket was closed by the recv thread prior to call 745 * send_record() and behave accordingly to the reason of the closure. 746 */ 747 if (recv_closure_rsn != RSN_UNDEFINED) { 748 *err_rsn = recv_closure_rsn; 749 if (recv_closure_rsn == RSN_GSS_CTX_EXP) { 750 rc = SEND_RECORD_RETRY; 751 } else { 752 rc = SEND_RECORD_NEXT; 753 } 754 recv_closure_rsn = RSN_UNDEFINED; 755 (void) pthread_mutex_unlock(&reset_lock); 756 return (rc); 757 } 758 759 /* 760 * Send request to other then previously used host. 761 */ 762 if (current_host != hostlptr->host) { 763 DPRINT((dfile, "Set new host: %s\n", hostlptr->host->h_name)); 764 if (sockfd != -1) { 765 (void) pthread_mutex_unlock(&reset_lock); 766 reset_transport(DO_CLOSE, DO_SYNC); 767 return (SEND_RECORD_RETRY); 768 } 769 current_host = (struct hostent *)hostlptr->host; 770 current_mech_oid = &hostlptr->mech; 771 current_port = hostlptr->port; 772 } 773 774 /* initiate the receiving thread */ 775 (void) pthread_once(&recv_once_control, init_recv_record); 776 777 /* create and connect() socket, negotiate the protocol version */ 778 if (sockfd == -1) { 779 /* socket operations */ 780 DPRINT((dfile, "Socket creation and connect\n")); 781 if (!sock_prepare(&sockfd, current_host, err_rsn)) { 782 /* we believe the err_rsn set by sock_prepare() */ 783 (void) pthread_mutex_unlock(&reset_lock); 784 return (SEND_RECORD_NEXT); 785 } 786 787 /* protocol version negotiation */ 788 DPRINT((dfile, "Protocol version negotiation\n")); 789 if (prot_ver_negotiate() != 0) { 790 DPRINT((dfile, 791 "Protocol version negotiation failed\n")); 792 (void) pthread_mutex_unlock(&reset_lock); 793 reset_transport(DO_CLOSE, DO_SYNC); 794 *err_rsn = RSN_PROTOCOL_NEGOTIATE; 795 return (SEND_RECORD_NEXT); 796 } 797 798 /* let the socket be initiated for poll() */ 799 init_sock_poll = B_TRUE; 800 } 801 802 if (!gss_ctx_initialized) { 803 DPRINT((dfile, "Establishing context..\n")); 804 if (establish_context() != 0) { 805 (void) pthread_mutex_unlock(&reset_lock); 806 reset_transport(DO_CLOSE, DO_SYNC); 807 *err_rsn = RSN_GSS_CTX_ESTABLISH; 808 return (SEND_RECORD_NEXT); 809 } 810 gss_ctx_initialized = B_TRUE; 811 } 812 813 /* let the recv thread poll() on the sockfd */ 814 if (init_sock_poll) { 815 init_sock_poll = B_FALSE; 816 if (!init_poll(sockfd)) { 817 *err_rsn = RSN_INIT_POLL; 818 (void) pthread_mutex_unlock(&reset_lock); 819 return (SEND_RECORD_RETRY); 820 } 821 } 822 823 (void) pthread_mutex_unlock(&reset_lock); 824 825 /* if not empty, retransmit contents of the transmission queue */ 826 if (flush_transq) { 827 DPRINT((dfile, "Retransmitting remaining (%ld) tokens from " 828 "the transmission queue\n", transq_hdr.count)); 829 if ((rc = transq_retransmit()) == 2) { /* gss context exp */ 830 reset_transport(DO_CLOSE, DO_SYNC); 831 *err_rsn = RSN_GSS_CTX_EXP; 832 return (SEND_RECORD_RETRY); 833 } else if (rc == 1) { 834 reset_transport(DO_CLOSE, DO_SYNC); 835 *err_rsn = RSN_OTHER_ERR; 836 return (SEND_RECORD_NEXT); 837 } 838 flush_transq = B_FALSE; 839 } 840 841 /* 842 * Concatenate sequence number and the new record. Note, that the 843 * pointer to the chunk of memory allocated for the concatenated values 844 * is later passed to the transq_enqueu() function which stores the 845 * pointer in the transmission queue; subsequently called 846 * transq_dequeue() frees the allocated memory once the MIC is verified 847 * by the recv_record() function. 848 * 849 * If we return earlier than the transq_enqueue() is called, it's 850 * necessary to free the in_buf.value explicitly prior to return. 851 * 852 */ 853 in_buf.length = in_len + sizeof (sequence); 854 in_buf.value = malloc(in_buf.length); 855 if (in_buf.value == NULL) { 856 report_err(gettext("Memory allocation failed")); 857 DPRINT((dfile, "Memory allocation failed: %s\n", 858 strerror(errno))); 859 reset_transport(DO_CLOSE, DO_SYNC); 860 *err_rsn = RSN_MEMORY_ALLOCATE; 861 return (SEND_RECORD_FAIL); 862 } 863 seq_n = htonll(sequence); 864 (void) memcpy(in_buf.value, &seq_n, sizeof (seq_n)); 865 (void) memcpy((char *)in_buf.value + sizeof (seq_n), input, in_len); 866 867 /* wrap sequence number and the new record to the per-message token */ 868 (void) pthread_mutex_lock(&gss_ctx_lock); 869 if (gss_ctx != NULL) { 870 maj_stat = gss_wrap(&min_stat, gss_ctx, 1, GSS_C_QOP_DEFAULT, 871 &in_buf, &conf_state, &out_buf); 872 (void) pthread_mutex_unlock(&gss_ctx_lock); 873 switch (maj_stat) { 874 case GSS_S_COMPLETE: 875 break; 876 case GSS_S_CONTEXT_EXPIRED: 877 reset_transport(DO_CLOSE, DO_SYNC); 878 free(in_buf.value); 879 *err_rsn = RSN_GSS_CTX_EXP; 880 return (SEND_RECORD_RETRY); 881 default: 882 report_gss_err(gettext("gss_wrap message"), maj_stat, 883 min_stat); 884 reset_transport(DO_CLOSE, DO_SYNC); 885 free(in_buf.value); 886 *err_rsn = RSN_OTHER_ERR; 887 return (SEND_RECORD_NEXT); 888 } 889 } else { /* GSS context deleted by the recv thread */ 890 (void) pthread_mutex_unlock(&gss_ctx_lock); 891 reset_transport(DO_CLOSE, DO_SYNC); 892 free(in_buf.value); 893 *err_rsn = RSN_OTHER_ERR; 894 return (SEND_RECORD_NEXT); 895 } 896 897 898 /* enqueue the to-be-sent token into transmission queue */ 899 (void) pthread_mutex_lock(&transq_lock); 900 if (!transq_enqueue(&node_ptr, &in_buf, sequence)) { 901 (void) pthread_mutex_unlock(&transq_lock); 902 reset_transport(DO_CLOSE, DO_SYNC); 903 free(in_buf.value); 904 (void) gss_release_buffer(&min_stat, &out_buf); 905 *err_rsn = RSN_OTHER_ERR; 906 return (SEND_RECORD_RETRY); 907 } 908 DPRINT((dfile, "Token enqueued for later verification\n")); 909 (void) pthread_mutex_unlock(&transq_lock); 910 911 /* send token */ 912 if (send_token(&sockfd, &out_buf) < 0) { 913 DPRINT((dfile, "Token sending failed\n")); 914 reset_transport(DO_CLOSE, DO_SYNC); 915 (void) gss_release_buffer(&min_stat, &out_buf); 916 917 (void) pthread_mutex_lock(&transq_lock); 918 transq_dequeue(node_ptr); 919 (void) pthread_mutex_unlock(&transq_lock); 920 921 *err_rsn = RSN_OTHER_ERR; 922 return (SEND_RECORD_NEXT); 923 } 924 DPRINT((dfile, "Token sent (transq size = %ld)\n", transq_hdr.count)); 925 926 (void) gss_release_buffer(&min_stat, &out_buf); 927 928 return (SEND_RECORD_SUCCESS); 929 } 930 931 /* 932 * init_recv_record() - initialize the receiver thread 933 */ 934 static void 935 init_recv_record() 936 { 937 DPRINT((dfile, "Initiating the recv thread\n")); 938 (void) pthread_create(&recv_tid, NULL, (void *(*)(void *))recv_record, 939 (void *)NULL); 940 941 } 942 943 944 /* 945 * recv_record() - the receiver thread routine 946 */ 947 static void 948 recv_record() 949 { 950 OM_uint32 maj_stat, min_stat; 951 gss_qop_t qop_state; 952 gss_buffer_desc in_buf = GSS_C_EMPTY_BUFFER; 953 gss_buffer_desc in_buf_mic = GSS_C_EMPTY_BUFFER; 954 transq_node_t *cur_node; 955 uint64_t r_seq_num; /* received sequence number */ 956 boolean_t token_verified; 957 boolean_t break_flag; 958 struct pollfd fds[2]; 959 int fds_cnt; 960 struct pollfd *pipe_fd = &fds[0]; 961 struct pollfd *recv_fd = &fds[1]; 962 uint32_t len; 963 int rc; 964 pipe_msg_t np_data; 965 966 DPRINT((dfile, "Receiver thread initiated\n")); 967 968 /* 969 * Fill in the information in the vector of file descriptors passed 970 * later on to the poll() function. In the initial state, there is only 971 * one struct pollfd in the vector which contains file descriptor of the 972 * notification pipe - notify_pipe[1]. There might be up to two file 973 * descriptors (struct pollfd) in the vector - notify_pipe[1] which 974 * resides in the vector during the entire life of the receiving thread, 975 * and the own file descriptor from which we read data sent by the 976 * remote server application. 977 */ 978 pipe_fd->fd = notify_pipe[1]; 979 pipe_fd->events = POLLIN; 980 recv_fd->fd = -1; 981 recv_fd->events = POLLIN; 982 fds_cnt = 1; 983 984 /* 985 * In the endless loop, try to grab some data from the socket or 986 * notify_pipe[1]. 987 */ 988 for (;;) { 989 990 pipe_fd->revents = 0; 991 recv_fd->revents = 0; 992 recv_closure_rsn = RSN_UNDEFINED; 993 994 /* block on poll, thus rc != 0 */ 995 rc = poll(fds, fds_cnt, -1); 996 if (rc == -1) { 997 if (errno == EAGAIN || errno == EINTR) { 998 /* silently continue on EAGAIN || EINTR */ 999 continue; 1000 } else { 1001 /* log the debug message in any other case */ 1002 DPRINT((dfile, "poll() failed: %s\n", 1003 strerror(errno))); 1004 report_err(gettext("poll() failed.\n")); 1005 continue; 1006 } 1007 } 1008 1009 /* 1010 * Receive a message from the notification pipe. Information 1011 * from the notification pipe takes precedence over the received 1012 * data from the remote server application. 1013 * 1014 * Notification pipe message format - message accepted 1015 * from the notify pipe comprises of two parts (int || 1016 * boolean_t), where if the first part (sizeof (int)) equals 1017 * NP_CLOSE, then the second part (sizeof (boolean_t)) signals 1018 * the necessity of broadcasting (DO_SYNC/DO_NOT_SYNC) the end 1019 * of the reset routine. 1020 */ 1021 if (pipe_fd->revents & POLLIN) { 1022 DPRINT((dfile, "An event on notify pipe detected\n")); 1023 if (!read_fd(pipe_fd->fd, (char *)&np_data, 1024 sizeof (np_data))) { 1025 DPRINT((dfile, "Reading notify pipe failed: " 1026 "%s\n", strerror(errno))); 1027 report_err(gettext("Reading notify pipe " 1028 "failed")); 1029 } else { 1030 switch (np_data.sock_num) { 1031 case NP_EXIT: /* exit receiving thread */ 1032 do_cleanup(&fds_cnt, recv_fd, 1033 np_data.sync); 1034 pthread_exit((void *)NULL); 1035 break; 1036 case NP_CLOSE: /* close and remove recv_fd */ 1037 do_reset(&fds_cnt, recv_fd, 1038 np_data.sync); 1039 continue; 1040 default: /* add rc_pipe to the fds */ 1041 recv_fd->fd = np_data.sock_num; 1042 fds_cnt = 2; 1043 continue; 1044 } 1045 } 1046 } 1047 /* Receive a token from the remote server application */ 1048 if (recv_fd->revents & POLLIN) { 1049 DPRINT((dfile, "An event on fd detected\n")); 1050 if (!read_fd(recv_fd->fd, (char *)&len, sizeof (len))) { 1051 DPRINT((dfile, "Token length recv failed\n")); 1052 recv_closure_rsn = RSN_TOK_RECV_FAILED; 1053 reset_transport(DO_CLOSE, DO_NOT_SYNC); 1054 continue; 1055 } 1056 len = ntohl(len); 1057 1058 /* simple DOS prevention mechanism */ 1059 if (len > MAX_TOK_LEN) { 1060 report_err(gettext("Indicated invalid token " 1061 "length")); 1062 DPRINT((dfile, "Indicated token length > %dB\n", 1063 MAX_TOK_LEN)); 1064 recv_closure_rsn = RSN_TOK_TOO_BIG; 1065 reset_transport(DO_CLOSE, DO_NOT_SYNC); 1066 continue; 1067 } 1068 1069 in_buf.value = (char *)malloc(len); 1070 if (in_buf.value == NULL) { 1071 report_err(gettext("Memory allocation failed")); 1072 DPRINT((dfile, "Memory allocation failed: %s\n", 1073 strerror(errno))); 1074 recv_closure_rsn = RSN_MEMORY_ALLOCATE; 1075 reset_transport(DO_CLOSE, DO_NOT_SYNC); 1076 continue; 1077 } 1078 if (!read_fd(recv_fd->fd, (char *)in_buf.value, len)) { 1079 DPRINT((dfile, "Token value recv failed\n")); 1080 free(in_buf.value); 1081 recv_closure_rsn = RSN_TOK_RECV_FAILED; 1082 reset_transport(DO_CLOSE, DO_NOT_SYNC); 1083 continue; 1084 } 1085 1086 in_buf.length = len; 1087 } 1088 1089 /* 1090 * Extract the sequence number and the MIC from 1091 * the per-message token 1092 */ 1093 (void) memcpy(&r_seq_num, in_buf.value, sizeof (r_seq_num)); 1094 r_seq_num = ntohll(r_seq_num); 1095 in_buf_mic.length = in_buf.length - sizeof (r_seq_num); 1096 in_buf_mic.value = (char *)in_buf.value + sizeof (r_seq_num); 1097 1098 /* 1099 * seq_num/r_seq_num - the sequence number does not need to 1100 * be unique in the transmission queue. Any token in the 1101 * transmission queue with the same seq_num as the acknowledge 1102 * token received from the server is tested. This is due to the 1103 * fact that the plugin cannot influence (in the current 1104 * implementation) sequence numbers generated by the kernel (we 1105 * are reusing record sequence numbers as a transmission queue 1106 * sequence numbers). The probability of having two or more 1107 * tokens in the transmission queue is low and at the same time 1108 * the performance gain due to using sequence numbers is quite 1109 * high. 1110 * 1111 * In case a harder condition with regard to duplicate sequence 1112 * numbers in the transmission queue will be desired over time, 1113 * the break_flag behavior used below should be 1114 * removed/changed_accordingly. 1115 */ 1116 break_flag = B_FALSE; 1117 token_verified = B_FALSE; 1118 (void) pthread_mutex_lock(&transq_lock); 1119 cur_node = transq_hdr.head; 1120 while (cur_node != NULL && !break_flag) { 1121 if (cur_node->seq_num != r_seq_num) { 1122 cur_node = cur_node->next; 1123 continue; 1124 } 1125 1126 (void) pthread_mutex_lock(&gss_ctx_lock); 1127 maj_stat = gss_verify_mic(&min_stat, gss_ctx, 1128 &(cur_node->seq_token), &in_buf_mic, 1129 &qop_state); 1130 (void) pthread_mutex_unlock(&gss_ctx_lock); 1131 1132 if (!GSS_ERROR(maj_stat)) { /* the success case */ 1133 switch (maj_stat) { 1134 /* 1135 * All the GSS_S_OLD_TOKEN, GSS_S_UNSEQ_TOKEN, 1136 * GSS_S_GAP_TOKEN are perceived as correct 1137 * behavior of the server side. The plugin 1138 * implementation is resistant to any of the 1139 * above mention cases of returned status codes. 1140 */ 1141 /*FALLTHRU*/ 1142 case GSS_S_OLD_TOKEN: 1143 case GSS_S_UNSEQ_TOKEN: 1144 case GSS_S_GAP_TOKEN: 1145 case GSS_S_COMPLETE: 1146 /* 1147 * remove the verified record/node from 1148 * the transmission queue 1149 */ 1150 transq_dequeue(cur_node); 1151 DPRINT((dfile, "Recv thread verified " 1152 "the token (transq len = %ld)\n", 1153 transq_hdr.count)); 1154 1155 token_verified = B_TRUE; 1156 break_flag = B_TRUE; 1157 break; 1158 1159 /* 1160 * Both the default case as well as 1161 * GSS_S_DUPLICATE_TOKEN case should never 1162 * occur. It's been left here for the sake of 1163 * completeness. 1164 * If any of the two cases occur, it is 1165 * subsequently cought because we don't set 1166 * the token_verified flag. 1167 */ 1168 /*FALLTHRU*/ 1169 case GSS_S_DUPLICATE_TOKEN: 1170 default: 1171 break_flag = B_TRUE; 1172 break; 1173 } /* switch (maj_stat) */ 1174 1175 } else { /* the failure case */ 1176 report_gss_err( 1177 gettext("signature verification of the " 1178 "received token failed"), 1179 maj_stat, min_stat); 1180 1181 switch (maj_stat) { 1182 case GSS_S_CONTEXT_EXPIRED: 1183 /* retransmission necessary */ 1184 recv_closure_rsn = RSN_GSS_CTX_EXP; 1185 break_flag = B_TRUE; 1186 DPRINT((dfile, "Recv thread detected " 1187 "the GSS context expiration\n")); 1188 break; 1189 case GSS_S_BAD_SIG: 1190 DPRINT((dfile, "Bad signature " 1191 "detected (seq_num = %lld)\n", 1192 cur_node->seq_num)); 1193 cur_node = cur_node->next; 1194 break; 1195 default: 1196 report_gss_err( 1197 gettext("signature verification"), 1198 maj_stat, min_stat); 1199 break_flag = B_TRUE; 1200 break; 1201 } 1202 } 1203 1204 } /* while */ 1205 (void) pthread_mutex_unlock(&transq_lock); 1206 1207 if (in_buf.value != NULL) { 1208 free(in_buf.value); 1209 in_buf.value = NULL; 1210 in_buf.length = 0; 1211 } 1212 1213 if (!token_verified) { 1214 /* 1215 * Received, but unverifiable token is perceived as 1216 * the protocol flow corruption with the penalty of 1217 * reinitializing the client/server connection. 1218 */ 1219 DPRINT((dfile, "received unverifiable token\n")); 1220 report_err(gettext("received unverifiable token\n")); 1221 if (recv_closure_rsn == RSN_UNDEFINED) { 1222 recv_closure_rsn = RSN_TOK_UNVERIFIABLE; 1223 } 1224 reset_transport(DO_CLOSE, DO_NOT_SYNC); 1225 } 1226 1227 } /* for (;;) */ 1228 1229 1230 } 1231 1232 1233 /* 1234 * init_poll() - initiates the polling in the receiving thread via sending the 1235 * appropriate message over the notify pipe. Message format = (int || 1236 * booleant_t), where the first part (sizeof (int)) contains the 1237 * newly_opened/to_be_polled socket file descriptor. The contents of the second 1238 * part (sizeof (boolean_t)) of the message works only as a padding here and no 1239 * action (no recv/send thread synchronisation) is made in the receiving thread 1240 * based on its value. 1241 */ 1242 static boolean_t 1243 init_poll(int fd) 1244 { 1245 pipe_msg_t np_data; 1246 int pipe_in = notify_pipe[0]; 1247 1248 np_data.sock_num = fd; 1249 np_data.sync = B_FALSE; /* padding only */ 1250 1251 if (!write_fd(pipe_in, (char *)&np_data, sizeof (np_data))) { 1252 DPRINT((dfile, "Cannot write to the notify pipe\n")); 1253 report_err(gettext("writing to the notify pipe failed")); 1254 return (B_FALSE); 1255 } 1256 1257 return (B_TRUE); 1258 } 1259 1260 1261 /* 1262 * reset_transport() - locked by the reset_lock initiates the reset of socket, 1263 * GSS security context and (possibly) flags the transq for retransmission; for 1264 * more detailed information see do_reset(). The reset_transport() also allows 1265 * the synchronization - waiting for the reset to be finished. 1266 * 1267 * do_close: DO_SYNC, DO_NOT_SYNC 1268 * sync_on_return: DO_EXIT (DO_NOT_CLOSE), DO_CLOSE (DO_NOT_EXIT) 1269 * 1270 */ 1271 void 1272 reset_transport(boolean_t do_close, boolean_t sync_on_return) 1273 { 1274 int pipe_in = notify_pipe[0]; 1275 pipe_msg_t np_data; 1276 1277 /* 1278 * Check if the reset routine is in progress or whether it was already 1279 * executed by some other thread. 1280 */ 1281 (void) pthread_mutex_lock(&reset_lock); 1282 if (reset_in_progress) { 1283 (void) pthread_mutex_unlock(&reset_lock); 1284 return; 1285 } 1286 reset_in_progress = B_TRUE; 1287 1288 np_data.sock_num = (do_close ? NP_CLOSE : NP_EXIT); 1289 np_data.sync = sync_on_return; 1290 (void) write_fd(pipe_in, (char *)&np_data, sizeof (np_data)); 1291 1292 if (sync_on_return) { 1293 while (reset_in_progress) { 1294 (void) pthread_cond_wait(&reset_cv, &reset_lock); 1295 DPRINT((dfile, "Wait for sync\n")); 1296 } 1297 DPRINT((dfile, "Synced\n")); 1298 } 1299 (void) pthread_mutex_unlock(&reset_lock); 1300 1301 } 1302 1303 1304 /* 1305 * do_reset() - the own reseting routine called from the recv thread. If the 1306 * synchronization was requested, signal the finish via conditional variable. 1307 */ 1308 static void 1309 do_reset(int *fds_cnt, struct pollfd *recv_fd, boolean_t do_signal) 1310 { 1311 1312 (void) pthread_mutex_lock(&reset_lock); 1313 1314 /* socket */ 1315 (void) pthread_mutex_lock(&sock_lock); 1316 if (sockfd == -1) { 1317 DPRINT((dfile, "socket already closed\n")); 1318 (void) pthread_mutex_unlock(&sock_lock); 1319 goto out; 1320 } else { 1321 (void) close(sockfd); 1322 sockfd = -1; 1323 recv_fd->fd = -1; 1324 (void) pthread_mutex_unlock(&sock_lock); 1325 } 1326 *fds_cnt = 1; 1327 1328 /* context */ 1329 if (gss_ctx_initialized) { 1330 delete_context(); 1331 } 1332 gss_ctx_initialized = B_FALSE; 1333 gss_ctx = NULL; 1334 1335 /* mark transq to be flushed */ 1336 (void) pthread_mutex_lock(&transq_lock); 1337 if (transq_hdr.count > 0) { 1338 flush_transq = B_TRUE; 1339 } 1340 (void) pthread_mutex_unlock(&transq_lock); 1341 1342 out: 1343 reset_in_progress = B_FALSE; 1344 if (do_signal) { 1345 (void) pthread_cond_broadcast(&reset_cv); 1346 } 1347 1348 (void) pthread_mutex_unlock(&reset_lock); 1349 } 1350 1351 /* 1352 * do_cleanup() - removes all the preallocated space by the plugin; prepares the 1353 * plugin/application to be gracefully finished. Even thought the function 1354 * allows execution without signalling the successful finish, it's recommended 1355 * to use it (we usually want to wait for cleanup before exiting). 1356 */ 1357 static void 1358 do_cleanup(int *fds_cnt, struct pollfd *recv_fd, boolean_t do_signal) 1359 { 1360 1361 (void) pthread_mutex_lock(&reset_lock); 1362 1363 /* 1364 * socket 1365 * note: keeping locking for safety, thought it shouldn't be necessary 1366 * in current implementation - we get here only in case the sending code 1367 * path calls auditd_plugin_close() (thus no socket manipulation) and 1368 * the recv thread is doing the own socket closure. 1369 */ 1370 (void) pthread_mutex_lock(&sock_lock); 1371 if (sockfd != -1) { 1372 DPRINT((dfile, "Closing socket: %d\n", sockfd)); 1373 (void) close(sockfd); 1374 sockfd = -1; 1375 recv_fd->fd = -1; 1376 } 1377 *fds_cnt = 1; 1378 (void) pthread_mutex_unlock(&sock_lock); 1379 1380 /* context */ 1381 if (gss_ctx_initialized) { 1382 DPRINT((dfile, "Deleting context: ")); 1383 delete_context(); 1384 } 1385 gss_ctx_initialized = B_FALSE; 1386 gss_ctx = NULL; 1387 1388 /* transmission queue */ 1389 (void) pthread_mutex_lock(&transq_lock); 1390 if (transq_hdr.count > 0) { 1391 DPRINT((dfile, "Deallocating the transmission queue " 1392 "(len = %ld)\n", transq_hdr.count)); 1393 while (transq_hdr.count > 0) { 1394 transq_dequeue(transq_hdr.head); 1395 } 1396 } 1397 (void) pthread_mutex_unlock(&transq_lock); 1398 1399 /* notification pipe */ 1400 if (notify_pipe_ready) { 1401 (void) close(notify_pipe[0]); 1402 (void) close(notify_pipe[1]); 1403 notify_pipe_ready = B_FALSE; 1404 } 1405 1406 reset_in_progress = B_FALSE; 1407 if (do_signal) { 1408 (void) pthread_cond_broadcast(&reset_cv); 1409 } 1410 (void) pthread_mutex_unlock(&reset_lock); 1411 } 1412 1413 1414 /* 1415 * transq_dequeue() - dequeues given node pointed by the node_ptr from the 1416 * transmission queue. Transmission queue should be locked prior to use of this 1417 * function. 1418 */ 1419 static void 1420 transq_dequeue(transq_node_t *node_ptr) 1421 { 1422 1423 if (node_ptr == NULL) { 1424 DPRINT((dfile, "transq_dequeue(): called with NULL pointer\n")); 1425 return; 1426 } 1427 1428 free(node_ptr->seq_token.value); 1429 1430 if (node_ptr->prev != NULL) { 1431 node_ptr->prev->next = node_ptr->next; 1432 } 1433 if (node_ptr->next != NULL) { 1434 node_ptr->next->prev = node_ptr->prev; 1435 } 1436 1437 1438 /* update the transq_hdr */ 1439 if (node_ptr->next == NULL) { 1440 transq_hdr.end = node_ptr->prev; 1441 } 1442 if (node_ptr->prev == NULL) { 1443 transq_hdr.head = node_ptr->next; 1444 } 1445 1446 transq_hdr.count--; 1447 1448 free(node_ptr); 1449 } 1450 1451 1452 /* 1453 * transq_enqueue() - creates new node in (at the end of) the transmission 1454 * queue. in_ptoken_ptr is a pointer to the plain token in a form of 1455 * gss_buffer_desc. Function returns 0 on success and updates the *node_ptr to 1456 * point to a newly added transmission queue node. In case of any failure 1457 * function returns 1 and sets the *node_ptr to NULL. 1458 * Transmission queue should be locked prior to use of this function. 1459 */ 1460 static boolean_t 1461 transq_enqueue(transq_node_t **node_ptr, gss_buffer_t in_seqtoken_ptr, 1462 uint64_t sequence) 1463 { 1464 1465 *node_ptr = calloc(1, sizeof (transq_node_t)); 1466 if (*node_ptr == NULL) { 1467 report_err(gettext("Memory allocation failed")); 1468 DPRINT((dfile, "Memory allocation failed: %s\n", 1469 strerror(errno))); 1470 goto errout; 1471 } 1472 1473 /* value of the seq_token.value = (sequence number || plain token) */ 1474 (*node_ptr)->seq_num = sequence; 1475 (*node_ptr)->seq_token.length = in_seqtoken_ptr->length; 1476 (*node_ptr)->seq_token.value = in_seqtoken_ptr->value; 1477 1478 /* update the transq_hdr */ 1479 if (transq_hdr.head == NULL) { 1480 transq_hdr.head = *node_ptr; 1481 } 1482 if (transq_hdr.end != NULL) { 1483 (transq_hdr.end)->next = *node_ptr; 1484 (*node_ptr)->prev = transq_hdr.end; 1485 } 1486 transq_hdr.end = *node_ptr; 1487 1488 transq_hdr.count++; 1489 1490 return (B_TRUE); 1491 1492 errout: 1493 if (*node_ptr != NULL) { 1494 if ((*node_ptr)->seq_token.value != NULL) { 1495 free((*node_ptr)->seq_token.value); 1496 } 1497 free(*node_ptr); 1498 *node_ptr = NULL; 1499 } 1500 return (B_FALSE); 1501 } 1502 1503 1504 /* 1505 * transq_retransmit() - traverse the transmission queue and try to, 1 by 1, 1506 * re-wrap the tokens with the recent context information and retransmit the 1507 * tokens from the transmission queue. 1508 * Function returns 2 on GSS context expiration, 1 on any other error, 0 on 1509 * successfully resent transmission queue. 1510 */ 1511 static int 1512 transq_retransmit() 1513 { 1514 1515 OM_uint32 maj_stat, min_stat; 1516 transq_node_t *cur_node = transq_hdr.head; 1517 gss_buffer_desc out_buf; 1518 int conf_state; 1519 1520 DPRINT((dfile, "Retransmission of the remainder in the transqueue\n")); 1521 1522 while (cur_node != NULL) { 1523 1524 (void) pthread_mutex_lock(&transq_lock); 1525 (void) pthread_mutex_lock(&gss_ctx_lock); 1526 maj_stat = gss_wrap(&min_stat, gss_ctx, 1, GSS_C_QOP_DEFAULT, 1527 &(cur_node->seq_token), &conf_state, &out_buf); 1528 (void) pthread_mutex_unlock(&gss_ctx_lock); 1529 1530 switch (maj_stat) { 1531 case GSS_S_COMPLETE: 1532 break; 1533 case GSS_S_CONTEXT_EXPIRED: 1534 DPRINT((dfile, "Context expired.\n")); 1535 report_gss_err(gettext("gss_wrap message"), maj_stat, 1536 min_stat); 1537 (void) pthread_mutex_unlock(&transq_lock); 1538 return (2); 1539 default: 1540 report_gss_err(gettext("gss_wrap message"), maj_stat, 1541 min_stat); 1542 (void) pthread_mutex_unlock(&transq_lock); 1543 return (1); 1544 } 1545 1546 DPRINT((dfile, "Sending transmission queue token (seq=%lld, " 1547 "size=%d, transq len=%ld)\n", cur_node->seq_num, 1548 out_buf.length, transq_hdr.count)); 1549 if (send_token(&sockfd, &out_buf) < 0) { 1550 (void) gss_release_buffer(&min_stat, &out_buf); 1551 (void) pthread_mutex_unlock(&transq_lock); 1552 return (1); 1553 } 1554 (void) gss_release_buffer(&min_stat, &out_buf); 1555 1556 cur_node = cur_node->next; 1557 (void) pthread_mutex_unlock(&transq_lock); 1558 1559 } /* while */ 1560 1561 return (0); 1562 } 1563