1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
3 * Copyright 1994 by OpenVision Technologies, Inc.
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.
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.
24 * Copyright (C) 2004,2008 by the Massachusetts Institute of Technology.
25 * All rights reserved.
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.
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.
53 #include <sys/types.h>
54 #include <sys/socket.h>
56 #include <netinet/in.h>
66 #include <gssapi/gssapi_generic.h>
68 #include "port-sockets.h"
79 fprintf(stderr, "Usage: gss-server [-port port] [-verbose] [-once]");
81 fprintf(stderr, " [-threads num]");
83 fprintf(stderr, "\n");
84 fprintf(stderr, " [-inetd] [-export] [-logfile file] "
94 * Function: server_acquire_creds
96 * Purpose: imports a service name and acquires credentials for it
100 * service_name (r) the ASCII service name
101 * server_creds (w) the GSS-API service credentials
103 * Returns: 0 on success, -1 on failure
107 * The service name is imported with gss_import_name, and service
108 * credentials are acquired with gss_acquire_cred. If either operation
109 * fails, an error message is displayed and -1 is returned; otherwise,
113 server_acquire_creds(char *service_name, gss_cred_id_t *server_creds)
115 gss_buffer_desc name_buf;
116 gss_name_t server_name;
117 OM_uint32 maj_stat, min_stat;
119 name_buf.value = service_name;
120 name_buf.length = strlen(name_buf.value) + 1;
121 maj_stat = gss_import_name(&min_stat, &name_buf,
122 (gss_OID)gss_nt_service_name, &server_name);
123 if (maj_stat != GSS_S_COMPLETE) {
124 display_status("importing name", maj_stat, min_stat);
128 maj_stat = gss_acquire_cred(&min_stat, server_name, 0,
129 GSS_C_NULL_OID_SET, GSS_C_ACCEPT,
130 server_creds, NULL, NULL);
131 if (maj_stat != GSS_S_COMPLETE) {
132 display_status("acquiring credentials", maj_stat, min_stat);
136 (void)gss_release_name(&min_stat, &server_name);
142 * Function: server_establish_context
144 * Purpose: establishses a GSS-API context as a specified service with
145 * an incoming client, and returns the context handle and associated
150 * s (r) an established TCP connection to the client
151 * service_creds (r) server credentials, from gss_acquire_cred
152 * context (w) the established GSS-API context
153 * client_name (w) the client's ASCII name
155 * Returns: 0 on success, -1 on failure
159 * Any valid client request is accepted. If a context is established,
160 * its handle is returned in context and the client name is returned
161 * in client_name and 0 is returned. If unsuccessful, an error
162 * message is displayed and -1 is returned.
165 server_establish_context(int s, gss_cred_id_t server_creds,
166 gss_ctx_id_t *context, gss_buffer_t client_name,
167 OM_uint32 *ret_flags)
169 gss_buffer_desc send_tok, recv_tok, oid_name;
172 OM_uint32 maj_stat, min_stat, acc_sec_min_stat;
175 if (recv_token(s, &token_flags, &recv_tok) < 0)
178 if (recv_tok.value) {
179 free(recv_tok.value);
180 recv_tok.value = NULL;
183 if (!(token_flags & TOKEN_NOOP)) {
185 fprintf(logfile, "Expected NOOP token, got %d token instead\n",
191 *context = GSS_C_NO_CONTEXT;
193 if (token_flags & TOKEN_CONTEXT_NEXT) {
195 if (recv_token(s, &token_flags, &recv_tok) < 0)
198 if (verbose && logfile) {
199 fprintf(logfile, "Received token (size=%d): \n",
200 (int)recv_tok.length);
201 print_token(&recv_tok);
204 maj_stat = gss_accept_sec_context(&acc_sec_min_stat, context,
205 server_creds, &recv_tok,
206 GSS_C_NO_CHANNEL_BINDINGS,
207 &client, &doid, &send_tok,
208 ret_flags, NULL, NULL);
210 if (recv_tok.value) {
211 free(recv_tok.value);
212 recv_tok.value = NULL;
215 if (send_tok.length != 0) {
216 if (verbose && logfile) {
218 "Sending accept_sec_context token (size=%d):\n",
219 (int)send_tok.length);
220 print_token(&send_tok);
222 if (send_token(s, TOKEN_CONTEXT, &send_tok) < 0) {
224 fprintf(logfile, "failure sending token\n");
228 (void)gss_release_buffer(&min_stat, &send_tok);
230 if (maj_stat != GSS_S_COMPLETE &&
231 maj_stat != GSS_S_CONTINUE_NEEDED) {
232 display_status("accepting context", maj_stat,
234 if (*context != GSS_C_NO_CONTEXT) {
235 gss_delete_sec_context(&min_stat, context,
241 if (verbose && logfile) {
242 if (maj_stat == GSS_S_CONTINUE_NEEDED)
243 fprintf(logfile, "continue needed...\n");
245 fprintf(logfile, "\n");
248 } while (maj_stat == GSS_S_CONTINUE_NEEDED);
250 /* display the flags */
251 display_ctx_flags(*ret_flags);
253 if (verbose && logfile) {
254 maj_stat = gss_oid_to_str(&min_stat, doid, &oid_name);
255 if (maj_stat != GSS_S_COMPLETE) {
256 display_status("converting oid->string", maj_stat, min_stat);
259 fprintf(logfile, "Accepted connection using mechanism OID %.*s.\n",
260 (int)oid_name.length, (char *)oid_name.value);
261 (void)gss_release_buffer(&min_stat, &oid_name);
264 maj_stat = gss_display_name(&min_stat, client, client_name, &doid);
265 if (maj_stat != GSS_S_COMPLETE) {
266 display_status("displaying name", maj_stat, min_stat);
269 maj_stat = gss_release_name(&min_stat, &client);
270 if (maj_stat != GSS_S_COMPLETE) {
271 display_status("releasing name", maj_stat, min_stat);
275 client_name->length = *ret_flags = 0;
278 fprintf(logfile, "Accepted unauthenticated connection.\n");
285 * Function: create_socket
287 * Purpose: Opens a listening TCP socket.
291 * port (r) the port number on which to listen
293 * Returns: the listening socket file descriptor, or -1 on failure
297 * A listening socket on the specified port and created and returned.
298 * On error, an error message is displayed and -1 is returned.
301 create_socket(u_short port)
303 struct sockaddr_in saddr;
306 saddr.sin_family = AF_INET;
307 saddr.sin_port = htons(port);
308 saddr.sin_addr.s_addr = INADDR_ANY;
310 s = socket(AF_INET, SOCK_STREAM, 0);
312 perror("creating socket");
315 /* Let the socket be reused right away. */
316 (void)setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on));
317 if (bind(s, (struct sockaddr *)&saddr, sizeof(saddr)) < 0) {
318 perror("binding socket");
322 if (listen(s, 5) < 0) {
323 perror("listening on socket");
331 timeval_subtract(struct timeval *tv1, struct timeval *tv2)
333 return ((tv1->tv_sec - tv2->tv_sec) +
334 ((float)(tv1->tv_usec - tv2->tv_usec)) / 1000000);
338 * Yes, yes, this isn't the best place for doing this test.
339 * DO NOT REMOVE THIS UNTIL A BETTER TEST HAS BEEN WRITTEN, THOUGH.
343 test_import_export_context(gss_ctx_id_t *context)
345 OM_uint32 min_stat, maj_stat;
346 gss_buffer_desc context_token, copied_token;
347 struct timeval tm1, tm2;
349 /* Attempt to save and then restore the context. */
350 gettimeofday(&tm1, (struct timezone *)0);
351 maj_stat = gss_export_sec_context(&min_stat, context, &context_token);
352 if (maj_stat != GSS_S_COMPLETE) {
353 display_status("exporting context", maj_stat, min_stat);
356 gettimeofday(&tm2, NULL);
357 if (verbose && logfile) {
358 fprintf(logfile, "Exported context: %d bytes, %7.4f seconds\n",
359 (int)context_token.length, timeval_subtract(&tm2, &tm1));
361 copied_token.length = context_token.length;
362 copied_token.value = malloc(context_token.length);
363 if (copied_token.value == 0) {
365 fprintf(logfile, "Couldn't allocate memory to copy context "
370 memcpy(copied_token.value, context_token.value, copied_token.length);
371 maj_stat = gss_import_sec_context(&min_stat, &copied_token, context);
372 if (maj_stat != GSS_S_COMPLETE) {
373 display_status("importing context", maj_stat, min_stat);
376 free(copied_token.value);
377 gettimeofday(&tm1, NULL);
378 if (verbose && logfile) {
379 fprintf(logfile, "Importing context: %7.4f seconds\n",
380 timeval_subtract(&tm1, &tm2));
382 (void)gss_release_buffer(&min_stat, &context_token);
387 * Function: sign_server
389 * Purpose: Performs the "sign" service.
393 * s (r) a TCP socket on which a connection has been
395 * service_name (r) the ASCII name of the GSS-API service to
396 * establish a context as
397 * export (r) whether to test context exporting
399 * Returns: -1 on error
403 * sign_server establishes a context, and performs a single sign request.
405 * A sign request is a single GSS-API sealed token. The token is
406 * unsealed and a signature block, produced with gss_sign, is returned
407 * to the sender. The context is the destroyed and the connection
410 * If any error occurs, -1 is returned.
413 sign_server(int s, gss_cred_id_t server_creds, int export)
415 gss_buffer_desc client_name, xmit_buf, msg_buf;
416 gss_ctx_id_t context;
417 OM_uint32 maj_stat, min_stat, ret_flags;
418 int i, conf_state, token_flags;
421 /* Establish a context with the client */
422 if (server_establish_context(s, server_creds, &context, &client_name,
426 if (context == GSS_C_NO_CONTEXT) {
427 printf("Accepted unauthenticated connection.\n");
429 printf("Accepted connection: \"%.*s\"\n", (int)client_name.length,
430 (char *)client_name.value);
431 (void)gss_release_buffer(&min_stat, &client_name);
434 for (i = 0; i < 3; i++) {
435 if (test_import_export_context(&context))
442 /* Receive the message token */
443 if (recv_token(s, &token_flags, &xmit_buf) < 0)
446 if (token_flags & TOKEN_NOOP) {
448 fprintf(logfile, "NOOP token\n");
449 if (xmit_buf.value) {
450 free(xmit_buf.value);
456 if (verbose && logfile) {
457 fprintf(logfile, "Message token (flags=%d):\n", token_flags);
458 print_token(&xmit_buf);
461 if (context == GSS_C_NO_CONTEXT &&
463 (TOKEN_WRAPPED | TOKEN_ENCRYPTED | TOKEN_SEND_MIC))) {
465 fprintf(logfile, "Unauthenticated client requested "
466 "authenticated services!\n");
468 if (xmit_buf.value) {
469 free(xmit_buf.value);
475 if (token_flags & TOKEN_WRAPPED) {
476 maj_stat = gss_unwrap(&min_stat, context, &xmit_buf, &msg_buf,
478 if (maj_stat != GSS_S_COMPLETE) {
479 display_status("unsealing message", maj_stat, min_stat);
480 if (xmit_buf.value) {
481 free(xmit_buf.value);
485 } else if (!conf_state && (token_flags & TOKEN_ENCRYPTED)) {
486 fprintf(stderr, "Warning! Message not encrypted.\n");
489 if (xmit_buf.value) {
490 free(xmit_buf.value);
498 fprintf(logfile, "Received message: ");
500 if (isprint((unsigned char)cp[0]) &&
501 isprint((unsigned char)cp[1])) {
502 fprintf(logfile, "\"%.*s\"\n", (int)msg_buf.length,
503 (char *)msg_buf.value);
505 fprintf(logfile, "\n");
506 print_token(&msg_buf);
510 if (token_flags & TOKEN_SEND_MIC) {
511 /* Produce a signature block for the message. */
512 maj_stat = gss_get_mic(&min_stat, context, GSS_C_QOP_DEFAULT,
513 &msg_buf, &xmit_buf);
514 if (maj_stat != GSS_S_COMPLETE) {
515 display_status("signing message", maj_stat, min_stat);
524 /* Send the signature block to the client. */
525 if (send_token(s, TOKEN_MIC, &xmit_buf) < 0)
528 if (xmit_buf.value) {
529 free(xmit_buf.value);
537 if (send_token(s, TOKEN_NOOP, empty_token) < 0)
540 } while (1 /* loop will break if NOOP received */);
542 if (context != GSS_C_NO_CONTEXT) {
543 /* Delete context. */
544 maj_stat = gss_delete_sec_context(&min_stat, &context, NULL);
545 if (maj_stat != GSS_S_COMPLETE) {
546 display_status("deleting context", maj_stat, min_stat);
557 static int max_threads = 1;
560 static thread_count = 0;
561 static HANDLE hMutex = NULL;
562 static HANDLE hEvent = NULL;
567 hMutex = CreateMutex(NULL, FALSE, NULL);
568 hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
572 cleanup_handles(void)
579 wait_and_increment_thread_counter(void)
582 if (WaitForSingleObject(hMutex, INFINITE) == WAIT_OBJECT_0) {
583 if (thread_count < max_threads) {
585 ReleaseMutex(hMutex);
588 ReleaseMutex(hMutex);
590 if (WaitForSingleObject(hEvent, INFINITE) == WAIT_OBJECT_0)
602 decrement_and_signal_thread_counter(void)
604 if (WaitForSingleObject(hMutex, INFINITE) == WAIT_OBJECT_0) {
605 if (thread_count == max_threads)
608 ReleaseMutex(hMutex);
615 #else /* assume pthread */
617 static pthread_mutex_t counter_mutex = PTHREAD_MUTEX_INITIALIZER;
618 static pthread_cond_t counter_cond = PTHREAD_COND_INITIALIZER;
622 wait_and_increment_thread_counter(void)
626 err = pthread_mutex_lock(&counter_mutex);
628 perror("pthread_mutex_lock");
631 if (counter == max_threads) {
632 err = pthread_cond_wait(&counter_cond, &counter_mutex);
634 pthread_mutex_unlock(&counter_mutex);
635 perror("pthread_cond_wait");
640 pthread_mutex_unlock(&counter_mutex);
645 decrement_and_signal_thread_counter(void)
649 err = pthread_mutex_lock(&counter_mutex);
651 perror("pthread_mutex_lock");
654 if (counter == max_threads)
655 pthread_cond_broadcast(&counter_cond);
657 pthread_mutex_unlock(&counter_mutex);
664 gss_cred_id_t server_creds;
669 worker_bee(void *param)
671 struct _work_plan *work = param;
673 /* This return value is not checked, because there's not really anything to
675 sign_server(work->s, work->server_creds, work->export);
676 closesocket(work->s);
679 #if defined _WIN32 || 1
681 decrement_and_signal_thread_counter();
687 main(int argc, char **argv)
690 gss_cred_id_t server_creds;
697 signal(SIGPIPE, SIG_IGN);
699 display_file = stdout;
703 if (strcmp(*argv, "-port") == 0) {
709 } else if (strcmp(*argv, "-threads") == 0) {
714 max_threads = atoi(*argv);
715 } else if (strcmp(*argv, "-verbose") == 0) {
717 } else if (strcmp(*argv, "-once") == 0) {
719 } else if (strcmp(*argv, "-inetd") == 0) {
721 } else if (strcmp(*argv, "-export") == 0) {
723 } else if (strcmp(*argv, "-logfile") == 0) {
729 * Gross hack, but it makes it unnecessary to add an extra argument
730 * to disable logging, and makes the code more efficient because it
731 * doesn't actually write data to /dev/null.
733 if (!strcmp(*argv, "/dev/null")) {
734 logfile = display_file = NULL;
736 logfile = fopen(*argv, "a");
737 display_file = logfile;
752 if ((*argv)[0] == '-')
756 if (max_threads < 1) {
757 fprintf(stderr, "warning: there must be at least one thread\n");
761 if (max_threads > 1 && do_inetd) {
762 fprintf(stderr, "warning: one thread may be used in conjunction "
769 service_name = *argv;
771 if (server_acquire_creds(service_name, &server_creds) < 0)
778 sign_server(0, server_creds, export);
783 stmp = create_socket(port);
786 struct _work_plan * work = malloc(sizeof(struct _work_plan));
789 fprintf(stderr, "fatal error: out of memory");
793 /* Accept a TCP connection */
794 work->s = accept(stmp, NULL, 0);
796 perror("accepting connection");
800 work->server_creds = server_creds;
801 work->export = export;
803 if (max_threads == 1) {
806 if (wait_and_increment_thread_counter()) {
808 uintptr_t handle = _beginthread(worker_bee, 0, work);
809 if (handle == (uintptr_t)-1) {
810 closesocket(work->s);
816 err = pthread_create(&thr, 0, worker_bee, work);
818 perror("pthread_create");
819 closesocket(work->s);
822 (void)pthread_detach(thr);
825 fprintf(stderr, "fatal error incrementing thread "
827 closesocket(work->s);
838 (void)gss_release_cred(&min_stat, &server_creds);
843 if (max_threads > 1) {