a9f980edb214c555978a83b4a98ec7de505615e3
[platform/upstream/krb5.git] / src / tests / gss-threads / gss-server.c
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 }