summaryrefslogtreecommitdiff
path: root/src/appl/gss-sample/gss-client.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/appl/gss-sample/gss-client.c')
-rw-r--r--src/appl/gss-sample/gss-client.c921
1 files changed, 921 insertions, 0 deletions
diff --git a/src/appl/gss-sample/gss-client.c b/src/appl/gss-sample/gss-client.c
new file mode 100644
index 000000000000..93fca1f6fb9d
--- /dev/null
+++ b/src/appl/gss-sample/gss-client.c
@@ -0,0 +1,921 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright 1994 by OpenVision Technologies, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without fee,
+ * provided that the above copyright notice appears in all copies and
+ * that both that copyright notice and this permission notice appear in
+ * supporting documentation, and that the name of OpenVision not be used
+ * in advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission. OpenVision makes no
+ * representations about the suitability of this software for any
+ * purpose. It is provided "as is" without express or implied warranty.
+ *
+ * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
+ * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+ * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+/*
+ * Copyright (C) 2003, 2004, 2005 by the Massachusetts Institute of Technology.
+ * All rights reserved.
+ *
+ * Export of this software from the United States of America may
+ * require a specific license from the United States Government.
+ * It is the responsibility of any person or organization contemplating
+ * export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#ifdef _WIN32
+#include <windows.h>
+#include <winsock2.h>
+#else
+#include <assert.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#endif
+
+#include <gssapi/gssapi_generic.h>
+#include <gssapi/gssapi_krb5.h>
+#include <gssapi/gssapi_ext.h>
+#include "gss-misc.h"
+#include "port-sockets.h"
+
+static int verbose = 1;
+static int spnego = 0;
+static gss_OID_desc gss_spnego_mechanism_oid_desc =
+{6, (void *)"\x2b\x06\x01\x05\x05\x02"};
+
+static void
+usage()
+{
+ fprintf(stderr, "Usage: gss-client [-port port] [-mech mechanism] "
+ "[-spnego] [-d]\n");
+ fprintf(stderr, " [-seq] [-noreplay] [-nomutual] [-user user] "
+ "[-pass pw]");
+#ifdef _WIN32
+ fprintf(stderr, " [-threads num]");
+#endif
+ fprintf(stderr, "\n");
+ fprintf(stderr, " [-f] [-q] [-ccount count] [-mcount count]\n");
+ fprintf(stderr, " [-v1] [-na] [-nw] [-nx] [-nm] host service msg\n");
+ exit(1);
+}
+
+/*
+ * Function: connect_to_server
+ *
+ * Purpose: Opens a TCP connection to the name host and port.
+ *
+ * Arguments:
+ *
+ * host (r) the target host name
+ * port (r) the target port, in host byte order
+ *
+ * Returns: the established socket file desciptor, or -1 on failure
+ *
+ * Effects:
+ *
+ * The host name is resolved with gethostbyname(), and the socket is
+ * opened and connected. If an error occurs, an error message is
+ * displayed and -1 is returned.
+ */
+static int
+connect_to_server(char *host, u_short port)
+{
+ struct sockaddr_in saddr;
+ struct hostent *hp;
+ int s;
+
+#ifdef _WIN32
+ WSADATA wsadata;
+ int wsastartuperror = WSAStartup(0x202, &wsadata);
+ if (wsastartuperror) {
+ fprintf(stderr, "WSAStartup error: %x\n", wsastartuperror);
+ return -1;
+ }
+#endif
+
+ if ((hp = gethostbyname(host)) == NULL) {
+ fprintf(stderr, "Unknown host: %s\n", host);
+ return -1;
+ }
+
+ saddr.sin_family = hp->h_addrtype;
+ memcpy(&saddr.sin_addr, hp->h_addr, sizeof(saddr.sin_addr));
+ saddr.sin_port = htons(port);
+
+ if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+ perror("creating socket");
+ return -1;
+ }
+ if (connect(s, (struct sockaddr *) &saddr, sizeof(saddr)) < 0) {
+ perror("connecting to server");
+ (void) closesocket(s);
+ return -1;
+ }
+ return s;
+}
+
+/*
+ * Function: client_establish_context
+ *
+ * Purpose: establishes a GSS-API context with a specified service and
+ * returns the context handle
+ *
+ * Arguments:
+ *
+ * s (r) an established TCP connection to the service
+ * service_name(r) the ASCII service name of the service
+ * gss_flags (r) GSS-API delegation flag (if any)
+ * auth_flag (r) whether to actually do authentication
+ * v1_format (r) whether the v1 sample protocol should be used
+ * oid (r) OID of the mechanism to use
+ * context (w) the established GSS-API context
+ * ret_flags (w) the returned flags from init_sec_context
+ *
+ * Returns: 0 on success, -1 on failure
+ *
+ * Effects:
+ *
+ * service_name is imported as a GSS-API name and a GSS-API context is
+ * established with the corresponding service; the service should be
+ * listening on the TCP connection s. The default GSS-API mechanism
+ * is used, and mutual authentication and replay detection are
+ * requested.
+ *
+ * If successful, the context handle is returned in context. If
+ * unsuccessful, the GSS-API error messages are displayed on stderr
+ * and -1 is returned.
+ */
+static int
+client_establish_context(int s, char *service_name, OM_uint32 gss_flags,
+ int auth_flag, int v1_format, gss_OID oid,
+ char *username, char *password,
+ gss_ctx_id_t *gss_context, OM_uint32 *ret_flags)
+{
+ if (auth_flag) {
+ gss_buffer_desc send_tok, recv_tok, *token_ptr;
+ gss_name_t target_name;
+ OM_uint32 maj_stat, min_stat, init_sec_min_stat;
+ int token_flags;
+ gss_cred_id_t cred = GSS_C_NO_CREDENTIAL;
+ gss_name_t gss_username = GSS_C_NO_NAME;
+ gss_OID_set_desc mechs, *mechsp = GSS_C_NO_OID_SET;
+
+ if (spnego) {
+ mechs.elements = &gss_spnego_mechanism_oid_desc;
+ mechs.count = 1;
+ mechsp = &mechs;
+ } else if (oid != GSS_C_NO_OID) {
+ mechs.elements = oid;
+ mechs.count = 1;
+ mechsp = &mechs;
+ } else {
+ mechs.elements = NULL;
+ mechs.count = 0;
+ }
+
+ if (username != NULL) {
+ send_tok.value = username;
+ send_tok.length = strlen(username);
+
+ maj_stat = gss_import_name(&min_stat, &send_tok,
+ (gss_OID) gss_nt_user_name,
+ &gss_username);
+ if (maj_stat != GSS_S_COMPLETE) {
+ display_status("parsing client name", maj_stat, min_stat);
+ return -1;
+ }
+ }
+
+ if (password != NULL) {
+ gss_buffer_desc pwbuf;
+
+ pwbuf.value = password;
+ pwbuf.length = strlen(password);
+
+ maj_stat = gss_acquire_cred_with_password(&min_stat,
+ gss_username,
+ &pwbuf, 0,
+ mechsp, GSS_C_INITIATE,
+ &cred, NULL, NULL);
+ } else if (gss_username != GSS_C_NO_NAME) {
+ maj_stat = gss_acquire_cred(&min_stat,
+ gss_username, 0,
+ mechsp, GSS_C_INITIATE,
+ &cred, NULL, NULL);
+ } else
+ maj_stat = GSS_S_COMPLETE;
+ if (maj_stat != GSS_S_COMPLETE) {
+ display_status("acquiring creds", maj_stat, min_stat);
+ gss_release_name(&min_stat, &gss_username);
+ return -1;
+ }
+ if (spnego && oid != GSS_C_NO_OID) {
+ gss_OID_set_desc neg_mechs;
+
+ neg_mechs.elements = oid;
+ neg_mechs.count = 1;
+
+ maj_stat = gss_set_neg_mechs(&min_stat, cred, &neg_mechs);
+ if (maj_stat != GSS_S_COMPLETE) {
+ display_status("setting neg mechs", maj_stat, min_stat);
+ gss_release_name(&min_stat, &gss_username);
+ gss_release_cred(&min_stat, &cred);
+ return -1;
+ }
+ }
+ gss_release_name(&min_stat, &gss_username);
+
+ /*
+ * Import the name into target_name. Use send_tok to save
+ * local variable space.
+ */
+ send_tok.value = service_name;
+ send_tok.length = strlen(service_name);
+ maj_stat = gss_import_name(&min_stat, &send_tok,
+ (gss_OID) gss_nt_service_name,
+ &target_name);
+ if (maj_stat != GSS_S_COMPLETE) {
+ display_status("parsing name", maj_stat, min_stat);
+ return -1;
+ }
+
+ if (!v1_format) {
+ if (send_token(s, TOKEN_NOOP | TOKEN_CONTEXT_NEXT, empty_token) <
+ 0) {
+ (void) gss_release_name(&min_stat, &target_name);
+ return -1;
+ }
+ }
+
+ /*
+ * Perform the context-establishement loop.
+ *
+ * On each pass through the loop, token_ptr points to the token
+ * to send to the server (or GSS_C_NO_BUFFER on the first pass).
+ * Every generated token is stored in send_tok which is then
+ * transmitted to the server; every received token is stored in
+ * recv_tok, which token_ptr is then set to, to be processed by
+ * the next call to gss_init_sec_context.
+ *
+ * GSS-API guarantees that send_tok's length will be non-zero
+ * if and only if the server is expecting another token from us,
+ * and that gss_init_sec_context returns GSS_S_CONTINUE_NEEDED if
+ * and only if the server has another token to send us.
+ */
+
+ token_ptr = GSS_C_NO_BUFFER;
+ *gss_context = GSS_C_NO_CONTEXT;
+
+ do {
+ maj_stat = gss_init_sec_context(&init_sec_min_stat,
+ cred, gss_context,
+ target_name, mechs.elements,
+ gss_flags, 0,
+ NULL, /* channel bindings */
+ token_ptr, NULL, /* mech type */
+ &send_tok, ret_flags,
+ NULL); /* time_rec */
+
+ if (token_ptr != GSS_C_NO_BUFFER)
+ free(recv_tok.value);
+
+ if (send_tok.length != 0) {
+ if (verbose)
+ printf("Sending init_sec_context token (size=%d)...",
+ (int) send_tok.length);
+ if (send_token(s, v1_format ? 0 : TOKEN_CONTEXT, &send_tok) <
+ 0) {
+ (void) gss_release_buffer(&min_stat, &send_tok);
+ (void) gss_release_name(&min_stat, &target_name);
+ return -1;
+ }
+ }
+ (void) gss_release_buffer(&min_stat, &send_tok);
+
+ if (maj_stat != GSS_S_COMPLETE
+ && maj_stat != GSS_S_CONTINUE_NEEDED) {
+ display_status("initializing context", maj_stat,
+ init_sec_min_stat);
+ (void) gss_release_name(&min_stat, &target_name);
+ (void) gss_release_cred(&min_stat, &cred);
+ if (*gss_context != GSS_C_NO_CONTEXT)
+ gss_delete_sec_context(&min_stat, gss_context,
+ GSS_C_NO_BUFFER);
+ return -1;
+ }
+
+ if (maj_stat == GSS_S_CONTINUE_NEEDED) {
+ if (verbose)
+ printf("continue needed...");
+ if (recv_token(s, &token_flags, &recv_tok) < 0) {
+ (void) gss_release_name(&min_stat, &target_name);
+ return -1;
+ }
+ token_ptr = &recv_tok;
+ }
+ if (verbose)
+ printf("\n");
+ } while (maj_stat == GSS_S_CONTINUE_NEEDED);
+
+ (void) gss_release_cred(&min_stat, &cred);
+ (void) gss_release_name(&min_stat, &target_name);
+ } else {
+ if (send_token(s, TOKEN_NOOP, empty_token) < 0)
+ return -1;
+ }
+
+ return 0;
+}
+
+static void
+read_file(file_name, in_buf)
+ char *file_name;
+ gss_buffer_t in_buf;
+{
+ int fd, count;
+ struct stat stat_buf;
+
+ if ((fd = open(file_name, O_RDONLY, 0)) < 0) {
+ perror("open");
+ fprintf(stderr, "Couldn't open file %s\n", file_name);
+ exit(1);
+ }
+ if (fstat(fd, &stat_buf) < 0) {
+ perror("fstat");
+ exit(1);
+ }
+ in_buf->length = stat_buf.st_size;
+
+ if (in_buf->length == 0) {
+ in_buf->value = NULL;
+ return;
+ }
+
+ if ((in_buf->value = malloc(in_buf->length)) == 0) {
+ fprintf(stderr, "Couldn't allocate %d byte buffer for reading file\n",
+ (int) in_buf->length);
+ exit(1);
+ }
+
+ /* this code used to check for incomplete reads, but you can't get
+ * an incomplete read on any file for which fstat() is meaningful */
+
+ count = read(fd, in_buf->value, in_buf->length);
+ if (count < 0) {
+ perror("read");
+ exit(1);
+ }
+ if (count < (int)in_buf->length)
+ fprintf(stderr, "Warning, only read in %d bytes, expected %d\n",
+ count, (int) in_buf->length);
+}
+
+/*
+ * Function: call_server
+ *
+ * Purpose: Call the "sign" service.
+ *
+ * Arguments:
+ *
+ * host (r) the host providing the service
+ * port (r) the port to connect to on host
+ * service_name (r) the GSS-API service name to authenticate to
+ * gss_flags (r) GSS-API delegation flag (if any)
+ * auth_flag (r) whether to do authentication
+ * wrap_flag (r) whether to do message wrapping at all
+ * encrypt_flag (r) whether to do encryption while wrapping
+ * mic_flag (r) whether to request a MIC from the server
+ * msg (r) the message to have "signed"
+ * use_file (r) whether to treat msg as an input file name
+ * mcount (r) the number of times to send the message
+ *
+ * Returns: 0 on success, -1 on failure
+ *
+ * Effects:
+ *
+ * call_server opens a TCP connection to <host:port> and establishes a
+ * GSS-API context with service_name over the connection. It then
+ * seals msg in a GSS-API token with gss_wrap, sends it to the server,
+ * reads back a GSS-API signature block for msg from the server, and
+ * verifies it with gss_verify. -1 is returned if any step fails,
+ * otherwise 0 is returned. */
+static int
+call_server(host, port, oid, service_name, gss_flags, auth_flag,
+ wrap_flag, encrypt_flag, mic_flag, v1_format, msg, use_file,
+ mcount, username, password)
+ char *host;
+ u_short port;
+ gss_OID oid;
+ char *service_name;
+ OM_uint32 gss_flags;
+ int auth_flag, wrap_flag, encrypt_flag, mic_flag;
+ int v1_format;
+ char *msg;
+ int use_file;
+ int mcount;
+ char *username;
+ char *password;
+{
+ gss_ctx_id_t context = GSS_C_NO_CONTEXT;
+ gss_buffer_desc in_buf, out_buf;
+ int s, state;
+ OM_uint32 ret_flags;
+ OM_uint32 maj_stat, min_stat;
+ gss_name_t src_name, targ_name;
+ gss_buffer_desc sname, tname;
+ OM_uint32 lifetime;
+ gss_OID mechanism, name_type;
+ int is_local;
+ OM_uint32 context_flags;
+ int is_open;
+ gss_qop_t qop_state;
+ gss_OID_set mech_names;
+ gss_buffer_desc oid_name;
+ size_t i;
+ int token_flags;
+
+ /* Open connection */
+ if ((s = connect_to_server(host, port)) < 0)
+ return -1;
+
+ /* Establish context */
+ if (client_establish_context(s, service_name, gss_flags, auth_flag,
+ v1_format, oid, username, password,
+ &context, &ret_flags) < 0) {
+ (void) closesocket(s);
+ return -1;
+ }
+
+ if (auth_flag && verbose) {
+ /* display the flags */
+ display_ctx_flags(ret_flags);
+
+ /* Get context information */
+ maj_stat = gss_inquire_context(&min_stat, context,
+ &src_name, &targ_name, &lifetime,
+ &mechanism, &context_flags,
+ &is_local, &is_open);
+ if (maj_stat != GSS_S_COMPLETE) {
+ display_status("inquiring context", maj_stat, min_stat);
+ return -1;
+ }
+
+ maj_stat = gss_display_name(&min_stat, src_name, &sname, &name_type);
+ if (maj_stat != GSS_S_COMPLETE) {
+ display_status("displaying source name", maj_stat, min_stat);
+ return -1;
+ }
+ maj_stat = gss_display_name(&min_stat, targ_name, &tname,
+ (gss_OID *) NULL);
+ if (maj_stat != GSS_S_COMPLETE) {
+ display_status("displaying target name", maj_stat, min_stat);
+ return -1;
+ }
+ printf("\"%.*s\" to \"%.*s\", lifetime %d, flags %x, %s, %s\n",
+ (int) sname.length, (char *) sname.value,
+ (int) tname.length, (char *) tname.value, lifetime,
+ context_flags,
+ (is_local) ? "locally initiated" : "remotely initiated",
+ (is_open) ? "open" : "closed");
+
+ (void) gss_release_name(&min_stat, &src_name);
+ (void) gss_release_name(&min_stat, &targ_name);
+ (void) gss_release_buffer(&min_stat, &sname);
+ (void) gss_release_buffer(&min_stat, &tname);
+
+ maj_stat = gss_oid_to_str(&min_stat, name_type, &oid_name);
+ if (maj_stat != GSS_S_COMPLETE) {
+ display_status("converting oid->string", maj_stat, min_stat);
+ return -1;
+ }
+ printf("Name type of source name is %.*s.\n",
+ (int) oid_name.length, (char *) oid_name.value);
+ (void) gss_release_buffer(&min_stat, &oid_name);
+
+ /* Now get the names supported by the mechanism */
+ maj_stat = gss_inquire_names_for_mech(&min_stat,
+ mechanism, &mech_names);
+ if (maj_stat != GSS_S_COMPLETE) {
+ display_status("inquiring mech names", maj_stat, min_stat);
+ return -1;
+ }
+
+ maj_stat = gss_oid_to_str(&min_stat, mechanism, &oid_name);
+ if (maj_stat != GSS_S_COMPLETE) {
+ display_status("converting oid->string", maj_stat, min_stat);
+ return -1;
+ }
+ printf("Mechanism %.*s supports %d names\n",
+ (int) oid_name.length, (char *) oid_name.value,
+ (int) mech_names->count);
+ (void) gss_release_buffer(&min_stat, &oid_name);
+
+ for (i = 0; i < mech_names->count; i++) {
+ maj_stat = gss_oid_to_str(&min_stat,
+ &mech_names->elements[i], &oid_name);
+ if (maj_stat != GSS_S_COMPLETE) {
+ display_status("converting oid->string", maj_stat, min_stat);
+ return -1;
+ }
+ printf(" %d: %.*s\n", (int) i,
+ (int) oid_name.length, (char *) oid_name.value);
+
+ (void) gss_release_buffer(&min_stat, &oid_name);
+ }
+ (void) gss_release_oid_set(&min_stat, &mech_names);
+ }
+
+ if (use_file) {
+ read_file(msg, &in_buf);
+ } else {
+ /* Seal the message */
+ in_buf.value = msg;
+ in_buf.length = strlen((char *)in_buf.value);
+ }
+
+ for (i = 0; i < (size_t)mcount; i++) {
+ if (wrap_flag) {
+ maj_stat =
+ gss_wrap(&min_stat, context, encrypt_flag, GSS_C_QOP_DEFAULT,
+ &in_buf, &state, &out_buf);
+ if (maj_stat != GSS_S_COMPLETE) {
+ display_status("wrapping message", maj_stat, min_stat);
+ (void) closesocket(s);
+ (void) gss_delete_sec_context(&min_stat, &context,
+ GSS_C_NO_BUFFER);
+ return -1;
+ } else if (encrypt_flag && !state) {
+ fprintf(stderr, "Warning! Message not encrypted.\n");
+ }
+ } else {
+ out_buf = in_buf;
+ }
+
+ /* Send to server */
+ if (send_token(s, (v1_format ? 0
+ : (TOKEN_DATA |
+ (wrap_flag ? TOKEN_WRAPPED : 0) |
+ (encrypt_flag ? TOKEN_ENCRYPTED : 0) |
+ (mic_flag ? TOKEN_SEND_MIC : 0))),
+ &out_buf) < 0) {
+ (void) closesocket(s);
+ (void) gss_delete_sec_context(&min_stat, &context,
+ GSS_C_NO_BUFFER);
+ return -1;
+ }
+ if (out_buf.value != in_buf.value)
+ (void) gss_release_buffer(&min_stat, &out_buf);
+
+ /* Read signature block into out_buf */
+ if (recv_token(s, &token_flags, &out_buf) < 0) {
+ (void) closesocket(s);
+ (void) gss_delete_sec_context(&min_stat, &context,
+ GSS_C_NO_BUFFER);
+ return -1;
+ }
+
+ if (mic_flag) {
+ /* Verify signature block */
+ maj_stat = gss_verify_mic(&min_stat, context, &in_buf,
+ &out_buf, &qop_state);
+ if (maj_stat != GSS_S_COMPLETE) {
+ display_status("verifying signature", maj_stat, min_stat);
+ (void) closesocket(s);
+ (void) gss_delete_sec_context(&min_stat, &context,
+ GSS_C_NO_BUFFER);
+ return -1;
+ }
+
+ if (verbose)
+ printf("Signature verified.\n");
+ } else {
+ if (verbose)
+ printf("Response received.\n");
+ }
+
+ free(out_buf.value);
+ }
+
+ if (use_file)
+ free(in_buf.value);
+
+ /* Send NOOP */
+ if (!v1_format)
+ (void) send_token(s, TOKEN_NOOP, empty_token);
+
+ if (auth_flag) {
+ /* Delete context */
+ maj_stat = gss_delete_sec_context(&min_stat, &context, &out_buf);
+ if (maj_stat != GSS_S_COMPLETE) {
+ display_status("deleting context", maj_stat, min_stat);
+ (void) closesocket(s);
+ (void) gss_delete_sec_context(&min_stat, &context,
+ GSS_C_NO_BUFFER);
+ return -1;
+ }
+
+ (void) gss_release_buffer(&min_stat, &out_buf);
+ }
+
+ (void) closesocket(s);
+
+ return 0;
+}
+
+static void
+parse_oid(char *mechanism, gss_OID * oid)
+{
+ char *mechstr = 0;
+ gss_buffer_desc tok;
+ OM_uint32 maj_stat, min_stat;
+ size_t i, mechlen = strlen(mechanism);
+
+ if (isdigit((int) mechanism[0])) {
+ mechstr = malloc(mechlen + 5);
+ if (!mechstr) {
+ fprintf(stderr, "Couldn't allocate mechanism scratch!\n");
+ return;
+ }
+ mechstr[0] = '{';
+ mechstr[1] = ' ';
+ for (i = 0; i < mechlen; i++)
+ mechstr[i + 2] = (mechanism[i] == '.') ? ' ' : mechanism[i];
+ mechstr[mechlen + 2] = ' ';
+ mechstr[mechlen + 3] = ' ';
+ mechstr[mechlen + 4] = '\0';
+ tok.value = mechstr;
+ } else
+ tok.value = mechanism;
+ tok.length = strlen(tok.value);
+ maj_stat = gss_str_to_oid(&min_stat, &tok, oid);
+ if (maj_stat != GSS_S_COMPLETE) {
+ display_status("str_to_oid", maj_stat, min_stat);
+ return;
+ }
+ if (mechstr)
+ free(mechstr);
+}
+
+static int max_threads = 1;
+
+#ifdef _WIN32
+static thread_count = 0;
+static HANDLE hMutex = NULL;
+static HANDLE hEvent = NULL;
+
+void
+InitHandles(void)
+{
+ hMutex = CreateMutex(NULL, FALSE, NULL);
+ hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
+}
+
+void
+CleanupHandles(void)
+{
+ CloseHandle(hMutex);
+ CloseHandle(hEvent);
+}
+
+BOOL
+WaitAndIncrementThreadCounter(void)
+{
+ for (;;) {
+ if (WaitForSingleObject(hMutex, INFINITE) == WAIT_OBJECT_0) {
+ if (thread_count < max_threads) {
+ thread_count++;
+ ReleaseMutex(hMutex);
+ return TRUE;
+ } else {
+ ReleaseMutex(hMutex);
+
+ if (WaitForSingleObject(hEvent, INFINITE) == WAIT_OBJECT_0) {
+ continue;
+ } else {
+ return FALSE;
+ }
+ }
+ } else {
+ return FALSE;
+ }
+ }
+}
+
+BOOL
+DecrementAndSignalThreadCounter(void)
+{
+ if (WaitForSingleObject(hMutex, INFINITE) == WAIT_OBJECT_0) {
+ if (thread_count == max_threads)
+ ResetEvent(hEvent);
+ thread_count--;
+ ReleaseMutex(hMutex);
+ return TRUE;
+ } else {
+ return FALSE;
+ }
+}
+#endif
+
+static char *service_name, *server_host, *msg;
+static char *mechanism = 0;
+static u_short port = 4444;
+static int use_file = 0;
+static OM_uint32 gss_flags = GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG;
+static OM_uint32 min_stat;
+static gss_OID oid = GSS_C_NULL_OID;
+static int mcount = 1, ccount = 1;
+static int auth_flag, wrap_flag, encrypt_flag, mic_flag, v1_format;
+static char *username = NULL;
+static char *password = NULL;
+
+static void
+worker_bee(void *unused)
+{
+ if (call_server(server_host, port, oid, service_name,
+ gss_flags, auth_flag, wrap_flag, encrypt_flag, mic_flag,
+ v1_format, msg, use_file, mcount, username, password) < 0)
+ exit(1);
+
+#ifdef _WIN32
+ if (max_threads > 1)
+ DecrementAndSignalThreadCounter();
+#endif
+}
+
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ int i;
+
+ display_file = stdout;
+ auth_flag = wrap_flag = encrypt_flag = mic_flag = 1;
+ v1_format = 0;
+
+ /* Parse arguments. */
+ argc--;
+ argv++;
+ while (argc) {
+ if (strcmp(*argv, "-port") == 0) {
+ argc--;
+ argv++;
+ if (!argc)
+ usage();
+ port = atoi(*argv);
+ } else if (strcmp(*argv, "-mech") == 0) {
+ argc--;
+ argv++;
+ if (!argc)
+ usage();
+ mechanism = *argv;
+ } else if (strcmp(*argv, "-user") == 0) {
+ argc--;
+ argv++;
+ if (!argc)
+ usage();
+ username = *argv;
+ } else if (strcmp(*argv, "-pass") == 0) {
+ argc--;
+ argv++;
+ if (!argc)
+ usage();
+ password = *argv;
+ } else if (strcmp(*argv, "-iakerb") == 0) {
+ mechanism = "{ 1 3 6 1 5 2 5 }";
+ } else if (strcmp(*argv, "-spnego") == 0) {
+ spnego = 1;
+ } else if (strcmp(*argv, "-krb5") == 0) {
+ mechanism = "{ 1 2 840 113554 1 2 2 }";
+#ifdef _WIN32
+ } else if (strcmp(*argv, "-threads") == 0) {
+ argc--;
+ argv++;
+ if (!argc)
+ usage();
+ max_threads = atoi(*argv);
+#endif
+ } else if (strcmp(*argv, "-dce") == 0) {
+ gss_flags |= GSS_C_DCE_STYLE;
+ } else if (strcmp(*argv, "-d") == 0) {
+ gss_flags |= GSS_C_DELEG_FLAG;
+ } else if (strcmp(*argv, "-seq") == 0) {
+ gss_flags |= GSS_C_SEQUENCE_FLAG;
+ } else if (strcmp(*argv, "-noreplay") == 0) {
+ gss_flags &= ~GSS_C_REPLAY_FLAG;
+ } else if (strcmp(*argv, "-nomutual") == 0) {
+ gss_flags &= ~GSS_C_MUTUAL_FLAG;
+ } else if (strcmp(*argv, "-f") == 0) {
+ use_file = 1;
+ } else if (strcmp(*argv, "-q") == 0) {
+ verbose = 0;
+ } else if (strcmp(*argv, "-ccount") == 0) {
+ argc--;
+ argv++;
+ if (!argc)
+ usage();
+ ccount = atoi(*argv);
+ if (ccount <= 0)
+ usage();
+ } else if (strcmp(*argv, "-mcount") == 0) {
+ argc--;
+ argv++;
+ if (!argc)
+ usage();
+ mcount = atoi(*argv);
+ if (mcount < 0)
+ usage();
+ } else if (strcmp(*argv, "-na") == 0) {
+ auth_flag = wrap_flag = encrypt_flag = mic_flag = 0;
+ } else if (strcmp(*argv, "-nw") == 0) {
+ wrap_flag = 0;
+ } else if (strcmp(*argv, "-nx") == 0) {
+ encrypt_flag = 0;
+ } else if (strcmp(*argv, "-nm") == 0) {
+ mic_flag = 0;
+ } else if (strcmp(*argv, "-v1") == 0) {
+ v1_format = 1;
+ } else
+ break;
+ argc--;
+ argv++;
+ }
+ if (argc != 3)
+ usage();
+
+#ifdef _WIN32
+ if (max_threads < 1) {
+ fprintf(stderr, "warning: there must be at least one thread\n");
+ max_threads = 1;
+ }
+#endif
+
+ server_host = *argv++;
+ service_name = *argv++;
+ msg = *argv++;
+
+ if (mechanism)
+ parse_oid(mechanism, &oid);
+
+ if (max_threads == 1) {
+ for (i = 0; i < ccount; i++) {
+ worker_bee(0);
+ }
+ } else {
+#ifdef _WIN32
+ for (i = 0; i < ccount; i++) {
+ if (WaitAndIncrementThreadCounter()) {
+ uintptr_t handle = _beginthread(worker_bee, 0, (void *) 0);
+ if (handle == (uintptr_t) - 1) {
+ exit(1);
+ }
+ } else {
+ exit(1);
+ }
+ }
+#else
+ /* boom */
+ assert(max_threads == 1);
+#endif
+ }
+
+ if (oid != GSS_C_NULL_OID)
+ (void) gss_release_oid(&min_stat, &oid);
+
+#ifdef _WIN32
+ CleanupHandles();
+#endif
+
+ return 0;
+}