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