Path: blob/main/crypto/krb5/src/tests/gss-threads/gss-server.c
34914 views
/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */1/*2* Copyright 1994 by OpenVision Technologies, Inc.3*4* Permission to use, copy, modify, distribute, and sell this software5* and its documentation for any purpose is hereby granted without fee,6* provided that the above copyright notice appears in all copies and7* that both that copyright notice and this permission notice appear in8* supporting documentation, and that the name of OpenVision not be used9* in advertising or publicity pertaining to distribution of the software10* without specific, written prior permission. OpenVision makes no11* representations about the suitability of this software for any12* purpose. It is provided "as is" without express or implied warranty.13*14* OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,15* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO16* EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR17* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF18* USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR19* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR20* PERFORMANCE OF THIS SOFTWARE.21*/22/*23* Copyright (C) 2004,2008 by the Massachusetts Institute of Technology.24* All rights reserved.25*26* Export of this software from the United States of America may27* require a specific license from the United States Government.28* It is the responsibility of any person or organization contemplating29* export to obtain such a license before exporting.30*31* WITHIN THAT CONSTRAINT, permission to use, copy, modify, and32* distribute this software and its documentation for any purpose and33* without fee is hereby granted, provided that the above copyright34* notice appear in all copies and that both that copyright notice and35* this permission notice appear in supporting documentation, and that36* the name of M.I.T. not be used in advertising or publicity pertaining37* to distribution of the software without specific, written prior38* permission. Furthermore if you modify this software you must label39* your software as modified software and not distribute it in such a40* fashion that it might be confused with the original M.I.T. software.41* M.I.T. makes no representations about the suitability of42* this software for any purpose. It is provided "as is" without express43* or implied warranty.44*/4546#include "autoconf.h"47#include <stdio.h>48#ifdef _WIN3249#include <windows.h>50#include <winsock.h>51#else52#include <sys/types.h>53#include <sys/socket.h>54#include <sys/time.h>55#include <netinet/in.h>56#include <pthread.h>57#include <signal.h>58#endif59#ifdef HAVE_UNISTD_H60#include <unistd.h>61#endif62#include <stdlib.h>63#include <ctype.h>6465#include <gssapi/gssapi_generic.h>66#include "gss-misc.h"67#include "port-sockets.h"6869#ifdef HAVE_STRING_H70#include <string.h>71#else72#include <strings.h>73#endif7475static void76usage(void)77{78fprintf(stderr, "Usage: gss-server [-port port] [-verbose] [-once]");79#ifdef _WIN3280fprintf(stderr, " [-threads num]");81#endif82fprintf(stderr, "\n");83fprintf(stderr, " [-inetd] [-export] [-logfile file] "84"service_name\n");85exit(1);86}8788FILE *logfile;8990int verbose = 0;9192/*93* Function: server_acquire_creds94*95* Purpose: imports a service name and acquires credentials for it96*97* Arguments:98*99* service_name (r) the ASCII service name100* server_creds (w) the GSS-API service credentials101*102* Returns: 0 on success, -1 on failure103*104* Effects:105*106* The service name is imported with gss_import_name, and service107* credentials are acquired with gss_acquire_cred. If either operation108* fails, an error message is displayed and -1 is returned; otherwise,109* 0 is returned.110*/111static int112server_acquire_creds(char *service_name, gss_cred_id_t *server_creds)113{114gss_buffer_desc name_buf;115gss_name_t server_name;116OM_uint32 maj_stat, min_stat;117118name_buf.value = service_name;119name_buf.length = strlen(name_buf.value) + 1;120maj_stat = gss_import_name(&min_stat, &name_buf,121(gss_OID)gss_nt_service_name, &server_name);122if (maj_stat != GSS_S_COMPLETE) {123display_status("importing name", maj_stat, min_stat);124return -1;125}126127maj_stat = gss_acquire_cred(&min_stat, server_name, 0,128GSS_C_NULL_OID_SET, GSS_C_ACCEPT,129server_creds, NULL, NULL);130if (maj_stat != GSS_S_COMPLETE) {131display_status("acquiring credentials", maj_stat, min_stat);132return -1;133}134135(void)gss_release_name(&min_stat, &server_name);136137return 0;138}139140/*141* Function: server_establish_context142*143* Purpose: establishses a GSS-API context as a specified service with144* an incoming client, and returns the context handle and associated145* client name146*147* Arguments:148*149* s (r) an established TCP connection to the client150* service_creds (r) server credentials, from gss_acquire_cred151* context (w) the established GSS-API context152* client_name (w) the client's ASCII name153*154* Returns: 0 on success, -1 on failure155*156* Effects:157*158* Any valid client request is accepted. If a context is established,159* its handle is returned in context and the client name is returned160* in client_name and 0 is returned. If unsuccessful, an error161* message is displayed and -1 is returned.162*/163static int164server_establish_context(int s, gss_cred_id_t server_creds,165gss_ctx_id_t *context, gss_buffer_t client_name,166OM_uint32 *ret_flags)167{168gss_buffer_desc send_tok, recv_tok, oid_name;169gss_name_t client;170gss_OID doid;171OM_uint32 maj_stat, min_stat, acc_sec_min_stat;172int token_flags;173174if (recv_token(s, &token_flags, &recv_tok) < 0)175return -1;176177if (recv_tok.value) {178free(recv_tok.value);179recv_tok.value = NULL;180}181182if (!(token_flags & TOKEN_NOOP)) {183if (logfile) {184fprintf(logfile, "Expected NOOP token, got %d token instead\n",185token_flags);186}187return -1;188}189190*context = GSS_C_NO_CONTEXT;191192if (token_flags & TOKEN_CONTEXT_NEXT) {193do {194if (recv_token(s, &token_flags, &recv_tok) < 0)195return -1;196197if (verbose && logfile) {198fprintf(logfile, "Received token (size=%d): \n",199(int)recv_tok.length);200print_token(&recv_tok);201}202203maj_stat = gss_accept_sec_context(&acc_sec_min_stat, context,204server_creds, &recv_tok,205GSS_C_NO_CHANNEL_BINDINGS,206&client, &doid, &send_tok,207ret_flags, NULL, NULL);208209if (recv_tok.value) {210free(recv_tok.value);211recv_tok.value = NULL;212}213214if (send_tok.length != 0) {215if (verbose && logfile) {216fprintf(logfile,217"Sending accept_sec_context token (size=%d):\n",218(int)send_tok.length);219print_token(&send_tok);220}221if (send_token(s, TOKEN_CONTEXT, &send_tok) < 0) {222if (logfile)223fprintf(logfile, "failure sending token\n");224return -1;225}226227(void)gss_release_buffer(&min_stat, &send_tok);228}229if (maj_stat != GSS_S_COMPLETE &&230maj_stat != GSS_S_CONTINUE_NEEDED) {231display_status("accepting context", maj_stat,232acc_sec_min_stat);233if (*context != GSS_C_NO_CONTEXT) {234gss_delete_sec_context(&min_stat, context,235GSS_C_NO_BUFFER);236}237return -1;238}239240if (verbose && logfile) {241if (maj_stat == GSS_S_CONTINUE_NEEDED)242fprintf(logfile, "continue needed...\n");243else244fprintf(logfile, "\n");245fflush(logfile);246}247} while (maj_stat == GSS_S_CONTINUE_NEEDED);248249/* display the flags */250display_ctx_flags(*ret_flags);251252if (verbose && logfile) {253maj_stat = gss_oid_to_str(&min_stat, doid, &oid_name);254if (maj_stat != GSS_S_COMPLETE) {255display_status("converting oid->string", maj_stat, min_stat);256return -1;257}258fprintf(logfile, "Accepted connection using mechanism OID %.*s.\n",259(int)oid_name.length, (char *)oid_name.value);260(void)gss_release_buffer(&min_stat, &oid_name);261}262263maj_stat = gss_display_name(&min_stat, client, client_name, &doid);264if (maj_stat != GSS_S_COMPLETE) {265display_status("displaying name", maj_stat, min_stat);266return -1;267}268maj_stat = gss_release_name(&min_stat, &client);269if (maj_stat != GSS_S_COMPLETE) {270display_status("releasing name", maj_stat, min_stat);271return -1;272}273} else {274client_name->length = *ret_flags = 0;275276if (logfile)277fprintf(logfile, "Accepted unauthenticated connection.\n");278}279280return 0;281}282283/*284* Function: create_socket285*286* Purpose: Opens a listening TCP socket.287*288* Arguments:289*290* port (r) the port number on which to listen291*292* Returns: the listening socket file descriptor, or -1 on failure293*294* Effects:295*296* A listening socket on the specified port and created and returned.297* On error, an error message is displayed and -1 is returned.298*/299static int300create_socket(u_short port)301{302struct sockaddr_in saddr;303int s, on = 1;304305saddr.sin_family = AF_INET;306saddr.sin_port = htons(port);307saddr.sin_addr.s_addr = INADDR_ANY;308309s = socket(AF_INET, SOCK_STREAM, 0);310if (s < 0) {311perror("creating socket");312return -1;313}314/* Let the socket be reused right away. */315(void)setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on));316if (bind(s, (struct sockaddr *)&saddr, sizeof(saddr)) < 0) {317perror("binding socket");318(void)close(s);319return -1;320}321if (listen(s, 5) < 0) {322perror("listening on socket");323(void)close(s);324return -1;325}326return s;327}328329static float330timeval_subtract(struct timeval *tv1, struct timeval *tv2)331{332return ((tv1->tv_sec - tv2->tv_sec) +333((float)(tv1->tv_usec - tv2->tv_usec)) / 1000000);334}335336/*337* Yes, yes, this isn't the best place for doing this test.338* DO NOT REMOVE THIS UNTIL A BETTER TEST HAS BEEN WRITTEN, THOUGH.339* -TYT340*/341static int342test_import_export_context(gss_ctx_id_t *context)343{344OM_uint32 min_stat, maj_stat;345gss_buffer_desc context_token, copied_token;346struct timeval tm1, tm2;347348/* Attempt to save and then restore the context. */349gettimeofday(&tm1, (struct timezone *)0);350maj_stat = gss_export_sec_context(&min_stat, context, &context_token);351if (maj_stat != GSS_S_COMPLETE) {352display_status("exporting context", maj_stat, min_stat);353return 1;354}355gettimeofday(&tm2, NULL);356if (verbose && logfile) {357fprintf(logfile, "Exported context: %d bytes, %7.4f seconds\n",358(int)context_token.length, timeval_subtract(&tm2, &tm1));359}360copied_token.length = context_token.length;361copied_token.value = malloc(context_token.length);362if (copied_token.value == 0) {363if (logfile) {364fprintf(logfile, "Couldn't allocate memory to copy context "365"token.\n");366}367return 1;368}369memcpy(copied_token.value, context_token.value, copied_token.length);370maj_stat = gss_import_sec_context(&min_stat, &copied_token, context);371if (maj_stat != GSS_S_COMPLETE) {372display_status("importing context", maj_stat, min_stat);373return 1;374}375free(copied_token.value);376gettimeofday(&tm1, NULL);377if (verbose && logfile) {378fprintf(logfile, "Importing context: %7.4f seconds\n",379timeval_subtract(&tm1, &tm2));380}381(void)gss_release_buffer(&min_stat, &context_token);382return 0;383}384385/*386* Function: sign_server387*388* Purpose: Performs the "sign" service.389*390* Arguments:391*392* s (r) a TCP socket on which a connection has been393* accept()ed394* service_name (r) the ASCII name of the GSS-API service to395* establish a context as396* export (r) whether to test context exporting397*398* Returns: -1 on error399*400* Effects:401*402* sign_server establishes a context, and performs a single sign request.403*404* A sign request is a single GSS-API sealed token. The token is405* unsealed and a signature block, produced with gss_sign, is returned406* to the sender. The context is the destroyed and the connection407* closed.408*409* If any error occurs, -1 is returned.410*/411static int412sign_server(int s, gss_cred_id_t server_creds, int export)413{414gss_buffer_desc client_name, xmit_buf, msg_buf;415gss_ctx_id_t context;416OM_uint32 maj_stat, min_stat, ret_flags;417int i, conf_state, token_flags;418char *cp;419420/* Establish a context with the client */421if (server_establish_context(s, server_creds, &context, &client_name,422&ret_flags) < 0)423return -1;424425if (context == GSS_C_NO_CONTEXT) {426printf("Accepted unauthenticated connection.\n");427} else {428printf("Accepted connection: \"%.*s\"\n", (int)client_name.length,429(char *)client_name.value);430(void)gss_release_buffer(&min_stat, &client_name);431432if (export) {433for (i = 0; i < 3; i++) {434if (test_import_export_context(&context))435return -1;436}437}438}439440do {441/* Receive the message token */442if (recv_token(s, &token_flags, &xmit_buf) < 0)443return -1;444445if (token_flags & TOKEN_NOOP) {446if (logfile)447fprintf(logfile, "NOOP token\n");448if (xmit_buf.value) {449free(xmit_buf.value);450xmit_buf.value = 0;451}452break;453}454455if (verbose && logfile) {456fprintf(logfile, "Message token (flags=%d):\n", token_flags);457print_token(&xmit_buf);458}459460if (context == GSS_C_NO_CONTEXT &&461(token_flags &462(TOKEN_WRAPPED | TOKEN_ENCRYPTED | TOKEN_SEND_MIC))) {463if (logfile) {464fprintf(logfile, "Unauthenticated client requested "465"authenticated services!\n");466}467if (xmit_buf.value) {468free(xmit_buf.value);469xmit_buf.value = 0;470}471return -1;472}473474if (token_flags & TOKEN_WRAPPED) {475maj_stat = gss_unwrap(&min_stat, context, &xmit_buf, &msg_buf,476&conf_state, NULL);477if (maj_stat != GSS_S_COMPLETE) {478display_status("unsealing message", maj_stat, min_stat);479if (xmit_buf.value) {480free(xmit_buf.value);481xmit_buf.value = 0;482}483return -1;484} else if (!conf_state && (token_flags & TOKEN_ENCRYPTED)) {485fprintf(stderr, "Warning! Message not encrypted.\n");486}487488if (xmit_buf.value) {489free(xmit_buf.value);490xmit_buf.value = 0;491}492} else {493msg_buf = xmit_buf;494}495496if (logfile) {497fprintf(logfile, "Received message: ");498cp = msg_buf.value;499if (isprint((unsigned char)cp[0]) &&500isprint((unsigned char)cp[1])) {501fprintf(logfile, "\"%.*s\"\n", (int)msg_buf.length,502(char *)msg_buf.value);503} else {504fprintf(logfile, "\n");505print_token(&msg_buf);506}507}508509if (token_flags & TOKEN_SEND_MIC) {510/* Produce a signature block for the message. */511maj_stat = gss_get_mic(&min_stat, context, GSS_C_QOP_DEFAULT,512&msg_buf, &xmit_buf);513if (maj_stat != GSS_S_COMPLETE) {514display_status("signing message", maj_stat, min_stat);515return -1;516}517518if (msg_buf.value) {519free(msg_buf.value);520msg_buf.value = 0;521}522523/* Send the signature block to the client. */524if (send_token(s, TOKEN_MIC, &xmit_buf) < 0)525return -1;526527if (xmit_buf.value) {528free(xmit_buf.value);529xmit_buf.value = 0;530}531} else {532if (msg_buf.value) {533free(msg_buf.value);534msg_buf.value = 0;535}536if (send_token(s, TOKEN_NOOP, empty_token) < 0)537return -1;538}539} while (1 /* loop will break if NOOP received */);540541if (context != GSS_C_NO_CONTEXT) {542/* Delete context. */543maj_stat = gss_delete_sec_context(&min_stat, &context, NULL);544if (maj_stat != GSS_S_COMPLETE) {545display_status("deleting context", maj_stat, min_stat);546return -1;547}548}549550if (logfile)551fflush(logfile);552553return 0;554}555556static int max_threads = 1;557558#ifdef _WIN32559static thread_count = 0;560static HANDLE hMutex = NULL;561static HANDLE hEvent = NULL;562563void564init_handles(void)565{566hMutex = CreateMutex(NULL, FALSE, NULL);567hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);568}569570void571cleanup_handles(void)572{573CloseHandle(hMutex);574CloseHandle(hEvent);575}576577BOOL578wait_and_increment_thread_counter(void)579{580for (;;) {581if (WaitForSingleObject(hMutex, INFINITE) == WAIT_OBJECT_0) {582if (thread_count < max_threads) {583thread_count++;584ReleaseMutex(hMutex);585return TRUE;586} else {587ReleaseMutex(hMutex);588589if (WaitForSingleObject(hEvent, INFINITE) == WAIT_OBJECT_0)590continue;591else592return FALSE;593}594} else {595return FALSE;596}597}598}599600BOOL601decrement_and_signal_thread_counter(void)602{603if (WaitForSingleObject(hMutex, INFINITE) == WAIT_OBJECT_0) {604if (thread_count == max_threads)605SetEvent(hEvent);606thread_count--;607ReleaseMutex(hMutex);608return TRUE;609} else {610return FALSE;611}612}613614#else /* assume pthread */615616static pthread_mutex_t counter_mutex = PTHREAD_MUTEX_INITIALIZER;617static pthread_cond_t counter_cond = PTHREAD_COND_INITIALIZER;618int counter = 0;619620static int621wait_and_increment_thread_counter(void)622{623int err;624625err = pthread_mutex_lock(&counter_mutex);626if (err) {627perror("pthread_mutex_lock");628return 0;629}630if (counter == max_threads) {631err = pthread_cond_wait(&counter_cond, &counter_mutex);632if (err) {633pthread_mutex_unlock(&counter_mutex);634perror("pthread_cond_wait");635return 0;636}637}638counter++;639pthread_mutex_unlock(&counter_mutex);640return 1;641}642643static void644decrement_and_signal_thread_counter(void)645{646int err;647648err = pthread_mutex_lock(&counter_mutex);649if (err) {650perror("pthread_mutex_lock");651return;652}653if (counter == max_threads)654pthread_cond_broadcast(&counter_cond);655counter--;656pthread_mutex_unlock(&counter_mutex);657}658659#endif660661struct _work_plan {662int s;663gss_cred_id_t server_creds;664int export;665};666667static void *668worker_bee(void *param)669{670struct _work_plan *work = param;671672/* This return value is not checked, because there's not really anything to673* do if it fails. */674sign_server(work->s, work->server_creds, work->export);675closesocket(work->s);676free(work);677678#if defined _WIN32 || 1679if (max_threads > 1)680decrement_and_signal_thread_counter();681#endif682return NULL;683}684685int686main(int argc, char **argv)687{688char *service_name;689gss_cred_id_t server_creds;690OM_uint32 min_stat;691u_short port = 4444;692int once = 0;693int do_inetd = 0;694int export = 0;695696signal(SIGPIPE, SIG_IGN);697logfile = stdout;698display_file = stdout;699argc--;700argv++;701while (argc) {702if (strcmp(*argv, "-port") == 0) {703argc--;704argv++;705if (!argc)706usage();707port = atoi(*argv);708} else if (strcmp(*argv, "-threads") == 0) {709argc--;710argv++;711if (!argc)712usage();713max_threads = atoi(*argv);714} else if (strcmp(*argv, "-verbose") == 0) {715verbose = 1;716} else if (strcmp(*argv, "-once") == 0) {717once = 1;718} else if (strcmp(*argv, "-inetd") == 0) {719do_inetd = 1;720} else if (strcmp(*argv, "-export") == 0) {721export = 1;722} else if (strcmp(*argv, "-logfile") == 0) {723argc--;724argv++;725if (!argc)726usage();727/*728* Gross hack, but it makes it unnecessary to add an extra argument729* to disable logging, and makes the code more efficient because it730* doesn't actually write data to /dev/null.731*/732if (!strcmp(*argv, "/dev/null")) {733logfile = display_file = NULL;734} else {735logfile = fopen(*argv, "a");736display_file = logfile;737if (!logfile) {738perror(*argv);739exit(1);740}741}742} else {743break;744}745argc--;746argv++;747}748if (argc != 1)749usage();750751if ((*argv)[0] == '-')752usage();753754#ifdef _WIN32755if (max_threads < 1) {756fprintf(stderr, "warning: there must be at least one thread\n");757max_threads = 1;758}759760if (max_threads > 1 && do_inetd) {761fprintf(stderr, "warning: one thread may be used in conjunction "762"with inetd\n");763}764765init_handles();766#endif767768service_name = *argv;769770if (server_acquire_creds(service_name, &server_creds) < 0)771return -1;772773if (do_inetd) {774close(1);775close(2);776777sign_server(0, server_creds, export);778close(0);779} else {780int stmp;781782stmp = create_socket(port);783if (stmp >= 0) {784do {785struct _work_plan * work = malloc(sizeof(struct _work_plan));786787if (work == NULL) {788fprintf(stderr, "fatal error: out of memory");789break;790}791792/* Accept a TCP connection */793work->s = accept(stmp, NULL, 0);794if (work->s < 0) {795perror("accepting connection");796continue;797}798799work->server_creds = server_creds;800work->export = export;801802if (max_threads == 1) {803worker_bee(work);804} else {805if (wait_and_increment_thread_counter()) {806#ifdef _WIN32807uintptr_t handle = _beginthread(worker_bee, 0, work);808if (handle == (uintptr_t)-1) {809closesocket(work->s);810free(work);811}812#else813int err;814pthread_t thr;815err = pthread_create(&thr, 0, worker_bee, work);816if (err) {817perror("pthread_create");818closesocket(work->s);819free(work);820}821(void)pthread_detach(thr);822#endif823} else {824fprintf(stderr, "fatal error incrementing thread "825"counter");826closesocket(work->s);827free(work);828break;829}830}831} while (!once);832833closesocket(stmp);834}835}836837(void)gss_release_cred(&min_stat, &server_creds);838839#ifdef _WIN32840cleanup_handles();841#else842if (max_threads > 1) {843while (1)844sleep(999999);845}846#endif847848return 0;849}850851852