Browse files

initial commit, please read README!

  • Loading branch information...
0 parents commit 27ff31a356dad663dcc480f9b088520af9ba165a @mike503 committed Jun 10, 2010
Showing with 5,483 additions and 0 deletions.
  1. +155 −0 ChangeLog
  2. +25 −0 LICENSE
  3. +37 −0 Makefile
  4. +78 −0 README
  5. +4 −0 TODO
  6. +4 −0 config
  7. +784 −0 ngx_http_auth_spnego_module.c
  8. +51 −0 spnegohelp/Makefile
  9. +732 −0 spnegohelp/derparse.c
  10. +207 −0 spnegohelp/derparse.h
  11. +806 −0 spnegohelp/spnego.c
  12. +245 −0 spnegohelp/spnego.h
  13. +248 −0 spnegohelp/spnegohelp.c
  14. +58 −0 spnegohelp/spnegohelp.h
  15. +1,880 −0 spnegohelp/spnegoparse.c
  16. +169 −0 spnegohelp/spnegoparse.h
155 ChangeLog
@@ -0,0 +1,155 @@
+commit 432a3e858970da65b8733160845d3ae86e01a90b
+Author: Yoctopetaborg <superflouosATgmailDOTcom>
+Date: Sat Oct 31 05:09:44 2009 +0100
+
+ main and srv config: it can not be that easy
+
+commit a6575bb2c360321426c65aedd58c8a09941d97a4
+Author: Yoctopetaborg <superflouosATgmailDOTcom>
+Date: Wed Sep 16 03:49:57 2009 +0200
+
+ add auth_gss_format_full option flag handling (i.e. FQUN)
+
+commit a1bb7ae1e23216c04bcb6dc037e8d9c2f9832b51
+Author: Yoctopetaborg <superflouosATgmailDOTcom>
+Date: Sat Jun 6 01:41:07 2009 +0200
+
+ ngx_snprintf with %V takes pointer to ngx_str_t, doh
+
+commit a3a7c329fb966d35e9ef86f8d914490c852ef5dc
+Author: Yoctopetaborg <superflouosATgmailDOTcom>
+Date: Sat Jun 6 00:35:03 2009 +0200
+
+ ngx_encode_base64 is void, doh
+
+commit 893937a96ce7074bc8fd4dc10af70801df0cdf43
+Author: Yoctopetaborg <superflouosATgmailDOTcom>
+Date: Sat Jun 6 00:30:26 2009 +0200
+
+ Redundant header manipulation
+
+commit 6ebe58b720f61c9fc67e424e946290c7b9ef0aef
+Author: Yoctopetaborg <superflouosATgmailDOTcom>
+Date: Sun Mar 22 03:56:55 2009 +0100
+
+ version 0.0.3
+
+commit 936722b37c261e69224d3b865e9fd66a81028ea6
+Author: Yoctopetaborg <superflouosATgmailDOTcom>
+Date: Sun Mar 22 03:45:44 2009 +0100
+
+ final touches up
+
+commit 2b817cc4bd9ab246f46270736acb25eb6a05aee6
+Author: Yoctopetaborg <superflouosATgmailDOTcom>
+Date: Sun Mar 22 02:21:46 2009 +0100
+
+ including spnegohelp third party lib source files
+
+commit 03ff04524bc2d5560bd9b169b6bd5d562422c8b0
+Author: Yoctopetaborg <superflouosATgmailDOTcom>
+Date: Sun Mar 22 02:16:44 2009 +0100
+
+ gss_buffer_desc.length contains null
+
+commit 79c33aae1d7d430eeee8b9817be8524c29acadb1
+Author: Yoctopetaborg <superflouosATgmailDOTcom>
+Date: Sat Mar 21 20:24:14 2009 +0100
+
+ perhaps if user set
+
+commit d5c9297a7ce40040810a8a2d93775373be2f2022
+Author: Yoctopetaborg <superflouosATgmailDOTcom>
+Date: Sat Mar 21 20:09:40 2009 +0100
+
+ unbelievable
+
+commit d67fc0fd3c9e6c6363500a31bc9f96e542975a69
+Author: Yoctopetaborg <superflouosATgmailDOTcom>
+Date: Sat Mar 21 19:46:10 2009 +0100
+
+ now it should freaking work
+
+commit 3312ab4ac9ff7664a4a505a4f6d664acee5274eb
+Author: Yoctopetaborg <superflouosATgmailDOTcom>
+Date: Sat Mar 21 04:41:03 2009 +0100
+
+ version 0.0.2
+
+commit 5f3a5d22c9b4e4b6069be9eeddea564efe9e2d47
+Author: Yoctopetaborg <superflouosATgmailDOTcom>
+Date: Sat Mar 21 04:21:55 2009 +0100
+
+ ngx_fubarprintf: jeezus H krist
+
+commit 8b9a26b9c0135aaff3a98730f2b7ec9592d8885c
+Author: Yoctopetaborg <superflouosATgmailDOTcom>
+Date: Sat Mar 21 02:35:01 2009 +0100
+
+ defixes: u_(c)har H'AR H'AR! (matee)
+
+commit 2e1c1e2d4db307015d5afa430d286d2a2bca5454
+Author: Yoctopetaborg <superflouosATgmailDOTcom>
+Date: Fri Mar 20 04:52:16 2009 +0100
+
+ debug: GSSAPI authorizing
+
+commit 6c5dc957c8addcc13781efe6bc21aa2447044772
+Author: Yoctopetaborg <superflouosATgmailDOTcom>
+Date: Fri Mar 20 04:42:46 2009 +0100
+
+ debug: Token decoded
+
+commit 4ef147337cf52d66211d1ea073f8080146844f54
+Author: Yoctopetaborg <superflouosATgmailDOTcom>
+Date: Fri Mar 20 04:11:20 2009 +0100
+
+ compiles, but SIGSEGVs
+
+commit 6ca762d32489399bec1a95f62377d26c75c0588d
+Author: Yoctopetaborg <superflouosATgmailDOTcom>
+Date: Fri Mar 20 00:49:00 2009 +0100
+
+ compile, may be, auth
+
+commit 8da2c1a5538a6f4136f6d498c8ce4a0f8a225e77
+Author: Yoctopetaborg <superflouosATgmailDOTcom>
+Date: Mon Mar 16 02:46:56 2009 +0100
+
+ negotiate, negotiate, negotiate
+
+commit 9c4838f5b15bc2222a0c071761c78324f5852514
+Author: Yoctopetaborg <superflouosATgmailDOTcom>
+Date: Tue Feb 24 01:42:30 2009 +0100
+
+ %X -> %p, wtf
+
+commit 987043b517f664f31b537eb182a74e49c9b8c4e4
+Author: Yoctopetaborg <superflouosATgmailDOTcom>
+Date: Sun Feb 22 23:54:27 2009 +0100
+
+ flag it baby
+
+commit db2ed27c9bd2c3a2e96d825dbc4f104be82a4091
+Author: Yoctopetaborg <superflouosATgmailDOTcom>
+Date: Sun Feb 22 04:05:14 2009 +0100
+
+ OK, perhaps now no err... warnings
+
+commit 4a9d3a012379a787d8b921776757eb9b9832111e
+Author: Yoctopetaborg <superflouosATgmailDOTcom>
+Date: Sat Feb 21 09:25:26 2009 +0100
+
+ not track ChangeLog, sort-of 0.0.0 version
+
+commit f310300f91e34f1d4131d1509b7d686bc300a733
+Author: Yoctopetaborg <superflouosATgmailDOTcom>
+Date: Sat Feb 21 04:56:30 2009 +0100
+
+ To track or not to track ChangeLog, silly archive
+
+commit 8a1c076a05eed0d47a24d9c7a64ce3ff2854fdaf
+Author: Yoctopetaborg <superflouosATgmailDOTcom>
+Date: Sat Feb 21 04:46:59 2009 +0100
+
+ Initial commit
25 LICENSE
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2009 Michal Kowalski <superflouos{at}gmail[dot]com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
37 Makefile
@@ -0,0 +1,37 @@
+
+NAME=ngx_http_auth_spnego_module
+VERSION=0.0.4
+
+NPKG=$(NAME)-$(VERSION)
+NHEAD=$(NAME)-HEAD
+NCURRENT=$(NAME)-current
+
+GIT-FILES:=$(shell git ls-files)
+FILES=ChangeLog $(GIT-FILES)
+
+ChangeLog: $(GIT-FILES)
+ git log > "$@"
+
+arch-release:
+ rm -f ../$(NPKG).tar.gz ../$(NPKG).zip
+ scripts/link-files-to .tmp/$(NPKG) $(FILES)
+ git log > .tmp/$(NPKG)/ChangeLog
+ tar cvzf ../$(NPKG).tar.gz -C .tmp $(NPKG)
+ cd .tmp && zip -r ../../$(NPKG).zip $(NPKG)
+ rm -rf .tmp
+
+arch-current:
+ rm -f ../$(NCURRENT).tar.gz ../$(NCURRENT).zip
+ scripts/link-files-to .tmp/$(NCURRENT) $(FILES)
+ git log > .tmp/$(NCURRENT)/ChangeLog
+ tar cvzf ../$(NCURRENT).tar.gz -C .tmp $(NCURRENT)
+ cd .tmp && zip -r ../../$(NCURRENT).zip $(NCURRENT)
+ rm -rf .tmp
+
+arch-head:
+ rm -f ../$(NNHEAD).tar.gz ../$(NHEAD).zip
+ git archive --format=zip --prefix=$(NHEAD)/ HEAD > ../$(NHEAD).zip
+ git archive --format=tar --prefix=$(NHEAD)/ HEAD | gzip > ../$(NHEAD).tar.gz
+
+clean:
+ rm -f *~
78 README
@@ -0,0 +1,78 @@
+Nginx module to use SPNEGO+GSSAPI+Kerberos for HTTP authentication
+==================================================================
+
+Foreword
+--------
+Michael Shadle paid YoctoPectaBorg from RentACoder to develop this extension.
+
+YPB's notes are what make up the rest of this document.
+
+I (Michael Shadle) have tried to string replace and rename this to be called
+"ngx_http_auth_spnego_module" instead of the previous "ngx_http_auth_sso_module" name.
+
+There may be some oddities due to this. Hopefully not.
+
+mike503@gmail.com
+
+EOM
+
+
+Whatsizit
+---------
+
+Code 97% stolen from mod_auth_gss_krb5 (http://modgssapache.sf.net);
+version 0.0.5.
+
+Compilation
+-----------
+
+First you need to compile the spnegohelp dynamic library. 'make' in that
+subdirectory should do it, then place it by hand somewhere where linker
+and loader can find it by default (probably /usr/lib or perhaps even
+/usr/local/lib depending on your setup).
+
+When compiling from source build as usual adding the --add-module option:
+
+ ./configure --add-module=$PATH_TO_MODULE
+
+inside top Nginx source directory.
+
+Configuration
+-------------
+
+The module has following directives:
+
+- auth_gss: "on"/"off", for ease of unsecuring while leaving other
+ options in the config file,
+
+- auth_gss_realm: what Kerberos realm name to use, for now only used to
+ remove it from full user@realm.name,
+
+- auth_gss_keytab: absolute path-name to keytab file containing service
+ credentials,
+
+- auth_gss_service_name: what service name to use when acquiring
+ credentials. (TOFIX: HTTP but should be a list in case of some other
+ browsers wanting perhaps khttp or http)
+
+TOFIX: for now they are all merely location specific. i.e. no way to
+specify main or per server defaults, except for ...
+
+Examples
+--------
+
+... current "hardcodeds" ;-}
+
+location /topsecret {
+ auth_gss on;
+ auth_gss_realm LOCALDOMAIN;
+ auth_gss_keytab /etc/krb5.keytab;
+ auth_gss_service_name HTTP;
+}
+
+Additional steps...
+-------------------
+
+pray for no segfaults...
+
+TOFIX: perhaps add instructions on how to create the service keytab...
4 TODO
@@ -0,0 +1,4 @@
+TODO
+----
+Change "auth_gss" to "auth_spnego" - this is probably more appropriate - mike503
+Test in various setups (currently cannot confirm this even works for me) - mike503
4 config
@@ -0,0 +1,4 @@
+ngx_addon_name=ngx_http_auth_spnego_module
+HTTP_MODULES="$HTTP_MODULES ngx_http_auth_spnego_module"
+NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_auth_spnego_module.c"
+CORE_LIBS="$CORE_LIBS -lspnegohelp -lgssapi_krb5"
784 ngx_http_auth_spnego_module.c
@@ -0,0 +1,784 @@
+/*
+ * Copyright (C) 2009 Michal Kowalski <superflouos{at}gmail[dot]com>
+ *
+ * Blah, blah, blah...
+ *
+ */
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+/*
+#include <ngx_string.h>
+*/
+
+#include <gssapi/gssapi.h>
+
+#include <krb5.h>
+
+/* #include <spnegohelp.h> */
+int parseNegTokenInit (const unsigned char * negTokenInit,
+ size_t negTokenInitLength,
+ const unsigned char ** kerberosToken,
+ size_t * kerberosTokenLength);
+int makeNegTokenTarg (const unsigned char * kerberosToken,
+ size_t kerberosTokenLength,
+ const unsigned char ** negTokenTarg,
+ size_t * negTokenTargLength);
+
+/* Module handler */
+static ngx_int_t ngx_http_auth_spnego_handler(ngx_http_request_t*);
+
+static void *ngx_http_auth_spnego_create_loc_conf(ngx_conf_t*);
+static char *ngx_http_auth_spnego_merge_loc_conf(ngx_conf_t*, void*, void*);
+static ngx_int_t ngx_http_auth_spnego_init(ngx_conf_t*);
+
+/* stolen straight from mod_auth_gss_krb5.c except for ngx_ mods */
+const char *
+get_gss_error(ngx_pool_t *p,
+ OM_uint32 error_status,
+ char *prefix)
+{
+ OM_uint32 maj_stat, min_stat;
+ OM_uint32 msg_ctx = 0;
+ gss_buffer_desc status_string;
+ char buf[1024];
+ size_t len;
+ ngx_str_t str;
+
+ /* ngx_fubarprintf... what a hack... %Z inserts '\0' */
+ ngx_snprintf((u_char *) buf, sizeof(buf), "%s: %Z", prefix);
+ len = ngx_strlen(buf);
+ do {
+ maj_stat = gss_display_status (&min_stat,
+ error_status,
+ GSS_C_MECH_CODE,
+ GSS_C_NO_OID,
+ &msg_ctx,
+ &status_string);
+ if (sizeof(buf) > len + status_string.length + 1) {
+/*
+ sprintf(buf, "%s:", (char*) status_string.value);
+*/
+ ngx_sprintf((u_char *) buf+len, "%s:%Z", (char*) status_string.value);
+ len += ( status_string.length + 1);
+ }
+ gss_release_buffer(&min_stat, &status_string);
+ } while (!GSS_ERROR(maj_stat) && msg_ctx != 0);
+
+ /* "include" '\0' */
+ str.len = len + 1;
+ str.data = (u_char *) buf;
+ return (char *)(ngx_pstrdup(p, &str));
+}
+
+/* Module per Req/Con CONTEXTUAL Struct */
+
+typedef struct {
+ ngx_str_t token; /* decoded Negotiate token */
+ ngx_int_t head; /* non-zero flag if headers set */
+ ngx_int_t ret; /* current return code */
+} ngx_http_auth_spnego_ctx_t;
+
+/* Module Configuration Struct(s) (main|srv|loc) */
+
+typedef struct {
+ ngx_flag_t protect;
+ ngx_str_t realm;
+ ngx_str_t keytab;
+ ngx_str_t srvcname;
+ ngx_flag_t fqun;
+} ngx_http_auth_spnego_loc_conf_t;
+
+/* Module Directives */
+
+static ngx_command_t ngx_http_auth_spnego_commands[] = {
+
+ /*
+ { ngx_str_t name;
+ ngx_uint_t type;
+ char *(*set)(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
+ ngx_uint_t conf;
+ ngx_uint_t offset;
+ void *post; }
+ */
+
+ { ngx_string("auth_gss"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_auth_spnego_loc_conf_t, protect),
+ NULL },
+
+ { ngx_string("auth_gss_realm"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_str_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_auth_spnego_loc_conf_t, realm),
+ NULL },
+
+ { ngx_string("auth_gss_keytab"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_str_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_auth_spnego_loc_conf_t, keytab),
+ NULL },
+
+ { ngx_string("auth_gss_service_name"),
+ /* TODO change to NGX_CONF_1MORE for "http", "khttp", besides "HTTP" */
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_str_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_auth_spnego_loc_conf_t, srvcname),
+ NULL },
+
+ { ngx_string("auth_gss_format_full"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_auth_spnego_loc_conf_t, fqun),
+ NULL },
+
+ ngx_null_command
+};
+
+/* Module Context */
+
+static ngx_http_module_t ngx_http_auth_spnego_module_ctx = {
+ NULL, /* preconf */
+ ngx_http_auth_spnego_init, /* postconf */
+
+ NULL, /* create main conf (defaults) */
+ NULL, /* init main conf (what's in nginx.conf) */
+
+ NULL, /* create server conf */
+ NULL, /* merge with main */
+
+ ngx_http_auth_spnego_create_loc_conf, /* create location conf */
+ ngx_http_auth_spnego_merge_loc_conf /* merge with server */
+};
+
+/* Module Definition */
+
+/* really ngx_module_s /shrug */
+ngx_module_t ngx_http_auth_spnego_module = {
+ /* ngx_uint_t ctx_index, index, spare{0-3}, version; */
+ NGX_MODULE_V1, /* 0, 0, 0, 0, 0, 0, 1 */
+ &ngx_http_auth_spnego_module_ctx, /* void *ctx */
+ ngx_http_auth_spnego_commands, /* ngx_command_t *commands */
+ NGX_HTTP_MODULE, /* ngx_uint_t type = 0x50545448 */
+ NULL, /* ngx_int_t (*init_master)(ngx_log_t *log) */
+ NULL, /* ngx_int_t (*init_module)(ngx_cycle_t *cycle) */
+ NULL, /* ngx_int_t (*init_process)(ngx_cycle_t *cycle) */
+ NULL, /* ngx_int_t (*init_thread)(ngx_cycle_t *cycle) */
+ NULL, /* void (*exit_thread)(ngx_cycle_t *cycle) */
+ NULL, /* void (*exit_process)(ngx_cycle_t *cycle) */
+ NULL, /* void (*exit_master)(ngx_cycle_t *cycle) */
+ NGX_MODULE_V1_PADDING /* 0, 0, 0, 0, 0, 0, 0, 0 */
+ /* uintptr_t spare_hook{0-7}; */
+};
+
+static void *
+ngx_http_auth_spnego_create_loc_conf(ngx_conf_t *cf)
+{
+ ngx_http_auth_spnego_loc_conf_t *conf;
+
+ conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_auth_spnego_loc_conf_t));
+ if (conf == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ conf->protect = NGX_CONF_UNSET;
+ conf->fqun = NGX_CONF_UNSET;
+
+ /* temporary "debug" */
+#if (NGX_DEBUG)
+ ngx_conf_log_error(NGX_LOG_INFO, cf, 0,
+ "auth_spnego: allocated loc_conf_t (0x%p)", conf);
+#endif
+ /* TODO find out if there is way to enable it only in debug mode */
+
+ return conf;
+}
+
+static char *
+ngx_http_auth_spnego_merge_loc_conf(ngx_conf_t *cf,
+ void *parent,
+ void *child)
+{
+ ngx_http_auth_spnego_loc_conf_t *prev = parent;
+ ngx_http_auth_spnego_loc_conf_t *conf = child;
+
+ /* "off" by default */
+ ngx_conf_merge_off_value(conf->protect, prev->protect, 0);
+
+ ngx_conf_merge_str_value(conf->realm, prev->realm, "LOCALDOMAIN");
+ ngx_conf_merge_str_value(conf->keytab, prev->keytab, "/etc/krb5.keytab");
+ ngx_conf_merge_str_value(conf->srvcname, prev->srvcname, "HTTP");
+
+ ngx_conf_merge_off_value(conf->fqun, prev->fqun, 0);
+
+ /* TODO make it only shout in debug */
+#if (NGX_DEBUG)
+ ngx_conf_log_error(NGX_LOG_INFO, cf, 0, "auth_spnego: protect = %i",
+ conf->protect);
+ ngx_conf_log_error(NGX_LOG_INFO, cf, 0, "auth_spnego: realm@0x%p = %s",
+ conf->realm.data, conf->realm.data);
+ ngx_conf_log_error(NGX_LOG_INFO, cf, 0, "auth_spnego: keytab@0x%p = %s",
+ conf->keytab.data, conf->keytab.data);
+ ngx_conf_log_error(NGX_LOG_INFO, cf, 0, "auth_spnego: srvcname@0x%p = %s",
+ conf->srvcname.data, conf->srvcname.data);
+ ngx_conf_log_error(NGX_LOG_INFO, cf, 0, "auth_spnego: fqun = %i",
+ conf->fqun);
+#endif
+
+ return NGX_CONF_OK;
+}
+
+static ngx_int_t
+ngx_http_auth_spnego_init(ngx_conf_t *cf)
+{
+ ngx_http_handler_pt *h;
+ ngx_http_core_main_conf_t *cmcf;
+
+ cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
+
+ h = ngx_array_push(&cmcf->phases[NGX_HTTP_ACCESS_PHASE].handlers);
+ if (h == NULL) {
+ return NGX_ERROR;
+ }
+
+ *h = ngx_http_auth_spnego_handler;
+
+ return NGX_OK;
+}
+
+static ngx_int_t
+ngx_http_auth_spnego_negotiate_headers(ngx_http_request_t *r,
+ ngx_http_auth_spnego_ctx_t *ctx,
+ ngx_str_t *token)
+{
+ ngx_str_t value = ngx_null_string;
+
+ if (token == NULL) {
+ value.len = sizeof("Negotiate") - 1;
+ value.data = (u_char *) "Negotiate";
+ } else {
+ value.len = sizeof("Negotiate") + token->len;
+ value.data = ngx_pcalloc(r->pool, value.len + 1);
+ if (value.data == NULL) {
+ return NGX_ERROR;
+ }
+ ngx_snprintf(value.data, value.len + 1, "Negotiate %V", token);
+ }
+
+ r->headers_out.www_authenticate = ngx_list_push(&r->headers_out.headers);
+ if (r->headers_out.www_authenticate == NULL) {
+ return NGX_ERROR;
+ }
+
+ r->headers_out.www_authenticate->hash = 1;
+ r->headers_out.www_authenticate->key.len = sizeof("WWW-Authenticate") - 1;
+ r->headers_out.www_authenticate->key.data = (u_char *) "WWW-Authenticate";
+
+ r->headers_out.www_authenticate->value.len = value.len;
+ r->headers_out.www_authenticate->value.data = value.data;
+
+ ctx->head = 1;
+
+ return NGX_OK;
+}
+
+/* sort of like ngx_http_auth_basic_user ... except we store in ctx_t? */
+ngx_int_t
+ngx_http_auth_spnego_token(ngx_http_request_t *r,
+ ngx_http_auth_spnego_ctx_t *ctx)
+{
+ /* not copying or decoding anything, just checking if token is present
+ and where? NOPE, koz ngx_decode_base64 uses ngx_str_t... so might as well... */
+ ngx_str_t token;
+ ngx_str_t decoded;
+
+ if (r->headers_in.authorization == NULL) {
+ return NGX_DECLINED;
+ }
+ /* but don't decode second time? */
+ if (ctx->token.len) return NGX_OK;
+
+ token = r->headers_in.authorization->value;
+
+ if (token.len < sizeof("Negotiate ") - 1
+ || ngx_strncasecmp(token.data, (u_char *) "Negotiate ",
+ sizeof("Negotiate ") - 1) != 0) {
+ return NGX_DECLINED;
+ }
+
+ token.len -= sizeof("Negotiate ") - 1;
+ token.data += sizeof("Negotiate ") - 1;
+
+ while (token.len && token.data[0] == ' ') {
+ token.len--;
+ token.data++;
+ }
+
+ if (token.len == 0) {
+ return NGX_DECLINED;
+ }
+
+ decoded.len = ngx_base64_decoded_length(token.len);
+ decoded.data = ngx_pnalloc(r->pool, decoded.len + 1);
+ if (decoded.data == NULL) {
+ return NGX_ERROR;
+ }
+
+ if (ngx_decode_base64(&decoded, &token) != NGX_OK) {
+ return NGX_DECLINED;
+ }
+
+ decoded.data[decoded.len] = '\0'; /* hmmm */
+
+/* ctx = ngx_palloc(r->pool, sizeof(ngx_http_auth_spnego_ctx_t)); */
+/* if (ctx == NULL) { */
+/* return NGX_ERROR; */
+/* } */
+
+/* ngx_http_set_ctx(r, ctx, ngx_http_auth_spnego_module); */
+
+ ctx->token.len = decoded.len;
+ ctx->token.data = decoded.data;
+ /* off by one? hmmm... */
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "Token decoded");
+
+ return NGX_OK;
+}
+
+/*
+ Because 'remote_user' is assumed to be provided by basic authorization
+ (see ngx_http_variable_remote_user) we are forced to create bogus
+ non-Negotiate authorization header. This may possibly clobber Negotiate
+ token too soon.
+*/
+
+ngx_int_t
+ngx_http_auth_spnego_set_bogus_authorization(ngx_http_request_t *r)
+{
+ ngx_str_t plain, encoded, final;
+ /* jezuz 3 allocs ;( */
+
+ if (r->headers_in.user.len == 0) {
+ return NGX_DECLINED;
+ }
+
+ /* including \0 from sizeof because it's "user:password" */
+ plain.len = r->headers_in.user.len + sizeof("bogus");
+ plain.data = ngx_pnalloc(r->pool, plain.len);
+ if (plain.data == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_snprintf(plain.data, plain.len, "%V:bogus", &r->headers_in.user);
+
+ encoded.len = ngx_base64_encoded_length(plain.len);
+ encoded.data = ngx_pnalloc(r->pool, encoded.len);
+ if (encoded.data == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_encode_base64(&encoded, &plain);
+
+ final.len = sizeof("Basic ") + encoded.len - 1;
+ final.data = ngx_pnalloc(r->pool, final.len);
+ if (final.data == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_snprintf(final.data, final.len, "Basic %V", &encoded);
+
+ /* WARNING clobbering authorization header value */
+ r->headers_in.authorization->value.len = final.len;
+ r->headers_in.authorization->value.data = final.data;
+
+ return NGX_OK;
+}
+
+ngx_int_t
+ngx_http_auth_spnego_auth_user_gss(ngx_http_request_t *r,
+ ngx_http_auth_spnego_ctx_t *ctx,
+ ngx_http_auth_spnego_loc_conf_t *alcf)
+{
+ static unsigned char ntlmProtocol [] = {'N', 'T', 'L', 'M', 'S', 'S', 'P', 0};
+
+ /*
+ nginx stuff
+ */
+ ngx_str_t host_name;
+ ngx_int_t ret = NGX_DECLINED;
+ int rc;
+ int spnego_flag = 0;
+ char *p;
+ /*
+ kerberos stuff
+ */
+ krb5_context krb_ctx = NULL;
+ char *ktname = NULL;
+ /* ngx_str_t kerberosToken; ? */
+ unsigned char *kerberosToken = NULL;
+ size_t kerberosTokenLength = 0;
+ /* this izgotten from de-SPNEGGING original token...
+ and put into gss_accept_sec_context...
+ silly...
+ */
+ ngx_str_t spnegoToken = ngx_null_string;
+ /* unsigned char *spnegoToken = NULL ;
+ size_t spnegoTokenLength = 0; */
+ /*
+ gssapi stuff
+ */
+ OM_uint32 major_status, minor_status, minor_status2;
+ gss_buffer_desc service = GSS_C_EMPTY_BUFFER;
+ gss_name_t my_gss_name = GSS_C_NO_NAME;
+ gss_cred_id_t my_gss_creds = GSS_C_NO_CREDENTIAL;
+ gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
+ gss_ctx_id_t gss_context = GSS_C_NO_CONTEXT;
+ gss_name_t client_name = GSS_C_NO_NAME;
+ gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
+ OM_uint32 ret_flags = 0;
+ gss_cred_id_t delegated_cred = GSS_C_NO_CREDENTIAL;
+
+ /* first, see if there is a point in runing */
+/* ctx = ngx_http_get_module_ctx(r, ngx_http_auth_spnego_module); */
+ /* this really shouldn't 'eppen */
+ if (!ctx || ctx->token.len == 0) {
+ return ret;
+ }
+ /* on with the copy cat show */
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "GSSAPI authorizing");
+
+ krb5_init_context(&krb_ctx);
+
+ ktname = (char *) ngx_pcalloc(r->pool, sizeof("KRB5_KTNAME=")+alcf->keytab.len);
+ if (ktname == NULL) {
+ ret = NGX_ERROR;
+ goto end;
+ }
+ ngx_snprintf((u_char *) ktname, sizeof("KRB5_KTNAME=")+alcf->keytab.len,
+ "KRB5_KTNAME=%V%Z", &alcf->keytab);
+ putenv(ktname);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "Use keytab %V", &alcf->keytab);
+
+ /* TODECIDE: wherefrom use the hostname value for the service name? */
+ host_name = r->headers_in.host->value;
+ /* for now using the name client thinks... */
+ service.length = alcf->srvcname.len + host_name.len + 2;
+ /* @ vel / */
+ service.value = ngx_palloc(r->pool, service.length);
+ if (service.value == NULL) {
+ ret = NGX_ERROR;
+ goto end;
+ }
+ ngx_snprintf(service.value, service.length, "%V@%V%Z",
+ &alcf->srvcname, &host_name);
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "Use service principal %V/%V", &alcf->srvcname, &host_name);
+
+ major_status = gss_import_name(&minor_status, &service,
+ GSS_C_NT_HOSTBASED_SERVICE, &my_gss_name);
+ if (GSS_ERROR(major_status)) {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "%s Used service principal: %s",
+ get_gss_error(r->pool, minor_status,
+ "gss_import_name() failed for service principal"),
+ (unsigned char *)service.value);
+ ret = NGX_ERROR;
+ goto end;
+ }
+
+ major_status = gss_acquire_cred(&minor_status,
+ my_gss_name,
+ GSS_C_INDEFINITE,
+ GSS_C_NO_OID_SET,
+ GSS_C_ACCEPT,
+ &my_gss_creds,
+ NULL,
+ NULL);
+ if (GSS_ERROR(major_status)) {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "%s Used service principal: %s",
+ get_gss_error(r->pool, minor_status,
+ "gss_acquire_cred() failed"),
+ (unsigned char *)service.value);
+ ret = NGX_ERROR;
+ goto end;
+ }
+
+ /* the MEAT? */
+ input_token.length = ctx->token.len + 1;
+ input_token.value = (void *) ctx->token.data;
+ /* Should check first if SPNEGO token */
+ /* but it looks like mit-kerberos version > 1.4.4 DOES include GSSAPI
+ code that supports SPNEGO... ("donated by SUN")... */
+ if ( (rc = parseNegTokenInit (input_token.value,
+ input_token.length,
+ (const unsigned char **) &kerberosToken,
+ &kerberosTokenLength)) != 0 ) {
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "parseNegTokenInit failed with rc=%d", rc);
+ /*
+ Error 1xy -> assume GSSAPI token and continue
+ */
+ if ( rc < 100 || rc > 199 ) {
+ ret = NGX_DECLINED;
+ goto end;
+ /* TOTALLY CLUELESS */
+ }
+ /* feeble NTLM... */
+ if ( (input_token.length >= sizeof ntlmProtocol + 1) &&
+ (!ngx_memcmp(input_token.value, ntlmProtocol, sizeof ntlmProtocol)) ) {
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "received type %d NTLM token",
+ (int) *((unsigned char *)input_token.value + sizeof ntlmProtocol)); /* jeez */
+ ret = NGX_DECLINED;
+ goto end;
+ }
+ spnego_flag = 0;
+ } else {
+ input_token.length = kerberosTokenLength;
+ input_token.value = ngx_pcalloc(r->pool, input_token.length);
+ if (input_token.value == NULL) {
+ ret = NGX_ERROR;
+ goto end;
+ }
+ ngx_memcpy(input_token.value, kerberosToken, input_token.length);
+ spnego_flag = 1;
+ }
+
+ major_status = gss_accept_sec_context(&minor_status,
+ &gss_context,
+ my_gss_creds,
+ &input_token,
+ GSS_C_NO_CHANNEL_BINDINGS,
+ &client_name,
+ NULL,
+ &output_token,
+ &ret_flags,
+ NULL,
+ &delegated_cred);
+
+ if (output_token.length) {
+ ngx_str_t token = ngx_null_string;
+
+ if (spnego_flag) {
+ if ( (rc = makeNegTokenTarg (output_token.value,
+ output_token.length,
+ (const unsigned char **) &spnegoToken.data,
+ &spnegoToken.len)) != 0 ) {
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "makeNegTokenTarg failed with rc=%d",rc);
+ ret = NGX_DECLINED;
+ goto end;
+ }
+ } else {
+ spnegoToken.data = (u_char *) output_token.value;
+ spnegoToken.len = output_token.length - 1;
+ }
+ /* XXX use ap_uuencode() */
+ token.len = ngx_base64_encoded_length(spnegoToken.len);
+ token.data = ngx_pcalloc(r->pool, token.len + 1);
+ if (token.data == NULL) {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "Not enough memory");
+ ret = NGX_ERROR;
+ /* ??? */
+ gss_release_buffer(&minor_status2, &output_token);
+ goto end;
+ }
+ ngx_encode_base64(&token, &spnegoToken); /* did it work (void) */
+
+ /* ??? */
+ gss_release_buffer(&minor_status2, &output_token);
+
+ /* and now here we had to rework ngx_http_auth_spnego_negotiate_headers... */
+
+ if ( (ret = ngx_http_auth_spnego_negotiate_headers(r, ctx, &token)) == NGX_ERROR ) {
+ goto end;
+ }
+ /* ap_table_set(r->err_headers_out, "WWW-Authenticate",
+ ap_pstrcat(r->pool, "Negotiate ", token, NULL)); */
+ }
+
+ /* theesee two ifs could/SHOULD? as well go before the block above?!?
+ headers shouldn't be set if we DECLINE, but i guess there won't be output_token anyway... */
+ if (GSS_ERROR(major_status)) {
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "%s Used service principal: %s",
+ get_gss_error(r->pool, minor_status,
+ "gss_accept_sec_context() failed"),
+ (unsigned char *)service.value);
+ ret = NGX_DECLINED;
+ goto end;
+ }
+
+ if (major_status & GSS_S_CONTINUE_NEEDED) {
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "only one authentication iteration allowed");
+ ret = NGX_DECLINED;
+ goto end;
+ }
+
+ /* INFO */
+ if ( !(ret_flags & GSS_C_REPLAY_FLAG || ret_flags & GSS_C_SEQUENCE_FLAG) ){
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "GSSAPI Warning: no replay protection !");
+ }
+ if ( !(ret_flags & GSS_C_SEQUENCE_FLAG) ){
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "GSSAPI Warning: no sequence protection !");
+ }
+
+ /* getting user name at the other end of the request */
+ major_status = gss_display_name(&minor_status,
+ client_name,
+ &output_token,
+ NULL);
+ gss_release_name(&minor_status, &client_name);
+
+ /* hmm... if he is going to ERROR out now we should do it before setting headers... */
+ if (GSS_ERROR(major_status)) {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "%s", get_gss_error(r->pool, minor_status,
+ "gss_display_name() failed"));
+ ret = NGX_ERROR;
+ goto end;
+ }
+
+ if (output_token.length) {
+ /* TOFIX dirty quick trick for now (no "-1" i.e. include '\0' */
+ ngx_str_t user = { output_token.length,
+ (u_char *) output_token.value };
+
+ r->headers_in.user.data = ngx_pstrdup(r->pool, &user);
+ /* NULL?!? */
+ r->headers_in.user.len = user.len;
+
+ if (alcf->fqun == 0) {
+ p = ngx_strchr(r->headers_in.user.data, '@');
+ if (p != NULL) {
+ if (ngx_strcmp(p+1, alcf->realm.data) == 0) {
+ *p = '\0';
+ r->headers_in.user.len = ngx_strlen(r->headers_in.user.data);
+ }
+ }
+ }
+
+ /* this for the sake of ngx_http_variable_remote_user */
+ ngx_http_auth_spnego_set_bogus_authorization(r);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "user is %V", &r->headers_in.user);
+ }
+
+ gss_release_buffer(&minor_status, &output_token);
+
+ /* saving creds... LATER, for now debug msg... */
+ if (delegated_cred != GSS_C_NO_CREDENTIAL) {
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "Had delegated_cred to save.");
+ }
+
+ ret = NGX_OK;
+ /* goto end; */
+
+ /* well, alright, the end, my friend */
+end:
+ if (delegated_cred)
+ gss_release_cred(&minor_status, &delegated_cred);
+
+ if (output_token.length)
+ gss_release_buffer(&minor_status, &output_token);
+
+ if (client_name != GSS_C_NO_NAME)
+ gss_release_name(&minor_status, &client_name);
+
+ if (gss_context != GSS_C_NO_CONTEXT)
+ gss_delete_sec_context(&minor_status, &gss_context, GSS_C_NO_BUFFER);
+
+ krb5_free_context(krb_ctx);
+ if (my_gss_name != GSS_C_NO_NAME)
+ gss_release_name(&minor_status, &my_gss_name);
+
+ if (my_gss_creds != GSS_C_NO_CREDENTIAL)
+ gss_release_cred(&minor_status, &my_gss_creds);
+
+ return ret;
+}
+
+static ngx_int_t
+ngx_http_auth_spnego_handler(ngx_http_request_t *r)
+{
+ ngx_int_t ret;
+ ngx_http_auth_spnego_ctx_t *ctx;
+ ngx_http_auth_spnego_loc_conf_t *alcf;
+
+ alcf = ngx_http_get_module_loc_conf(r, ngx_http_auth_spnego_module);
+
+ if (alcf->protect == 0) {
+ return NGX_DECLINED;
+ }
+
+ /* looks like we need ctx_t "frst" after all, there is URI level
+ access phase and filesystem level access phase... */
+ ctx = ngx_http_get_module_ctx(r, ngx_http_auth_spnego_module);
+ if (ctx == NULL) {
+ ctx = ngx_palloc(r->pool, sizeof(ngx_http_auth_spnego_ctx_t));
+ if (ctx == NULL) {
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+ ctx->token.len = 0;
+ ctx->token.data = NULL;
+ ctx->head = 0;
+ ctx->ret = NGX_HTTP_UNAUTHORIZED;
+ ngx_http_set_ctx(r, ctx, ngx_http_auth_spnego_module);
+ }
+ /* nope... local fs req creates new ctx... useless... */
+
+ ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "SSO auth handling IN: token.len=%d, head=%d, ret=%d",
+ ctx->token.len, ctx->head, ctx->ret);
+
+ if (ctx->token.len && ctx->head)
+ return ctx->ret;
+ if (r->headers_in.user.data != NULL)
+ return NGX_OK;
+
+ ret = ngx_http_auth_spnego_token(r, ctx);
+
+ if (ret == NGX_OK) {
+ /* ok... looks like client sent some Negotiate'ing authorization header... */
+ ret = ngx_http_auth_spnego_auth_user_gss(r, ctx, alcf);
+ }
+
+ if (ret == NGX_DECLINED) {
+ /* TODEBATE skip if (ctx->head)... */
+ ret = ngx_http_auth_spnego_negotiate_headers(r, ctx, NULL);
+ if (ret == NGX_ERROR) {
+ return (ctx->ret = NGX_HTTP_INTERNAL_SERVER_ERROR);
+ }
+ return (ctx->ret = NGX_HTTP_UNAUTHORIZED);
+ }
+
+ if (ret == NGX_ERROR) {
+ return (ctx->ret = NGX_HTTP_INTERNAL_SERVER_ERROR);
+ }
+
+ /* else NGX_OK */
+ ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "SSO auth handling OUT: token.len=%d, head=%d, ret=%d",
+ ctx->token.len, ctx->head, ret);
+ return (ctx->ret = ret);
+}
51 spnegohelp/Makefile
@@ -0,0 +1,51 @@
+#
+# Linux:
+# -D__LITTLE_ENDIAN__
+# Solaris:
+# -D__BIG_ENDIAN__
+#
+
+CFLAGS = -fpic
+
+LIB = libspnegohelp.a
+SLIB = libspnegohelp.so
+
+OBJS = derparse.o spnego.o spnegohelp.o spnegoparse.o
+
+all:
+ make `uname`
+
+debug:
+ make CFLAGS="$(CFLAGS) -DDEBUG" `uname`
+
+SunOS:
+ make CFLAGS="$(CFLAGS) -D__BIG_ENDIAN__" libs
+
+AIX:
+ make CFLAGS="$(CFLAGS) -D__BIG_ENDIAN__" libs
+
+Linux:
+ make CFLAGS="$(CFLAGS) -D__LITTLE_ENDIAN__" libs
+
+libs: $(LIB) $(SLIB)
+
+$(LIB): $(OBJS)
+ ar -r $(LIB) $(OBJS)
+
+$(SLIB): $(OBJS)
+ gcc --shared -o $(SLIB) $(OBJS)
+
+derparse.o: derparse.c derparse.h spnego.h Makefile
+ gcc -c $(CFLAGS) derparse.c -o $@
+
+spnego.o: spnego.c derparse.h spnego.h spnegoparse.h Makefile
+ gcc -c $(CFLAGS) spnego.c -o $@
+
+spnegoparse.o: spnegoparse.c derparse.h spnego.h spnegoparse.h Makefile
+ gcc -c $(CFLAGS) spnegoparse.c -o $@
+
+spnegohelp.o: spnegohelp.c spnego.h spnegohelp.h Makefile
+ gcc -c $(CFLAGS) spnegohelp.c -o $@
+
+clean:
+ rm $(OBJS) $(LIB) $(SLIB)
732 spnegohelp/derparse.c
@@ -0,0 +1,732 @@
+// Copyright (C) 2002 Microsoft Corporation
+// All rights reserved.
+//
+// THIS CODE AND INFORMATION IS PROVIDED "AS IS"
+// WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+// OR IMPLIED, INCLUDING BUT NOT LIMITED
+// TO THE IMPLIED WARRANTIES OF MERCHANTIBILITY
+// AND/OR FITNESS FOR A PARTICULAR PURPOSE.
+//
+// Date - 10/08/2002
+// Author - Sanj Surati
+
+
+/////////////////////////////////////////////////////////////
+//
+// DERPARSE.C
+//
+// SPNEGO Token Handler Source File
+//
+// Contains implementation of ASN.1 DER read/write functions
+// as defined in DERPARSE.H.
+//
+/////////////////////////////////////////////////////////////
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <memory.h>
+#include "spnego.h"
+#include "derparse.h"
+
+//
+// The GSS Mechanism OID enumeration values (SPNEGO_MECH_OID) control which offset in
+// the array below, that a mechanism can be found.
+//
+MECH_OID g_stcMechOIDList [] =
+{
+ { (unsigned char*) "\x06\x09\x2a\x86\x48\x82\xf7\x12\x01\x02\x02", 11, 9, spnego_mech_oid_Kerberos_V5_Legacy }, // 1.2.840.48018.1.2.2
+ { (unsigned char*) "\x06\x09\x2a\x86\x48\x86\xf7\x12\x01\x02\x02", 11, 9, spnego_mech_oid_Kerberos_V5 }, // 1.2.840.113554.1.2.2
+ { (unsigned char*) "\x06\x06\x2b\x06\x01\x05\x05\x02", 8, 6, spnego_mech_oid_Spnego }, // 1.3.6.1.1.5.5.2
+ { (unsigned char*) "", 0, 0, spnego_mech_oid_NotUsed } // Placeholder
+};
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Function:
+// ASNDerGetLength
+//
+// Parameters:
+// [in] pbLengthData - DER Length Data
+// [in] nBoundaryLength - Length that value must not exceed.
+// [out] pnLength - Filled out with length value
+// [out] pnNumLengthBytes - Filled out with number of bytes
+// consumed by DER length.
+//
+// Returns:
+// int Success - SPNEGO_E_SUCCESS
+// Failure - SPNEGO API Error code
+//
+// Comments :
+// Interprets the data at pbLengthData as a DER length. The length must
+// fit within the bounds of nBoundary length. We do not currently
+// process lengths that take more than 4 bytes.
+//
+////////////////////////////////////////////////////////////////////////////
+
+int ASNDerGetLength( unsigned char* pbLengthData, long nBoundaryLength, long* pnLength,
+ long* pnNumLengthBytes )
+{
+ int nReturn = SPNEGO_E_INVALID_LENGTH;
+ int nNumLengthBytes = 0;
+
+ // First check if the extended length bit is set
+
+ if ( *pbLengthData & LEN_XTND )
+ {
+ // Lower 7 bits contain the number of trailing bytes that describe the length
+ nNumLengthBytes = *pbLengthData & LEN_MASK;
+
+ // Check that the number of bytes we are about to read is within our boundary
+ // constraints
+
+ if ( nNumLengthBytes <= nBoundaryLength - 1 )
+ {
+
+ // For now, our handler won't deal with lengths greater than 4 bytes
+ if ( nNumLengthBytes >= 1 && nNumLengthBytes <= 4 )
+ {
+ // 0 out the initial length
+ *pnLength = 0L;
+
+ // Bump by 1 byte
+ pbLengthData++;
+
+ #ifdef __LITTLE_ENDIAN__
+
+ // There may be a cleaner way to do this, but for now, this seems to be
+ // an easy way to do the transformation
+ switch ( nNumLengthBytes )
+ {
+ case 1:
+ {
+ *( ( (unsigned char*) pnLength ) ) = *pbLengthData;
+ break;
+ }
+
+ case 2:
+ {
+ *( ( (unsigned char*) pnLength ) ) = *(pbLengthData + 1);
+ *( ( (unsigned char*) pnLength ) + 1 ) = *(pbLengthData);
+
+ break;
+ }
+
+ case 3:
+ {
+ *( ( (unsigned char*) pnLength ) ) = *(pbLengthData + 2);
+ *( ( (unsigned char*) pnLength ) + 2 ) = *(pbLengthData + 1);
+ *( ( (unsigned char*) pnLength ) + 3 ) = *(pbLengthData);
+ break;
+ }
+
+ case 4:
+ {
+ *( ( (unsigned char*) pnLength ) ) = *(pbLengthData + 3);
+ *( ( (unsigned char*) pnLength ) + 1 ) = *(pbLengthData + 2);
+ *( ( (unsigned char*) pnLength ) + 2 ) = *(pbLengthData + 1);
+ *( ( (unsigned char*) pnLength ) + 3 ) = *(pbLengthData);
+ break;
+ }
+
+ } // SWITCH ( nNumLengthBytes )
+
+ #else
+ // We are Big-Endian, so the length can be copied in from the source
+ // as is. Ensure that we adjust for the number of bytes we actually
+ // copy.
+
+ memcpy( ( (unsigned char *) pnLength ) + ( 4 - nNumLengthBytes ),
+ pbLengthData, nNumLengthBytes );
+ #endif
+
+ // Account for the initial length byte
+ *pnNumLengthBytes = nNumLengthBytes + 1;
+ nReturn = SPNEGO_E_SUCCESS;
+
+ } // IF Valid Length
+
+ } // IF num bytes to read is within the boundary length
+
+ } // IF xtended length
+ else
+ {
+
+ // Extended bit is not set, so the length is in the value and the one
+ // byte describes the length
+ *pnLength = *pbLengthData & LEN_MASK;
+ *pnNumLengthBytes = 1;
+ nReturn = SPNEGO_E_SUCCESS;
+
+ }
+ LOG(("ASNDerGetLength returned %d\n",nReturn));
+ return nReturn;
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Function:
+// ASNDerCheckToken
+//
+// Parameters:
+// [in] pbTokenData - Token Data
+// [in] nToken - Token identifier to check for
+// [in] nLengthWithToken - Expected token length (with data)
+// [in] nBoundaryLength - Length that value must not exceed.
+// [out] pnLength - Filled out with data length
+// [out] pnTokenLength - Filled out with number of bytes
+// consumed by token identifier and length.
+//
+// Returns:
+// int Success - SPNEGO_E_SUCCESS
+// Failure - SPNEGO API Error code
+//
+// Comments :
+// Checks the data pointed to by pbTokenData for the specified token
+// identifier and the length that immediately follows. If
+// nLengthWithToken is > 0, the calculated length must match. The
+// length must also not exceed the specified boundary length .
+//
+////////////////////////////////////////////////////////////////////////////
+
+int ASNDerCheckToken( unsigned char* pbTokenData, unsigned char nToken,
+ long nLengthWithToken, long nBoundaryLength,
+ long* pnLength, long* pnTokenLength )
+{
+
+ int nReturn = SPNEGO_E_INVALID_LENGTH;
+ long nNumLengthBytes = 0L;
+
+ // Make sure that we've at least got 2 bytes of room to work with
+
+ if ( nBoundaryLength >= 2 )
+ {
+ // The first byte of the token data MUST match the specified token
+ if ( *pbTokenData == nToken )
+ {
+ // Next byte indicates the length
+ pbTokenData++;
+
+ // Get the length described by the token
+ if ( ( nReturn = ASNDerGetLength( pbTokenData, nBoundaryLength, pnLength,
+ &nNumLengthBytes ) ) == SPNEGO_E_SUCCESS )
+ {
+ // Verify that the length is LESS THAN the boundary length
+ // (this should prevent us walking out of our buffer)
+ if ( ( nBoundaryLength - ( nNumLengthBytes + 1 ) < *pnLength ) )
+ {
+
+ nReturn = SPNEGO_E_INVALID_LENGTH;
+
+ }
+
+ // If we were passed a length to check, do so now
+ if ( nLengthWithToken > 0L )
+ {
+
+ // Check that the expected length matches
+ if ( ( nLengthWithToken - ( nNumLengthBytes + 1 ) ) != *pnLength )
+ {
+
+ nReturn = SPNEGO_E_INVALID_LENGTH;
+
+ }
+
+ } // IF need to validate length
+
+ if ( SPNEGO_E_SUCCESS == nReturn )
+ {
+ *pnTokenLength = nNumLengthBytes + 1;
+ }
+
+ } // IF ASNDerGetLength
+
+ } // IF token matches
+ else
+ {
+ nReturn = SPNEGO_E_TOKEN_NOT_FOUND;
+ }
+
+ } // IF Boundary Length is at least 2 bytes
+
+ LOG(("ASNDerCheckToken returned %d\n",nReturn));
+ return nReturn;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Function:
+// ASNDerCheckOID
+//
+// Parameters:
+// [in] pbTokenData - Token Data
+// [in] nMechOID - OID we are looking for
+// [in] nBoundaryLength - Length that value must not exceed.
+// [out] pnTokenLength - Filled out with number of bytes
+// consumed by token and data.
+//
+// Returns:
+// int Success - SPNEGO_E_SUCCESS
+// Failure - SPNEGO API Error code
+//
+// Comments :
+// Checks the data pointed to by pbTokenData for the specified OID.
+//
+////////////////////////////////////////////////////////////////////////////
+
+int ASNDerCheckOID( unsigned char* pbTokenData, SPNEGO_MECH_OID nMechOID, long nBoundaryLength,
+ long* pnTokenLength )
+{
+ int nReturn = 0L;
+ long nLength = 0L;
+
+ // Verify that we have an OID token
+ if ( ( nReturn = ASNDerCheckToken( pbTokenData, OID, 0L, nBoundaryLength,
+ &nLength, pnTokenLength ) ) == SPNEGO_E_SUCCESS )
+ {
+ // Add the data length to the Token Length
+ *pnTokenLength += nLength;
+
+ // Token Lengths plus the actual length must match the length in our OID list element.
+ // If it doesn't, we're done
+ if ( *pnTokenLength == g_stcMechOIDList[nMechOID].iLen )
+ {
+ // Memcompare the token and the expected field
+ if ( memcmp( pbTokenData, g_stcMechOIDList[nMechOID].ucOid, *pnTokenLength ) != 0 )
+ {
+ LOG(("ASNDerCheckOID memcmp failed\n"));
+ nReturn = SPNEGO_E_UNEXPECTED_OID;
+ }
+ }
+ else
+ {
+ LOG(("ASNDerCheckOID token length failed\n"));
+ nReturn = SPNEGO_E_UNEXPECTED_OID;
+ }
+
+ } // IF OID Token CHecks
+
+ LOG(("ASNDerCheckOID returned %d\n",nReturn));
+ return nReturn;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Function:
+// ASNDerCalcNumLengthBytes
+//
+// Parameters:
+// [in] nLength - Length to calculate length bytes for.
+//
+// Returns:
+// int Number of bytes necessary to represent length
+//
+// Comments :
+// Helper function to calculate the number of length bytes necessary to
+// represent a length value. For our purposes, a 32-bit value should be
+// enough to describea length.
+//
+////////////////////////////////////////////////////////////////////////////
+
+int ASNDerCalcNumLengthBytes( long nLength )
+{
+ if ( nLength <= 0x7F )
+ {
+ // A single byte will be sufficient for describing this length.
+ // The byte will simply contain the length
+ return 1;
+ }
+ else if ( nLength <= 0xFF )
+ {
+ // Two bytes are necessary, one to say how many following bytes
+ // describe the length, and one to give the length
+ return 2;
+ }
+ else if ( nLength <= 0xFFFF )
+ {
+ // Three bytes are necessary, one to say how many following bytes
+ // describe the length, and two to give the length
+ return 3;
+ }
+ else if ( nLength <= 0xFFFFFF )
+ {
+ // Four bytes are necessary, one to say how many following bytes
+ // describe the length, and three to give the length
+ return 4;
+ }
+ else
+ {
+ // Five bytes are necessary, one to say how many following bytes
+ // describe the length, and four to give the length
+ return 5;
+ }
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Function:
+// ASNDerCalcTokenLength
+//
+// Parameters:
+// [in] nLength - Length to calculate length bytes for.
+// [in] nDataLength - Actual Data length value.
+//
+// Returns:
+// long Number of bytes necessary to represent a token, length and data
+//
+// Comments :
+// Helper function to calculate a token and value size, based on a
+// supplied length value, and any binary data that will need to be
+// written out.
+//
+////////////////////////////////////////////////////////////////////////////
+
+long ASNDerCalcTokenLength( long nLength, long nDataLength )
+{
+ // Add a byte to the length size to account for a single byte to
+ // hold the token type.
+ long nTotalLength = ASNDerCalcNumLengthBytes( nLength ) + 1;
+
+ return nTotalLength + nDataLength;
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Function:
+// ASNDerCalcElementLength
+//
+// Parameters:
+// [in] nDataLength - Length of data.
+// [out] pnInternalLength - Filled out with length of element
+// without sequence info.
+//
+// Returns:
+// long Number of bytes necessary to represent an element
+//
+// Comments :
+// Helper function to calculate an element length. An element consists
+// of a sequence token, a type token and then the data.
+//
+////////////////////////////////////////////////////////////////////////////
+
+long ASNDerCalcElementLength( long nDataLength, long* pnInternalLength )
+{
+ // First the type token and the actual data
+ long nTotalLength = ASNDerCalcTokenLength( nDataLength, nDataLength );
+
+ // Internal length is the length without the element sequence token
+ if ( NULL != pnInternalLength )
+ {
+ *pnInternalLength = nTotalLength;
+ }
+
+ // Next add in the element's sequence token (remember that its
+ // length is the total length of the type token and data)
+ nTotalLength += ASNDerCalcTokenLength( nTotalLength, 0L );
+
+ return nTotalLength;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Function:
+// ASNDerCalcMechListLength
+//
+// Parameters:
+// [in] mechoid - Mech OID to put in list.
+// [out] pnInternalLength - Filled out with length of element
+// without the primary sequence token.
+//
+// Returns:
+// long Number of bytes necessary to represent a mechList
+//
+// Comments :
+// Helper function to calculate a MechList length. A mechlist consists
+// of a NegTokenInit sequence token, a sequence token for the MechList
+// and finally a list of OIDs. In our case, we only really have one
+// OID.
+//
+////////////////////////////////////////////////////////////////////////////
+
+long ASNDerCalcMechListLength( SPNEGO_MECH_OID mechoid, long* pnInternalLength )
+{
+ // First the OID
+ long nTotalLength = g_stcMechOIDList[mechoid].iLen;
+
+ // Next add in a sequence token
+ nTotalLength += ASNDerCalcTokenLength( nTotalLength, 0L );
+
+ // Internal length is the length without the element sequence token
+ if ( NULL != pnInternalLength )
+ {
+ *pnInternalLength = nTotalLength;
+ }
+
+ // Finally add in the element's sequence token
+ nTotalLength += ASNDerCalcTokenLength( nTotalLength, 0L );
+
+ return nTotalLength;
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Function:
+// ASNDerWriteLength
+//
+// Parameters:
+// [out] pbData - Buffer to write into.
+// [in] nLength - Length to write out.
+//
+// Returns:
+// int Number of bytes written out
+//
+// Comments :
+// Helper function to write out a length value following DER rules .
+//
+////////////////////////////////////////////////////////////////////////////
+
+int ASNDerWriteLength( unsigned char* pbData, long nLength )
+{
+ int nNumBytesRequired = ASNDerCalcNumLengthBytes( nLength );
+ int nNumLengthBytes = nNumBytesRequired - 1;
+
+
+ if ( nNumBytesRequired > 1 )
+ {
+
+ // Write out the number of bytes following which will be used
+ *pbData = (unsigned char ) ( LEN_XTND | nNumLengthBytes );
+
+ // Point to where we'll actually write the length
+ pbData++;
+
+#ifdef __LITTLE_ENDIAN__
+
+ // There may be a cleaner way to do this, but for now, this seems to be
+ // an easy way to do the transformation
+ switch ( nNumLengthBytes )
+ {
+ case 1:
+ {
+ // Cast the length to a single byte, since we know that it
+ // is 0x7F or less (or we wouldn't only need a single byte).
+
+ *pbData = (unsigned char) nLength;
+ break;
+ }
+
+ case 2:
+ {
+ *pbData = *( ( (unsigned char*) &nLength ) + 1 );
+ *( pbData + 1) = *( ( (unsigned char*) &nLength ) );
+ break;
+ }
+
+ case 3:
+ {
+ *pbData = *( ( (unsigned char*) &nLength ) + 3 );
+ *( pbData + 1) = *( ( (unsigned char*) &nLength ) + 2 );
+ *( pbData + 2) = *( ( (unsigned char*) &nLength ) );
+ break;
+ }
+
+ case 4:
+ {
+ *pbData = *( ( (unsigned char*) &nLength ) + 3 );
+ *( pbData + 1) = *( ( (unsigned char*) &nLength ) + 2 );
+ *( pbData + 2) = *( ( (unsigned char*) &nLength ) + 1 );
+ *( pbData + 3) = *( ( (unsigned char*) &nLength ) );
+ break;
+ }
+
+ } // SWITCH ( nNumLengthBytes )
+
+#else
+ // We are Big-Endian, so the length can be copied in from the source
+ // as is. Ensure that we adjust for the number of bytes we actually
+ // copy.
+
+ memcpy( pbData,
+ ( (unsigned char *) &nLength ) + ( 4 - nNumLengthBytes ), nNumLengthBytes );
+#endif
+
+ } // IF > 1 byte for length
+ else
+ {
+ // Cast the length to a single byte, since we know that it
+ // is 0x7F or less (or we wouldn't only need a single byte).
+
+ *pbData = (unsigned char) nLength;
+ }
+
+ return nNumBytesRequired;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Function:
+// ASNDerWriteToken
+//
+// Parameters:
+// [out] pbData - Buffer to write into.
+// [in] ucType - Token Type
+// [in] pbTokenValue - Actual Value
+// [in] nLength - Length of Data.
+//
+// Returns:
+// int Number of bytes written out
+//
+// Comments :
+// Helper function to write out a token and any associated data. If
+// pbTokenValue is non-NULL, then it is written out in addition to the
+// token identifier and the length bytes.
+//
+////////////////////////////////////////////////////////////////////////////
+
+int ASNDerWriteToken( unsigned char* pbData, unsigned char ucType,
+ unsigned char* pbTokenValue, long nLength )
+{
+ int nTotalBytesWrittenOut = 0L;
+ int nNumLengthBytesWritten = 0L;
+
+ // Write out the type
+ *pbData = ucType;
+
+ // Wrote 1 byte, and move data pointer
+ nTotalBytesWrittenOut++;
+ pbData++;
+
+ // Now write out the length and adjust the number of bytes written out
+ nNumLengthBytesWritten = ASNDerWriteLength( pbData, nLength );
+
+ nTotalBytesWrittenOut += nNumLengthBytesWritten;
+ pbData += nNumLengthBytesWritten;
+
+ // Write out the token value if we got one. The assumption is that the
+ // nLength value indicates how many bytes are in pbTokenValue.
+
+ if ( NULL != pbTokenValue )
+ {
+ memcpy( pbData, pbTokenValue, nLength );
+ nTotalBytesWrittenOut += nLength;
+ }
+
+ return nTotalBytesWrittenOut;
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Function:
+// ASNDerWriteOID
+//
+// Parameters:
+// [out] pbData - Buffer to write into.
+// [in] eMechOID - OID to write out.
+//
+// Returns:
+// int Number of bytes written out
+//
+// Comments :
+// Helper function to write out an OID. For these we have the raw bytes
+// listed in a global structure. The caller simply indicates which OID
+// should be written and we will splat out the data.
+//
+////////////////////////////////////////////////////////////////////////////
+
+int ASNDerWriteOID( unsigned char* pbData, SPNEGO_MECH_OID eMechOID )
+{
+
+ memcpy( pbData, g_stcMechOIDList[eMechOID].ucOid, g_stcMechOIDList[eMechOID].iLen );
+
+ return g_stcMechOIDList[eMechOID].iLen;
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Function:
+// ASNDerWriteMechList
+//
+// Parameters:
+// [out] pbData - Buffer to write into.
+// [in] eMechOID - OID to put in MechList.
+//
+// Returns:
+// int Number of bytes written out
+//
+// Comments :
+// Helper function to write out a MechList. A MechList consists of the
+// Init Token Sequence, a sequence token and then the list of OIDs. In
+// our case the OID is from a global array of known OIDs.
+//
+////////////////////////////////////////////////////////////////////////////
+
+long ASNDerWriteMechList( unsigned char* pbData, SPNEGO_MECH_OID mechoid )
+{
+ // First get the length
+ long nInternalLength = 0L;
+ long nMechListLength = ASNDerCalcMechListLength( mechoid, &nInternalLength );
+ long nTempLength = 0L;
+
+ nTempLength = ASNDerWriteToken( pbData, SPNEGO_NEGINIT_ELEMENT_MECHTYPES,
+ NULL, nInternalLength );
+
+ // Adjust the data pointer
+ pbData += nTempLength;
+
+ // Now write the Sequence token and the OID (the OID is a BLOB in the global
+ // structure.
+
+ nTempLength = ASNDerWriteToken( pbData, SPNEGO_CONSTRUCTED_SEQUENCE,
+ g_stcMechOIDList[mechoid].ucOid,
+ g_stcMechOIDList[mechoid].iLen );
+
+ return nMechListLength;
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Function:
+// ASNDerWriteElement
+//
+// Parameters:
+// [out] pbData - Buffer to write into.
+// [in] ucElementSequence - Sequence Token
+// [in] ucType - Token Type
+// [in] pbTokenValue - Actual Value
+// [in] nLength - Length of Data.
+//
+// Returns:
+// int Number of bytes written out
+//
+// Comments :
+// Helper function to write out a SPNEGO Token element. An element
+// consists of a sequence token, a type token and the associated data.
+//
+////////////////////////////////////////////////////////////////////////////
+
+int ASNDerWriteElement( unsigned char* pbData, unsigned char ucElementSequence,
+ unsigned char ucType, unsigned char* pbTokenValue, long nLength )
+{
+ // First get the length
+ long nInternalLength = 0L;
+ long nElementLength = ASNDerCalcElementLength( nLength, &nInternalLength );
+ long nTempLength = 0L;
+
+ // Write out the sequence byte and the length of the type and data
+ nTempLength = ASNDerWriteToken( pbData, ucElementSequence, NULL, nInternalLength );
+
+ // Adjust the data pointer
+ pbData += nTempLength;
+
+ // Now write the type and the data.
+ nTempLength = ASNDerWriteToken( pbData, ucType, pbTokenValue, nLength );
+
+ return nElementLength;
+}
+
207 spnegohelp/derparse.h
@@ -0,0 +1,207 @@
+// Copyright (C) 2002 Microsoft Corporation
+// All rights reserved.
+//
+// THIS CODE AND INFORMATION IS PROVIDED "AS IS"
+// WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+// OR IMPLIED, INCLUDING BUT NOT LIMITED
+// TO THE IMPLIED WARRANTIES OF MERCHANTIBILITY
+// AND/OR FITNESS FOR A PARTICULAR PURPOSE.
+//
+// Date - 10/08/2002
+// Author - Sanj Surati
+
+/////////////////////////////////////////////////////////////
+//
+// DERPARSE.H
+//
+// SPNEGO Token Handler Header File
+//
+// Contains the definitions required to properly parse the
+// SPNEGO DER encoding.
+//
+/////////////////////////////////////////////////////////////
+
+#ifndef __DERPARSE_H__
+#define __DERPARSE_H__
+
+// C++ Specific
+#if defined(__cplusplus)
+extern "C"
+{
+#endif
+
+/* Identifier Types */
+#define IDENTIFIER_MASK 0xC0 // Bits 7 and 8
+#define IDENTIFIER_UNIVERSAL 0x00 // 00 = universal
+#define IDENTIFIER_APPLICATION 0x40 // 01 = application
+#define IDENTIFIER_CONTEXT_SPECIFIC 0x80 // 10 = context specific
+#define IDENTIFIER_PRIVATE 0xC0 // 11 = Private
+
+/* Encoding type */
+
+#define FORM_MASK 0x20 /* Bit 6 */
+#define PRIMITIVE 0x00 /* 0 = primitive */
+#define CONSTRUCTED 0x20 /* 1 = constructed */
+
+/* Universal tags */
+
+#define TAG_MASK 0x1F /* Bits 5 - 1 */
+#define BOOLEAN 0x01 /* 1: TRUE or FALSE */
+#define INTEGER 0x02 /* 2: Arbitrary precision integer */
+#define BITSTRING 0x03 /* 2: Sequence of bits */
+#define OCTETSTRING 0x04 /* 4: Sequence of bytes */
+#define NULLTAG 0x05 /* 5: NULL */
+#define OID 0x06 /* 6: Object Identifier (numeric sequence) */
+#define OBJDESCRIPTOR 0x07 /* 7: Object Descriptor (human readable) */
+#define EXTERNAL 0x08 /* 8: External / Instance Of */
+#define REAL 0x09 /* 9: Real (Mantissa * Base^Exponent) */
+#define ENUMERATED 0x0A /* 10: Enumerated */
+#define EMBEDDED_PDV 0x0B /* 11: Embedded Presentation Data Value */
+#define SEQUENCE 0x10 /* 16: Constructed Sequence / Sequence Of */
+#define SET 0x11 /* 17: Constructed Set / Set Of */
+#define NUMERICSTR 0x12 /* 18: Numeric String (digits only) */
+#define PRINTABLESTR 0x13 /* 19: Printable String */
+#define T61STR 0x14 /* 20: T61 String (Teletex) */
+#define VIDEOTEXSTR 0x15 /* 21: Videotex String */
+#define IA5STR 0x16 /* 22: IA5 String */
+#define UTCTIME 0x17 /* 23: UTC Time */
+#define GENERALIZEDTIME 0x18 /* 24: Generalized Time */
+#define GRAPHICSTR 0x19 /* 25: Graphic String */
+#define VISIBLESTR 0x1A /* 26: Visible String (ISO 646) */
+#define GENERALSTR 0x1B /* 27: General String */
+#define UNIVERSALSTR 0x1C /* 28: Universal String */
+#define BMPSTR 0x1E /* 30: Basic Multilingual Plane String */
+
+/* Length encoding */
+
+#define LEN_XTND 0x80 /* Indefinite or long form */
+#define LEN_MASK 0x7f /* Bits 7 - 1 */
+
+//
+// SPNEGO Token Parsing Constants
+//
+
+
+// Fixed Length of NegTokenInit ReqFlags field
+#define SPNEGO_NEGINIT_MAXLEN_REQFLAGS 2
+
+// Difference in bits for ReqFlags token
+#define SPNEGO_NEGINIT_REQFLAGS_BITDIFF 1
+
+// Fixed Length of NegTokenTarg NegResult field
+#define SPNEGO_NEGTARG_MAXLEN_NEGRESULT 1
+
+// Application Specific Construct - Always at the start of a NegTokenInit
+#define SPNEGO_NEGINIT_APP_CONSTRUCT ( IDENTIFIER_APPLICATION | CONSTRUCTED ) // 0x60
+
+// Constructed Sequence token - after the actual token identifier token
+#define SPNEGO_CONSTRUCTED_SEQUENCE ( SEQUENCE | CONSTRUCTED )
+
+// MechList Type Identifier
+#define SPNEGO_MECHLIST_TYPE ( SEQUENCE | CONSTRUCTED | OID )
+
+//
+// NegTokenInit - Token Identifier and Elements
+//
+
+// NegTokenInit - 0xa0
+#define SPNEGO_NEGINIT_TOKEN_IDENTIFIER ( IDENTIFIER_CONTEXT_SPECIFIC | CONSTRUCTED | \
+ SPNEGO_TOKEN_INIT )
+
+// Structure elements for NegTokenInit
+#define SPNEGO_NEGINIT_MECHTYPES 0x0 // MechTypes is element 0
+#define SPNEGO_NEGINIT_REQFLAGS 0x1 // ReqFlags is element 1
+#define SPNEGO_NEGINIT_MECHTOKEN 0x2 // MechToken is element 2
+#define SPNEGO_NEGINIT_MECHLISTMIC 0x3 // MechListMIC is element 3
+
+// MechTypes element is 0xa0
+#define SPNEGO_NEGINIT_ELEMENT_MECHTYPES ( IDENTIFIER_CONTEXT_SPECIFIC | CONSTRUCTED | \
+ SPNEGO_NEGINIT_MECHTYPES )
+
+// ReqFlags element is 0xa1
+#define SPNEGO_NEGINIT_ELEMENT_REQFLAGS ( IDENTIFIER_CONTEXT_SPECIFIC | CONSTRUCTED | \
+ SPNEGO_NEGINIT_REQFLAGS )
+
+// MechToken element is 0xa2
+#define SPNEGO_NEGINIT_ELEMENT_MECHTOKEN ( IDENTIFIER_CONTEXT_SPECIFIC | CONSTRUCTED | \
+ SPNEGO_NEGINIT_MECHTOKEN )
+
+// MechListMIC element is 0xa3
+#define SPNEGO_NEGINIT_ELEMENT_MECHLISTMIC ( IDENTIFIER_CONTEXT_SPECIFIC | CONSTRUCTED | \
+ SPNEGO_NEGINIT_MECHLISTMIC )
+
+//
+// NegTokenTarg - Token Identifier and Elements
+//
+
+// NegTokenTarg - 0xa1
+#define SPNEGO_NEGTARG_TOKEN_IDENTIFIER ( IDENTIFIER_CONTEXT_SPECIFIC | CONSTRUCTED | \
+ SPNEGO_TOKEN_TARG )
+
+// Structure elements for NegTokenTarg
+#define SPNEGO_NEGTARG_NEGRESULT 0x0 // NegResult is element 0
+#define SPNEGO_NEGTARG_SUPPORTEDMECH 0x1 // SupportedMech is element 1
+#define SPNEGO_NEGTARG_RESPONSETOKEN 0x2 // ResponseToken is element 2
+#define SPNEGO_NEGTARG_MECHLISTMIC 0x3 // MechListMIC is element 3
+
+// NegResult element is 0xa0
+#define SPNEGO_NEGTARG_ELEMENT_NEGRESULT ( IDENTIFIER_CONTEXT_SPECIFIC | CONSTRUCTED | \
+ SPNEGO_NEGTARG_NEGRESULT )
+
+// SupportedMech element is 0xa1
+#define SPNEGO_NEGTARG_ELEMENT_SUPPORTEDMECH ( IDENTIFIER_CONTEXT_SPECIFIC | CONSTRUCTED | \
+ SPNEGO_NEGTARG_SUPPORTEDMECH )
+
+// ResponseToken element is 0xa2
+#define SPNEGO_NEGTARG_ELEMENT_RESPONSETOKEN ( IDENTIFIER_CONTEXT_SPECIFIC | CONSTRUCTED | \
+ SPNEGO_NEGTARG_RESPONSETOKEN )
+
+// MechListMIC element is 0xa3
+#define SPNEGO_NEGTARG_ELEMENT_MECHLISTMIC ( IDENTIFIER_CONTEXT_SPECIFIC | CONSTRUCTED | \
+ SPNEGO_NEGTARG_MECHLISTMIC )
+
+//
+// Defines a GSS Mechanism OID. We keep a single static array
+// of these which we'll use for validation/searches/parsing.
+//
+
+typedef struct _mechOID
+{
+ unsigned char* ucOid; // Byte representation of OID
+ int iLen; // Length of the OID, length and identifier
+ int iActualDataLen; // Length of the actual OID
+ SPNEGO_MECH_OID eMechanismOID; // Which OID is this?
+} MECH_OID;
+
+
+//
+// ASN Der functions
+//
+
+int ASNDerGetLength( unsigned char* pbLengthData, long nBoundaryLength, long* pnLength,
+ long* pnNumLengthBytes );
+int ASNDerCheckToken( unsigned char* pbTokenData, unsigned char nToken,
+ long nCheckLength, long nBoundaryLength, long* pnLength,
+ long* pnTokenLength );
+int ASNDerCheckOID( unsigned char* pbTokenData, SPNEGO_MECH_OID nMechOID, long nBoundaryLength,
+ long* pnTokenLength );
+int ASNDerCalcNumLengthBytes( long nLength );
+long ASNDerCalcTokenLength( long nLength, long nDataLength );
+long ASNDerCalcElementLength( long nDataLength, long* pnInternalLength );
+long ASNDerCalcMechListLength( SPNEGO_MECH_OID mechoid, long* pnInternalLength );
+int ASNDerWriteLength( unsigned char* pbData, long nLength );
+int ASNDerWriteToken( unsigned char* pbData, unsigned char ucType,
+ unsigned char* pbTokenValue, long nLength );
+int ASNDerWriteOID( unsigned char* pbData, SPNEGO_MECH_OID eMechOID );
+long ASNDerWriteMechList( unsigned char* pbData, SPNEGO_MECH_OID mechoid );
+int ASNDerWriteElement( unsigned char* pbData, unsigned char ucElementSequence,
+ unsigned char ucType, unsigned char* pbTokenValue, long nLength );
+
+
+ // C++ Specific
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
+
806 spnegohelp/spnego.c
@@ -0,0 +1,806 @@
+// Copyright (C) 2002 Microsoft Corporation
+// All rights reserved.
+//
+// THIS CODE AND INFORMATION IS PROVIDED "AS IS"
+// WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+// OR IMPLIED, INCLUDING BUT NOT LIMITED
+// TO THE IMPLIED WARRANTIES OF MERCHANTIBILITY
+// AND/OR FITNESS FOR A PARTICULAR PURPOSE.
+//
+// Date - 10/08/2002
+// Author - Sanj Surati
+
+/////////////////////////////////////////////////////////////
+//
+// SPNEGO.C
+//
+// SPNEGO Token Handler Source File
+//
+// Contains implementation of SPNEGO Token Handling API
+// as defined in SPNEGO.H.
+//
+/////////////////////////////////////////////////////////////
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <memory.h>
+#include "spnego.h"
+#include "derparse.h"
+#include "spnegoparse.h"
+
+//
+// Defined in DERPARSE.C
+//
+
+extern MECH_OID g_stcMechOIDList [];
+
+
+/**********************************************************************/
+/** **/
+/** **/
+/** **/
+/** **/
+/** SPNEGO Token Handler API implementation **/
+/** **/
+/** **/
+/** **/
+/** **/
+/**********************************************************************/
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Function:
+// spnegoInitFromBinary
+//
+// Parameters:
+// [in] pbTokenData - Binary Token Data
+// [in] ulLength - Length of binary Token Data
+// [out] phSpnegoToken - SPNEGO_TOKEN_HANDLE pointer
+//
+// Returns:
+// int Success - SPNEGO_E_SUCCESS
+// Failure - SPNEGO API Error code
+//
+// Comments :
+// Initializes a SPNEGO_TOKEN_HANDLE from the supplied
+// binary data. Data is copied locally. Returned data structure
+// must be freed by calling spnegoFreeData().
+//
+////////////////////////////////////////////////////////////////////////////
+
+int spnegoInitFromBinary( unsigned char* pbTokenData, unsigned long ulLength, SPNEGO_TOKEN_HANDLE* phSpnegoToken )
+{
+ int nReturn = SPNEGO_E_INVALID_PARAMETER;
+ SPNEGO_TOKEN** ppSpnegoToken = (SPNEGO_TOKEN**) phSpnegoToken;
+
+ // Pass off to a handler function that allows tighter control over how the token structure
+ // is handled. In this case, we want the token data copied and we want the associated buffer
+ // freed.
+ nReturn = InitTokenFromBinary( SPNEGO_TOKEN_INTERNAL_COPYDATA,
+ SPNEGO_TOKEN_INTERNAL_FLAGS_FREEDATA, pbTokenData,
+ ulLength, ppSpnegoToken );
+
+ LOG(("spnegoInitFromBinary returned %d\n",nReturn));
+ return nReturn;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Function:
+// spnegoCreateNegTokenInit
+//
+// Parameters:
+// [in] MechType - MechType to specify in MechTypeList element
+// [in] ucContextFlags - Context Flags element value
+// [in] pbMechToken - Pointer to binary MechToken Data
+// [in] ulMechTokenLen - Length of MechToken Data
+// [in] pbMechListMIC - Pointer to binary MechListMIC Data
+// [in] ulMechListMICLen - Length of MechListMIC Data
+// [out] phSpnegoToken - SPNEGO_TOKEN_HANDLE pointer
+//
+// Returns:
+// int Success - SPNEGO_E_SUCCESS
+// Failure - SPNEGO API Error code
+//
+// Comments :
+// Initializes a SPNEGO_TOKEN_HANDLE for a NegTokenInit type
+// from the supplied parameters. ucContextFlags may be 0 or must be
+// a valid flag combination. MechToken data can be NULL - if not, it
+// must correspond to the MechType. MechListMIC can also be NULL.
+// Returned data structure must be freed by calling spnegoFreeData().
+//
+////////////////////////////////////////////////////////////////////////////
+
+int spnegoCreateNegTokenInit( SPNEGO_MECH_OID MechType,
+ unsigned char ucContextFlags, unsigned char* pbMechToken,
+ unsigned long ulMechTokenLen, unsigned char* pbMechListMIC,
+ unsigned long ulMechListMICLen, SPNEGO_TOKEN_HANDLE* phSpnegoToken )
+{
+ int nReturn = SPNEGO_E_INVALID_PARAMETER;
+ long nTokenLength = 0L;
+ long nInternalTokenLength = 0L;
+ unsigned char* pbTokenData = NULL;
+ SPNEGO_TOKEN** ppSpnegoToken = (SPNEGO_TOKEN**) phSpnegoToken;
+
+ if ( NULL != ppSpnegoToken &&
+ IsValidMechOid( MechType ) &&
+ IsValidContextFlags( ucContextFlags ) )
+ {
+ // Get the actual token size
+
+ if ( ( nReturn = CalculateMinSpnegoInitTokenSize( ulMechTokenLen, ulMechListMICLen,
+ MechType, ( ucContextFlags != 0L ),
+ &nTokenLength, &nInternalTokenLength ) )
+ == SPNEGO_E_SUCCESS )
+ {
+ // Allocate a buffer to hold the data.
+ pbTokenData = calloc( 1, nTokenLength );
+
+ if ( NULL != pbTokenData )
+ {
+
+ // Now write the token
+ if ( ( nReturn = CreateSpnegoInitToken( MechType,
+ ucContextFlags, pbMechToken,
+ ulMechTokenLen, pbMechListMIC,
+ ulMechListMICLen, pbTokenData,
+ nTokenLength, nInternalTokenLength ) )
+ == SPNEGO_E_SUCCESS )
+ {
+
+ // This will copy our allocated pointer, and ensure that the sructure cleans
+ // up the data later
+ nReturn = InitTokenFromBinary( SPNEGO_TOKEN_INTERNAL_COPYPTR,
+ SPNEGO_TOKEN_INTERNAL_FLAGS_FREEDATA,
+ pbTokenData, nTokenLength, ppSpnegoToken );
+
+ }
+
+ // Cleanup on failure
+ if ( SPNEGO_E_SUCCESS != nReturn )
+ {
+ free( pbTokenData );
+ }
+
+ } // IF alloc succeeded
+ else
+ {
+ nReturn = SPNEGO_E_OUT_OF_MEMORY;
+ }
+
+ } // If calculated token size
+
+ } // IF Valid Parameters
+
+ LOG(("spnegoCreateNegTokenInit returned %d\n",nReturn));
+ return nReturn;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Function:
+// spnegoCreateNegTokenTarg
+//
+// Parameters:
+// [in] MechType - MechType to specify in supported MechType element
+// [in] spnegoNegResult - NegResult value
+// [in] pbMechToken - Pointer to response MechToken Data
+// [in] ulMechTokenLen - Length of MechToken Data
+// [in] pbMechListMIC - Pointer to binary MechListMIC Data
+// [in] ulMechListMICLen - Length of MechListMIC Data
+// [out] phSpnegoToken - SPNEGO_TOKEN_HANDLE pointer
+//
+// Returns:
+// int Success - SPNEGO_E_SUCCESS
+// Failure - SPNEGO API Error code
+//
+// Comments :
+// Initializes a SPNEGO_TOKEN_HANDLE for a NegTokenTarg type
+// from the supplied parameters. MechToken data can be NULL - if not,
+// it must correspond to the MechType. MechListMIC can also be NULL.
+// Returned data structure must be freed by calling spnegoFreeData().
+//
+////////////////////////////////////////////////////////////////////////////
+
+int spnegoCreateNegTokenTarg( SPNEGO_MECH_OID MechType,
+ SPNEGO_NEGRESULT spnegoNegResult, unsigned char* pbMechToken,
+ unsigned long ulMechTokenLen, unsigned char* pbMechListMIC,
+ unsigned long ulMechListMICLen, SPNEGO_TOKEN_HANDLE* phSpnegoToken )
+{
+ int nReturn = SPNEGO_E_INVALID_PARAMETER;
+ long nTokenLength = 0L;
+ long nInternalTokenLength = 0L;
+ unsigned char* pbTokenData = NULL;
+ SPNEGO_TOKEN** ppSpnegoToken = (SPNEGO_TOKEN**) phSpnegoToken;
+
+ //
+ // spnego_mech_oid_NotUsed and spnego_negresult_NotUsed
+ // are okay here, however a valid MechOid is required
+ // if spnego_negresult_success or spnego_negresult_incomplete
+ // is specified.
+ //
+
+ if ( NULL != ppSpnegoToken &&
+
+ ( IsValidMechOid( MechType ) ||
+ spnego_mech_oid_NotUsed == MechType ) &&
+
+ ( IsValidNegResult( spnegoNegResult ) ||
+ spnego_negresult_NotUsed == spnegoNegResult ) &&
+
+ !( !IsValidMechOid( MechType ) &&
+ ( spnego_negresult_success == spnegoNegResult ||
+ spnego_negresult_incomplete == spnegoNegResult ) ) )
+ {
+
+ // Get the actual token size
+
+ if ( ( nReturn = CalculateMinSpnegoTargTokenSize( MechType, spnegoNegResult, ulMechTokenLen,
+ ulMechListMICLen, &nTokenLength,
+ &nInternalTokenLength ) )
+ == SPNEGO_E_SUCCESS )
+ {
+ // Allocate a buffer to hold the data.
+ pbTokenData = calloc( 1, nTokenLength );
+
+ if ( NULL != pbTokenData )
+ {
+
+ // Now write the token
+ if ( ( nReturn = CreateSpnegoTargToken( MechType,
+ spnegoNegResult, pbMechToken,
+ ulMechTokenLen, pbMechListMIC,
+ ulMechListMICLen, pbTokenData,
+ nTokenLength, nInternalTokenLength ) )
+ == SPNEGO_E_SUCCESS )
+ {
+
+ // This will copy our allocated pointer, and ensure that the sructure cleans
+ // up the data later
+ nReturn = InitTokenFromBinary( SPNEGO_TOKEN_INTERNAL_COPYPTR,
+ SPNEGO_TOKEN_INTERNAL_FLAGS_FREEDATA,
+ pbTokenData, nTokenLength, ppSpnegoToken );
+
+ }
+
+ // Cleanup on failure
+ if ( SPNEGO_E_SUCCESS != nReturn )
+ {
+ free( pbTokenData );
+ }
+
+ } // IF alloc succeeded
+ else
+ {
+ nReturn = SPNEGO_E_OUT_OF_MEMORY;
+ }
+
+ } // If calculated token size
+
+ } // IF Valid Parameters
+
+ LOG(("spnegoCreateNegTokenTarg returned %d\n",nReturn));
+ return nReturn;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Function:
+// spnegoTokenGetBinary
+//
+// Parameters:
+// [in] hSpnegoToken - Initialized SPNEGO_TOKEN_HANDLE
+// [out] pbTokenData - Buffer to copy token into
+// [in/out] pulDataLen - Length of pbTokenData buffer, filled out
+// with actual size used upon function return.
+//
+// Returns:
+// int Success - SPNEGO_E_SUCCESS
+// Failure - SPNEGO API Error code
+//
+// Comments :
+// Copies binary SPNEGO token data from hSpnegoToken into the user
+// supplied buffer. If pbTokenData is NULL, or the value in pulDataLen
+// is too small, the function will return SPNEGO_E_BUFFER_TOO_SMALL and
+// fill out pulDataLen with the minimum required buffer size.
+//
+////////////////////////////////////////////////////////////////////////////
+
+int spnegoTokenGetBinary( SPNEGO_TOKEN_HANDLE hSpnegoToken, unsigned char* pbTokenData,
+ unsigned long * pulDataLen )
+{
+ int nReturn = SPNEGO_E_INVALID_PARAMETER;
+ SPNEGO_TOKEN* pSpnegoToken = (SPNEGO_TOKEN*) hSpnegoToken;
+
+ // Check parameters - pbTokenData is optional
+ if ( IsValidSpnegoToken( pSpnegoToken ) &&
+ NULL != pulDataLen )
+ {
+
+ // Check for Buffer too small conditions
+ if ( NULL == pbTokenData ||
+ pSpnegoToken->ulBinaryDataLen > *pulDataLen )
+ {
+ *pulDataLen = pSpnegoToken->ulBinaryDataLen;
+ nReturn = SPNEGO_E_BUFFER_TOO_SMALL;
+ }
+ else
+ {
+ memcpy( pbTokenData, pSpnegoToken->pbBinaryData, pSpnegoToken->ulBinaryDataLen );
+ *pulDataLen = pSpnegoToken->ulBinaryDataLen;
+ nReturn = SPNEGO_E_SUCCESS;
+ }
+
+ } // IF parameters OK
+
+ LOG(("spnegoTokenGetBinary returned %d\n",nReturn));
+ return nReturn;;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Function:
+// spnegoFreeData
+//
+// Parameters:
+// [in] hSpnegoToken - Initialized SPNEGO_TOKEN_HANDLE
+//
+// Returns:
+// void
+//
+// Comments :
+// Frees up resources consumed by hSpnegoToken. The supplied data
+// pointer is invalidated by this function.
+//
+////////////////////////////////////////////////////////////////////////////
+
+void spnegoFreeData( SPNEGO_TOKEN_HANDLE hSpnegoToken )
+{
+ FreeSpnegoToken( (SPNEGO_TOKEN*) hSpnegoToken);
+ return;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Function:
+// spnegoGetTokenType
+//
+// Parameters:
+// [in] hSpnegoToken - Initialized SPNEGO_TOKEN_HANDLE
+// [out] piTokenType - Filled out with token type value.
+//
+// Returns:
+// int Success - SPNEGO_E_SUCCESS
+// Failure - SPNEGO API Error code
+//
+// Comments :
+// The function will analyze hSpnegoToken and return the appropriate
+// type in piTokenType.
+//
+////////////////////////////////////////////////////////////////////////////
+
+int spnegoGetTokenType( SPNEGO_TOKEN_HANDLE hSpnegoToken, int * piTokenType )
+{
+ int nReturn = SPNEGO_E_INVALID_PARAMETER;
+ SPNEGO_TOKEN* pSpnegoToken = (SPNEGO_TOKEN*) hSpnegoToken;
+
+ // Check parameters
+ if ( IsValidSpnegoToken( pSpnegoToken ) &&
+ NULL != piTokenType &&
+ pSpnegoToken)
+ {
+
+ // Check that the type in the structure makes sense
+ if ( SPNEGO_TOKEN_INIT == pSpnegoToken->ucTokenType ||
+ SPNEGO_TOKEN_TARG == pSpnegoToken->ucTokenType )
+ {
+ *piTokenType = pSpnegoToken->ucTokenType;
+ nReturn = SPNEGO_E_SUCCESS;
+ }
+
+ } // IF parameters OK
+
+ LOG(("spnegoGetTokenType returned %d\n",nReturn));
+ return nReturn;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Function:
+// spnegoIsMechTypeAvailable
+//
+// Parameters:
+// [in] hSpnegoToken - Initialized SPNEGO_TOKEN_HANDLE
+// [in] MechOID - MechOID to search MechTypeList for
+// [out] piMechTypeIndex - Filled out with index in MechTypeList
+// element if MechOID is found.
+//
+// Returns:
+// int Success - SPNEGO_E_SUCCESS
+// Failure - SPNEGO API Error code
+//
+// Comments :
+// hSpnegoToken must reference a token of type NegTokenInit. The
+// function will search the MechTypeList element for an OID corresponding
+// to the specified MechOID. If one is found, the index (0 based) will
+// be passed into the piMechTypeIndex parameter.
+//
+////////////////////////////////////////////////////////////////////////////
+
+// Returns the Initial Mech Type in the MechList element in the NegInitToken.
+int spnegoIsMechTypeAvailable( SPNEGO_TOKEN_HANDLE hSpnegoToken, SPNEGO_MECH_OID MechOID, int * piMechTypeIndex )
+{
+ int nReturn = SPNEGO_E_INVALID_PARAMETER;
+ SPNEGO_TOKEN* pSpnegoToken = (SPNEGO_TOKEN*) hSpnegoToken;
+
+ // Check parameters
+ if ( IsValidSpnegoToken( pSpnegoToken ) &&
+ NULL != piMechTypeIndex &&
+ IsValidMechOid( MechOID ) &&
+ SPNEGO_TOKEN_INIT == pSpnegoToken->ucTokenType )
+ {
+
+ // Check if MechList is available
+ if ( pSpnegoToken->aElementArray[SPNEGO_INIT_MECHTYPES_ELEMENT].iElementPresent
+ == SPNEGO_TOKEN_ELEMENT_AVAILABLE )
+ {
+ // Locate the MechOID in the list element
+ nReturn = FindMechOIDInMechList(
+ &pSpnegoToken->aElementArray[SPNEGO_INIT_MECHTYPES_ELEMENT],
+ MechOID, piMechTypeIndex );
+ }
+ else
+ {
+ nReturn = SPNEGO_E_ELEMENT_UNAVAILABLE;
+ }
+
+ } // IF parameters OK
+
+ LOG(("spnegoIsMechTypeAvailable returned %d\n",nReturn));
+ return nReturn;;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Function:
+// spnegoGetContextFlags
+//
+// Parameters:
+// [in] hSpnegoToken - Initialized SPNEGO_TOKEN_HANDLE
+// [out] pucContextFlags - Filled out with ContextFlags value.
+//
+// Returns:
+// int Success - SPNEGO_E_SUCCESS
+// Failure - SPNEGO API Error code
+//
+// Comments :
+// hSpnegoToken must reference a token of type NegTokenInit. The
+// function will copy data from the ContextFlags element into the
+// location pucContextFlags points to. Note that the function will
+// fail if the actual ContextFlags data appears invalid.
+//
+////////////////////////////////////////////////////////////////////////////
+
+int spnegoGetContextFlags( SPNEGO_TOKEN_HAND