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