Path: blob/main/crypto/krb5/src/appl/gss-sample/gss-client.c
34907 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) 2003, 2004, 2005 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 <stdio.h>47#include <stdlib.h>48#include <string.h>49#ifdef _WIN3250#include <windows.h>51#include <winsock2.h>52#else53#include <assert.h>54#include <unistd.h>55#include <ctype.h>56#include <sys/types.h>57#include <sys/socket.h>58#include <netinet/in.h>59#include <netdb.h>60#include <errno.h>61#include <sys/stat.h>62#include <fcntl.h>63#endif6465#include <gssapi/gssapi_generic.h>66#include <gssapi/gssapi_krb5.h>67#include <gssapi/gssapi_ext.h>68#include "gss-misc.h"69#include "port-sockets.h"7071static int verbose = 1;72static int spnego = 0;73static gss_OID_desc gss_spnego_mechanism_oid_desc =74{6, (void *)"\x2b\x06\x01\x05\x05\x02"};7576static void77usage(void)78{79fprintf(stderr, "Usage: gss-client [-port port] [-mech mechanism] "80"[-spnego] [-d]\n");81fprintf(stderr, " [-seq] [-noreplay] [-nomutual] [-user user] "82"[-pass pw]");83#ifdef _WIN3284fprintf(stderr, " [-threads num]");85#endif86fprintf(stderr, "\n");87fprintf(stderr, " [-f] [-q] [-ccount count] [-mcount count]\n");88fprintf(stderr, " [-v1] [-na] [-nw] [-nx] [-nm] host service msg\n");89exit(1);90}9192/*93* Function: connect_to_server94*95* Purpose: Opens a TCP connection to the name host and port.96*97* Arguments:98*99* host (r) the target host name100* port (r) the target port, in host byte order101*102* Returns: the established socket file descriptor, or -1 on failure103*104* Effects:105*106* The host name is resolved with gethostbyname(), and the socket is107* opened and connected. If an error occurs, an error message is108* displayed and -1 is returned.109*/110static int111connect_to_server(char *host, u_short port)112{113struct sockaddr_in saddr;114struct hostent *hp;115int s;116117#ifdef _WIN32118WSADATA wsadata;119int wsastartuperror = WSAStartup(0x202, &wsadata);120if (wsastartuperror) {121fprintf(stderr, "WSAStartup error: %x\n", wsastartuperror);122return -1;123}124#endif125126if ((hp = gethostbyname(host)) == NULL) {127fprintf(stderr, "Unknown host: %s\n", host);128return -1;129}130131saddr.sin_family = hp->h_addrtype;132memcpy(&saddr.sin_addr, hp->h_addr, sizeof(saddr.sin_addr));133saddr.sin_port = htons(port);134135if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) {136perror("creating socket");137return -1;138}139if (connect(s, (struct sockaddr *) &saddr, sizeof(saddr)) < 0) {140perror("connecting to server");141(void) closesocket(s);142return -1;143}144return s;145}146147/*148* Function: client_establish_context149*150* Purpose: establishes a GSS-API context with a specified service and151* returns the context handle152*153* Arguments:154*155* s (r) an established TCP connection to the service156* service_name(r) the ASCII service name of the service157* gss_flags (r) GSS-API delegation flag (if any)158* auth_flag (r) whether to actually do authentication159* v1_format (r) whether the v1 sample protocol should be used160* oid (r) OID of the mechanism to use161* context (w) the established GSS-API context162* ret_flags (w) the returned flags from init_sec_context163*164* Returns: 0 on success, -1 on failure165*166* Effects:167*168* service_name is imported as a GSS-API name and a GSS-API context is169* established with the corresponding service; the service should be170* listening on the TCP connection s. The default GSS-API mechanism171* is used, and mutual authentication and replay detection are172* requested.173*174* If successful, the context handle is returned in context. If175* unsuccessful, the GSS-API error messages are displayed on stderr176* and -1 is returned.177*/178static int179client_establish_context(int s, char *service_name, OM_uint32 gss_flags,180int auth_flag, int v1_format, gss_OID oid,181char *username, char *password,182gss_ctx_id_t *gss_context, OM_uint32 *ret_flags)183{184int result = -1, st;185gss_buffer_desc send_tok, recv_tok, pwbuf, *token_ptr;186gss_name_t target_name = GSS_C_NO_NAME, gss_username = GSS_C_NO_NAME;187OM_uint32 maj_stat, min_stat, init_sec_min_stat;188int token_flags;189gss_cred_id_t cred = GSS_C_NO_CREDENTIAL;190gss_OID_set_desc mechs, neg_mechs, *mechsp = GSS_C_NO_OID_SET;191192if (!auth_flag)193return send_token(s, TOKEN_NOOP, empty_token);194195if (spnego) {196mechs.elements = &gss_spnego_mechanism_oid_desc;197mechs.count = 1;198mechsp = &mechs;199} else if (oid != GSS_C_NO_OID) {200mechs.elements = oid;201mechs.count = 1;202mechsp = &mechs;203} else {204mechs.elements = NULL;205mechs.count = 0;206}207208if (username != NULL) {209send_tok.value = username;210send_tok.length = strlen(username);211212maj_stat = gss_import_name(&min_stat, &send_tok,213(gss_OID) gss_nt_user_name, &gss_username);214if (maj_stat != GSS_S_COMPLETE) {215display_status("parsing client name", maj_stat, min_stat);216goto cleanup;217}218}219220if (password != NULL) {221pwbuf.value = password;222pwbuf.length = strlen(password);223224maj_stat = gss_acquire_cred_with_password(&min_stat, gss_username,225&pwbuf, 0, mechsp,226GSS_C_INITIATE, &cred, NULL,227NULL);228} else if (gss_username != GSS_C_NO_NAME) {229maj_stat = gss_acquire_cred(&min_stat, gss_username, 0, mechsp,230GSS_C_INITIATE, &cred, NULL, NULL);231} else {232maj_stat = GSS_S_COMPLETE;233}234if (maj_stat != GSS_S_COMPLETE) {235display_status("acquiring creds", maj_stat, min_stat);236goto cleanup;237}238if (spnego && oid != GSS_C_NO_OID) {239neg_mechs.elements = oid;240neg_mechs.count = 1;241maj_stat = gss_set_neg_mechs(&min_stat, cred, &neg_mechs);242if (maj_stat != GSS_S_COMPLETE) {243display_status("setting neg mechs", maj_stat, min_stat);244goto cleanup;245}246}247248/* Import the name into target_name. Use send_tok to save local variable249* space. */250send_tok.value = service_name;251send_tok.length = strlen(service_name);252maj_stat = gss_import_name(&min_stat, &send_tok,253(gss_OID) gss_nt_service_name, &target_name);254if (maj_stat != GSS_S_COMPLETE) {255display_status("parsing name", maj_stat, min_stat);256goto cleanup;257}258259if (!v1_format) {260if (send_token(s, TOKEN_NOOP | TOKEN_CONTEXT_NEXT, empty_token) < 0)261goto cleanup;262}263264/*265* Perform the context-establishment loop.266*267* On each pass through the loop, token_ptr points to the token to send to268* the server (or GSS_C_NO_BUFFER on the first pass). Every generated269* token is stored in send_tok which is then transmitted to the server;270* every received token is stored in recv_tok, which token_ptr is then set271* to, to be processed by the next call to gss_init_sec_context.272*273* GSS-API guarantees that send_tok's length will be non-zero if and only274* if the server is expecting another token from us, and that275* gss_init_sec_context returns GSS_S_CONTINUE_NEEDED if and only if the276* server has another token to send us.277*/278279token_ptr = GSS_C_NO_BUFFER;280*gss_context = GSS_C_NO_CONTEXT;281282do {283maj_stat = gss_init_sec_context(&init_sec_min_stat, cred, gss_context,284target_name, mechs.elements, gss_flags,2850, NULL, token_ptr, NULL, &send_tok,286ret_flags, NULL);287288if (token_ptr != GSS_C_NO_BUFFER)289free(recv_tok.value);290291if (send_tok.length > 0) {292if (verbose) {293printf("Sending init_sec_context token (size=%d)...",294(int) send_tok.length);295}296st = send_token(s, v1_format ? 0 : TOKEN_CONTEXT, &send_tok);297(void) gss_release_buffer(&min_stat, &send_tok);298if (st < 0)299goto cleanup;300}301302if (maj_stat != GSS_S_COMPLETE && maj_stat != GSS_S_CONTINUE_NEEDED) {303display_status("initializing context", maj_stat,304init_sec_min_stat);305goto cleanup;306}307308if (maj_stat == GSS_S_CONTINUE_NEEDED) {309if (verbose)310printf("continue needed...");311if (recv_token(s, &token_flags, &recv_tok) < 0)312goto cleanup;313token_ptr = &recv_tok;314}315if (verbose)316printf("\n");317} while (maj_stat == GSS_S_CONTINUE_NEEDED);318319result = 0;320321cleanup:322(void) gss_release_name(&min_stat, &gss_username);323(void) gss_release_cred(&min_stat, &cred);324(void) gss_release_name(&min_stat, &target_name);325return result;326}327328static void329read_file(char *file_name, gss_buffer_t in_buf)330{331int fd, count;332struct stat stat_buf;333334if ((fd = open(file_name, O_RDONLY, 0)) < 0) {335perror("open");336fprintf(stderr, "Couldn't open file %s\n", file_name);337exit(1);338}339if (fstat(fd, &stat_buf) < 0) {340perror("fstat");341exit(1);342}343in_buf->length = stat_buf.st_size;344345if (in_buf->length == 0) {346in_buf->value = NULL;347return;348}349350if ((in_buf->value = malloc(in_buf->length)) == 0) {351fprintf(stderr, "Couldn't allocate %d byte buffer for reading file\n",352(int) in_buf->length);353exit(1);354}355356/* this code used to check for incomplete reads, but you can't get357* an incomplete read on any file for which fstat() is meaningful */358359count = read(fd, in_buf->value, in_buf->length);360if (count < 0) {361perror("read");362exit(1);363}364if (count < (int)in_buf->length)365fprintf(stderr, "Warning, only read in %d bytes, expected %d\n",366count, (int) in_buf->length);367}368369/*370* Function: call_server371*372* Purpose: Call the "sign" service.373*374* Arguments:375*376* host (r) the host providing the service377* port (r) the port to connect to on host378* service_name (r) the GSS-API service name to authenticate to379* gss_flags (r) GSS-API delegation flag (if any)380* auth_flag (r) whether to do authentication381* wrap_flag (r) whether to do message wrapping at all382* encrypt_flag (r) whether to do encryption while wrapping383* mic_flag (r) whether to request a MIC from the server384* msg (r) the message to have "signed"385* use_file (r) whether to treat msg as an input file name386* mcount (r) the number of times to send the message387*388* Returns: 0 on success, -1 on failure389*390* Effects:391*392* call_server opens a TCP connection to <host:port> and establishes a393* GSS-API context with service_name over the connection. It then394* seals msg in a GSS-API token with gss_wrap, sends it to the server,395* reads back a GSS-API signature block for msg from the server, and396* verifies it with gss_verify. -1 is returned if any step fails,397* otherwise 0 is returned. */398static int399call_server(char *host, u_short port, gss_OID oid, char *service_name,400OM_uint32 gss_flags, int auth_flag, int wrap_flag,401int encrypt_flag, int mic_flag, int v1_format, char *msg,402int use_file, int mcount, char *username, char *password)403{404gss_ctx_id_t context = GSS_C_NO_CONTEXT;405gss_buffer_desc in_buf, out_buf;406int s = -1, result = -1, state;407OM_uint32 ret_flags;408OM_uint32 maj_stat, min_stat;409gss_name_t src_name = GSS_C_NO_NAME, targ_name = GSS_C_NO_NAME;410gss_buffer_desc sname = GSS_C_EMPTY_BUFFER, tname = GSS_C_EMPTY_BUFFER;411OM_uint32 lifetime;412gss_OID mechanism, name_type;413int is_local;414OM_uint32 context_flags;415int is_open;416gss_qop_t qop_state;417gss_OID_set mech_names;418gss_buffer_desc oid_name;419size_t i;420int token_flags;421422/* Open connection */423if ((s = connect_to_server(host, port)) < 0)424goto cleanup;425426/* Establish context */427if (client_establish_context(s, service_name, gss_flags, auth_flag,428v1_format, oid, username, password,429&context, &ret_flags) < 0) {430goto cleanup;431}432433if (auth_flag && verbose) {434/* display the flags */435display_ctx_flags(ret_flags);436437/* Get context information */438maj_stat = gss_inquire_context(&min_stat, context,439&src_name, &targ_name, &lifetime,440&mechanism, &context_flags,441&is_local, &is_open);442if (maj_stat != GSS_S_COMPLETE) {443display_status("inquiring context", maj_stat, min_stat);444goto cleanup;445}446447maj_stat = gss_display_name(&min_stat, src_name, &sname, &name_type);448if (maj_stat != GSS_S_COMPLETE) {449display_status("displaying source name", maj_stat, min_stat);450goto cleanup;451}452maj_stat = gss_display_name(&min_stat, targ_name, &tname,453(gss_OID *) NULL);454if (maj_stat != GSS_S_COMPLETE) {455display_status("displaying target name", maj_stat, min_stat);456goto cleanup;457}458printf("\"%.*s\" to \"%.*s\", lifetime %d, flags %x, %s, %s\n",459(int) sname.length, (char *) sname.value,460(int) tname.length, (char *) tname.value, lifetime,461context_flags,462(is_local) ? "locally initiated" : "remotely initiated",463(is_open) ? "open" : "closed");464465maj_stat = gss_oid_to_str(&min_stat, name_type, &oid_name);466if (maj_stat != GSS_S_COMPLETE) {467display_status("converting oid->string", maj_stat, min_stat);468goto cleanup;469}470printf("Name type of source name is %.*s.\n",471(int) oid_name.length, (char *) oid_name.value);472(void) gss_release_buffer(&min_stat, &oid_name);473474/* Now get the names supported by the mechanism */475maj_stat = gss_inquire_names_for_mech(&min_stat,476mechanism, &mech_names);477if (maj_stat != GSS_S_COMPLETE) {478display_status("inquiring mech names", maj_stat, min_stat);479goto cleanup;480}481482maj_stat = gss_oid_to_str(&min_stat, mechanism, &oid_name);483if (maj_stat != GSS_S_COMPLETE) {484display_status("converting oid->string", maj_stat, min_stat);485goto cleanup;486}487printf("Mechanism %.*s supports %d names\n",488(int) oid_name.length, (char *) oid_name.value,489(int) mech_names->count);490(void) gss_release_buffer(&min_stat, &oid_name);491492for (i = 0; i < mech_names->count; i++) {493maj_stat = gss_oid_to_str(&min_stat,494&mech_names->elements[i], &oid_name);495if (maj_stat != GSS_S_COMPLETE) {496display_status("converting oid->string", maj_stat, min_stat);497goto cleanup;498}499printf(" %d: %.*s\n", (int) i,500(int) oid_name.length, (char *) oid_name.value);501502(void) gss_release_buffer(&min_stat, &oid_name);503}504(void) gss_release_oid_set(&min_stat, &mech_names);505}506507if (use_file) {508read_file(msg, &in_buf);509} else {510/* Seal the message */511in_buf.value = msg;512in_buf.length = strlen((char *)in_buf.value);513}514515for (i = 0; i < (size_t)mcount; i++) {516if (wrap_flag) {517maj_stat =518gss_wrap(&min_stat, context, encrypt_flag, GSS_C_QOP_DEFAULT,519&in_buf, &state, &out_buf);520if (maj_stat != GSS_S_COMPLETE) {521display_status("wrapping message", maj_stat, min_stat);522goto cleanup;523} else if (encrypt_flag && !state) {524fprintf(stderr, "Warning! Message not encrypted.\n");525}526} else {527out_buf = in_buf;528}529530/* Send to server */531if (send_token(s, (v1_format ? 0532: (TOKEN_DATA |533(wrap_flag ? TOKEN_WRAPPED : 0) |534(encrypt_flag ? TOKEN_ENCRYPTED : 0) |535(mic_flag ? TOKEN_SEND_MIC : 0))),536&out_buf) < 0)537goto cleanup;538539if (out_buf.value != in_buf.value)540(void) gss_release_buffer(&min_stat, &out_buf);541542/* Read signature block into out_buf */543if (recv_token(s, &token_flags, &out_buf) < 0)544goto cleanup;545546if (mic_flag) {547/* Verify signature block */548maj_stat = gss_verify_mic(&min_stat, context, &in_buf,549&out_buf, &qop_state);550if (maj_stat != GSS_S_COMPLETE) {551display_status("verifying signature", maj_stat, min_stat);552goto cleanup;553}554555if (verbose)556printf("Signature verified.\n");557} else {558if (verbose)559printf("Response received.\n");560}561562free(out_buf.value);563}564565if (use_file)566free(in_buf.value);567568/* Send NOOP */569if (!v1_format)570(void) send_token(s, TOKEN_NOOP, empty_token);571572result = 0;573574cleanup:575(void) gss_release_name(&min_stat, &src_name);576(void) gss_release_name(&min_stat, &targ_name);577(void) gss_release_buffer(&min_stat, &sname);578(void) gss_release_buffer(&min_stat, &tname);579(void) gss_delete_sec_context(&min_stat, &context, GSS_C_NO_BUFFER);580if (s >= 0)581(void) closesocket(s);582return result;583}584585static void586parse_oid(char *mechanism, gss_OID * oid)587{588char *mechstr = 0;589gss_buffer_desc tok;590OM_uint32 maj_stat, min_stat;591size_t i, mechlen = strlen(mechanism);592593if (isdigit((int) mechanism[0])) {594mechstr = malloc(mechlen + 5);595if (!mechstr) {596fprintf(stderr, "Couldn't allocate mechanism scratch!\n");597return;598}599mechstr[0] = '{';600mechstr[1] = ' ';601for (i = 0; i < mechlen; i++)602mechstr[i + 2] = (mechanism[i] == '.') ? ' ' : mechanism[i];603mechstr[mechlen + 2] = ' ';604mechstr[mechlen + 3] = ' ';605mechstr[mechlen + 4] = '\0';606tok.value = mechstr;607} else608tok.value = mechanism;609tok.length = strlen(tok.value);610maj_stat = gss_str_to_oid(&min_stat, &tok, oid);611if (maj_stat != GSS_S_COMPLETE) {612display_status("str_to_oid", maj_stat, min_stat);613return;614}615if (mechstr)616free(mechstr);617}618619static int max_threads = 1;620621#ifdef _WIN32622static thread_count = 0;623static HANDLE hMutex = NULL;624static HANDLE hEvent = NULL;625626void627InitHandles(void)628{629hMutex = CreateMutex(NULL, FALSE, NULL);630hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);631}632633void634CleanupHandles(void)635{636CloseHandle(hMutex);637CloseHandle(hEvent);638}639640BOOL641WaitAndIncrementThreadCounter(void)642{643for (;;) {644if (WaitForSingleObject(hMutex, INFINITE) == WAIT_OBJECT_0) {645if (thread_count < max_threads) {646thread_count++;647ReleaseMutex(hMutex);648return TRUE;649} else {650ReleaseMutex(hMutex);651652if (WaitForSingleObject(hEvent, INFINITE) == WAIT_OBJECT_0) {653continue;654} else {655return FALSE;656}657}658} else {659return FALSE;660}661}662}663664BOOL665DecrementAndSignalThreadCounter(void)666{667if (WaitForSingleObject(hMutex, INFINITE) == WAIT_OBJECT_0) {668if (thread_count == max_threads)669ResetEvent(hEvent);670thread_count--;671ReleaseMutex(hMutex);672return TRUE;673} else {674return FALSE;675}676}677#endif678679static char *service_name, *server_host, *msg;680static char *mechanism = 0;681static u_short port = 4444;682static int use_file = 0;683static OM_uint32 gss_flags = GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG;684static OM_uint32 min_stat;685static gss_OID oid = GSS_C_NULL_OID;686static int mcount = 1, ccount = 1;687static int auth_flag, wrap_flag, encrypt_flag, mic_flag, v1_format;688static char *username = NULL;689static char *password = NULL;690691static void692worker_bee(void *unused)693{694if (call_server(server_host, port, oid, service_name,695gss_flags, auth_flag, wrap_flag, encrypt_flag, mic_flag,696v1_format, msg, use_file, mcount, username, password) < 0)697exit(1);698699#ifdef _WIN32700if (max_threads > 1)701DecrementAndSignalThreadCounter();702#endif703}704705int706main(int argc, char **argv)707{708int i;709710display_file = stdout;711auth_flag = wrap_flag = encrypt_flag = mic_flag = 1;712v1_format = 0;713714/* Parse arguments. */715argc--;716argv++;717while (argc) {718if (strcmp(*argv, "-port") == 0) {719argc--;720argv++;721if (!argc)722usage();723port = atoi(*argv);724} else if (strcmp(*argv, "-mech") == 0) {725argc--;726argv++;727if (!argc)728usage();729mechanism = *argv;730} else if (strcmp(*argv, "-user") == 0) {731argc--;732argv++;733if (!argc)734usage();735username = *argv;736} else if (strcmp(*argv, "-pass") == 0) {737argc--;738argv++;739if (!argc)740usage();741password = *argv;742} else if (strcmp(*argv, "-iakerb") == 0) {743mechanism = "{ 1 3 6 1 5 2 5 }";744} else if (strcmp(*argv, "-spnego") == 0) {745spnego = 1;746} else if (strcmp(*argv, "-krb5") == 0) {747mechanism = "{ 1 2 840 113554 1 2 2 }";748#ifdef _WIN32749} else if (strcmp(*argv, "-threads") == 0) {750argc--;751argv++;752if (!argc)753usage();754max_threads = atoi(*argv);755#endif756} else if (strcmp(*argv, "-dce") == 0) {757gss_flags |= GSS_C_DCE_STYLE;758} else if (strcmp(*argv, "-d") == 0) {759gss_flags |= GSS_C_DELEG_FLAG;760} else if (strcmp(*argv, "-seq") == 0) {761gss_flags |= GSS_C_SEQUENCE_FLAG;762} else if (strcmp(*argv, "-noreplay") == 0) {763gss_flags &= ~GSS_C_REPLAY_FLAG;764} else if (strcmp(*argv, "-nomutual") == 0) {765gss_flags &= ~GSS_C_MUTUAL_FLAG;766} else if (strcmp(*argv, "-f") == 0) {767use_file = 1;768} else if (strcmp(*argv, "-q") == 0) {769verbose = 0;770} else if (strcmp(*argv, "-ccount") == 0) {771argc--;772argv++;773if (!argc)774usage();775ccount = atoi(*argv);776if (ccount <= 0)777usage();778} else if (strcmp(*argv, "-mcount") == 0) {779argc--;780argv++;781if (!argc)782usage();783mcount = atoi(*argv);784if (mcount < 0)785usage();786} else if (strcmp(*argv, "-na") == 0) {787auth_flag = wrap_flag = encrypt_flag = mic_flag = 0;788} else if (strcmp(*argv, "-nw") == 0) {789wrap_flag = 0;790} else if (strcmp(*argv, "-nx") == 0) {791encrypt_flag = 0;792} else if (strcmp(*argv, "-nm") == 0) {793mic_flag = 0;794} else if (strcmp(*argv, "-v1") == 0) {795v1_format = 1;796} else797break;798argc--;799argv++;800}801if (argc != 3)802usage();803804#ifdef _WIN32805if (max_threads < 1) {806fprintf(stderr, "warning: there must be at least one thread\n");807max_threads = 1;808}809#endif810811server_host = *argv++;812service_name = *argv++;813msg = *argv++;814815if (mechanism)816parse_oid(mechanism, &oid);817818if (max_threads == 1) {819for (i = 0; i < ccount; i++) {820worker_bee(0);821}822} else {823#ifdef _WIN32824for (i = 0; i < ccount; i++) {825if (WaitAndIncrementThreadCounter()) {826uintptr_t handle = _beginthread(worker_bee, 0, (void *) 0);827if (handle == (uintptr_t) - 1) {828exit(1);829}830} else {831exit(1);832}833}834#else835/* boom */836assert(max_threads == 1);837#endif838}839840if (oid != GSS_C_NULL_OID)841(void) gss_release_oid(&min_stat, &oid);842843#ifdef _WIN32844CleanupHandles();845#endif846847return 0;848}849850851