xref: /freebsd/crypto/krb5/src/appl/gss-sample/gss-server.c (revision 7f2fe78b9dd5f51c821d771b63d2e096f6fd49e9)
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,2005 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 <stdio.h>
48 #ifdef _WIN32
49 #include <windows.h>
50 #include <winsock.h>
51 #else
52 #include "port-sockets.h"
53 #endif
54 #ifdef HAVE_UNISTD_H
55 #include <unistd.h>
56 #endif
57 #include <stdlib.h>
58 #include <ctype.h>
59 
60 #include <gssapi/gssapi_generic.h>
61 #include <gssapi/gssapi_krb5.h>
62 #include "gss-misc.h"
63 
64 #ifdef HAVE_STRING_H
65 #include <string.h>
66 #else
67 #include <strings.h>
68 #endif
69 
70 static OM_uint32
71 enumerateAttributes(OM_uint32 *minor, gss_name_t name, int noisy);
72 static OM_uint32
73 showLocalIdentity(OM_uint32 *minor, gss_name_t name);
74 
75 static void
usage()76 usage()
77 {
78     fprintf(stderr, "Usage: gss-server [-port port] [-verbose] [-once]");
79 #ifdef _WIN32
80     fprintf(stderr, " [-threads num]");
81 #endif
82     fprintf(stderr, "\n");
83     fprintf(stderr,
84             "       [-inetd] [-export] [-logfile file] [-keytab keytab]\n"
85             "       service_name\n");
86     exit(1);
87 }
88 
89 static 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  *      mech            (r) the desired mechanism (or GSS_C_NO_OID)
102  *      server_creds    (w) the GSS-API service credentials
103  *
104  * Returns: 0 on success, -1 on failure
105  *
106  * Effects:
107  *
108  * The service name is imported with gss_import_name, and service
109  * credentials are acquired with gss_acquire_cred.  If either operation
110  * fails, an error message is displayed and -1 is returned; otherwise,
111  * 0 is returned.  If mech is given, credentials are acquired for the
112  * specified mechanism.
113  */
114 
115 static int
server_acquire_creds(char * service_name,gss_OID mech,gss_cred_id_t * server_creds)116 server_acquire_creds(char *service_name, gss_OID mech,
117                      gss_cred_id_t *server_creds)
118 {
119     gss_buffer_desc name_buf;
120     gss_name_t server_name;
121     OM_uint32 maj_stat, min_stat;
122     gss_OID_set_desc mechlist;
123     gss_OID_set mechs = GSS_C_NO_OID_SET;
124 
125     name_buf.value = service_name;
126     name_buf.length = strlen(name_buf.value) + 1;
127     maj_stat = gss_import_name(&min_stat, &name_buf,
128                                (gss_OID) gss_nt_service_name, &server_name);
129     if (maj_stat != GSS_S_COMPLETE) {
130         display_status("importing name", maj_stat, min_stat);
131         return -1;
132     }
133 
134     if (mech != GSS_C_NO_OID) {
135         mechlist.count = 1;
136         mechlist.elements = mech;
137         mechs = &mechlist;
138     }
139     maj_stat = gss_acquire_cred(&min_stat, server_name, 0, mechs, GSS_C_ACCEPT,
140                                 server_creds, NULL, NULL);
141     if (maj_stat != GSS_S_COMPLETE) {
142         display_status("acquiring credentials", maj_stat, min_stat);
143         return -1;
144     }
145 
146     (void) gss_release_name(&min_stat, &server_name);
147 
148     return 0;
149 }
150 
151 /*
152  * Function: server_establish_context
153  *
154  * Purpose: establishses a GSS-API context as a specified service with
155  * an incoming client, and returns the context handle and associated
156  * client name
157  *
158  * Arguments:
159  *
160  *      s               (r) an established TCP connection to the client
161  *      service_creds   (r) server credentials, from gss_acquire_cred
162  *      context         (w) the established GSS-API context
163  *      client_name     (w) the client's ASCII name
164  *
165  * Returns: 0 on success, -1 on failure
166  *
167  * Effects:
168  *
169  * Any valid client request is accepted.  If a context is established,
170  * its handle is returned in context and the client name is returned
171  * in client_name and 0 is returned.  If unsuccessful, an error
172  * message is displayed and -1 is returned.
173  */
174 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)175 server_establish_context(int s, gss_cred_id_t server_creds,
176                          gss_ctx_id_t *context, gss_buffer_t client_name,
177                          OM_uint32 *ret_flags)
178 {
179     gss_buffer_desc send_tok, recv_tok;
180     gss_name_t client;
181     gss_OID doid;
182     OM_uint32 maj_stat, min_stat, acc_sec_min_stat;
183     gss_buffer_desc oid_name;
184     int     token_flags;
185 
186     if (recv_token(s, &token_flags, &recv_tok) < 0)
187         return -1;
188 
189     if (recv_tok.value) {
190         free(recv_tok.value);
191         recv_tok.value = NULL;
192     }
193 
194     if (!(token_flags & TOKEN_NOOP)) {
195         if (logfile)
196             fprintf(logfile, "Expected NOOP token, got %d token instead\n",
197                     token_flags);
198         return -1;
199     }
200 
201     *context = GSS_C_NO_CONTEXT;
202 
203     if (token_flags & TOKEN_CONTEXT_NEXT) {
204         do {
205             if (recv_token(s, &token_flags, &recv_tok) < 0)
206                 return -1;
207 
208             if (verbose && logfile) {
209                 fprintf(logfile, "Received token (size=%d): \n",
210                         (int) recv_tok.length);
211                 print_token(&recv_tok);
212             }
213 
214             maj_stat = gss_accept_sec_context(&acc_sec_min_stat, context,
215                                               server_creds, &recv_tok,
216                                               GSS_C_NO_CHANNEL_BINDINGS,
217                                               &client, &doid, &send_tok,
218                                               ret_flags,
219                                               NULL,  /* time_rec */
220                                               NULL); /* del_cred_handle */
221 
222             if (recv_tok.value) {
223                 free(recv_tok.value);
224                 recv_tok.value = NULL;
225             }
226 
227             if (send_tok.length != 0) {
228                 if (verbose && logfile) {
229                     fprintf(logfile,
230                             "Sending accept_sec_context token (size=%d):\n",
231                             (int) send_tok.length);
232                     print_token(&send_tok);
233                 }
234                 if (send_token(s, TOKEN_CONTEXT, &send_tok) < 0) {
235                     if (logfile)
236                         fprintf(logfile, "failure sending token\n");
237                     return -1;
238                 }
239 
240                 (void) gss_release_buffer(&min_stat, &send_tok);
241             }
242             if (maj_stat != GSS_S_COMPLETE
243                 && maj_stat != GSS_S_CONTINUE_NEEDED) {
244                 display_status("accepting context", maj_stat,
245                                acc_sec_min_stat);
246                 if (*context != GSS_C_NO_CONTEXT)
247                     gss_delete_sec_context(&min_stat, context,
248                                            GSS_C_NO_BUFFER);
249                 return -1;
250             }
251 
252             if (verbose && logfile) {
253                 if (maj_stat == GSS_S_CONTINUE_NEEDED)
254                     fprintf(logfile, "continue needed...\n");
255                 else
256                     fprintf(logfile, "\n");
257                 fflush(logfile);
258             }
259         } while (maj_stat == GSS_S_CONTINUE_NEEDED);
260 
261         /* display the flags */
262         display_ctx_flags(*ret_flags);
263 
264         if (verbose && logfile) {
265             maj_stat = gss_oid_to_str(&min_stat, doid, &oid_name);
266             if (maj_stat != GSS_S_COMPLETE) {
267                 display_status("converting oid->string", maj_stat, min_stat);
268                 return -1;
269             }
270             fprintf(logfile, "Accepted connection using mechanism OID %.*s.\n",
271                     (int) oid_name.length, (char *) oid_name.value);
272             (void) gss_release_buffer(&min_stat, &oid_name);
273         }
274 
275         maj_stat = gss_display_name(&min_stat, client, client_name, &doid);
276         if (maj_stat != GSS_S_COMPLETE) {
277             display_status("displaying name", maj_stat, min_stat);
278             return -1;
279         }
280         enumerateAttributes(&min_stat, client, TRUE);
281         showLocalIdentity(&min_stat, client);
282         maj_stat = gss_release_name(&min_stat, &client);
283         if (maj_stat != GSS_S_COMPLETE) {
284             display_status("releasing name", maj_stat, min_stat);
285             return -1;
286         }
287     } else {
288         client_name->length = *ret_flags = 0;
289 
290         if (logfile)
291             fprintf(logfile, "Accepted unauthenticated connection.\n");
292     }
293 
294     return 0;
295 }
296 
297 /*
298  * Function: create_socket
299  *
300  * Purpose: Opens a listening TCP socket.
301  *
302  * Arguments:
303  *
304  *      port            (r) the port number on which to listen
305  *
306  * Returns: the listening socket file descriptor, or -1 on failure
307  *
308  * Effects:
309  *
310  * A listening socket on the specified port and created and returned.
311  * On error, an error message is displayed and -1 is returned.
312  */
313 static int
create_socket(u_short port)314 create_socket(u_short port)
315 {
316     struct sockaddr_in saddr;
317     int     s;
318     int     on = 1;
319 
320     saddr.sin_family = AF_INET;
321     saddr.sin_port = htons(port);
322     saddr.sin_addr.s_addr = INADDR_ANY;
323 
324     if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
325         perror("creating socket");
326         return -1;
327     }
328     /* Let the socket be reused right away */
329     (void) setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof(on));
330     if (bind(s, (struct sockaddr *) &saddr, sizeof(saddr)) < 0) {
331         perror("binding socket");
332         (void) closesocket(s);
333         return -1;
334     }
335     if (listen(s, 5) < 0) {
336         perror("listening on socket");
337         (void) closesocket(s);
338         return -1;
339     }
340     return s;
341 }
342 
343 static float
timeval_subtract(struct timeval * tv1,struct timeval * tv2)344 timeval_subtract(struct timeval *tv1, struct timeval *tv2)
345 {
346     return ((tv1->tv_sec - tv2->tv_sec) +
347             ((float) (tv1->tv_usec - tv2->tv_usec)) / 1000000);
348 }
349 
350 /*
351  * Yes, yes, this isn't the best place for doing this test.
352  * DO NOT REMOVE THIS UNTIL A BETTER TEST HAS BEEN WRITTEN, THOUGH.
353  *                                      -TYT
354  */
355 static int
test_import_export_context(gss_ctx_id_t * context)356 test_import_export_context(gss_ctx_id_t *context)
357 {
358     OM_uint32 min_stat, maj_stat;
359     gss_buffer_desc context_token, copied_token;
360     struct timeval tm1, tm2;
361 
362     /*
363      * Attempt to save and then restore the context.
364      */
365     gettimeofday(&tm1, (struct timezone *) 0);
366     maj_stat = gss_export_sec_context(&min_stat, context, &context_token);
367     if (maj_stat != GSS_S_COMPLETE) {
368         display_status("exporting context", maj_stat, min_stat);
369         return 1;
370     }
371     gettimeofday(&tm2, (struct timezone *) 0);
372     if (verbose && logfile)
373         fprintf(logfile, "Exported context: %d bytes, %7.4f seconds\n",
374                 (int) context_token.length, timeval_subtract(&tm2, &tm1));
375     copied_token.length = context_token.length;
376     copied_token.value = malloc(context_token.length);
377     if (copied_token.value == 0) {
378         if (logfile)
379             fprintf(logfile,
380                     "Couldn't allocate memory to copy context token.\n");
381         return 1;
382     }
383     memcpy(copied_token.value, context_token.value, copied_token.length);
384     maj_stat = gss_import_sec_context(&min_stat, &copied_token, context);
385     if (maj_stat != GSS_S_COMPLETE) {
386         display_status("importing context", maj_stat, min_stat);
387         return 1;
388     }
389     free(copied_token.value);
390     gettimeofday(&tm1, (struct timezone *) 0);
391     if (verbose && logfile)
392         fprintf(logfile, "Importing context: %7.4f seconds\n",
393                 timeval_subtract(&tm1, &tm2));
394     (void) gss_release_buffer(&min_stat, &context_token);
395     return 0;
396 }
397 
398 /*
399  * Function: sign_server
400  *
401  * Purpose: Performs the "sign" service.
402  *
403  * Arguments:
404  *
405  *      s               (r) a TCP socket on which a connection has been
406  *                      accept()ed
407  *      service_name    (r) the ASCII name of the GSS-API service to
408  *                      establish a context as
409  *      export          (r) whether to test context exporting
410  *
411  * Returns: -1 on error
412  *
413  * Effects:
414  *
415  * sign_server establishes a context, and performs a single sign request.
416  *
417  * A sign request is a single GSS-API sealed token.  The token is
418  * unsealed and a signature block, produced with gss_sign, is returned
419  * to the sender.  The context is the destroyed and the connection
420  * closed.
421  *
422  * If any error occurs, -1 is returned.
423  */
424 static int
sign_server(int s,gss_cred_id_t server_creds,int export)425 sign_server(int s, gss_cred_id_t server_creds, int export)
426 {
427     gss_buffer_desc client_name, recv_buf, unwrap_buf, mic_buf, *msg_buf, *send_buf;
428     gss_ctx_id_t context;
429     OM_uint32 maj_stat, min_stat;
430     int     i, conf_state;
431     OM_uint32 ret_flags;
432     char   *cp;
433     int     token_flags;
434     int     send_flags;
435 
436     /* Establish a context with the client */
437     if (server_establish_context(s, server_creds, &context,
438                                  &client_name, &ret_flags) < 0)
439         return (-1);
440 
441     if (context == GSS_C_NO_CONTEXT) {
442         printf("Accepted unauthenticated connection.\n");
443     } else {
444         printf("Accepted connection: \"%.*s\"\n",
445                (int) client_name.length, (char *) client_name.value);
446         (void) gss_release_buffer(&min_stat, &client_name);
447 
448         if (export) {
449             for (i = 0; i < 3; i++)
450                 if (test_import_export_context(&context))
451                     return -1;
452         }
453     }
454 
455     do {
456         /* Receive the message token */
457         if (recv_token(s, &token_flags, &recv_buf) < 0)
458             return (-1);
459 
460         if (token_flags & TOKEN_NOOP) {
461             if (logfile)
462                 fprintf(logfile, "NOOP token\n");
463             if (recv_buf.value) {
464                 free(recv_buf.value);
465                 recv_buf.value = 0;
466             }
467             break;
468         }
469 
470         if (verbose && logfile) {
471             fprintf(logfile, "Message token (flags=%d):\n", token_flags);
472             print_token(&recv_buf);
473         }
474 
475         if ((context == GSS_C_NO_CONTEXT) &&
476             (token_flags & (TOKEN_WRAPPED | TOKEN_ENCRYPTED | TOKEN_SEND_MIC)))
477         {
478             if (logfile)
479                 fprintf(logfile,
480                         "Unauthenticated client requested authenticated services!\n");
481             if (recv_buf.value) {
482                 free(recv_buf.value);
483                 recv_buf.value = 0;
484             }
485             return (-1);
486         }
487 
488         if (token_flags & TOKEN_WRAPPED) {
489             maj_stat = gss_unwrap(&min_stat, context, &recv_buf, &unwrap_buf,
490                                   &conf_state, (gss_qop_t *) NULL);
491             if (maj_stat != GSS_S_COMPLETE) {
492                 display_status("unsealing message", maj_stat, min_stat);
493                 if (recv_buf.value) {
494                     free(recv_buf.value);
495                     recv_buf.value = 0;
496                 }
497                 return (-1);
498             } else if (!conf_state && (token_flags & TOKEN_ENCRYPTED)) {
499                 fprintf(stderr, "Warning!  Message not encrypted.\n");
500             }
501 
502             if (recv_buf.value) {
503                 free(recv_buf.value);
504                 recv_buf.value = 0;
505             }
506             msg_buf = &unwrap_buf;
507         } else {
508             unwrap_buf.value = NULL;
509             unwrap_buf.length = 0;
510             msg_buf = &recv_buf;
511         }
512 
513         if (logfile) {
514             fprintf(logfile, "Received message: ");
515             cp = msg_buf->value;
516             if ((isprint((int) cp[0]) || isspace((int) cp[0])) &&
517                 (isprint((int) cp[1]) || isspace((int) cp[1]))) {
518                 fprintf(logfile, "\"%.*s\"\n", (int) msg_buf->length,
519                         (char *) msg_buf->value);
520             } else {
521                 fprintf(logfile, "\n");
522                 print_token(msg_buf);
523             }
524         }
525 
526         if (token_flags & TOKEN_SEND_MIC) {
527             /* Produce a signature block for the message */
528             maj_stat = gss_get_mic(&min_stat, context, GSS_C_QOP_DEFAULT,
529                                    msg_buf, &mic_buf);
530             if (maj_stat != GSS_S_COMPLETE) {
531                 display_status("signing message", maj_stat, min_stat);
532                 return (-1);
533             }
534             send_flags = TOKEN_MIC;
535             send_buf = &mic_buf;
536         } else {
537             mic_buf.value = NULL;
538             mic_buf.length = 0;
539             send_flags = TOKEN_NOOP;
540             send_buf = empty_token;
541         }
542         if (recv_buf.value) {
543             free(recv_buf.value);
544             recv_buf.value = NULL;
545         }
546         if (unwrap_buf.value) {
547             gss_release_buffer(&min_stat, &unwrap_buf);
548         }
549 
550         /* Send the signature block or NOOP to the client */
551         if (send_token(s, send_flags, send_buf) < 0)
552             return (-1);
553 
554         if (mic_buf.value) {
555             gss_release_buffer(&min_stat, &mic_buf);
556         }
557     } while (1 /* loop will break if NOOP received */ );
558 
559     if (context != GSS_C_NO_CONTEXT) {
560         /* Delete context */
561         maj_stat = gss_delete_sec_context(&min_stat, &context, NULL);
562         if (maj_stat != GSS_S_COMPLETE) {
563             display_status("deleting context", maj_stat, min_stat);
564             return (-1);
565         }
566     }
567 
568     if (logfile)
569         fflush(logfile);
570 
571     return (0);
572 }
573 
574 static int max_threads = 1;
575 
576 #ifdef _WIN32
577 static  thread_count = 0;
578 static HANDLE hMutex = NULL;
579 static HANDLE hEvent = NULL;
580 
581 void
InitHandles(void)582 InitHandles(void)
583 {
584     hMutex = CreateMutex(NULL, FALSE, NULL);
585     hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
586 }
587 
588 void
CleanupHandles(void)589 CleanupHandles(void)
590 {
591     CloseHandle(hMutex);
592     CloseHandle(hEvent);
593 }
594 
595 BOOL
WaitAndIncrementThreadCounter(void)596 WaitAndIncrementThreadCounter(void)
597 {
598     for (;;) {
599         if (WaitForSingleObject(hMutex, INFINITE) == WAIT_OBJECT_0) {
600             if (thread_count < max_threads) {
601                 thread_count++;
602                 ReleaseMutex(hMutex);
603                 return TRUE;
604             } else {
605                 ReleaseMutex(hMutex);
606 
607                 if (WaitForSingleObject(hEvent, INFINITE) == WAIT_OBJECT_0) {
608                     continue;
609                 } else {
610                     return FALSE;
611                 }
612             }
613         } else {
614             return FALSE;
615         }
616     }
617 }
618 
619 BOOL
DecrementAndSignalThreadCounter(void)620 DecrementAndSignalThreadCounter(void)
621 {
622     if (WaitForSingleObject(hMutex, INFINITE) == WAIT_OBJECT_0) {
623         if (thread_count == max_threads)
624             ResetEvent(hEvent);
625         thread_count--;
626         ReleaseMutex(hMutex);
627         return TRUE;
628     } else {
629         return FALSE;
630     }
631 }
632 #endif
633 
634 struct _work_plan
635 {
636     int     s;
637     gss_cred_id_t server_creds;
638     int     export;
639 };
640 
641 static void
worker_bee(void * param)642 worker_bee(void *param)
643 {
644     struct _work_plan *work = (struct _work_plan *) param;
645 
646     /* this return value is not checked, because there's
647      * not really anything to do if it fails
648      */
649     sign_server(work->s, work->server_creds, work->export);
650     closesocket(work->s);
651     free(work);
652 
653 #ifdef _WIN32
654     if (max_threads > 1)
655         DecrementAndSignalThreadCounter();
656 #endif
657 }
658 
659 int
main(int argc,char ** argv)660 main(int argc, char **argv)
661 {
662     char   *service_name;
663     gss_cred_id_t server_creds;
664     gss_OID mech = GSS_C_NO_OID;
665     OM_uint32 min_stat;
666     u_short port = 4444;
667     int     once = 0;
668     int     do_inetd = 0;
669     int     export = 0;
670 
671     logfile = stdout;
672     display_file = stdout;
673     argc--;
674     argv++;
675     while (argc) {
676         if (strcmp(*argv, "-port") == 0) {
677             argc--;
678             argv++;
679             if (!argc)
680                 usage();
681             port = atoi(*argv);
682         }
683 #ifdef _WIN32
684         else if (strcmp(*argv, "-threads") == 0) {
685             argc--;
686             argv++;
687             if (!argc)
688                 usage();
689             max_threads = atoi(*argv);
690         }
691 #endif
692         else if (strcmp(*argv, "-verbose") == 0) {
693             verbose = 1;
694         } else if (strcmp(*argv, "-once") == 0) {
695             once = 1;
696         } else if (strcmp(*argv, "-inetd") == 0) {
697             do_inetd = 1;
698         } else if (strcmp(*argv, "-export") == 0) {
699             export = 1;
700         } else if (strcmp(*argv, "-logfile") == 0) {
701             argc--;
702             argv++;
703             if (!argc)
704                 usage();
705             /* Gross hack, but it makes it unnecessary to add an
706              * extra argument to disable logging, and makes the code
707              * more efficient because it doesn't actually write data
708              * to /dev/null. */
709             if (!strcmp(*argv, "/dev/null")) {
710                 logfile = display_file = NULL;
711             } else {
712                 logfile = fopen(*argv, "a");
713                 display_file = logfile;
714                 if (!logfile) {
715                     perror(*argv);
716                     exit(1);
717                 }
718             }
719         } else if (strcmp(*argv, "-keytab") == 0) {
720             argc--;
721             argv++;
722             if (!argc)
723                 usage();
724             if (krb5_gss_register_acceptor_identity(*argv)) {
725                 fprintf(stderr, "failed to register keytab\n");
726                 exit(1);
727             }
728         } else if (strcmp(*argv, "-iakerb") == 0) {
729             mech = (gss_OID)gss_mech_iakerb;
730         } else
731             break;
732         argc--;
733         argv++;
734     }
735     if (argc != 1)
736         usage();
737 
738     if ((*argv)[0] == '-')
739         usage();
740 
741 #ifdef _WIN32
742     if (max_threads < 1) {
743         fprintf(stderr, "warning: there must be at least one thread\n");
744         max_threads = 1;
745     }
746 
747     if (max_threads > 1 && do_inetd)
748         fprintf(stderr,
749                 "warning: one thread may be used in conjunction with inetd\n");
750 
751     InitHandles();
752 #endif
753 
754     service_name = *argv;
755 
756     if (server_acquire_creds(service_name, mech, &server_creds) < 0)
757         return -1;
758 
759     if (do_inetd) {
760         close(1);
761         close(2);
762 
763         sign_server(0, server_creds, export);
764         close(0);
765     } else {
766         int     stmp;
767 
768         if ((stmp = create_socket(port)) >= 0) {
769             fprintf(stderr, "starting...\n");
770 
771             do {
772                 struct _work_plan *work = malloc(sizeof(struct _work_plan));
773 
774                 if (work == NULL) {
775                     fprintf(stderr, "fatal error: out of memory");
776                     break;
777                 }
778 
779                 /* Accept a TCP connection */
780                 if ((work->s = accept(stmp, NULL, 0)) < 0) {
781                     perror("accepting connection");
782                     free(work);
783                     continue;
784                 }
785 
786                 work->server_creds = server_creds;
787                 work->export = export;
788 
789                 if (max_threads == 1) {
790                     worker_bee((void *) work);
791                 }
792 #ifdef _WIN32
793                 else {
794                     if (WaitAndIncrementThreadCounter()) {
795                         uintptr_t handle =
796                             _beginthread(worker_bee, 0, (void *) work);
797                         if (handle == (uintptr_t) - 1) {
798                             closesocket(work->s);
799                             free(work);
800                         }
801                     } else {
802                         fprintf(stderr,
803                                 "fatal error incrementing thread counter");
804                         closesocket(work->s);
805                         free(work);
806                         break;
807                     }
808                 }
809 #endif
810             } while (!once);
811 
812             closesocket(stmp);
813         }
814     }
815 
816     (void) gss_release_cred(&min_stat, &server_creds);
817 
818 #ifdef _WIN32
819     CleanupHandles();
820 #endif
821 
822     return 0;
823 }
824 
825 static void
dumpAttribute(OM_uint32 * minor,gss_name_t name,gss_buffer_t attribute,int noisy)826 dumpAttribute(OM_uint32 *minor,
827               gss_name_t name,
828               gss_buffer_t attribute,
829               int noisy)
830 {
831     OM_uint32 major, tmp;
832     gss_buffer_desc value;
833     gss_buffer_desc display_value;
834     int authenticated = 0;
835     int complete = 0;
836     int more = -1;
837     unsigned int i;
838 
839     while (more != 0) {
840         value.value = NULL;
841         display_value.value = NULL;
842 
843         major = gss_get_name_attribute(minor, name, attribute, &authenticated,
844                                        &complete, &value, &display_value,
845                                        &more);
846         if (GSS_ERROR(major)) {
847             display_status("gss_get_name_attribute", major, *minor);
848             break;
849         }
850 
851         printf("Attribute %.*s %s %s\n\n%.*s\n",
852                (int)attribute->length, (char *)attribute->value,
853                authenticated ? "Authenticated" : "",
854                complete ? "Complete" : "",
855                (int)display_value.length, (char *)display_value.value);
856 
857         if (noisy) {
858             for (i = 0; i < value.length; i++) {
859                 if ((i % 32) == 0)
860                     printf("\n");
861                 printf("%02x", ((char *)value.value)[i] & 0xFF);
862             }
863             printf("\n\n");
864         }
865 
866         gss_release_buffer(&tmp, &value);
867         gss_release_buffer(&tmp, &display_value);
868     }
869 }
870 
871 static OM_uint32
enumerateAttributes(OM_uint32 * minor,gss_name_t name,int noisy)872 enumerateAttributes(OM_uint32 *minor,
873                     gss_name_t name,
874                     int noisy)
875 {
876     OM_uint32 major, tmp;
877     int name_is_MN;
878     gss_OID mech = GSS_C_NO_OID;
879     gss_buffer_set_t attrs = GSS_C_NO_BUFFER_SET;
880     unsigned int i;
881 
882     major = gss_inquire_name(minor, name, &name_is_MN, &mech, &attrs);
883     if (GSS_ERROR(major)) {
884         display_status("gss_inquire_name", major, *minor);
885         return major;
886     }
887 
888     if (attrs != GSS_C_NO_BUFFER_SET) {
889         for (i = 0; i < attrs->count; i++)
890             dumpAttribute(minor, name, &attrs->elements[i], noisy);
891     }
892 
893     gss_release_oid(&tmp, &mech);
894     gss_release_buffer_set(&tmp, &attrs);
895 
896     return major;
897 }
898 
899 static OM_uint32
showLocalIdentity(OM_uint32 * minor,gss_name_t name)900 showLocalIdentity(OM_uint32 *minor, gss_name_t name)
901 {
902     OM_uint32 major;
903     gss_buffer_desc buf;
904 
905     major = gss_localname(minor, name, GSS_C_NO_OID, &buf);
906     if (major == GSS_S_COMPLETE)
907         printf("localname: %-*s\n", (int)buf.length, (char *)buf.value);
908     else if (major != GSS_S_UNAVAILABLE)
909         display_status("gss_localname", major, *minor);
910     gss_release_buffer(minor, &buf);
911     return major;
912 }
913