1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ 2 /* 3 * Copyright 1994 by OpenVision Technologies, Inc. 4 * 5 * Permission to use, copy, modify, distribute, and sell this software 6 * and its documentation for any purpose is hereby granted without fee, 7 * provided that the above copyright notice appears in all copies and 8 * that both that copyright notice and this permission notice appear in 9 * supporting documentation, and that the name of OpenVision not be used 10 * in advertising or publicity pertaining to distribution of the software 11 * without specific, written prior permission. OpenVision makes no 12 * representations about the suitability of this software for any 13 * purpose. It is provided "as is" without express or implied warranty. 14 * 15 * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, 16 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO 17 * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR 18 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF 19 * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR 20 * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 21 * PERFORMANCE OF THIS SOFTWARE. 22 */ 23 /* 24 * Copyright (C) 2004,2008 by the Massachusetts Institute of Technology. 25 * All rights reserved. 26 * 27 * Export of this software from the United States of America may 28 * require a specific license from the United States Government. 29 * It is the responsibility of any person or organization contemplating 30 * export to obtain such a license before exporting. 31 * 32 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and 33 * distribute this software and its documentation for any purpose and 34 * without fee is hereby granted, provided that the above copyright 35 * notice appear in all copies and that both that copyright notice and 36 * this permission notice appear in supporting documentation, and that 37 * the name of M.I.T. not be used in advertising or publicity pertaining 38 * to distribution of the software without specific, written prior 39 * permission. Furthermore if you modify this software you must label 40 * your software as modified software and not distribute it in such a 41 * fashion that it might be confused with the original M.I.T. software. 42 * M.I.T. makes no representations about the suitability of 43 * this software for any purpose. It is provided "as is" without express 44 * or implied warranty. 45 */ 46 47 #include "autoconf.h" 48 #include <stdio.h> 49 #ifdef _WIN32 50 #include <windows.h> 51 #include <winsock.h> 52 #else 53 #include <sys/types.h> 54 #include <sys/socket.h> 55 #include <sys/time.h> 56 #include <netinet/in.h> 57 #include <pthread.h> 58 #include <signal.h> 59 #endif 60 #ifdef HAVE_UNISTD_H 61 #include <unistd.h> 62 #endif 63 #include <stdlib.h> 64 #include <ctype.h> 65 66 #include <gssapi/gssapi_generic.h> 67 #include "gss-misc.h" 68 #include "port-sockets.h" 69 70 #ifdef HAVE_STRING_H 71 #include <string.h> 72 #else 73 #include <strings.h> 74 #endif 75 76 static void 77 usage() 78 { 79 fprintf(stderr, "Usage: gss-server [-port port] [-verbose] [-once]"); 80 #ifdef _WIN32 81 fprintf(stderr, " [-threads num]"); 82 #endif 83 fprintf(stderr, "\n"); 84 fprintf(stderr, " [-inetd] [-export] [-logfile file] " 85 "service_name\n"); 86 exit(1); 87 } 88 89 FILE *logfile; 90 91 int verbose = 0; 92 93 /* 94 * Function: server_acquire_creds 95 * 96 * Purpose: imports a service name and acquires credentials for it 97 * 98 * Arguments: 99 * 100 * service_name (r) the ASCII service name 101 * server_creds (w) the GSS-API service credentials 102 * 103 * Returns: 0 on success, -1 on failure 104 * 105 * Effects: 106 * 107 * The service name is imported with gss_import_name, and service 108 * credentials are acquired with gss_acquire_cred. If either operation 109 * fails, an error message is displayed and -1 is returned; otherwise, 110 * 0 is returned. 111 */ 112 static int 113 server_acquire_creds(char *service_name, gss_cred_id_t *server_creds) 114 { 115 gss_buffer_desc name_buf; 116 gss_name_t server_name; 117 OM_uint32 maj_stat, min_stat; 118 119 name_buf.value = service_name; 120 name_buf.length = strlen(name_buf.value) + 1; 121 maj_stat = gss_import_name(&min_stat, &name_buf, 122 (gss_OID)gss_nt_service_name, &server_name); 123 if (maj_stat != GSS_S_COMPLETE) { 124 display_status("importing name", maj_stat, min_stat); 125 return -1; 126 } 127 128 maj_stat = gss_acquire_cred(&min_stat, server_name, 0, 129 GSS_C_NULL_OID_SET, GSS_C_ACCEPT, 130 server_creds, NULL, NULL); 131 if (maj_stat != GSS_S_COMPLETE) { 132 display_status("acquiring credentials", maj_stat, min_stat); 133 return -1; 134 } 135 136 (void)gss_release_name(&min_stat, &server_name); 137 138 return 0; 139 } 140 141 /* 142 * Function: server_establish_context 143 * 144 * Purpose: establishses a GSS-API context as a specified service with 145 * an incoming client, and returns the context handle and associated 146 * client name 147 * 148 * Arguments: 149 * 150 * s (r) an established TCP connection to the client 151 * service_creds (r) server credentials, from gss_acquire_cred 152 * context (w) the established GSS-API context 153 * client_name (w) the client's ASCII name 154 * 155 * Returns: 0 on success, -1 on failure 156 * 157 * Effects: 158 * 159 * Any valid client request is accepted. If a context is established, 160 * its handle is returned in context and the client name is returned 161 * in client_name and 0 is returned. If unsuccessful, an error 162 * message is displayed and -1 is returned. 163 */ 164 static int 165 server_establish_context(int s, gss_cred_id_t server_creds, 166 gss_ctx_id_t *context, gss_buffer_t client_name, 167 OM_uint32 *ret_flags) 168 { 169 gss_buffer_desc send_tok, recv_tok, oid_name; 170 gss_name_t client; 171 gss_OID doid; 172 OM_uint32 maj_stat, min_stat, acc_sec_min_stat; 173 int token_flags; 174 175 if (recv_token(s, &token_flags, &recv_tok) < 0) 176 return -1; 177 178 if (recv_tok.value) { 179 free(recv_tok.value); 180 recv_tok.value = NULL; 181 } 182 183 if (!(token_flags & TOKEN_NOOP)) { 184 if (logfile) { 185 fprintf(logfile, "Expected NOOP token, got %d token instead\n", 186 token_flags); 187 } 188 return -1; 189 } 190 191 *context = GSS_C_NO_CONTEXT; 192 193 if (token_flags & TOKEN_CONTEXT_NEXT) { 194 do { 195 if (recv_token(s, &token_flags, &recv_tok) < 0) 196 return -1; 197 198 if (verbose && logfile) { 199 fprintf(logfile, "Received token (size=%d): \n", 200 (int)recv_tok.length); 201 print_token(&recv_tok); 202 } 203 204 maj_stat = gss_accept_sec_context(&acc_sec_min_stat, context, 205 server_creds, &recv_tok, 206 GSS_C_NO_CHANNEL_BINDINGS, 207 &client, &doid, &send_tok, 208 ret_flags, NULL, NULL); 209 210 if (recv_tok.value) { 211 free(recv_tok.value); 212 recv_tok.value = NULL; 213 } 214 215 if (send_tok.length != 0) { 216 if (verbose && logfile) { 217 fprintf(logfile, 218 "Sending accept_sec_context token (size=%d):\n", 219 (int)send_tok.length); 220 print_token(&send_tok); 221 } 222 if (send_token(s, TOKEN_CONTEXT, &send_tok) < 0) { 223 if (logfile) 224 fprintf(logfile, "failure sending token\n"); 225 return -1; 226 } 227 228 (void)gss_release_buffer(&min_stat, &send_tok); 229 } 230 if (maj_stat != GSS_S_COMPLETE && 231 maj_stat != GSS_S_CONTINUE_NEEDED) { 232 display_status("accepting context", maj_stat, 233 acc_sec_min_stat); 234 if (*context != GSS_C_NO_CONTEXT) { 235 gss_delete_sec_context(&min_stat, context, 236 GSS_C_NO_BUFFER); 237 } 238 return -1; 239 } 240 241 if (verbose && logfile) { 242 if (maj_stat == GSS_S_CONTINUE_NEEDED) 243 fprintf(logfile, "continue needed...\n"); 244 else 245 fprintf(logfile, "\n"); 246 fflush(logfile); 247 } 248 } while (maj_stat == GSS_S_CONTINUE_NEEDED); 249 250 /* display the flags */ 251 display_ctx_flags(*ret_flags); 252 253 if (verbose && logfile) { 254 maj_stat = gss_oid_to_str(&min_stat, doid, &oid_name); 255 if (maj_stat != GSS_S_COMPLETE) { 256 display_status("converting oid->string", maj_stat, min_stat); 257 return -1; 258 } 259 fprintf(logfile, "Accepted connection using mechanism OID %.*s.\n", 260 (int)oid_name.length, (char *)oid_name.value); 261 (void)gss_release_buffer(&min_stat, &oid_name); 262 } 263 264 maj_stat = gss_display_name(&min_stat, client, client_name, &doid); 265 if (maj_stat != GSS_S_COMPLETE) { 266 display_status("displaying name", maj_stat, min_stat); 267 return -1; 268 } 269 maj_stat = gss_release_name(&min_stat, &client); 270 if (maj_stat != GSS_S_COMPLETE) { 271 display_status("releasing name", maj_stat, min_stat); 272 return -1; 273 } 274 } else { 275 client_name->length = *ret_flags = 0; 276 277 if (logfile) 278 fprintf(logfile, "Accepted unauthenticated connection.\n"); 279 } 280 281 return 0; 282 } 283 284 /* 285 * Function: create_socket 286 * 287 * Purpose: Opens a listening TCP socket. 288 * 289 * Arguments: 290 * 291 * port (r) the port number on which to listen 292 * 293 * Returns: the listening socket file descriptor, or -1 on failure 294 * 295 * Effects: 296 * 297 * A listening socket on the specified port and created and returned. 298 * On error, an error message is displayed and -1 is returned. 299 */ 300 static int 301 create_socket(u_short port) 302 { 303 struct sockaddr_in saddr; 304 int s, on = 1; 305 306 saddr.sin_family = AF_INET; 307 saddr.sin_port = htons(port); 308 saddr.sin_addr.s_addr = INADDR_ANY; 309 310 s = socket(AF_INET, SOCK_STREAM, 0); 311 if (s < 0) { 312 perror("creating socket"); 313 return -1; 314 } 315 /* Let the socket be reused right away. */ 316 (void)setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)); 317 if (bind(s, (struct sockaddr *)&saddr, sizeof(saddr)) < 0) { 318 perror("binding socket"); 319 (void)close(s); 320 return -1; 321 } 322 if (listen(s, 5) < 0) { 323 perror("listening on socket"); 324 (void)close(s); 325 return -1; 326 } 327 return s; 328 } 329 330 static float 331 timeval_subtract(struct timeval *tv1, struct timeval *tv2) 332 { 333 return ((tv1->tv_sec - tv2->tv_sec) + 334 ((float)(tv1->tv_usec - tv2->tv_usec)) / 1000000); 335 } 336 337 /* 338 * Yes, yes, this isn't the best place for doing this test. 339 * DO NOT REMOVE THIS UNTIL A BETTER TEST HAS BEEN WRITTEN, THOUGH. 340 * -TYT 341 */ 342 static int 343 test_import_export_context(gss_ctx_id_t *context) 344 { 345 OM_uint32 min_stat, maj_stat; 346 gss_buffer_desc context_token, copied_token; 347 struct timeval tm1, tm2; 348 349 /* Attempt to save and then restore the context. */ 350 gettimeofday(&tm1, (struct timezone *)0); 351 maj_stat = gss_export_sec_context(&min_stat, context, &context_token); 352 if (maj_stat != GSS_S_COMPLETE) { 353 display_status("exporting context", maj_stat, min_stat); 354 return 1; 355 } 356 gettimeofday(&tm2, NULL); 357 if (verbose && logfile) { 358 fprintf(logfile, "Exported context: %d bytes, %7.4f seconds\n", 359 (int)context_token.length, timeval_subtract(&tm2, &tm1)); 360 } 361 copied_token.length = context_token.length; 362 copied_token.value = malloc(context_token.length); 363 if (copied_token.value == 0) { 364 if (logfile) { 365 fprintf(logfile, "Couldn't allocate memory to copy context " 366 "token.\n"); 367 } 368 return 1; 369 } 370 memcpy(copied_token.value, context_token.value, copied_token.length); 371 maj_stat = gss_import_sec_context(&min_stat, &copied_token, context); 372 if (maj_stat != GSS_S_COMPLETE) { 373 display_status("importing context", maj_stat, min_stat); 374 return 1; 375 } 376 free(copied_token.value); 377 gettimeofday(&tm1, NULL); 378 if (verbose && logfile) { 379 fprintf(logfile, "Importing context: %7.4f seconds\n", 380 timeval_subtract(&tm1, &tm2)); 381 } 382 (void)gss_release_buffer(&min_stat, &context_token); 383 return 0; 384 } 385 386 /* 387 * Function: sign_server 388 * 389 * Purpose: Performs the "sign" service. 390 * 391 * Arguments: 392 * 393 * s (r) a TCP socket on which a connection has been 394 * accept()ed 395 * service_name (r) the ASCII name of the GSS-API service to 396 * establish a context as 397 * export (r) whether to test context exporting 398 * 399 * Returns: -1 on error 400 * 401 * Effects: 402 * 403 * sign_server establishes a context, and performs a single sign request. 404 * 405 * A sign request is a single GSS-API sealed token. The token is 406 * unsealed and a signature block, produced with gss_sign, is returned 407 * to the sender. The context is the destroyed and the connection 408 * closed. 409 * 410 * If any error occurs, -1 is returned. 411 */ 412 static int 413 sign_server(int s, gss_cred_id_t server_creds, int export) 414 { 415 gss_buffer_desc client_name, xmit_buf, msg_buf; 416 gss_ctx_id_t context; 417 OM_uint32 maj_stat, min_stat, ret_flags; 418 int i, conf_state, token_flags; 419 char *cp; 420 421 /* Establish a context with the client */ 422 if (server_establish_context(s, server_creds, &context, &client_name, 423 &ret_flags) < 0) 424 return -1; 425 426 if (context == GSS_C_NO_CONTEXT) { 427 printf("Accepted unauthenticated connection.\n"); 428 } else { 429 printf("Accepted connection: \"%.*s\"\n", (int)client_name.length, 430 (char *)client_name.value); 431 (void)gss_release_buffer(&min_stat, &client_name); 432 433 if (export) { 434 for (i = 0; i < 3; i++) { 435 if (test_import_export_context(&context)) 436 return -1; 437 } 438 } 439 } 440 441 do { 442 /* Receive the message token */ 443 if (recv_token(s, &token_flags, &xmit_buf) < 0) 444 return -1; 445 446 if (token_flags & TOKEN_NOOP) { 447 if (logfile) 448 fprintf(logfile, "NOOP token\n"); 449 if (xmit_buf.value) { 450 free(xmit_buf.value); 451 xmit_buf.value = 0; 452 } 453 break; 454 } 455 456 if (verbose && logfile) { 457 fprintf(logfile, "Message token (flags=%d):\n", token_flags); 458 print_token(&xmit_buf); 459 } 460 461 if (context == GSS_C_NO_CONTEXT && 462 (token_flags & 463 (TOKEN_WRAPPED | TOKEN_ENCRYPTED | TOKEN_SEND_MIC))) { 464 if (logfile) { 465 fprintf(logfile, "Unauthenticated client requested " 466 "authenticated services!\n"); 467 } 468 if (xmit_buf.value) { 469 free(xmit_buf.value); 470 xmit_buf.value = 0; 471 } 472 return -1; 473 } 474 475 if (token_flags & TOKEN_WRAPPED) { 476 maj_stat = gss_unwrap(&min_stat, context, &xmit_buf, &msg_buf, 477 &conf_state, NULL); 478 if (maj_stat != GSS_S_COMPLETE) { 479 display_status("unsealing message", maj_stat, min_stat); 480 if (xmit_buf.value) { 481 free(xmit_buf.value); 482 xmit_buf.value = 0; 483 } 484 return -1; 485 } else if (!conf_state && (token_flags & TOKEN_ENCRYPTED)) { 486 fprintf(stderr, "Warning! Message not encrypted.\n"); 487 } 488 489 if (xmit_buf.value) { 490 free(xmit_buf.value); 491 xmit_buf.value = 0; 492 } 493 } else { 494 msg_buf = xmit_buf; 495 } 496 497 if (logfile) { 498 fprintf(logfile, "Received message: "); 499 cp = msg_buf.value; 500 if (isprint((unsigned char)cp[0]) && 501 isprint((unsigned char)cp[1])) { 502 fprintf(logfile, "\"%.*s\"\n", (int)msg_buf.length, 503 (char *)msg_buf.value); 504 } else { 505 fprintf(logfile, "\n"); 506 print_token(&msg_buf); 507 } 508 } 509 510 if (token_flags & TOKEN_SEND_MIC) { 511 /* Produce a signature block for the message. */ 512 maj_stat = gss_get_mic(&min_stat, context, GSS_C_QOP_DEFAULT, 513 &msg_buf, &xmit_buf); 514 if (maj_stat != GSS_S_COMPLETE) { 515 display_status("signing message", maj_stat, min_stat); 516 return -1; 517 } 518 519 if (msg_buf.value) { 520 free(msg_buf.value); 521 msg_buf.value = 0; 522 } 523 524 /* Send the signature block to the client. */ 525 if (send_token(s, TOKEN_MIC, &xmit_buf) < 0) 526 return -1; 527 528 if (xmit_buf.value) { 529 free(xmit_buf.value); 530 xmit_buf.value = 0; 531 } 532 } else { 533 if (msg_buf.value) { 534 free(msg_buf.value); 535 msg_buf.value = 0; 536 } 537 if (send_token(s, TOKEN_NOOP, empty_token) < 0) 538 return -1; 539 } 540 } while (1 /* loop will break if NOOP received */); 541 542 if (context != GSS_C_NO_CONTEXT) { 543 /* Delete context. */ 544 maj_stat = gss_delete_sec_context(&min_stat, &context, NULL); 545 if (maj_stat != GSS_S_COMPLETE) { 546 display_status("deleting context", maj_stat, min_stat); 547 return -1; 548 } 549 } 550 551 if (logfile) 552 fflush(logfile); 553 554 return 0; 555 } 556 557 static int max_threads = 1; 558 559 #ifdef _WIN32 560 static thread_count = 0; 561 static HANDLE hMutex = NULL; 562 static HANDLE hEvent = NULL; 563 564 void 565 init_handles(void) 566 { 567 hMutex = CreateMutex(NULL, FALSE, NULL); 568 hEvent = CreateEvent(NULL, FALSE, FALSE, NULL); 569 } 570 571 void 572 cleanup_handles(void) 573 { 574 CloseHandle(hMutex); 575 CloseHandle(hEvent); 576 } 577 578 BOOL 579 wait_and_increment_thread_counter(void) 580 { 581 for (;;) { 582 if (WaitForSingleObject(hMutex, INFINITE) == WAIT_OBJECT_0) { 583 if (thread_count < max_threads) { 584 thread_count++; 585 ReleaseMutex(hMutex); 586 return TRUE; 587 } else { 588 ReleaseMutex(hMutex); 589 590 if (WaitForSingleObject(hEvent, INFINITE) == WAIT_OBJECT_0) 591 continue; 592 else 593 return FALSE; 594 } 595 } else { 596 return FALSE; 597 } 598 } 599 } 600 601 BOOL 602 decrement_and_signal_thread_counter(void) 603 { 604 if (WaitForSingleObject(hMutex, INFINITE) == WAIT_OBJECT_0) { 605 if (thread_count == max_threads) 606 SetEvent(hEvent); 607 thread_count--; 608 ReleaseMutex(hMutex); 609 return TRUE; 610 } else { 611 return FALSE; 612 } 613 } 614 615 #else /* assume pthread */ 616 617 static pthread_mutex_t counter_mutex = PTHREAD_MUTEX_INITIALIZER; 618 static pthread_cond_t counter_cond = PTHREAD_COND_INITIALIZER; 619 int counter = 0; 620 621 static int 622 wait_and_increment_thread_counter(void) 623 { 624 int err; 625 626 err = pthread_mutex_lock(&counter_mutex); 627 if (err) { 628 perror("pthread_mutex_lock"); 629 return 0; 630 } 631 if (counter == max_threads) { 632 err = pthread_cond_wait(&counter_cond, &counter_mutex); 633 if (err) { 634 pthread_mutex_unlock(&counter_mutex); 635 perror("pthread_cond_wait"); 636 return 0; 637 } 638 } 639 counter++; 640 pthread_mutex_unlock(&counter_mutex); 641 return 1; 642 } 643 644 static void 645 decrement_and_signal_thread_counter(void) 646 { 647 int err; 648 649 err = pthread_mutex_lock(&counter_mutex); 650 if (err) { 651 perror("pthread_mutex_lock"); 652 return; 653 } 654 if (counter == max_threads) 655 pthread_cond_broadcast(&counter_cond); 656 counter--; 657 pthread_mutex_unlock(&counter_mutex); 658 } 659 660 #endif 661 662 struct _work_plan { 663 int s; 664 gss_cred_id_t server_creds; 665 int export; 666 }; 667 668 static void * 669 worker_bee(void *param) 670 { 671 struct _work_plan *work = param; 672 673 /* This return value is not checked, because there's not really anything to 674 * do if it fails. */ 675 sign_server(work->s, work->server_creds, work->export); 676 closesocket(work->s); 677 free(work); 678 679 #if defined _WIN32 || 1 680 if (max_threads > 1) 681 decrement_and_signal_thread_counter(); 682 #endif 683 return NULL; 684 } 685 686 int 687 main(int argc, char **argv) 688 { 689 char *service_name; 690 gss_cred_id_t server_creds; 691 OM_uint32 min_stat; 692 u_short port = 4444; 693 int once = 0; 694 int do_inetd = 0; 695 int export = 0; 696 697 signal(SIGPIPE, SIG_IGN); 698 logfile = stdout; 699 display_file = stdout; 700 argc--; 701 argv++; 702 while (argc) { 703 if (strcmp(*argv, "-port") == 0) { 704 argc--; 705 argv++; 706 if (!argc) 707 usage(); 708 port = atoi(*argv); 709 } else if (strcmp(*argv, "-threads") == 0) { 710 argc--; 711 argv++; 712 if (!argc) 713 usage(); 714 max_threads = atoi(*argv); 715 } else if (strcmp(*argv, "-verbose") == 0) { 716 verbose = 1; 717 } else if (strcmp(*argv, "-once") == 0) { 718 once = 1; 719 } else if (strcmp(*argv, "-inetd") == 0) { 720 do_inetd = 1; 721 } else if (strcmp(*argv, "-export") == 0) { 722 export = 1; 723 } else if (strcmp(*argv, "-logfile") == 0) { 724 argc--; 725 argv++; 726 if (!argc) 727 usage(); 728 /* 729 * Gross hack, but it makes it unnecessary to add an extra argument 730 * to disable logging, and makes the code more efficient because it 731 * doesn't actually write data to /dev/null. 732 */ 733 if (!strcmp(*argv, "/dev/null")) { 734 logfile = display_file = NULL; 735 } else { 736 logfile = fopen(*argv, "a"); 737 display_file = logfile; 738 if (!logfile) { 739 perror(*argv); 740 exit(1); 741 } 742 } 743 } else { 744 break; 745 } 746 argc--; 747 argv++; 748 } 749 if (argc != 1) 750 usage(); 751 752 if ((*argv)[0] == '-') 753 usage(); 754 755 #ifdef _WIN32 756 if (max_threads < 1) { 757 fprintf(stderr, "warning: there must be at least one thread\n"); 758 max_threads = 1; 759 } 760 761 if (max_threads > 1 && do_inetd) { 762 fprintf(stderr, "warning: one thread may be used in conjunction " 763 "with inetd\n"); 764 } 765 766 init_handles(); 767 #endif 768 769 service_name = *argv; 770 771 if (server_acquire_creds(service_name, &server_creds) < 0) 772 return -1; 773 774 if (do_inetd) { 775 close(1); 776 close(2); 777 778 sign_server(0, server_creds, export); 779 close(0); 780 } else { 781 int stmp; 782 783 stmp = create_socket(port); 784 if (stmp >= 0) { 785 do { 786 struct _work_plan * work = malloc(sizeof(struct _work_plan)); 787 788 if (work == NULL) { 789 fprintf(stderr, "fatal error: out of memory"); 790 break; 791 } 792 793 /* Accept a TCP connection */ 794 work->s = accept(stmp, NULL, 0); 795 if (work->s < 0) { 796 perror("accepting connection"); 797 continue; 798 } 799 800 work->server_creds = server_creds; 801 work->export = export; 802 803 if (max_threads == 1) { 804 worker_bee(work); 805 } else { 806 if (wait_and_increment_thread_counter()) { 807 #ifdef _WIN32 808 uintptr_t handle = _beginthread(worker_bee, 0, work); 809 if (handle == (uintptr_t)-1) { 810 closesocket(work->s); 811 free(work); 812 } 813 #else 814 int err; 815 pthread_t thr; 816 err = pthread_create(&thr, 0, worker_bee, work); 817 if (err) { 818 perror("pthread_create"); 819 closesocket(work->s); 820 free(work); 821 } 822 (void)pthread_detach(thr); 823 #endif 824 } else { 825 fprintf(stderr, "fatal error incrementing thread " 826 "counter"); 827 closesocket(work->s); 828 free(work); 829 break; 830 } 831 } 832 } while (!once); 833 834 closesocket(stmp); 835 } 836 } 837 838 (void)gss_release_cred(&min_stat, &server_creds); 839 840 #ifdef _WIN32 841 cleanup_handles(); 842 #else 843 if (max_threads > 1) { 844 while (1) 845 sleep(999999); 846 } 847 #endif 848 849 return 0; 850 } 851