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