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
usage()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
server_acquire_creds(char * service_name,gss_cred_id_t * server_creds)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
server_establish_context(int s,gss_cred_id_t server_creds,gss_ctx_id_t * context,gss_buffer_t client_name,OM_uint32 * ret_flags)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
create_socket(u_short port)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
timeval_subtract(struct timeval * tv1,struct timeval * tv2)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
test_import_export_context(gss_ctx_id_t * context)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
sign_server(int s,gss_cred_id_t server_creds,int export)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
init_handles(void)565 init_handles(void)
566 {
567 hMutex = CreateMutex(NULL, FALSE, NULL);
568 hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
569 }
570
571 void
cleanup_handles(void)572 cleanup_handles(void)
573 {
574 CloseHandle(hMutex);
575 CloseHandle(hEvent);
576 }
577
578 BOOL
wait_and_increment_thread_counter(void)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
decrement_and_signal_thread_counter(void)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
wait_and_increment_thread_counter(void)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
decrement_and_signal_thread_counter(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 *
worker_bee(void * param)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
main(int argc,char ** argv)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