aboutsummaryrefslogtreecommitdiff
path: root/src/windows/gss/gss-client.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/windows/gss/gss-client.c')
-rw-r--r--src/windows/gss/gss-client.c746
1 files changed, 534 insertions, 212 deletions
diff --git a/src/windows/gss/gss-client.c b/src/windows/gss/gss-client.c
index d5e8972..d9c1491 100644
--- a/src/windows/gss/gss-client.c
+++ b/src/windows/gss/gss-client.c
@@ -9,7 +9,7 @@
* 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.
+ * 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
@@ -19,119 +19,52 @@
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
-
-#include "gss.h"
-
-static const gss_OID_desc oids[] = {
- {10, "\052\206\110\206\367\022\001\002\001\004"},
-};
-
-const gss_OID_desc * nt_service_name = oids+0;
-
-int
-gss (char *host, char *name, char *oid, char *msg, int port)
-{
- if (port == 0 || port == -1)
- port = 4444;
-
- if (call_server(host, port, name, oid, msg) < 0)
- return 1;
-
- return 0;
-}
-
-/*+
- * 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
- * msg (r) the message to have "signed"
- *
- * Returns: 0 on success, -1 on failure
+/*
+ * Copyright (C) 2004 by the Massachusetts Institute of Technology.
+ * All rights reserved.
*
- * Effects:
+ * 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.
*
- * 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_seal, 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.
+ * 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.
*/
-int
-call_server (char *host, u_short port, char *service_name, char *oid, char *msg)
-{
- gss_ctx_id_t context;
- gss_buffer_desc in_buf, out_buf;
- int s, state;
- OM_uint32 maj_stat, min_stat;
-
- /* Open connection */
- if ((s = connect_to_server(host, port)) < 0)
- return -1;
-
- /* Establish context */
- if (client_establish_context(s, service_name, oid, &context) < 0)
- return -1;
-
- /* Seal the message */
- in_buf.value = msg;
- in_buf.length = strlen(msg) + 1;
- maj_stat = gss_seal(&min_stat, context, 1, GSS_C_QOP_DEFAULT,
- &in_buf, &state, &out_buf);
- if (maj_stat != GSS_S_COMPLETE) {
- display_status("sealing message", maj_stat, min_stat);
- return -1;
- } else if (! state) {
- OkMsgBox ("Warning! Message not encrypted.\n");
- }
-
- /* Send to server */
- if (send_token(s, &out_buf) < 0)
- return -1;
- (void) gss_release_buffer(&min_stat, &out_buf);
-
- /* Read signature block into out_buf */
- if (recv_token(s, &out_buf) < 0)
- return -1;
-
- /* Verify signature block */
- maj_stat = gss_verify(&min_stat, context, &in_buf, &out_buf, &state);
- if (maj_stat != GSS_S_COMPLETE) {
- display_status("verifying signature", maj_stat, min_stat);
- return -1;
- }
- (void) gss_release_buffer(&min_stat, &out_buf);
- OkMsgBox ("Signature verified.");
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <windows.h>
+#include <winsock.h>
- /* 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);
- return -1;
- }
- (void) gss_release_buffer(&min_stat, &out_buf);
+#include <gssapi/gssapi_generic.h>
+#include <gssapi\gssapi_krb5.h>
+#include "gss.h"
+#include "gss-misc.h"
- closesocket(s);
-
- return 0;
-}
+static int verbose = 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
+ * 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
*
@@ -141,34 +74,36 @@ call_server (char *host, u_short port, char *service_name, char *oid, char *msg)
* opened and connected. If an error occurs, an error message is
* displayed and -1 is returned.
*/
-int
-connect_to_server (char *host, u_short port)
+static int connect_to_server(host, port)
+ char *host;
+ u_short port;
{
- struct sockaddr_in saddr;
- struct hostent *hp;
- int s;
-
- if ((hp = gethostbyname(host)) == NULL) {
- OkMsgBox ("Unknown host: %s\n", host);
- return -1;
- }
-
- saddr.sin_family = hp->h_addrtype;
- memcpy((char *)&saddr.sin_addr, hp->h_addr, sizeof(saddr.sin_addr));
- saddr.sin_port = htons(port);
+ struct sockaddr_in saddr;
+ struct hostent *hp;
+ int s;
+
+ if ((hp = gethostbyname(host)) == NULL) {
+ printf("Unknown host: %s\r\n", host);
+ return -1;
+ }
+
+ saddr.sin_family = hp->h_addrtype;
+ memcpy((char *)&saddr.sin_addr, hp->h_addr, sizeof(saddr.sin_addr));
+ saddr.sin_port = htons(port);
- if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
- my_perror("creating socket");
- return -1;
- }
- if (connect(s, (struct sockaddr *)&saddr, sizeof(saddr)) < 0) {
- my_perror("connecting to server");
- return -1;
- }
- return s;
+ 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
@@ -176,15 +111,19 @@ connect_to_server (char *host, u_short port)
*
* Arguments:
*
- * s (r) an established TCP connection to the service
- * sname (r) the ASCII service name of the service
- * context (w) the established GSS-API context
+ * 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
+ * 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:
*
- * sname is imported as a GSS-API name and a GSS-API context is
+ * 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
@@ -194,100 +133,483 @@ connect_to_server (char *host, u_short port)
* unsuccessful, the GSS-API error messages are displayed on stderr
* and -1 is returned.
*/
-int
-client_establish_context (int s, char *sname, char *oid_name,
- gss_ctx_id_t *gss_context)
+int client_establish_context( int s,
+ char *service_name,
+ OM_uint32 gss_flags,
+ int auth_flag,
+ int v1_format,
+ gss_OID oid,
+ gss_ctx_id_t *gss_context,
+ OM_uint32 *ret_flags)
{
- gss_buffer_desc send_tok, recv_tok, *token_ptr;
- gss_name_t target_name;
- OM_uint32 maj_stat, min_stat;
- gss_OID oid = GSS_C_NULL_OID;
-
- if (oid_name && oid_name[0]) {
- send_tok.value = oid_name;
- send_tok.length = strlen(oid_name);
- maj_stat = gss_str_to_oid(&min_stat, &send_tok, &oid);
- if (maj_stat != GSS_S_COMPLETE) {
- display_status("str_to_oid", maj_stat, min_stat);
- return -1;
- }
- }
+ 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;
- /*
- * Import the name into target_name. Use send_tok to save
- * local variable space.
- */
- send_tok.value = sname;
- send_tok.length = strlen(sname) + 1;
- maj_stat = gss_import_name(&min_stat, &send_tok,
- (gss_OID) nt_service_name, &target_name);
- if (maj_stat != GSS_S_COMPLETE) {
- display_status("parsing name", maj_stat, min_stat);
- 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(&min_stat,
- GSS_C_NO_CREDENTIAL,
- gss_context,
- target_name,
- oid,
- GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG,
- 0,
- NULL, /* no channel bindings */
- token_ptr,
- NULL, /* ignore mech type */
- &send_tok,
- NULL, /* ignore ret_flags */
- NULL); /* ignore time_rec */
-
- if (token_ptr != GSS_C_NO_BUFFER)
- (void) gss_release_buffer(&min_stat, &recv_tok);
-
- if (maj_stat!=GSS_S_COMPLETE && maj_stat!=GSS_S_CONTINUE_NEEDED) {
- display_status("initializing context", maj_stat, min_stat);
- (void) gss_release_name(&min_stat, &target_name);
+ /*
+ * 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 (send_tok.length != 0) {
- if (send_token(s, &send_tok) < 0) {
- (void) gss_release_buffer(&min_stat, &send_tok);
+
+ 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;
}
}
- (void) gss_release_buffer(&min_stat, &send_tok);
-
- if (maj_stat == GSS_S_CONTINUE_NEEDED) {
- if (recv_token(s, &recv_tok) < 0) {
+
+ /*
+ * 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,
+ GSS_C_NO_CREDENTIAL,
+ gss_context,
+ target_name,
+ oid,
+ gss_flags,
+ 0,
+ NULL, /* no channel bindings */
+ token_ptr,
+ NULL, /* ignore mech type */
+ &send_tok,
+ ret_flags,
+ NULL); /* ignore 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);
+ if (*gss_context != GSS_C_NO_CONTEXT)
+ gss_delete_sec_context(&min_stat, gss_context,
+ GSS_C_NO_BUFFER);
return -1;
}
- token_ptr = &recv_tok;
- }
- } while (maj_stat == GSS_S_CONTINUE_NEEDED);
- (void) gss_release_name(&min_stat, &target_name);
+ 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("\r\n");
+ } while (maj_stat == GSS_S_CONTINUE_NEEDED);
+
+ (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");
+ printf("Couldn't open file %s\r\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) {
+ printf("Couldn't allocate %d byte buffer for reading file\r\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 < in_buf->length)
+ printf("Warning, only read in %d bytes, expected %d\r\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. */
+int call_server(char *host, u_short port, gss_OID oid, char *service_name,
+ OM_uint32 gss_flags, int auth_flag,
+ int wrap_flag, int encrypt_flag, int mic_flag, int v1_format,
+ char *msg, int use_file, int mcount)
+{
+ gss_ctx_id_t 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, &context,
+ &ret_flags) < 0) {
+ (void) closesocket(s);
+ return -1;
+ }
+
+ if (auth_flag) {
+ if (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\r\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.\r\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\r\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\r\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(msg);
+ }
+
+ for (i = 0; i < 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.\r\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.\r\n");
+ }
+ else {
+ if (verbose)
+ printf("Response received.\r\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, *cp;
+ gss_buffer_desc tok;
+ OM_uint32 maj_stat, min_stat;
+
+ if (isdigit((int) mechanism[0])) {
+ mechstr = malloc(strlen(mechanism)+5);
+ if (!mechstr) {
+ printf("Couldn't allocate mechanism scratch!\r\n");
+ return;
+ }
+ sprintf(mechstr, "{ %s }", mechanism);
+ for (cp = mechstr; *cp; cp++)
+ if (*cp == '.')
+ *cp = ' ';
+ 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);
+}
+
+int
+gss (char *server_host, char *service_name, char *mechanism, char *msg, int port,
+ int verbose, int delegate, int mutual, int replay, int sequence,
+ int v1_format, int auth_flag, int wrap_flag,
+ int encrypt_flag, int mic_flag, int ccount, int mcount, char *ccache)
+{
+ int use_file = 0;
+ OM_uint32 gss_flags = 0, min_stat;
+ gss_OID oid = GSS_C_NULL_OID;
+ OM_uint32 minor_status;
+ int i;
+ int rc = 0;
+
+ if (ccount <= 0) ccount = 1;
+ if (mcount <= 0) mcount = 1;
+
+ if (mechanism && mechanism[0])
+ parse_oid(mechanism, &oid);
+
+ if ( delegate )
+ gss_flags |= GSS_C_DELEG_FLAG;
+ if ( mutual )
+ gss_flags |= GSS_C_MUTUAL_FLAG;
+ if ( replay )
+ gss_flags |= GSS_C_REPLAY_FLAG;
+ if ( sequence )
+ gss_flags |= GSS_C_SEQUENCE_FLAG;
+
+ /* By using this function the independence between the application and
+ * the underlying authentication system is broken
+ */
+ if ( ccache && ccache[0] )
+ gss_krb5_ccache_name(&minor_status, ccache, NULL);
+
+ for (i = 0; i < ccount; i++) {
+ 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) < 0)
+ rc = -1;
+ break;
+ }
+
+ if (oid != GSS_C_NULL_OID)
+ (void) gss_release_oid(&min_stat, &oid);
+
+ return rc;
+}