├── ChangeLog ├── LICENSE.md ├── Makefile ├── README.md ├── config ├── ngx_http_auth_sso_module.c └── spnegohelp ├── Makefile ├── derparse.c ├── derparse.h ├── spnego.c ├── spnego.h ├── spnegohelp.c ├── spnegohelp.h ├── spnegoparse.c └── spnegoparse.h /ChangeLog: -------------------------------------------------------------------------------- 1 | commit 432a3e858970da65b8733160845d3ae86e01a90b 2 | Author: Yoctopetaborg 3 | Date: Sat Oct 31 05:09:44 2009 +0100 4 | 5 | main and srv config: it can not be that easy 6 | 7 | commit a6575bb2c360321426c65aedd58c8a09941d97a4 8 | Author: Yoctopetaborg 9 | Date: Wed Sep 16 03:49:57 2009 +0200 10 | 11 | add auth_gss_format_full option flag handling (i.e. FQUN) 12 | 13 | commit a1bb7ae1e23216c04bcb6dc037e8d9c2f9832b51 14 | Author: Yoctopetaborg 15 | Date: Sat Jun 6 01:41:07 2009 +0200 16 | 17 | ngx_snprintf with %V takes pointer to ngx_str_t, doh 18 | 19 | commit a3a7c329fb966d35e9ef86f8d914490c852ef5dc 20 | Author: Yoctopetaborg 21 | Date: Sat Jun 6 00:35:03 2009 +0200 22 | 23 | ngx_encode_base64 is void, doh 24 | 25 | commit 893937a96ce7074bc8fd4dc10af70801df0cdf43 26 | Author: Yoctopetaborg 27 | Date: Sat Jun 6 00:30:26 2009 +0200 28 | 29 | Redundant header manipulation 30 | 31 | commit 6ebe58b720f61c9fc67e424e946290c7b9ef0aef 32 | Author: Yoctopetaborg 33 | Date: Sun Mar 22 03:56:55 2009 +0100 34 | 35 | version 0.0.3 36 | 37 | commit 936722b37c261e69224d3b865e9fd66a81028ea6 38 | Author: Yoctopetaborg 39 | Date: Sun Mar 22 03:45:44 2009 +0100 40 | 41 | final touches up 42 | 43 | commit 2b817cc4bd9ab246f46270736acb25eb6a05aee6 44 | Author: Yoctopetaborg 45 | Date: Sun Mar 22 02:21:46 2009 +0100 46 | 47 | including spnegohelp third party lib source files 48 | 49 | commit 03ff04524bc2d5560bd9b169b6bd5d562422c8b0 50 | Author: Yoctopetaborg 51 | Date: Sun Mar 22 02:16:44 2009 +0100 52 | 53 | gss_buffer_desc.length contains null 54 | 55 | commit 79c33aae1d7d430eeee8b9817be8524c29acadb1 56 | Author: Yoctopetaborg 57 | Date: Sat Mar 21 20:24:14 2009 +0100 58 | 59 | perhaps if user set 60 | 61 | commit d5c9297a7ce40040810a8a2d93775373be2f2022 62 | Author: Yoctopetaborg 63 | Date: Sat Mar 21 20:09:40 2009 +0100 64 | 65 | unbelievable 66 | 67 | commit d67fc0fd3c9e6c6363500a31bc9f96e542975a69 68 | Author: Yoctopetaborg 69 | Date: Sat Mar 21 19:46:10 2009 +0100 70 | 71 | now it should freaking work 72 | 73 | commit 3312ab4ac9ff7664a4a505a4f6d664acee5274eb 74 | Author: Yoctopetaborg 75 | Date: Sat Mar 21 04:41:03 2009 +0100 76 | 77 | version 0.0.2 78 | 79 | commit 5f3a5d22c9b4e4b6069be9eeddea564efe9e2d47 80 | Author: Yoctopetaborg 81 | Date: Sat Mar 21 04:21:55 2009 +0100 82 | 83 | ngx_fubarprintf: jeezus H krist 84 | 85 | commit 8b9a26b9c0135aaff3a98730f2b7ec9592d8885c 86 | Author: Yoctopetaborg 87 | Date: Sat Mar 21 02:35:01 2009 +0100 88 | 89 | defixes: u_(c)har H'AR H'AR! (matee) 90 | 91 | commit 2e1c1e2d4db307015d5afa430d286d2a2bca5454 92 | Author: Yoctopetaborg 93 | Date: Fri Mar 20 04:52:16 2009 +0100 94 | 95 | debug: GSSAPI authorizing 96 | 97 | commit 6c5dc957c8addcc13781efe6bc21aa2447044772 98 | Author: Yoctopetaborg 99 | Date: Fri Mar 20 04:42:46 2009 +0100 100 | 101 | debug: Token decoded 102 | 103 | commit 4ef147337cf52d66211d1ea073f8080146844f54 104 | Author: Yoctopetaborg 105 | Date: Fri Mar 20 04:11:20 2009 +0100 106 | 107 | compiles, but SIGSEGVs 108 | 109 | commit 6ca762d32489399bec1a95f62377d26c75c0588d 110 | Author: Yoctopetaborg 111 | Date: Fri Mar 20 00:49:00 2009 +0100 112 | 113 | compile, may be, auth 114 | 115 | commit 8da2c1a5538a6f4136f6d498c8ce4a0f8a225e77 116 | Author: Yoctopetaborg 117 | Date: Mon Mar 16 02:46:56 2009 +0100 118 | 119 | negotiate, negotiate, negotiate 120 | 121 | commit 9c4838f5b15bc2222a0c071761c78324f5852514 122 | Author: Yoctopetaborg 123 | Date: Tue Feb 24 01:42:30 2009 +0100 124 | 125 | %X -> %p, wtf 126 | 127 | commit 987043b517f664f31b537eb182a74e49c9b8c4e4 128 | Author: Yoctopetaborg 129 | Date: Sun Feb 22 23:54:27 2009 +0100 130 | 131 | flag it baby 132 | 133 | commit db2ed27c9bd2c3a2e96d825dbc4f104be82a4091 134 | Author: Yoctopetaborg 135 | Date: Sun Feb 22 04:05:14 2009 +0100 136 | 137 | OK, perhaps now no err... warnings 138 | 139 | commit 4a9d3a012379a787d8b921776757eb9b9832111e 140 | Author: Yoctopetaborg 141 | Date: Sat Feb 21 09:25:26 2009 +0100 142 | 143 | not track ChangeLog, sort-of 0.0.0 version 144 | 145 | commit f310300f91e34f1d4131d1509b7d686bc300a733 146 | Author: Yoctopetaborg 147 | Date: Sat Feb 21 04:56:30 2009 +0100 148 | 149 | To track or not to track ChangeLog, silly archive 150 | 151 | commit 8a1c076a05eed0d47a24d9c7a64ce3ff2854fdaf 152 | Author: Yoctopetaborg 153 | Date: Sat Feb 21 04:46:59 2009 +0100 154 | 155 | Initial commit 156 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (C) 2009 Michal Kowalski . 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions 5 | are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | 2. Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | 13 | THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND 14 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 17 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19 | OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 | SUCH DAMAGE. 24 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | NAME=ngx_http_auth_sso_module 3 | VERSION=0.0.3 4 | 5 | NPKG=$(NAME)-$(VERSION) 6 | NHEAD=$(NAME)-HEAD 7 | NCURRENT=$(NAME)-current 8 | 9 | GIT-FILES:=$(shell git ls-files) 10 | FILES=ChangeLog $(GIT-FILES) 11 | 12 | ChangeLog: $(GIT-FILES) 13 | git log > "$@" 14 | 15 | arch-release: 16 | rm -f ../$(NPKG).tar.gz ../$(NPKG).zip 17 | scripts/link-files-to .tmp/$(NPKG) $(FILES) 18 | git log > .tmp/$(NPKG)/ChangeLog 19 | tar cvzf ../$(NPKG).tar.gz -C .tmp $(NPKG) 20 | cd .tmp && zip -r ../../$(NPKG).zip $(NPKG) 21 | rm -rf .tmp 22 | 23 | arch-current: 24 | rm -f ../$(NCURRENT).tar.gz ../$(NCURRENT).zip 25 | scripts/link-files-to .tmp/$(NCURRENT) $(FILES) 26 | git log > .tmp/$(NCURRENT)/ChangeLog 27 | tar cvzf ../$(NCURRENT).tar.gz -C .tmp $(NCURRENT) 28 | cd .tmp && zip -r ../../$(NCURRENT).zip $(NCURRENT) 29 | rm -rf .tmp 30 | 31 | arch-head: 32 | rm -f ../$(NNHEAD).tar.gz ../$(NHEAD).zip 33 | git archive --format=zip --prefix=$(NHEAD)/ HEAD > ../$(NHEAD).zip 34 | git archive --format=tar --prefix=$(NHEAD)/ HEAD | gzip > ../$(NHEAD).tar.gz 35 | 36 | clean: 37 | rm -f *~ 38 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # nginx-mod-auth-kerb 2 | This is an [nginx](http://nginx.org/) module to enable the use of SPNEGO, 3 | GSSAPI, and Kerberos for HTTP SSO authentication. 4 | 5 | *Warning*: This code probably has a ton of bugs. Personally, I suggest that you 6 | don't use it in production without a bit of auditing. This repository was 7 | created because the original download site disappeared. 8 | 9 | ## Compilation 10 | First, you need to compile the spnegohelp dynamic library. 'make' in that 11 | subdirectory should do it, then place it by hand somewhere where linker 12 | and loader can find it by default (probably /usr/lib or perhaps even 13 | /usr/local/lib depending on your setup). 14 | 15 | When compiling from source build as usual adding the -add-module option: 16 | 17 | ```` 18 | ./configure --add-module=$PATH_TO_MODULE 19 | ```` 20 | 21 | inside top Nginx source directory. 22 | 23 | ## Configuration 24 | The module has following directives: 25 | 26 | - auth_gss: "on"/"off", for ease of unsecuring while leaving other 27 | options in the config file, 28 | 29 | - auth_gss_realm: what Kerberos realm name to use, for now only used to 30 | remove it from full user@realm.name, 31 | 32 | - auth_gss_keytab: absolute path-name to keytab file containing service 33 | credentials, 34 | 35 | - auth_gss_service_name: what service name to use when acquiring 36 | credentials. (TOFIX: HTTP but should be a list in case of some other 37 | browsers wanting perhaps khttp or http), 38 | 39 | - auth_gss_format_full: "on"/"off", default "off", when "on" realm name will not 40 | be stripped from $remote_user variable 41 | 42 | FIXME: for now they are all merely location specific. i.e. no way to 43 | specify main or per server defaults, except for ... 44 | 45 | ## Examples 46 | ```` 47 | ... current "hardcodeds" ;-} 48 | 49 | location /topsecret { 50 | auth_gss on; 51 | auth_gss_realm LOCALDOMAIN; 52 | auth_gss_keytab /etc/krb5.keytab; 53 | auth_gss_service_name HTTP; 54 | } 55 | ```` 56 | 57 | ## Credit and License 58 | This code is derived from the [Apache Kerberos/SPNEGO module](http://modgssapache.sf.net). 59 | 60 | Please see the LICENSE.md file for more information. 61 | -------------------------------------------------------------------------------- /config: -------------------------------------------------------------------------------- 1 | ngx_addon_name=ngx_http_auth_sso_module 2 | HTTP_MODULES="$HTTP_MODULES ngx_http_auth_sso_module" 3 | NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_auth_sso_module.c" 4 | CORE_LIBS="$CORE_LIBS -lspnegohelp -lgssapi_krb5 `krb5-config --libs gssapi`" 5 | -------------------------------------------------------------------------------- /ngx_http_auth_sso_module.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2009 Michal Kowalski 3 | * 4 | * Blah, blah, blah... 5 | * 6 | */ 7 | 8 | #include 9 | #include 10 | #include 11 | /* 12 | #include 13 | */ 14 | 15 | #include 16 | 17 | #include 18 | 19 | /* #include */ 20 | int parseNegTokenInit (const unsigned char * negTokenInit, 21 | size_t negTokenInitLength, 22 | const unsigned char ** kerberosToken, 23 | size_t * kerberosTokenLength); 24 | int makeNegTokenTarg (const unsigned char * kerberosToken, 25 | size_t kerberosTokenLength, 26 | const unsigned char ** negTokenTarg, 27 | size_t * negTokenTargLength); 28 | 29 | /* Module handler */ 30 | static ngx_int_t ngx_http_auth_sso_handler(ngx_http_request_t*); 31 | 32 | static void *ngx_http_auth_sso_create_loc_conf(ngx_conf_t*); 33 | static char *ngx_http_auth_sso_merge_loc_conf(ngx_conf_t*, void*, void*); 34 | static ngx_int_t ngx_http_auth_sso_init(ngx_conf_t*); 35 | 36 | /* stolen straight from mod_auth_gss_krb5.c except for ngx_ mods */ 37 | const char * 38 | get_gss_error(ngx_pool_t *p, 39 | OM_uint32 error_status, 40 | char *prefix) 41 | { 42 | OM_uint32 maj_stat, min_stat; 43 | OM_uint32 msg_ctx = 0; 44 | gss_buffer_desc status_string; 45 | char buf[1024]; 46 | size_t len; 47 | ngx_str_t str; 48 | 49 | /* ngx_fubarprintf... what a hack... %Z inserts '\0' */ 50 | ngx_snprintf((u_char *) buf, sizeof(buf), "%s: %Z", prefix); 51 | len = ngx_strlen(buf); 52 | do { 53 | maj_stat = gss_display_status (&min_stat, 54 | error_status, 55 | GSS_C_MECH_CODE, 56 | GSS_C_NO_OID, 57 | &msg_ctx, 58 | &status_string); 59 | if (sizeof(buf) > len + status_string.length + 1) { 60 | /* 61 | sprintf(buf, "%s:", (char*) status_string.value); 62 | */ 63 | ngx_sprintf((u_char *) buf+len, "%s:%Z", (char*) status_string.value); 64 | len += ( status_string.length + 1); 65 | } 66 | gss_release_buffer(&min_stat, &status_string); 67 | } while (!GSS_ERROR(maj_stat) && msg_ctx != 0); 68 | 69 | /* "include" '\0' */ 70 | str.len = len + 1; 71 | str.data = (u_char *) buf; 72 | return (char *)(ngx_pstrdup(p, &str)); 73 | } 74 | 75 | /* Module per Req/Con CONTEXTUAL Struct */ 76 | 77 | typedef struct { 78 | ngx_str_t token; /* decoded Negotiate token */ 79 | ngx_int_t head; /* non-zero flag if headers set */ 80 | ngx_int_t ret; /* current return code */ 81 | } ngx_http_auth_sso_ctx_t; 82 | 83 | /* Module Configuration Struct(s) (main|srv|loc) */ 84 | 85 | typedef struct { 86 | ngx_flag_t protect; 87 | ngx_str_t realm; 88 | ngx_str_t keytab; 89 | ngx_str_t srvcname; 90 | ngx_flag_t fqun; 91 | } ngx_http_auth_sso_loc_conf_t; 92 | 93 | /* Module Directives */ 94 | 95 | static ngx_command_t ngx_http_auth_sso_commands[] = { 96 | 97 | /* 98 | { ngx_str_t name; 99 | ngx_uint_t type; 100 | char *(*set)(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); 101 | ngx_uint_t conf; 102 | ngx_uint_t offset; 103 | void *post; } 104 | */ 105 | 106 | { ngx_string("auth_gss"), 107 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, 108 | ngx_conf_set_flag_slot, 109 | NGX_HTTP_LOC_CONF_OFFSET, 110 | offsetof(ngx_http_auth_sso_loc_conf_t, protect), 111 | NULL }, 112 | 113 | { ngx_string("auth_gss_realm"), 114 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, 115 | ngx_conf_set_str_slot, 116 | NGX_HTTP_LOC_CONF_OFFSET, 117 | offsetof(ngx_http_auth_sso_loc_conf_t, realm), 118 | NULL }, 119 | 120 | { ngx_string("auth_gss_keytab"), 121 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, 122 | ngx_conf_set_str_slot, 123 | NGX_HTTP_LOC_CONF_OFFSET, 124 | offsetof(ngx_http_auth_sso_loc_conf_t, keytab), 125 | NULL }, 126 | 127 | { ngx_string("auth_gss_service_name"), 128 | /* TODO change to NGX_CONF_1MORE for "http", "khttp", besides "HTTP" */ 129 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, 130 | ngx_conf_set_str_slot, 131 | NGX_HTTP_LOC_CONF_OFFSET, 132 | offsetof(ngx_http_auth_sso_loc_conf_t, srvcname), 133 | NULL }, 134 | 135 | { ngx_string("auth_gss_format_full"), 136 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, 137 | ngx_conf_set_flag_slot, 138 | NGX_HTTP_LOC_CONF_OFFSET, 139 | offsetof(ngx_http_auth_sso_loc_conf_t, fqun), 140 | NULL }, 141 | 142 | ngx_null_command 143 | }; 144 | 145 | /* Module Context */ 146 | 147 | static ngx_http_module_t ngx_http_auth_sso_module_ctx = { 148 | NULL, /* preconf */ 149 | ngx_http_auth_sso_init, /* postconf */ 150 | 151 | NULL, /* create main conf (defaults) */ 152 | NULL, /* init main conf (what's in nginx.conf) */ 153 | 154 | NULL, /* create server conf */ 155 | NULL, /* merge with main */ 156 | 157 | ngx_http_auth_sso_create_loc_conf, /* create location conf */ 158 | ngx_http_auth_sso_merge_loc_conf /* merge with server */ 159 | }; 160 | 161 | /* Module Definition */ 162 | 163 | /* really ngx_module_s /shrug */ 164 | ngx_module_t ngx_http_auth_sso_module = { 165 | /* ngx_uint_t ctx_index, index, spare{0-3}, version; */ 166 | NGX_MODULE_V1, /* 0, 0, 0, 0, 0, 0, 1 */ 167 | &ngx_http_auth_sso_module_ctx, /* void *ctx */ 168 | ngx_http_auth_sso_commands, /* ngx_command_t *commands */ 169 | NGX_HTTP_MODULE, /* ngx_uint_t type = 0x50545448 */ 170 | NULL, /* ngx_int_t (*init_master)(ngx_log_t *log) */ 171 | NULL, /* ngx_int_t (*init_module)(ngx_cycle_t *cycle) */ 172 | NULL, /* ngx_int_t (*init_process)(ngx_cycle_t *cycle) */ 173 | NULL, /* ngx_int_t (*init_thread)(ngx_cycle_t *cycle) */ 174 | NULL, /* void (*exit_thread)(ngx_cycle_t *cycle) */ 175 | NULL, /* void (*exit_process)(ngx_cycle_t *cycle) */ 176 | NULL, /* void (*exit_master)(ngx_cycle_t *cycle) */ 177 | NGX_MODULE_V1_PADDING /* 0, 0, 0, 0, 0, 0, 0, 0 */ 178 | /* uintptr_t spare_hook{0-7}; */ 179 | }; 180 | 181 | static void * 182 | ngx_http_auth_sso_create_loc_conf(ngx_conf_t *cf) 183 | { 184 | ngx_http_auth_sso_loc_conf_t *conf; 185 | 186 | conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_auth_sso_loc_conf_t)); 187 | if (conf == NULL) { 188 | return NGX_CONF_ERROR; 189 | } 190 | 191 | conf->protect = NGX_CONF_UNSET; 192 | conf->fqun = NGX_CONF_UNSET; 193 | 194 | /* temporary "debug" */ 195 | #if (NGX_DEBUG) 196 | ngx_conf_log_error(NGX_LOG_INFO, cf, 0, 197 | "auth_sso: allocated loc_conf_t (0x%p)", conf); 198 | #endif 199 | /* TODO find out if there is way to enable it only in debug mode */ 200 | 201 | return conf; 202 | } 203 | 204 | static char * 205 | ngx_http_auth_sso_merge_loc_conf(ngx_conf_t *cf, 206 | void *parent, 207 | void *child) 208 | { 209 | ngx_http_auth_sso_loc_conf_t *prev = parent; 210 | ngx_http_auth_sso_loc_conf_t *conf = child; 211 | 212 | /* "off" by default */ 213 | ngx_conf_merge_off_value(conf->protect, prev->protect, 0); 214 | 215 | ngx_conf_merge_str_value(conf->realm, prev->realm, "LOCALDOMAIN"); 216 | ngx_conf_merge_str_value(conf->keytab, prev->keytab, "/etc/krb5.keytab"); 217 | ngx_conf_merge_str_value(conf->srvcname, prev->srvcname, "HTTP"); 218 | 219 | ngx_conf_merge_off_value(conf->fqun, prev->fqun, 0); 220 | 221 | /* TODO make it only shout in debug */ 222 | #if (NGX_DEBUG) 223 | ngx_conf_log_error(NGX_LOG_INFO, cf, 0, "auth_sso: protect = %i", 224 | conf->protect); 225 | ngx_conf_log_error(NGX_LOG_INFO, cf, 0, "auth_sso: realm@0x%p = %s", 226 | conf->realm.data, conf->realm.data); 227 | ngx_conf_log_error(NGX_LOG_INFO, cf, 0, "auth_sso: keytab@0x%p = %s", 228 | conf->keytab.data, conf->keytab.data); 229 | ngx_conf_log_error(NGX_LOG_INFO, cf, 0, "auth_sso: srvcname@0x%p = %s", 230 | conf->srvcname.data, conf->srvcname.data); 231 | ngx_conf_log_error(NGX_LOG_INFO, cf, 0, "auth_sso: fqun = %i", 232 | conf->fqun); 233 | #endif 234 | 235 | return NGX_CONF_OK; 236 | } 237 | 238 | static ngx_int_t 239 | ngx_http_auth_sso_init(ngx_conf_t *cf) 240 | { 241 | ngx_http_handler_pt *h; 242 | ngx_http_core_main_conf_t *cmcf; 243 | 244 | cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module); 245 | 246 | h = ngx_array_push(&cmcf->phases[NGX_HTTP_ACCESS_PHASE].handlers); 247 | if (h == NULL) { 248 | return NGX_ERROR; 249 | } 250 | 251 | *h = ngx_http_auth_sso_handler; 252 | 253 | return NGX_OK; 254 | } 255 | 256 | static ngx_int_t 257 | ngx_http_auth_sso_negotiate_headers(ngx_http_request_t *r, 258 | ngx_http_auth_sso_ctx_t *ctx, 259 | ngx_str_t *token) 260 | { 261 | ngx_str_t value = ngx_null_string; 262 | 263 | if (token == NULL) { 264 | value.len = sizeof("Negotiate") - 1; 265 | value.data = (u_char *) "Negotiate"; 266 | } else { 267 | value.len = sizeof("Negotiate") + token->len; 268 | value.data = ngx_pcalloc(r->pool, value.len + 1); 269 | if (value.data == NULL) { 270 | return NGX_ERROR; 271 | } 272 | ngx_snprintf(value.data, value.len + 1, "Negotiate %V", token); 273 | } 274 | 275 | r->headers_out.www_authenticate = ngx_list_push(&r->headers_out.headers); 276 | if (r->headers_out.www_authenticate == NULL) { 277 | return NGX_ERROR; 278 | } 279 | 280 | r->headers_out.www_authenticate->hash = 1; 281 | r->headers_out.www_authenticate->key.len = sizeof("WWW-Authenticate") - 1; 282 | r->headers_out.www_authenticate->key.data = (u_char *) "WWW-Authenticate"; 283 | 284 | r->headers_out.www_authenticate->value.len = value.len; 285 | r->headers_out.www_authenticate->value.data = value.data; 286 | 287 | ctx->head = 1; 288 | 289 | return NGX_OK; 290 | } 291 | 292 | /* sort of like ngx_http_auth_basic_user ... except we store in ctx_t? */ 293 | ngx_int_t 294 | ngx_http_auth_sso_token(ngx_http_request_t *r, 295 | ngx_http_auth_sso_ctx_t *ctx) 296 | { 297 | /* not copying or decoding anything, just checking if token is present 298 | and where? NOPE, koz ngx_decode_base64 uses ngx_str_t... so might as well... */ 299 | ngx_str_t token; 300 | ngx_str_t decoded; 301 | 302 | if (r->headers_in.authorization == NULL) { 303 | return NGX_DECLINED; 304 | } 305 | /* but don't decode second time? */ 306 | if (ctx->token.len) return NGX_OK; 307 | 308 | token = r->headers_in.authorization->value; 309 | 310 | if (token.len < sizeof("Negotiate ") - 1 311 | || ngx_strncasecmp(token.data, (u_char *) "Negotiate ", 312 | sizeof("Negotiate ") - 1) != 0) { 313 | return NGX_DECLINED; 314 | } 315 | 316 | token.len -= sizeof("Negotiate ") - 1; 317 | token.data += sizeof("Negotiate ") - 1; 318 | 319 | while (token.len && token.data[0] == ' ') { 320 | token.len--; 321 | token.data++; 322 | } 323 | 324 | if (token.len == 0) { 325 | return NGX_DECLINED; 326 | } 327 | 328 | decoded.len = ngx_base64_decoded_length(token.len); 329 | decoded.data = ngx_pnalloc(r->pool, decoded.len + 1); 330 | if (decoded.data == NULL) { 331 | return NGX_ERROR; 332 | } 333 | 334 | if (ngx_decode_base64(&decoded, &token) != NGX_OK) { 335 | return NGX_DECLINED; 336 | } 337 | 338 | decoded.data[decoded.len] = '\0'; /* hmmm */ 339 | 340 | /* ctx = ngx_palloc(r->pool, sizeof(ngx_http_auth_sso_ctx_t)); */ 341 | /* if (ctx == NULL) { */ 342 | /* return NGX_ERROR; */ 343 | /* } */ 344 | 345 | /* ngx_http_set_ctx(r, ctx, ngx_http_auth_sso_module); */ 346 | 347 | ctx->token.len = decoded.len; 348 | ctx->token.data = decoded.data; 349 | /* off by one? hmmm... */ 350 | ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, 351 | "Token decoded"); 352 | 353 | return NGX_OK; 354 | } 355 | 356 | /* 357 | Because 'remote_user' is assumed to be provided by basic authorization 358 | (see ngx_http_variable_remote_user) we are forced to create bogus 359 | non-Negotiate authorization header. This may possibly clobber Negotiate 360 | token too soon. 361 | */ 362 | 363 | ngx_int_t 364 | ngx_http_auth_sso_set_bogus_authorization(ngx_http_request_t *r) 365 | { 366 | ngx_str_t plain, encoded, final; 367 | /* jezuz 3 allocs ;( */ 368 | 369 | if (r->headers_in.user.len == 0) { 370 | return NGX_DECLINED; 371 | } 372 | 373 | /* including \0 from sizeof because it's "user:password" */ 374 | plain.len = r->headers_in.user.len + sizeof("bogus"); 375 | plain.data = ngx_pnalloc(r->pool, plain.len); 376 | if (plain.data == NULL) { 377 | return NGX_ERROR; 378 | } 379 | 380 | ngx_snprintf(plain.data, plain.len, "%V:bogus", &r->headers_in.user); 381 | 382 | encoded.len = ngx_base64_encoded_length(plain.len); 383 | encoded.data = ngx_pnalloc(r->pool, encoded.len); 384 | if (encoded.data == NULL) { 385 | return NGX_ERROR; 386 | } 387 | 388 | ngx_encode_base64(&encoded, &plain); 389 | 390 | final.len = sizeof("Basic ") + encoded.len - 1; 391 | final.data = ngx_pnalloc(r->pool, final.len); 392 | if (final.data == NULL) { 393 | return NGX_ERROR; 394 | } 395 | 396 | ngx_snprintf(final.data, final.len, "Basic %V", &encoded); 397 | 398 | /* WARNING clobbering authorization header value */ 399 | r->headers_in.authorization->value.len = final.len; 400 | r->headers_in.authorization->value.data = final.data; 401 | 402 | return NGX_OK; 403 | } 404 | 405 | ngx_int_t 406 | ngx_http_auth_sso_auth_user_gss(ngx_http_request_t *r, 407 | ngx_http_auth_sso_ctx_t *ctx, 408 | ngx_http_auth_sso_loc_conf_t *alcf) 409 | { 410 | static unsigned char ntlmProtocol [] = {'N', 'T', 'L', 'M', 'S', 'S', 'P', 0}; 411 | 412 | /* 413 | nginx stuff 414 | */ 415 | ngx_str_t host_name; 416 | ngx_int_t ret = NGX_DECLINED; 417 | int rc; 418 | int spnego_flag = 0; 419 | char *p; 420 | /* 421 | kerberos stuff 422 | */ 423 | krb5_context krb_ctx = NULL; 424 | char *ktname = NULL; 425 | /* ngx_str_t kerberosToken; ? */ 426 | unsigned char *kerberosToken = NULL; 427 | size_t kerberosTokenLength = 0; 428 | /* this izgotten from de-SPNEGGING original token... 429 | and put into gss_accept_sec_context... 430 | silly... 431 | */ 432 | ngx_str_t spnegoToken = ngx_null_string; 433 | /* unsigned char *spnegoToken = NULL ; 434 | size_t spnegoTokenLength = 0; */ 435 | /* 436 | gssapi stuff 437 | */ 438 | OM_uint32 major_status, minor_status, minor_status2; 439 | gss_buffer_desc service = GSS_C_EMPTY_BUFFER; 440 | gss_name_t my_gss_name = GSS_C_NO_NAME; 441 | gss_cred_id_t my_gss_creds = GSS_C_NO_CREDENTIAL; 442 | gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER; 443 | gss_ctx_id_t gss_context = GSS_C_NO_CONTEXT; 444 | gss_name_t client_name = GSS_C_NO_NAME; 445 | gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER; 446 | OM_uint32 ret_flags = 0; 447 | gss_cred_id_t delegated_cred = GSS_C_NO_CREDENTIAL; 448 | 449 | /* first, see if there is a point in runing */ 450 | /* ctx = ngx_http_get_module_ctx(r, ngx_http_auth_sso_module); */ 451 | /* this really shouldn't 'eppen */ 452 | if (!ctx || ctx->token.len == 0) { 453 | return ret; 454 | } 455 | /* on with the copy cat show */ 456 | ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, 457 | "GSSAPI authorizing"); 458 | 459 | krb5_init_context(&krb_ctx); 460 | 461 | ktname = (char *) ngx_pcalloc(r->pool, sizeof("KRB5_KTNAME=")+alcf->keytab.len); 462 | if (ktname == NULL) { 463 | ret = NGX_ERROR; 464 | goto end; 465 | } 466 | ngx_snprintf((u_char *) ktname, sizeof("KRB5_KTNAME=")+alcf->keytab.len, 467 | "KRB5_KTNAME=%V%Z", &alcf->keytab); 468 | putenv(ktname); 469 | 470 | ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, 471 | "Use keytab %V", &alcf->keytab); 472 | 473 | /* TODECIDE: wherefrom use the hostname value for the service name? */ 474 | host_name = r->headers_in.host->value; 475 | /* for now using the name client thinks... */ 476 | service.length = alcf->srvcname.len + host_name.len + 2; 477 | /* @ vel / */ 478 | service.value = ngx_palloc(r->pool, service.length); 479 | if (service.value == NULL) { 480 | ret = NGX_ERROR; 481 | goto end; 482 | } 483 | ngx_snprintf(service.value, service.length, "%V@%V%Z", 484 | &alcf->srvcname, &host_name); 485 | 486 | ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, 487 | "Use service principal %V/%V", &alcf->srvcname, &host_name); 488 | 489 | major_status = gss_import_name(&minor_status, &service, 490 | GSS_C_NT_HOSTBASED_SERVICE, &my_gss_name); 491 | if (GSS_ERROR(major_status)) { 492 | ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, 493 | "%s Used service principal: %s", 494 | get_gss_error(r->pool, minor_status, 495 | "gss_import_name() failed for service principal"), 496 | (unsigned char *)service.value); 497 | ret = NGX_ERROR; 498 | goto end; 499 | } 500 | 501 | major_status = gss_acquire_cred(&minor_status, 502 | my_gss_name, 503 | GSS_C_INDEFINITE, 504 | GSS_C_NO_OID_SET, 505 | GSS_C_ACCEPT, 506 | &my_gss_creds, 507 | NULL, 508 | NULL); 509 | if (GSS_ERROR(major_status)) { 510 | ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, 511 | "%s Used service principal: %s", 512 | get_gss_error(r->pool, minor_status, 513 | "gss_acquire_cred() failed"), 514 | (unsigned char *)service.value); 515 | ret = NGX_ERROR; 516 | goto end; 517 | } 518 | 519 | /* the MEAT? */ 520 | input_token.length = ctx->token.len + 1; 521 | input_token.value = (void *) ctx->token.data; 522 | /* Should check first if SPNEGO token */ 523 | /* but it looks like mit-kerberos version > 1.4.4 DOES include GSSAPI 524 | code that supports SPNEGO... ("donated by SUN")... */ 525 | if ( (rc = parseNegTokenInit (input_token.value, 526 | input_token.length, 527 | (const unsigned char **) &kerberosToken, 528 | &kerberosTokenLength)) != 0 ) { 529 | 530 | ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, 531 | "parseNegTokenInit failed with rc=%d", rc); 532 | /* 533 | Error 1xy -> assume GSSAPI token and continue 534 | */ 535 | if ( rc < 100 || rc > 199 ) { 536 | ret = NGX_DECLINED; 537 | goto end; 538 | /* TOTALLY CLUELESS */ 539 | } 540 | /* feeble NTLM... */ 541 | if ( (input_token.length >= sizeof ntlmProtocol + 1) && 542 | (!ngx_memcmp(input_token.value, ntlmProtocol, sizeof ntlmProtocol)) ) { 543 | ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, 544 | "received type %d NTLM token", 545 | (int) *((unsigned char *)input_token.value + sizeof ntlmProtocol)); /* jeez */ 546 | ret = NGX_DECLINED; 547 | goto end; 548 | } 549 | spnego_flag = 0; 550 | } else { 551 | input_token.length = kerberosTokenLength; 552 | input_token.value = ngx_pcalloc(r->pool, input_token.length); 553 | if (input_token.value == NULL) { 554 | ret = NGX_ERROR; 555 | goto end; 556 | } 557 | ngx_memcpy(input_token.value, kerberosToken, input_token.length); 558 | spnego_flag = 1; 559 | } 560 | 561 | major_status = gss_accept_sec_context(&minor_status, 562 | &gss_context, 563 | my_gss_creds, 564 | &input_token, 565 | GSS_C_NO_CHANNEL_BINDINGS, 566 | &client_name, 567 | NULL, 568 | &output_token, 569 | &ret_flags, 570 | NULL, 571 | &delegated_cred); 572 | 573 | if (output_token.length) { 574 | ngx_str_t token = ngx_null_string; 575 | 576 | if (spnego_flag) { 577 | if ( (rc = makeNegTokenTarg (output_token.value, 578 | output_token.length, 579 | (const unsigned char **) &spnegoToken.data, 580 | &spnegoToken.len)) != 0 ) { 581 | ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, 582 | "makeNegTokenTarg failed with rc=%d",rc); 583 | ret = NGX_DECLINED; 584 | goto end; 585 | } 586 | } else { 587 | spnegoToken.data = (u_char *) output_token.value; 588 | spnegoToken.len = output_token.length - 1; 589 | } 590 | /* XXX use ap_uuencode() */ 591 | token.len = ngx_base64_encoded_length(spnegoToken.len); 592 | token.data = ngx_pcalloc(r->pool, token.len + 1); 593 | if (token.data == NULL) { 594 | ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, 595 | "Not enough memory"); 596 | ret = NGX_ERROR; 597 | /* ??? */ 598 | gss_release_buffer(&minor_status2, &output_token); 599 | goto end; 600 | } 601 | ngx_encode_base64(&token, &spnegoToken); /* did it work (void) */ 602 | 603 | /* ??? */ 604 | gss_release_buffer(&minor_status2, &output_token); 605 | 606 | /* and now here we had to rework ngx_http_auth_sso_negotiate_headers... */ 607 | 608 | if ( (ret = ngx_http_auth_sso_negotiate_headers(r, ctx, &token)) == NGX_ERROR ) { 609 | goto end; 610 | } 611 | /* ap_table_set(r->err_headers_out, "WWW-Authenticate", 612 | ap_pstrcat(r->pool, "Negotiate ", token, NULL)); */ 613 | } 614 | 615 | /* theesee two ifs could/SHOULD? as well go before the block above?!? 616 | headers shouldn't be set if we DECLINE, but i guess there won't be output_token anyway... */ 617 | if (GSS_ERROR(major_status)) { 618 | ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, 619 | "%s Used service principal: %s", 620 | get_gss_error(r->pool, minor_status, 621 | "gss_accept_sec_context() failed"), 622 | (unsigned char *)service.value); 623 | ret = NGX_DECLINED; 624 | goto end; 625 | } 626 | 627 | if (major_status & GSS_S_CONTINUE_NEEDED) { 628 | ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, 629 | "only one authentication iteration allowed"); 630 | ret = NGX_DECLINED; 631 | goto end; 632 | } 633 | 634 | /* INFO */ 635 | if ( !(ret_flags & GSS_C_REPLAY_FLAG || ret_flags & GSS_C_SEQUENCE_FLAG) ){ 636 | ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, 637 | "GSSAPI Warning: no replay protection !"); 638 | } 639 | if ( !(ret_flags & GSS_C_SEQUENCE_FLAG) ){ 640 | ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, 641 | "GSSAPI Warning: no sequence protection !"); 642 | } 643 | 644 | /* getting user name at the other end of the request */ 645 | major_status = gss_display_name(&minor_status, 646 | client_name, 647 | &output_token, 648 | NULL); 649 | gss_release_name(&minor_status, &client_name); 650 | 651 | /* hmm... if he is going to ERROR out now we should do it before setting headers... */ 652 | if (GSS_ERROR(major_status)) { 653 | ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, 654 | "%s", get_gss_error(r->pool, minor_status, 655 | "gss_display_name() failed")); 656 | ret = NGX_ERROR; 657 | goto end; 658 | } 659 | 660 | if (output_token.length) { 661 | /* TOFIX dirty quick trick for now (no "-1" i.e. include '\0' */ 662 | ngx_str_t user = { output_token.length, 663 | (u_char *) output_token.value }; 664 | 665 | r->headers_in.user.data = ngx_pstrdup(r->pool, &user); 666 | /* NULL?!? */ 667 | r->headers_in.user.len = user.len; 668 | 669 | if (alcf->fqun == 0) { 670 | p = ngx_strchr(r->headers_in.user.data, '@'); 671 | if (p != NULL) { 672 | if (ngx_strcmp(p+1, alcf->realm.data) == 0) { 673 | *p = '\0'; 674 | r->headers_in.user.len = ngx_strlen(r->headers_in.user.data); 675 | } 676 | } 677 | } 678 | 679 | /* this for the sake of ngx_http_variable_remote_user */ 680 | ngx_http_auth_sso_set_bogus_authorization(r); 681 | 682 | ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, 683 | "user is %V", &r->headers_in.user); 684 | } 685 | 686 | gss_release_buffer(&minor_status, &output_token); 687 | 688 | /* saving creds... LATER, for now debug msg... */ 689 | if (delegated_cred != GSS_C_NO_CREDENTIAL) { 690 | ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, 691 | "Had delegated_cred to save."); 692 | } 693 | 694 | ret = NGX_OK; 695 | /* goto end; */ 696 | 697 | /* well, alright, the end, my friend */ 698 | end: 699 | if (delegated_cred) 700 | gss_release_cred(&minor_status, &delegated_cred); 701 | 702 | if (output_token.length) 703 | gss_release_buffer(&minor_status, &output_token); 704 | 705 | if (client_name != GSS_C_NO_NAME) 706 | gss_release_name(&minor_status, &client_name); 707 | 708 | if (gss_context != GSS_C_NO_CONTEXT) 709 | gss_delete_sec_context(&minor_status, &gss_context, GSS_C_NO_BUFFER); 710 | 711 | krb5_free_context(krb_ctx); 712 | if (my_gss_name != GSS_C_NO_NAME) 713 | gss_release_name(&minor_status, &my_gss_name); 714 | 715 | if (my_gss_creds != GSS_C_NO_CREDENTIAL) 716 | gss_release_cred(&minor_status, &my_gss_creds); 717 | 718 | return ret; 719 | } 720 | 721 | static ngx_int_t 722 | ngx_http_auth_sso_handler(ngx_http_request_t *r) 723 | { 724 | ngx_int_t ret; 725 | ngx_http_auth_sso_ctx_t *ctx; 726 | ngx_http_auth_sso_loc_conf_t *alcf; 727 | 728 | alcf = ngx_http_get_module_loc_conf(r, ngx_http_auth_sso_module); 729 | 730 | if (alcf->protect == 0) { 731 | return NGX_DECLINED; 732 | } 733 | 734 | /* looks like we need ctx_t "frst" after all, there is URI level 735 | access phase and filesystem level access phase... */ 736 | ctx = ngx_http_get_module_ctx(r, ngx_http_auth_sso_module); 737 | if (ctx == NULL) { 738 | ctx = ngx_palloc(r->pool, sizeof(ngx_http_auth_sso_ctx_t)); 739 | if (ctx == NULL) { 740 | return NGX_HTTP_INTERNAL_SERVER_ERROR; 741 | } 742 | ctx->token.len = 0; 743 | ctx->token.data = NULL; 744 | ctx->head = 0; 745 | ctx->ret = NGX_HTTP_UNAUTHORIZED; 746 | ngx_http_set_ctx(r, ctx, ngx_http_auth_sso_module); 747 | } 748 | /* nope... local fs req creates new ctx... useless... */ 749 | 750 | ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, 751 | "SSO auth handling IN: token.len=%d, head=%d, ret=%d", 752 | ctx->token.len, ctx->head, ctx->ret); 753 | 754 | if (ctx->token.len && ctx->head) 755 | return ctx->ret; 756 | if (r->headers_in.user.data != NULL) 757 | return NGX_OK; 758 | 759 | ret = ngx_http_auth_sso_token(r, ctx); 760 | 761 | if (ret == NGX_OK) { 762 | /* ok... looks like client sent some Negotiate'ing authorization header... */ 763 | ret = ngx_http_auth_sso_auth_user_gss(r, ctx, alcf); 764 | } 765 | 766 | if (ret == NGX_DECLINED) { 767 | /* TODEBATE skip if (ctx->head)... */ 768 | ret = ngx_http_auth_sso_negotiate_headers(r, ctx, NULL); 769 | if (ret == NGX_ERROR) { 770 | return (ctx->ret = NGX_HTTP_INTERNAL_SERVER_ERROR); 771 | } 772 | return (ctx->ret = NGX_HTTP_UNAUTHORIZED); 773 | } 774 | 775 | if (ret == NGX_ERROR) { 776 | return (ctx->ret = NGX_HTTP_INTERNAL_SERVER_ERROR); 777 | } 778 | 779 | /* else NGX_OK */ 780 | ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, 781 | "SSO auth handling OUT: token.len=%d, head=%d, ret=%d", 782 | ctx->token.len, ctx->head, ret); 783 | return (ctx->ret = ret); 784 | } 785 | -------------------------------------------------------------------------------- /spnegohelp/Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # Linux: 3 | # -D__LITTLE_ENDIAN__ 4 | # Solaris: 5 | # -D__BIG_ENDIAN__ 6 | # 7 | 8 | CFLAGS = -fpic 9 | 10 | LIB = libspnegohelp.a 11 | SLIB = libspnegohelp.so 12 | 13 | OBJS = derparse.o spnego.o spnegohelp.o spnegoparse.o 14 | 15 | all: 16 | make `uname` 17 | 18 | debug: 19 | make CFLAGS="$(CFLAGS) -DDEBUG" `uname` 20 | 21 | SunOS: 22 | make CFLAGS="$(CFLAGS) -D__BIG_ENDIAN__" libs 23 | 24 | AIX: 25 | make CFLAGS="$(CFLAGS) -D__BIG_ENDIAN__" libs 26 | 27 | Linux: 28 | make CFLAGS="$(CFLAGS) -D__LITTLE_ENDIAN__" libs 29 | 30 | libs: $(LIB) $(SLIB) 31 | 32 | $(LIB): $(OBJS) 33 | ar -r $(LIB) $(OBJS) 34 | 35 | $(SLIB): $(OBJS) 36 | gcc --shared -o $(SLIB) $(OBJS) 37 | 38 | derparse.o: derparse.c derparse.h spnego.h Makefile 39 | gcc -c $(CFLAGS) derparse.c -o $@ 40 | 41 | spnego.o: spnego.c derparse.h spnego.h spnegoparse.h Makefile 42 | gcc -c $(CFLAGS) spnego.c -o $@ 43 | 44 | spnegoparse.o: spnegoparse.c derparse.h spnego.h spnegoparse.h Makefile 45 | gcc -c $(CFLAGS) spnegoparse.c -o $@ 46 | 47 | spnegohelp.o: spnegohelp.c spnego.h spnegohelp.h Makefile 48 | gcc -c $(CFLAGS) spnegohelp.c -o $@ 49 | 50 | clean: 51 | rm $(OBJS) $(LIB) $(SLIB) 52 | -------------------------------------------------------------------------------- /spnegohelp/derparse.c: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2002 Microsoft Corporation 2 | // All rights reserved. 3 | // 4 | // THIS CODE AND INFORMATION IS PROVIDED "AS IS" 5 | // WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 6 | // OR IMPLIED, INCLUDING BUT NOT LIMITED 7 | // TO THE IMPLIED WARRANTIES OF MERCHANTIBILITY 8 | // AND/OR FITNESS FOR A PARTICULAR PURPOSE. 9 | // 10 | // Date - 10/08/2002 11 | // Author - Sanj Surati 12 | 13 | 14 | ///////////////////////////////////////////////////////////// 15 | // 16 | // DERPARSE.C 17 | // 18 | // SPNEGO Token Handler Source File 19 | // 20 | // Contains implementation of ASN.1 DER read/write functions 21 | // as defined in DERPARSE.H. 22 | // 23 | ///////////////////////////////////////////////////////////// 24 | 25 | #include 26 | #include 27 | #include 28 | #include "spnego.h" 29 | #include "derparse.h" 30 | 31 | // 32 | // The GSS Mechanism OID enumeration values (SPNEGO_MECH_OID) control which offset in 33 | // the array below, that a mechanism can be found. 34 | // 35 | MECH_OID g_stcMechOIDList [] = 36 | { 37 | { (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 38 | { (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 39 | { (unsigned char*) "\x06\x06\x2b\x06\x01\x05\x05\x02", 8, 6, spnego_mech_oid_Spnego }, // 1.3.6.1.1.5.5.2 40 | { (unsigned char*) "", 0, 0, spnego_mech_oid_NotUsed } // Placeholder 41 | }; 42 | 43 | ///////////////////////////////////////////////////////////////////////////// 44 | // 45 | // Function: 46 | // ASNDerGetLength 47 | // 48 | // Parameters: 49 | // [in] pbLengthData - DER Length Data 50 | // [in] nBoundaryLength - Length that value must not exceed. 51 | // [out] pnLength - Filled out with length value 52 | // [out] pnNumLengthBytes - Filled out with number of bytes 53 | // consumed by DER length. 54 | // 55 | // Returns: 56 | // int Success - SPNEGO_E_SUCCESS 57 | // Failure - SPNEGO API Error code 58 | // 59 | // Comments : 60 | // Interprets the data at pbLengthData as a DER length. The length must 61 | // fit within the bounds of nBoundary length. We do not currently 62 | // process lengths that take more than 4 bytes. 63 | // 64 | //////////////////////////////////////////////////////////////////////////// 65 | 66 | int ASNDerGetLength( unsigned char* pbLengthData, long nBoundaryLength, long* pnLength, 67 | long* pnNumLengthBytes ) 68 | { 69 | int nReturn = SPNEGO_E_INVALID_LENGTH; 70 | int nNumLengthBytes = 0; 71 | 72 | // First check if the extended length bit is set 73 | 74 | if ( *pbLengthData & LEN_XTND ) 75 | { 76 | // Lower 7 bits contain the number of trailing bytes that describe the length 77 | nNumLengthBytes = *pbLengthData & LEN_MASK; 78 | 79 | // Check that the number of bytes we are about to read is within our boundary 80 | // constraints 81 | 82 | if ( nNumLengthBytes <= nBoundaryLength - 1 ) 83 | { 84 | 85 | // For now, our handler won't deal with lengths greater than 4 bytes 86 | if ( nNumLengthBytes >= 1 && nNumLengthBytes <= 4 ) 87 | { 88 | // 0 out the initial length 89 | *pnLength = 0L; 90 | 91 | // Bump by 1 byte 92 | pbLengthData++; 93 | 94 | #ifdef __LITTLE_ENDIAN__ 95 | 96 | // There may be a cleaner way to do this, but for now, this seems to be 97 | // an easy way to do the transformation 98 | switch ( nNumLengthBytes ) 99 | { 100 | case 1: 101 | { 102 | *( ( (unsigned char*) pnLength ) ) = *pbLengthData; 103 | break; 104 | } 105 | 106 | case 2: 107 | { 108 | *( ( (unsigned char*) pnLength ) ) = *(pbLengthData + 1); 109 | *( ( (unsigned char*) pnLength ) + 1 ) = *(pbLengthData); 110 | 111 | break; 112 | } 113 | 114 | case 3: 115 | { 116 | *( ( (unsigned char*) pnLength ) ) = *(pbLengthData + 2); 117 | *( ( (unsigned char*) pnLength ) + 2 ) = *(pbLengthData + 1); 118 | *( ( (unsigned char*) pnLength ) + 3 ) = *(pbLengthData); 119 | break; 120 | } 121 | 122 | case 4: 123 | { 124 | *( ( (unsigned char*) pnLength ) ) = *(pbLengthData + 3); 125 | *( ( (unsigned char*) pnLength ) + 1 ) = *(pbLengthData + 2); 126 | *( ( (unsigned char*) pnLength ) + 2 ) = *(pbLengthData + 1); 127 | *( ( (unsigned char*) pnLength ) + 3 ) = *(pbLengthData); 128 | break; 129 | } 130 | 131 | } // SWITCH ( nNumLengthBytes ) 132 | 133 | #else 134 | // We are Big-Endian, so the length can be copied in from the source 135 | // as is. Ensure that we adjust for the number of bytes we actually 136 | // copy. 137 | 138 | memcpy( ( (unsigned char *) pnLength ) + ( 4 - nNumLengthBytes ), 139 | pbLengthData, nNumLengthBytes ); 140 | #endif 141 | 142 | // Account for the initial length byte 143 | *pnNumLengthBytes = nNumLengthBytes + 1; 144 | nReturn = SPNEGO_E_SUCCESS; 145 | 146 | } // IF Valid Length 147 | 148 | } // IF num bytes to read is within the boundary length 149 | 150 | } // IF xtended length 151 | else 152 | { 153 | 154 | // Extended bit is not set, so the length is in the value and the one 155 | // byte describes the length 156 | *pnLength = *pbLengthData & LEN_MASK; 157 | *pnNumLengthBytes = 1; 158 | nReturn = SPNEGO_E_SUCCESS; 159 | 160 | } 161 | LOG(("ASNDerGetLength returned %d\n",nReturn)); 162 | return nReturn; 163 | } 164 | 165 | 166 | ///////////////////////////////////////////////////////////////////////////// 167 | // 168 | // Function: 169 | // ASNDerCheckToken 170 | // 171 | // Parameters: 172 | // [in] pbTokenData - Token Data 173 | // [in] nToken - Token identifier to check for 174 | // [in] nLengthWithToken - Expected token length (with data) 175 | // [in] nBoundaryLength - Length that value must not exceed. 176 | // [out] pnLength - Filled out with data length 177 | // [out] pnTokenLength - Filled out with number of bytes 178 | // consumed by token identifier and length. 179 | // 180 | // Returns: 181 | // int Success - SPNEGO_E_SUCCESS 182 | // Failure - SPNEGO API Error code 183 | // 184 | // Comments : 185 | // Checks the data pointed to by pbTokenData for the specified token 186 | // identifier and the length that immediately follows. If 187 | // nLengthWithToken is > 0, the calculated length must match. The 188 | // length must also not exceed the specified boundary length . 189 | // 190 | //////////////////////////////////////////////////////////////////////////// 191 | 192 | int ASNDerCheckToken( unsigned char* pbTokenData, unsigned char nToken, 193 | long nLengthWithToken, long nBoundaryLength, 194 | long* pnLength, long* pnTokenLength ) 195 | { 196 | 197 | int nReturn = SPNEGO_E_INVALID_LENGTH; 198 | long nNumLengthBytes = 0L; 199 | 200 | // Make sure that we've at least got 2 bytes of room to work with 201 | 202 | if ( nBoundaryLength >= 2 ) 203 | { 204 | // The first byte of the token data MUST match the specified token 205 | if ( *pbTokenData == nToken ) 206 | { 207 | // Next byte indicates the length 208 | pbTokenData++; 209 | 210 | // Get the length described by the token 211 | if ( ( nReturn = ASNDerGetLength( pbTokenData, nBoundaryLength, pnLength, 212 | &nNumLengthBytes ) ) == SPNEGO_E_SUCCESS ) 213 | { 214 | // Verify that the length is LESS THAN the boundary length 215 | // (this should prevent us walking out of our buffer) 216 | if ( ( nBoundaryLength - ( nNumLengthBytes + 1 ) < *pnLength ) ) 217 | { 218 | 219 | nReturn = SPNEGO_E_INVALID_LENGTH; 220 | 221 | } 222 | 223 | // If we were passed a length to check, do so now 224 | if ( nLengthWithToken > 0L ) 225 | { 226 | 227 | // Check that the expected length matches 228 | if ( ( nLengthWithToken - ( nNumLengthBytes + 1 ) ) != *pnLength ) 229 | { 230 | 231 | nReturn = SPNEGO_E_INVALID_LENGTH; 232 | 233 | } 234 | 235 | } // IF need to validate length 236 | 237 | if ( SPNEGO_E_SUCCESS == nReturn ) 238 | { 239 | *pnTokenLength = nNumLengthBytes + 1; 240 | } 241 | 242 | } // IF ASNDerGetLength 243 | 244 | } // IF token matches 245 | else 246 | { 247 | nReturn = SPNEGO_E_TOKEN_NOT_FOUND; 248 | } 249 | 250 | } // IF Boundary Length is at least 2 bytes 251 | 252 | LOG(("ASNDerCheckToken returned %d\n",nReturn)); 253 | return nReturn; 254 | } 255 | 256 | ///////////////////////////////////////////////////////////////////////////// 257 | // 258 | // Function: 259 | // ASNDerCheckOID 260 | // 261 | // Parameters: 262 | // [in] pbTokenData - Token Data 263 | // [in] nMechOID - OID we are looking for 264 | // [in] nBoundaryLength - Length that value must not exceed. 265 | // [out] pnTokenLength - Filled out with number of bytes 266 | // consumed by token and data. 267 | // 268 | // Returns: 269 | // int Success - SPNEGO_E_SUCCESS 270 | // Failure - SPNEGO API Error code 271 | // 272 | // Comments : 273 | // Checks the data pointed to by pbTokenData for the specified OID. 274 | // 275 | //////////////////////////////////////////////////////////////////////////// 276 | 277 | int ASNDerCheckOID( unsigned char* pbTokenData, SPNEGO_MECH_OID nMechOID, long nBoundaryLength, 278 | long* pnTokenLength ) 279 | { 280 | int nReturn = 0L; 281 | long nLength = 0L; 282 | 283 | // Verify that we have an OID token 284 | if ( ( nReturn = ASNDerCheckToken( pbTokenData, OID, 0L, nBoundaryLength, 285 | &nLength, pnTokenLength ) ) == SPNEGO_E_SUCCESS ) 286 | { 287 | // Add the data length to the Token Length 288 | *pnTokenLength += nLength; 289 | 290 | // Token Lengths plus the actual length must match the length in our OID list element. 291 | // If it doesn't, we're done 292 | if ( *pnTokenLength == g_stcMechOIDList[nMechOID].iLen ) 293 | { 294 | // Memcompare the token and the expected field 295 | if ( memcmp( pbTokenData, g_stcMechOIDList[nMechOID].ucOid, *pnTokenLength ) != 0 ) 296 | { 297 | LOG(("ASNDerCheckOID memcmp failed\n")); 298 | nReturn = SPNEGO_E_UNEXPECTED_OID; 299 | } 300 | } 301 | else 302 | { 303 | LOG(("ASNDerCheckOID token length failed\n")); 304 | nReturn = SPNEGO_E_UNEXPECTED_OID; 305 | } 306 | 307 | } // IF OID Token CHecks 308 | 309 | LOG(("ASNDerCheckOID returned %d\n",nReturn)); 310 | return nReturn; 311 | } 312 | 313 | ///////////////////////////////////////////////////////////////////////////// 314 | // 315 | // Function: 316 | // ASNDerCalcNumLengthBytes 317 | // 318 | // Parameters: 319 | // [in] nLength - Length to calculate length bytes for. 320 | // 321 | // Returns: 322 | // int Number of bytes necessary to represent length 323 | // 324 | // Comments : 325 | // Helper function to calculate the number of length bytes necessary to 326 | // represent a length value. For our purposes, a 32-bit value should be 327 | // enough to describea length. 328 | // 329 | //////////////////////////////////////////////////////////////////////////// 330 | 331 | int ASNDerCalcNumLengthBytes( long nLength ) 332 | { 333 | if ( nLength <= 0x7F ) 334 | { 335 | // A single byte will be sufficient for describing this length. 336 | // The byte will simply contain the length 337 | return 1; 338 | } 339 | else if ( nLength <= 0xFF ) 340 | { 341 | // Two bytes are necessary, one to say how many following bytes 342 | // describe the length, and one to give the length 343 | return 2; 344 | } 345 | else if ( nLength <= 0xFFFF ) 346 | { 347 | // Three bytes are necessary, one to say how many following bytes 348 | // describe the length, and two to give the length 349 | return 3; 350 | } 351 | else if ( nLength <= 0xFFFFFF ) 352 | { 353 | // Four bytes are necessary, one to say how many following bytes 354 | // describe the length, and three to give the length 355 | return 4; 356 | } 357 | else 358 | { 359 | // Five bytes are necessary, one to say how many following bytes 360 | // describe the length, and four to give the length 361 | return 5; 362 | } 363 | } 364 | 365 | 366 | ///////////////////////////////////////////////////////////////////////////// 367 | // 368 | // Function: 369 | // ASNDerCalcTokenLength 370 | // 371 | // Parameters: 372 | // [in] nLength - Length to calculate length bytes for. 373 | // [in] nDataLength - Actual Data length value. 374 | // 375 | // Returns: 376 | // long Number of bytes necessary to represent a token, length and data 377 | // 378 | // Comments : 379 | // Helper function to calculate a token and value size, based on a 380 | // supplied length value, and any binary data that will need to be 381 | // written out. 382 | // 383 | //////////////////////////////////////////////////////////////////////////// 384 | 385 | long ASNDerCalcTokenLength( long nLength, long nDataLength ) 386 | { 387 | // Add a byte to the length size to account for a single byte to 388 | // hold the token type. 389 | long nTotalLength = ASNDerCalcNumLengthBytes( nLength ) + 1; 390 | 391 | return nTotalLength + nDataLength; 392 | } 393 | 394 | 395 | ///////////////////////////////////////////////////////////////////////////// 396 | // 397 | // Function: 398 | // ASNDerCalcElementLength 399 | // 400 | // Parameters: 401 | // [in] nDataLength - Length of data. 402 | // [out] pnInternalLength - Filled out with length of element 403 | // without sequence info. 404 | // 405 | // Returns: 406 | // long Number of bytes necessary to represent an element 407 | // 408 | // Comments : 409 | // Helper function to calculate an element length. An element consists 410 | // of a sequence token, a type token and then the data. 411 | // 412 | //////////////////////////////////////////////////////////////////////////// 413 | 414 | long ASNDerCalcElementLength( long nDataLength, long* pnInternalLength ) 415 | { 416 | // First the type token and the actual data 417 | long nTotalLength = ASNDerCalcTokenLength( nDataLength, nDataLength ); 418 | 419 | // Internal length is the length without the element sequence token 420 | if ( NULL != pnInternalLength ) 421 | { 422 | *pnInternalLength = nTotalLength; 423 | } 424 | 425 | // Next add in the element's sequence token (remember that its 426 | // length is the total length of the type token and data) 427 | nTotalLength += ASNDerCalcTokenLength( nTotalLength, 0L ); 428 | 429 | return nTotalLength; 430 | } 431 | 432 | ///////////////////////////////////////////////////////////////////////////// 433 | // 434 | // Function: 435 | // ASNDerCalcMechListLength 436 | // 437 | // Parameters: 438 | // [in] mechoid - Mech OID to put in list. 439 | // [out] pnInternalLength - Filled out with length of element 440 | // without the primary sequence token. 441 | // 442 | // Returns: 443 | // long Number of bytes necessary to represent a mechList 444 | // 445 | // Comments : 446 | // Helper function to calculate a MechList length. A mechlist consists 447 | // of a NegTokenInit sequence token, a sequence token for the MechList 448 | // and finally a list of OIDs. In our case, we only really have one 449 | // OID. 450 | // 451 | //////////////////////////////////////////////////////////////////////////// 452 | 453 | long ASNDerCalcMechListLength( SPNEGO_MECH_OID mechoid, long* pnInternalLength ) 454 | { 455 | // First the OID 456 | long nTotalLength = g_stcMechOIDList[mechoid].iLen; 457 | 458 | // Next add in a sequence token 459 | nTotalLength += ASNDerCalcTokenLength( nTotalLength, 0L ); 460 | 461 | // Internal length is the length without the element sequence token 462 | if ( NULL != pnInternalLength ) 463 | { 464 | *pnInternalLength = nTotalLength; 465 | } 466 | 467 | // Finally add in the element's sequence token 468 | nTotalLength += ASNDerCalcTokenLength( nTotalLength, 0L ); 469 | 470 | return nTotalLength; 471 | } 472 | 473 | 474 | ///////////////////////////////////////////////////////////////////////////// 475 | // 476 | // Function: 477 | // ASNDerWriteLength 478 | // 479 | // Parameters: 480 | // [out] pbData - Buffer to write into. 481 | // [in] nLength - Length to write out. 482 | // 483 | // Returns: 484 | // int Number of bytes written out 485 | // 486 | // Comments : 487 | // Helper function to write out a length value following DER rules . 488 | // 489 | //////////////////////////////////////////////////////////////////////////// 490 | 491 | int ASNDerWriteLength( unsigned char* pbData, long nLength ) 492 | { 493 | int nNumBytesRequired = ASNDerCalcNumLengthBytes( nLength ); 494 | int nNumLengthBytes = nNumBytesRequired - 1; 495 | 496 | 497 | if ( nNumBytesRequired > 1 ) 498 | { 499 | 500 | // Write out the number of bytes following which will be used 501 | *pbData = (unsigned char ) ( LEN_XTND | nNumLengthBytes ); 502 | 503 | // Point to where we'll actually write the length 504 | pbData++; 505 | 506 | #ifdef __LITTLE_ENDIAN__ 507 | 508 | // There may be a cleaner way to do this, but for now, this seems to be 509 | // an easy way to do the transformation 510 | switch ( nNumLengthBytes ) 511 | { 512 | case 1: 513 | { 514 | // Cast the length to a single byte, since we know that it 515 | // is 0x7F or less (or we wouldn't only need a single byte). 516 | 517 | *pbData = (unsigned char) nLength; 518 | break; 519 | } 520 | 521 | case 2: 522 | { 523 | *pbData = *( ( (unsigned char*) &nLength ) + 1 ); 524 | *( pbData + 1) = *( ( (unsigned char*) &nLength ) ); 525 | break; 526 | } 527 | 528 | case 3: 529 | { 530 | *pbData = *( ( (unsigned char*) &nLength ) + 3 ); 531 | *( pbData + 1) = *( ( (unsigned char*) &nLength ) + 2 ); 532 | *( pbData + 2) = *( ( (unsigned char*) &nLength ) ); 533 | break; 534 | } 535 | 536 | case 4: 537 | { 538 | *pbData = *( ( (unsigned char*) &nLength ) + 3 ); 539 | *( pbData + 1) = *( ( (unsigned char*) &nLength ) + 2 ); 540 | *( pbData + 2) = *( ( (unsigned char*) &nLength ) + 1 ); 541 | *( pbData + 3) = *( ( (unsigned char*) &nLength ) ); 542 | break; 543 | } 544 | 545 | } // SWITCH ( nNumLengthBytes ) 546 | 547 | #else 548 | // We are Big-Endian, so the length can be copied in from the source 549 | // as is. Ensure that we adjust for the number of bytes we actually 550 | // copy. 551 | 552 | memcpy( pbData, 553 | ( (unsigned char *) &nLength ) + ( 4 - nNumLengthBytes ), nNumLengthBytes ); 554 | #endif 555 | 556 | } // IF > 1 byte for length 557 | else 558 | { 559 | // Cast the length to a single byte, since we know that it 560 | // is 0x7F or less (or we wouldn't only need a single byte). 561 | 562 | *pbData = (unsigned char) nLength; 563 | } 564 | 565 | return nNumBytesRequired; 566 | } 567 | 568 | ///////////////////////////////////////////////////////////////////////////// 569 | // 570 | // Function: 571 | // ASNDerWriteToken 572 | // 573 | // Parameters: 574 | // [out] pbData - Buffer to write into. 575 | // [in] ucType - Token Type 576 | // [in] pbTokenValue - Actual Value 577 | // [in] nLength - Length of Data. 578 | // 579 | // Returns: 580 | // int Number of bytes written out 581 | // 582 | // Comments : 583 | // Helper function to write out a token and any associated data. If 584 | // pbTokenValue is non-NULL, then it is written out in addition to the 585 | // token identifier and the length bytes. 586 | // 587 | //////////////////////////////////////////////////////////////////////////// 588 | 589 | int ASNDerWriteToken( unsigned char* pbData, unsigned char ucType, 590 | unsigned char* pbTokenValue, long nLength ) 591 | { 592 | int nTotalBytesWrittenOut = 0L; 593 | int nNumLengthBytesWritten = 0L; 594 | 595 | // Write out the type 596 | *pbData = ucType; 597 | 598 | // Wrote 1 byte, and move data pointer 599 | nTotalBytesWrittenOut++; 600 | pbData++; 601 | 602 | // Now write out the length and adjust the number of bytes written out 603 | nNumLengthBytesWritten = ASNDerWriteLength( pbData, nLength ); 604 | 605 | nTotalBytesWrittenOut += nNumLengthBytesWritten; 606 | pbData += nNumLengthBytesWritten; 607 | 608 | // Write out the token value if we got one. The assumption is that the 609 | // nLength value indicates how many bytes are in pbTokenValue. 610 | 611 | if ( NULL != pbTokenValue ) 612 | { 613 | memcpy( pbData, pbTokenValue, nLength ); 614 | nTotalBytesWrittenOut += nLength; 615 | } 616 | 617 | return nTotalBytesWrittenOut; 618 | } 619 | 620 | 621 | ///////////////////////////////////////////////////////////////////////////// 622 | // 623 | // Function: 624 | // ASNDerWriteOID 625 | // 626 | // Parameters: 627 | // [out] pbData - Buffer to write into. 628 | // [in] eMechOID - OID to write out. 629 | // 630 | // Returns: 631 | // int Number of bytes written out 632 | // 633 | // Comments : 634 | // Helper function to write out an OID. For these we have the raw bytes 635 | // listed in a global structure. The caller simply indicates which OID 636 | // should be written and we will splat out the data. 637 | // 638 | //////////////////////////////////////////////////////////////////////////// 639 | 640 | int ASNDerWriteOID( unsigned char* pbData, SPNEGO_MECH_OID eMechOID ) 641 | { 642 | 643 | memcpy( pbData, g_stcMechOIDList[eMechOID].ucOid, g_stcMechOIDList[eMechOID].iLen ); 644 | 645 | return g_stcMechOIDList[eMechOID].iLen; 646 | } 647 | 648 | 649 | ///////////////////////////////////////////////////////////////////////////// 650 | // 651 | // Function: 652 | // ASNDerWriteMechList 653 | // 654 | // Parameters: 655 | // [out] pbData - Buffer to write into. 656 | // [in] eMechOID - OID to put in MechList. 657 | // 658 | // Returns: 659 | // int Number of bytes written out 660 | // 661 | // Comments : 662 | // Helper function to write out a MechList. A MechList consists of the 663 | // Init Token Sequence, a sequence token and then the list of OIDs. In 664 | // our case the OID is from a global array of known OIDs. 665 | // 666 | //////////////////////////////////////////////////////////////////////////// 667 | 668 | long ASNDerWriteMechList( unsigned char* pbData, SPNEGO_MECH_OID mechoid ) 669 | { 670 | // First get the length 671 | long nInternalLength = 0L; 672 | long nMechListLength = ASNDerCalcMechListLength( mechoid, &nInternalLength ); 673 | long nTempLength = 0L; 674 | 675 | nTempLength = ASNDerWriteToken( pbData, SPNEGO_NEGINIT_ELEMENT_MECHTYPES, 676 | NULL, nInternalLength ); 677 | 678 | // Adjust the data pointer 679 | pbData += nTempLength; 680 | 681 | // Now write the Sequence token and the OID (the OID is a BLOB in the global 682 | // structure. 683 | 684 | nTempLength = ASNDerWriteToken( pbData, SPNEGO_CONSTRUCTED_SEQUENCE, 685 | g_stcMechOIDList[mechoid].ucOid, 686 | g_stcMechOIDList[mechoid].iLen ); 687 | 688 | return nMechListLength; 689 | } 690 | 691 | 692 | ///////////////////////////////////////////////////////////////////////////// 693 | // 694 | // Function: 695 | // ASNDerWriteElement 696 | // 697 | // Parameters: 698 | // [out] pbData - Buffer to write into. 699 | // [in] ucElementSequence - Sequence Token 700 | // [in] ucType - Token Type 701 | // [in] pbTokenValue - Actual Value 702 | // [in] nLength - Length of Data. 703 | // 704 | // Returns: 705 | // int Number of bytes written out 706 | // 707 | // Comments : 708 | // Helper function to write out a SPNEGO Token element. An element 709 | // consists of a sequence token, a type token and the associated data. 710 | // 711 | //////////////////////////////////////////////////////////////////////////// 712 | 713 | int ASNDerWriteElement( unsigned char* pbData, unsigned char ucElementSequence, 714 | unsigned char ucType, unsigned char* pbTokenValue, long nLength ) 715 | { 716 | // First get the length 717 | long nInternalLength = 0L; 718 | long nElementLength = ASNDerCalcElementLength( nLength, &nInternalLength ); 719 | long nTempLength = 0L; 720 | 721 | // Write out the sequence byte and the length of the type and data 722 | nTempLength = ASNDerWriteToken( pbData, ucElementSequence, NULL, nInternalLength ); 723 | 724 | // Adjust the data pointer 725 | pbData += nTempLength; 726 | 727 | // Now write the type and the data. 728 | nTempLength = ASNDerWriteToken( pbData, ucType, pbTokenValue, nLength ); 729 | 730 | return nElementLength; 731 | } 732 | 733 | -------------------------------------------------------------------------------- /spnegohelp/derparse.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2002 Microsoft Corporation 2 | // All rights reserved. 3 | // 4 | // THIS CODE AND INFORMATION IS PROVIDED "AS IS" 5 | // WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 6 | // OR IMPLIED, INCLUDING BUT NOT LIMITED 7 | // TO THE IMPLIED WARRANTIES OF MERCHANTIBILITY 8 | // AND/OR FITNESS FOR A PARTICULAR PURPOSE. 9 | // 10 | // Date - 10/08/2002 11 | // Author - Sanj Surati 12 | 13 | ///////////////////////////////////////////////////////////// 14 | // 15 | // DERPARSE.H 16 | // 17 | // SPNEGO Token Handler Header File 18 | // 19 | // Contains the definitions required to properly parse the 20 | // SPNEGO DER encoding. 21 | // 22 | ///////////////////////////////////////////////////////////// 23 | 24 | #ifndef __DERPARSE_H__ 25 | #define __DERPARSE_H__ 26 | 27 | // C++ Specific 28 | #if defined(__cplusplus) 29 | extern "C" 30 | { 31 | #endif 32 | 33 | /* Identifier Types */ 34 | #define IDENTIFIER_MASK 0xC0 // Bits 7 and 8 35 | #define IDENTIFIER_UNIVERSAL 0x00 // 00 = universal 36 | #define IDENTIFIER_APPLICATION 0x40 // 01 = application 37 | #define IDENTIFIER_CONTEXT_SPECIFIC 0x80 // 10 = context specific 38 | #define IDENTIFIER_PRIVATE 0xC0 // 11 = Private 39 | 40 | /* Encoding type */ 41 | 42 | #define FORM_MASK 0x20 /* Bit 6 */ 43 | #define PRIMITIVE 0x00 /* 0 = primitive */ 44 | #define CONSTRUCTED 0x20 /* 1 = constructed */ 45 | 46 | /* Universal tags */ 47 | 48 | #define TAG_MASK 0x1F /* Bits 5 - 1 */ 49 | #define BOOLEAN 0x01 /* 1: TRUE or FALSE */ 50 | #define INTEGER 0x02 /* 2: Arbitrary precision integer */ 51 | #define BITSTRING 0x03 /* 2: Sequence of bits */ 52 | #define OCTETSTRING 0x04 /* 4: Sequence of bytes */ 53 | #define NULLTAG 0x05 /* 5: NULL */ 54 | #define OID 0x06 /* 6: Object Identifier (numeric sequence) */ 55 | #define OBJDESCRIPTOR 0x07 /* 7: Object Descriptor (human readable) */ 56 | #define EXTERNAL 0x08 /* 8: External / Instance Of */ 57 | #define REAL 0x09 /* 9: Real (Mantissa * Base^Exponent) */ 58 | #define ENUMERATED 0x0A /* 10: Enumerated */ 59 | #define EMBEDDED_PDV 0x0B /* 11: Embedded Presentation Data Value */ 60 | #define SEQUENCE 0x10 /* 16: Constructed Sequence / Sequence Of */ 61 | #define SET 0x11 /* 17: Constructed Set / Set Of */ 62 | #define NUMERICSTR 0x12 /* 18: Numeric String (digits only) */ 63 | #define PRINTABLESTR 0x13 /* 19: Printable String */ 64 | #define T61STR 0x14 /* 20: T61 String (Teletex) */ 65 | #define VIDEOTEXSTR 0x15 /* 21: Videotex String */ 66 | #define IA5STR 0x16 /* 22: IA5 String */ 67 | #define UTCTIME 0x17 /* 23: UTC Time */ 68 | #define GENERALIZEDTIME 0x18 /* 24: Generalized Time */ 69 | #define GRAPHICSTR 0x19 /* 25: Graphic String */ 70 | #define VISIBLESTR 0x1A /* 26: Visible String (ISO 646) */ 71 | #define GENERALSTR 0x1B /* 27: General String */ 72 | #define UNIVERSALSTR 0x1C /* 28: Universal String */ 73 | #define BMPSTR 0x1E /* 30: Basic Multilingual Plane String */ 74 | 75 | /* Length encoding */ 76 | 77 | #define LEN_XTND 0x80 /* Indefinite or long form */ 78 | #define LEN_MASK 0x7f /* Bits 7 - 1 */ 79 | 80 | // 81 | // SPNEGO Token Parsing Constants 82 | // 83 | 84 | 85 | // Fixed Length of NegTokenInit ReqFlags field 86 | #define SPNEGO_NEGINIT_MAXLEN_REQFLAGS 2 87 | 88 | // Difference in bits for ReqFlags token 89 | #define SPNEGO_NEGINIT_REQFLAGS_BITDIFF 1 90 | 91 | // Fixed Length of NegTokenTarg NegResult field 92 | #define SPNEGO_NEGTARG_MAXLEN_NEGRESULT 1 93 | 94 | // Application Specific Construct - Always at the start of a NegTokenInit 95 | #define SPNEGO_NEGINIT_APP_CONSTRUCT ( IDENTIFIER_APPLICATION | CONSTRUCTED ) // 0x60 96 | 97 | // Constructed Sequence token - after the actual token identifier token 98 | #define SPNEGO_CONSTRUCTED_SEQUENCE ( SEQUENCE | CONSTRUCTED ) 99 | 100 | // MechList Type Identifier 101 | #define SPNEGO_MECHLIST_TYPE ( SEQUENCE | CONSTRUCTED | OID ) 102 | 103 | // 104 | // NegTokenInit - Token Identifier and Elements 105 | // 106 | 107 | // NegTokenInit - 0xa0 108 | #define SPNEGO_NEGINIT_TOKEN_IDENTIFIER ( IDENTIFIER_CONTEXT_SPECIFIC | CONSTRUCTED | \ 109 | SPNEGO_TOKEN_INIT ) 110 | 111 | // Structure elements for NegTokenInit 112 | #define SPNEGO_NEGINIT_MECHTYPES 0x0 // MechTypes is element 0 113 | #define SPNEGO_NEGINIT_REQFLAGS 0x1 // ReqFlags is element 1 114 | #define SPNEGO_NEGINIT_MECHTOKEN 0x2 // MechToken is element 2 115 | #define SPNEGO_NEGINIT_MECHLISTMIC 0x3 // MechListMIC is element 3 116 | 117 | // MechTypes element is 0xa0 118 | #define SPNEGO_NEGINIT_ELEMENT_MECHTYPES ( IDENTIFIER_CONTEXT_SPECIFIC | CONSTRUCTED | \ 119 | SPNEGO_NEGINIT_MECHTYPES ) 120 | 121 | // ReqFlags element is 0xa1 122 | #define SPNEGO_NEGINIT_ELEMENT_REQFLAGS ( IDENTIFIER_CONTEXT_SPECIFIC | CONSTRUCTED | \ 123 | SPNEGO_NEGINIT_REQFLAGS ) 124 | 125 | // MechToken element is 0xa2 126 | #define SPNEGO_NEGINIT_ELEMENT_MECHTOKEN ( IDENTIFIER_CONTEXT_SPECIFIC | CONSTRUCTED | \ 127 | SPNEGO_NEGINIT_MECHTOKEN ) 128 | 129 | // MechListMIC element is 0xa3 130 | #define SPNEGO_NEGINIT_ELEMENT_MECHLISTMIC ( IDENTIFIER_CONTEXT_SPECIFIC | CONSTRUCTED | \ 131 | SPNEGO_NEGINIT_MECHLISTMIC ) 132 | 133 | // 134 | // NegTokenTarg - Token Identifier and Elements 135 | // 136 | 137 | // NegTokenTarg - 0xa1 138 | #define SPNEGO_NEGTARG_TOKEN_IDENTIFIER ( IDENTIFIER_CONTEXT_SPECIFIC | CONSTRUCTED | \ 139 | SPNEGO_TOKEN_TARG ) 140 | 141 | // Structure elements for NegTokenTarg 142 | #define SPNEGO_NEGTARG_NEGRESULT 0x0 // NegResult is element 0 143 | #define SPNEGO_NEGTARG_SUPPORTEDMECH 0x1 // SupportedMech is element 1 144 | #define SPNEGO_NEGTARG_RESPONSETOKEN 0x2 // ResponseToken is element 2 145 | #define SPNEGO_NEGTARG_MECHLISTMIC 0x3 // MechListMIC is element 3 146 | 147 | // NegResult element is 0xa0 148 | #define SPNEGO_NEGTARG_ELEMENT_NEGRESULT ( IDENTIFIER_CONTEXT_SPECIFIC | CONSTRUCTED | \ 149 | SPNEGO_NEGTARG_NEGRESULT ) 150 | 151 | // SupportedMech element is 0xa1 152 | #define SPNEGO_NEGTARG_ELEMENT_SUPPORTEDMECH ( IDENTIFIER_CONTEXT_SPECIFIC | CONSTRUCTED | \ 153 | SPNEGO_NEGTARG_SUPPORTEDMECH ) 154 | 155 | // ResponseToken element is 0xa2 156 | #define SPNEGO_NEGTARG_ELEMENT_RESPONSETOKEN ( IDENTIFIER_CONTEXT_SPECIFIC | CONSTRUCTED | \ 157 | SPNEGO_NEGTARG_RESPONSETOKEN ) 158 | 159 | // MechListMIC element is 0xa3 160 | #define SPNEGO_NEGTARG_ELEMENT_MECHLISTMIC ( IDENTIFIER_CONTEXT_SPECIFIC | CONSTRUCTED | \ 161 | SPNEGO_NEGTARG_MECHLISTMIC ) 162 | 163 | // 164 | // Defines a GSS Mechanism OID. We keep a single static array 165 | // of these which we'll use for validation/searches/parsing. 166 | // 167 | 168 | typedef struct _mechOID 169 | { 170 | unsigned char* ucOid; // Byte representation of OID 171 | int iLen; // Length of the OID, length and identifier 172 | int iActualDataLen; // Length of the actual OID 173 | SPNEGO_MECH_OID eMechanismOID; // Which OID is this? 174 | } MECH_OID; 175 | 176 | 177 | // 178 | // ASN Der functions 179 | // 180 | 181 | int ASNDerGetLength( unsigned char* pbLengthData, long nBoundaryLength, long* pnLength, 182 | long* pnNumLengthBytes ); 183 | int ASNDerCheckToken( unsigned char* pbTokenData, unsigned char nToken, 184 | long nCheckLength, long nBoundaryLength, long* pnLength, 185 | long* pnTokenLength ); 186 | int ASNDerCheckOID( unsigned char* pbTokenData, SPNEGO_MECH_OID nMechOID, long nBoundaryLength, 187 | long* pnTokenLength ); 188 | int ASNDerCalcNumLengthBytes( long nLength ); 189 | long ASNDerCalcTokenLength( long nLength, long nDataLength ); 190 | long ASNDerCalcElementLength( long nDataLength, long* pnInternalLength ); 191 | long ASNDerCalcMechListLength( SPNEGO_MECH_OID mechoid, long* pnInternalLength ); 192 | int ASNDerWriteLength( unsigned char* pbData, long nLength ); 193 | int ASNDerWriteToken( unsigned char* pbData, unsigned char ucType, 194 | unsigned char* pbTokenValue, long nLength ); 195 | int ASNDerWriteOID( unsigned char* pbData, SPNEGO_MECH_OID eMechOID ); 196 | long ASNDerWriteMechList( unsigned char* pbData, SPNEGO_MECH_OID mechoid ); 197 | int ASNDerWriteElement( unsigned char* pbData, unsigned char ucElementSequence, 198 | unsigned char ucType, unsigned char* pbTokenValue, long nLength ); 199 | 200 | 201 | // C++ Specific 202 | #if defined(__cplusplus) 203 | } 204 | #endif 205 | 206 | #endif 207 | 208 | -------------------------------------------------------------------------------- /spnegohelp/spnego.c: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2002 Microsoft Corporation 2 | // All rights reserved. 3 | // 4 | // THIS CODE AND INFORMATION IS PROVIDED "AS IS" 5 | // WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 6 | // OR IMPLIED, INCLUDING BUT NOT LIMITED 7 | // TO THE IMPLIED WARRANTIES OF MERCHANTIBILITY 8 | // AND/OR FITNESS FOR A PARTICULAR PURPOSE. 9 | // 10 | // Date - 10/08/2002 11 | // Author - Sanj Surati 12 | 13 | ///////////////////////////////////////////////////////////// 14 | // 15 | // SPNEGO.C 16 | // 17 | // SPNEGO Token Handler Source File 18 | // 19 | // Contains implementation of SPNEGO Token Handling API 20 | // as defined in SPNEGO.H. 21 | // 22 | ///////////////////////////////////////////////////////////// 23 | 24 | #include 25 | #include 26 | #include 27 | #include "spnego.h" 28 | #include "derparse.h" 29 | #include "spnegoparse.h" 30 | 31 | // 32 | // Defined in DERPARSE.C 33 | // 34 | 35 | extern MECH_OID g_stcMechOIDList []; 36 | 37 | 38 | /**********************************************************************/ 39 | /** **/ 40 | /** **/ 41 | /** **/ 42 | /** **/ 43 | /** SPNEGO Token Handler API implementation **/ 44 | /** **/ 45 | /** **/ 46 | /** **/ 47 | /** **/ 48 | /**********************************************************************/ 49 | 50 | 51 | ///////////////////////////////////////////////////////////////////////////// 52 | // 53 | // Function: 54 | // spnegoInitFromBinary 55 | // 56 | // Parameters: 57 | // [in] pbTokenData - Binary Token Data 58 | // [in] ulLength - Length of binary Token Data 59 | // [out] phSpnegoToken - SPNEGO_TOKEN_HANDLE pointer 60 | // 61 | // Returns: 62 | // int Success - SPNEGO_E_SUCCESS 63 | // Failure - SPNEGO API Error code 64 | // 65 | // Comments : 66 | // Initializes a SPNEGO_TOKEN_HANDLE from the supplied 67 | // binary data. Data is copied locally. Returned data structure 68 | // must be freed by calling spnegoFreeData(). 69 | // 70 | //////////////////////////////////////////////////////////////////////////// 71 | 72 | int spnegoInitFromBinary( unsigned char* pbTokenData, unsigned long ulLength, SPNEGO_TOKEN_HANDLE* phSpnegoToken ) 73 | { 74 | int nReturn = SPNEGO_E_INVALID_PARAMETER; 75 | SPNEGO_TOKEN** ppSpnegoToken = (SPNEGO_TOKEN**) phSpnegoToken; 76 | 77 | // Pass off to a handler function that allows tighter control over how the token structure 78 | // is handled. In this case, we want the token data copied and we want the associated buffer 79 | // freed. 80 | nReturn = InitTokenFromBinary( SPNEGO_TOKEN_INTERNAL_COPYDATA, 81 | SPNEGO_TOKEN_INTERNAL_FLAGS_FREEDATA, pbTokenData, 82 | ulLength, ppSpnegoToken ); 83 | 84 | LOG(("spnegoInitFromBinary returned %d\n",nReturn)); 85 | return nReturn; 86 | } 87 | 88 | ///////////////////////////////////////////////////////////////////////////// 89 | // 90 | // Function: 91 | // spnegoCreateNegTokenInit 92 | // 93 | // Parameters: 94 | // [in] MechType - MechType to specify in MechTypeList element 95 | // [in] ucContextFlags - Context Flags element value 96 | // [in] pbMechToken - Pointer to binary MechToken Data 97 | // [in] ulMechTokenLen - Length of MechToken Data 98 | // [in] pbMechListMIC - Pointer to binary MechListMIC Data 99 | // [in] ulMechListMICLen - Length of MechListMIC Data 100 | // [out] phSpnegoToken - SPNEGO_TOKEN_HANDLE pointer 101 | // 102 | // Returns: 103 | // int Success - SPNEGO_E_SUCCESS 104 | // Failure - SPNEGO API Error code 105 | // 106 | // Comments : 107 | // Initializes a SPNEGO_TOKEN_HANDLE for a NegTokenInit type 108 | // from the supplied parameters. ucContextFlags may be 0 or must be 109 | // a valid flag combination. MechToken data can be NULL - if not, it 110 | // must correspond to the MechType. MechListMIC can also be NULL. 111 | // Returned data structure must be freed by calling spnegoFreeData(). 112 | // 113 | //////////////////////////////////////////////////////////////////////////// 114 | 115 | int spnegoCreateNegTokenInit( SPNEGO_MECH_OID MechType, 116 | unsigned char ucContextFlags, unsigned char* pbMechToken, 117 | unsigned long ulMechTokenLen, unsigned char* pbMechListMIC, 118 | unsigned long ulMechListMICLen, SPNEGO_TOKEN_HANDLE* phSpnegoToken ) 119 | { 120 | int nReturn = SPNEGO_E_INVALID_PARAMETER; 121 | long nTokenLength = 0L; 122 | long nInternalTokenLength = 0L; 123 | unsigned char* pbTokenData = NULL; 124 | SPNEGO_TOKEN** ppSpnegoToken = (SPNEGO_TOKEN**) phSpnegoToken; 125 | 126 | if ( NULL != ppSpnegoToken && 127 | IsValidMechOid( MechType ) && 128 | IsValidContextFlags( ucContextFlags ) ) 129 | { 130 | // Get the actual token size 131 | 132 | if ( ( nReturn = CalculateMinSpnegoInitTokenSize( ulMechTokenLen, ulMechListMICLen, 133 | MechType, ( ucContextFlags != 0L ), 134 | &nTokenLength, &nInternalTokenLength ) ) 135 | == SPNEGO_E_SUCCESS ) 136 | { 137 | // Allocate a buffer to hold the data. 138 | pbTokenData = calloc( 1, nTokenLength ); 139 | 140 | if ( NULL != pbTokenData ) 141 | { 142 | 143 | // Now write the token 144 | if ( ( nReturn = CreateSpnegoInitToken( MechType, 145 | ucContextFlags, pbMechToken, 146 | ulMechTokenLen, pbMechListMIC, 147 | ulMechListMICLen, pbTokenData, 148 | nTokenLength, nInternalTokenLength ) ) 149 | == SPNEGO_E_SUCCESS ) 150 | { 151 | 152 | // This will copy our allocated pointer, and ensure that the sructure cleans 153 | // up the data later 154 | nReturn = InitTokenFromBinary( SPNEGO_TOKEN_INTERNAL_COPYPTR, 155 | SPNEGO_TOKEN_INTERNAL_FLAGS_FREEDATA, 156 | pbTokenData, nTokenLength, ppSpnegoToken ); 157 | 158 | } 159 | 160 | // Cleanup on failure 161 | if ( SPNEGO_E_SUCCESS != nReturn ) 162 | { 163 | free( pbTokenData ); 164 | } 165 | 166 | } // IF alloc succeeded 167 | else 168 | { 169 | nReturn = SPNEGO_E_OUT_OF_MEMORY; 170 | } 171 | 172 | } // If calculated token size 173 | 174 | } // IF Valid Parameters 175 | 176 | LOG(("spnegoCreateNegTokenInit returned %d\n",nReturn)); 177 | return nReturn; 178 | } 179 | 180 | ///////////////////////////////////////////////////////////////////////////// 181 | // 182 | // Function: 183 | // spnegoCreateNegTokenTarg 184 | // 185 | // Parameters: 186 | // [in] MechType - MechType to specify in supported MechType element 187 | // [in] spnegoNegResult - NegResult value 188 | // [in] pbMechToken - Pointer to response MechToken Data 189 | // [in] ulMechTokenLen - Length of MechToken Data 190 | // [in] pbMechListMIC - Pointer to binary MechListMIC Data 191 | // [in] ulMechListMICLen - Length of MechListMIC Data 192 | // [out] phSpnegoToken - SPNEGO_TOKEN_HANDLE pointer 193 | // 194 | // Returns: 195 | // int Success - SPNEGO_E_SUCCESS 196 | // Failure - SPNEGO API Error code 197 | // 198 | // Comments : 199 | // Initializes a SPNEGO_TOKEN_HANDLE for a NegTokenTarg type 200 | // from the supplied parameters. MechToken data can be NULL - if not, 201 | // it must correspond to the MechType. MechListMIC can also be NULL. 202 | // Returned data structure must be freed by calling spnegoFreeData(). 203 | // 204 | //////////////////////////////////////////////////////////////////////////// 205 | 206 | int spnegoCreateNegTokenTarg( SPNEGO_MECH_OID MechType, 207 | SPNEGO_NEGRESULT spnegoNegResult, unsigned char* pbMechToken, 208 | unsigned long ulMechTokenLen, unsigned char* pbMechListMIC, 209 | unsigned long ulMechListMICLen, SPNEGO_TOKEN_HANDLE* phSpnegoToken ) 210 | { 211 | int nReturn = SPNEGO_E_INVALID_PARAMETER; 212 | long nTokenLength = 0L; 213 | long nInternalTokenLength = 0L; 214 | unsigned char* pbTokenData = NULL; 215 | SPNEGO_TOKEN** ppSpnegoToken = (SPNEGO_TOKEN**) phSpnegoToken; 216 | 217 | // 218 | // spnego_mech_oid_NotUsed and spnego_negresult_NotUsed 219 | // are okay here, however a valid MechOid is required 220 | // if spnego_negresult_success or spnego_negresult_incomplete 221 | // is specified. 222 | // 223 | 224 | if ( NULL != ppSpnegoToken && 225 | 226 | ( IsValidMechOid( MechType ) || 227 | spnego_mech_oid_NotUsed == MechType ) && 228 | 229 | ( IsValidNegResult( spnegoNegResult ) || 230 | spnego_negresult_NotUsed == spnegoNegResult ) && 231 | 232 | !( !IsValidMechOid( MechType ) && 233 | ( spnego_negresult_success == spnegoNegResult || 234 | spnego_negresult_incomplete == spnegoNegResult ) ) ) 235 | { 236 | 237 | // Get the actual token size 238 | 239 | if ( ( nReturn = CalculateMinSpnegoTargTokenSize( MechType, spnegoNegResult, ulMechTokenLen, 240 | ulMechListMICLen, &nTokenLength, 241 | &nInternalTokenLength ) ) 242 | == SPNEGO_E_SUCCESS ) 243 | { 244 | // Allocate a buffer to hold the data. 245 | pbTokenData = calloc( 1, nTokenLength ); 246 | 247 | if ( NULL != pbTokenData ) 248 | { 249 | 250 | // Now write the token 251 | if ( ( nReturn = CreateSpnegoTargToken( MechType, 252 | spnegoNegResult, pbMechToken, 253 | ulMechTokenLen, pbMechListMIC, 254 | ulMechListMICLen, pbTokenData, 255 | nTokenLength, nInternalTokenLength ) ) 256 | == SPNEGO_E_SUCCESS ) 257 | { 258 | 259 | // This will copy our allocated pointer, and ensure that the sructure cleans 260 | // up the data later 261 | nReturn = InitTokenFromBinary( SPNEGO_TOKEN_INTERNAL_COPYPTR, 262 | SPNEGO_TOKEN_INTERNAL_FLAGS_FREEDATA, 263 | pbTokenData, nTokenLength, ppSpnegoToken ); 264 | 265 | } 266 | 267 | // Cleanup on failure 268 | if ( SPNEGO_E_SUCCESS != nReturn ) 269 | { 270 | free( pbTokenData ); 271 | } 272 | 273 | } // IF alloc succeeded 274 | else 275 | { 276 | nReturn = SPNEGO_E_OUT_OF_MEMORY; 277 | } 278 | 279 | } // If calculated token size 280 | 281 | } // IF Valid Parameters 282 | 283 | LOG(("spnegoCreateNegTokenTarg returned %d\n",nReturn)); 284 | return nReturn; 285 | } 286 | 287 | ///////////////////////////////////////////////////////////////////////////// 288 | // 289 | // Function: 290 | // spnegoTokenGetBinary 291 | // 292 | // Parameters: 293 | // [in] hSpnegoToken - Initialized SPNEGO_TOKEN_HANDLE 294 | // [out] pbTokenData - Buffer to copy token into 295 | // [in/out] pulDataLen - Length of pbTokenData buffer, filled out 296 | // with actual size used upon function return. 297 | // 298 | // Returns: 299 | // int Success - SPNEGO_E_SUCCESS 300 | // Failure - SPNEGO API Error code 301 | // 302 | // Comments : 303 | // Copies binary SPNEGO token data from hSpnegoToken into the user 304 | // supplied buffer. If pbTokenData is NULL, or the value in pulDataLen 305 | // is too small, the function will return SPNEGO_E_BUFFER_TOO_SMALL and 306 | // fill out pulDataLen with the minimum required buffer size. 307 | // 308 | //////////////////////////////////////////////////////////////////////////// 309 | 310 | int spnegoTokenGetBinary( SPNEGO_TOKEN_HANDLE hSpnegoToken, unsigned char* pbTokenData, 311 | unsigned long * pulDataLen ) 312 | { 313 | int nReturn = SPNEGO_E_INVALID_PARAMETER; 314 | SPNEGO_TOKEN* pSpnegoToken = (SPNEGO_TOKEN*) hSpnegoToken; 315 | 316 | // Check parameters - pbTokenData is optional 317 | if ( IsValidSpnegoToken( pSpnegoToken ) && 318 | NULL != pulDataLen ) 319 | { 320 | 321 | // Check for Buffer too small conditions 322 | if ( NULL == pbTokenData || 323 | pSpnegoToken->ulBinaryDataLen > *pulDataLen ) 324 | { 325 | *pulDataLen = pSpnegoToken->ulBinaryDataLen; 326 | nReturn = SPNEGO_E_BUFFER_TOO_SMALL; 327 | } 328 | else 329 | { 330 | memcpy( pbTokenData, pSpnegoToken->pbBinaryData, pSpnegoToken->ulBinaryDataLen ); 331 | *pulDataLen = pSpnegoToken->ulBinaryDataLen; 332 | nReturn = SPNEGO_E_SUCCESS; 333 | } 334 | 335 | } // IF parameters OK 336 | 337 | LOG(("spnegoTokenGetBinary returned %d\n",nReturn)); 338 | return nReturn;; 339 | } 340 | 341 | ///////////////////////////////////////////////////////////////////////////// 342 | // 343 | // Function: 344 | // spnegoFreeData 345 | // 346 | // Parameters: 347 | // [in] hSpnegoToken - Initialized SPNEGO_TOKEN_HANDLE 348 | // 349 | // Returns: 350 | // void 351 | // 352 | // Comments : 353 | // Frees up resources consumed by hSpnegoToken. The supplied data 354 | // pointer is invalidated by this function. 355 | // 356 | //////////////////////////////////////////////////////////////////////////// 357 | 358 | void spnegoFreeData( SPNEGO_TOKEN_HANDLE hSpnegoToken ) 359 | { 360 | FreeSpnegoToken( (SPNEGO_TOKEN*) hSpnegoToken); 361 | return; 362 | } 363 | 364 | ///////////////////////////////////////////////////////////////////////////// 365 | // 366 | // Function: 367 | // spnegoGetTokenType 368 | // 369 | // Parameters: 370 | // [in] hSpnegoToken - Initialized SPNEGO_TOKEN_HANDLE 371 | // [out] piTokenType - Filled out with token type value. 372 | // 373 | // Returns: 374 | // int Success - SPNEGO_E_SUCCESS 375 | // Failure - SPNEGO API Error code 376 | // 377 | // Comments : 378 | // The function will analyze hSpnegoToken and return the appropriate 379 | // type in piTokenType. 380 | // 381 | //////////////////////////////////////////////////////////////////////////// 382 | 383 | int spnegoGetTokenType( SPNEGO_TOKEN_HANDLE hSpnegoToken, int * piTokenType ) 384 | { 385 | int nReturn = SPNEGO_E_INVALID_PARAMETER; 386 | SPNEGO_TOKEN* pSpnegoToken = (SPNEGO_TOKEN*) hSpnegoToken; 387 | 388 | // Check parameters 389 | if ( IsValidSpnegoToken( pSpnegoToken ) && 390 | NULL != piTokenType && 391 | pSpnegoToken) 392 | { 393 | 394 | // Check that the type in the structure makes sense 395 | if ( SPNEGO_TOKEN_INIT == pSpnegoToken->ucTokenType || 396 | SPNEGO_TOKEN_TARG == pSpnegoToken->ucTokenType ) 397 | { 398 | *piTokenType = pSpnegoToken->ucTokenType; 399 | nReturn = SPNEGO_E_SUCCESS; 400 | } 401 | 402 | } // IF parameters OK 403 | 404 | LOG(("spnegoGetTokenType returned %d\n",nReturn)); 405 | return nReturn; 406 | } 407 | 408 | ///////////////////////////////////////////////////////////////////////////// 409 | // 410 | // Function: 411 | // spnegoIsMechTypeAvailable 412 | // 413 | // Parameters: 414 | // [in] hSpnegoToken - Initialized SPNEGO_TOKEN_HANDLE 415 | // [in] MechOID - MechOID to search MechTypeList for 416 | // [out] piMechTypeIndex - Filled out with index in MechTypeList 417 | // element if MechOID is found. 418 | // 419 | // Returns: 420 | // int Success - SPNEGO_E_SUCCESS 421 | // Failure - SPNEGO API Error code 422 | // 423 | // Comments : 424 | // hSpnegoToken must reference a token of type NegTokenInit. The 425 | // function will search the MechTypeList element for an OID corresponding 426 | // to the specified MechOID. If one is found, the index (0 based) will 427 | // be passed into the piMechTypeIndex parameter. 428 | // 429 | //////////////////////////////////////////////////////////////////////////// 430 | 431 | // Returns the Initial Mech Type in the MechList element in the NegInitToken. 432 | int spnegoIsMechTypeAvailable( SPNEGO_TOKEN_HANDLE hSpnegoToken, SPNEGO_MECH_OID MechOID, int * piMechTypeIndex ) 433 | { 434 | int nReturn = SPNEGO_E_INVALID_PARAMETER; 435 | SPNEGO_TOKEN* pSpnegoToken = (SPNEGO_TOKEN*) hSpnegoToken; 436 | 437 | // Check parameters 438 | if ( IsValidSpnegoToken( pSpnegoToken ) && 439 | NULL != piMechTypeIndex && 440 | IsValidMechOid( MechOID ) && 441 | SPNEGO_TOKEN_INIT == pSpnegoToken->ucTokenType ) 442 | { 443 | 444 | // Check if MechList is available 445 | if ( pSpnegoToken->aElementArray[SPNEGO_INIT_MECHTYPES_ELEMENT].iElementPresent 446 | == SPNEGO_TOKEN_ELEMENT_AVAILABLE ) 447 | { 448 | // Locate the MechOID in the list element 449 | nReturn = FindMechOIDInMechList( 450 | &pSpnegoToken->aElementArray[SPNEGO_INIT_MECHTYPES_ELEMENT], 451 | MechOID, piMechTypeIndex ); 452 | } 453 | else 454 | { 455 | nReturn = SPNEGO_E_ELEMENT_UNAVAILABLE; 456 | } 457 | 458 | } // IF parameters OK 459 | 460 | LOG(("spnegoIsMechTypeAvailable returned %d\n",nReturn)); 461 | return nReturn;; 462 | } 463 | 464 | ///////////////////////////////////////////////////////////////////////////// 465 | // 466 | // Function: 467 | // spnegoGetContextFlags 468 | // 469 | // Parameters: 470 | // [in] hSpnegoToken - Initialized SPNEGO_TOKEN_HANDLE 471 | // [out] pucContextFlags - Filled out with ContextFlags value. 472 | // 473 | // Returns: 474 | // int Success - SPNEGO_E_SUCCESS 475 | // Failure - SPNEGO API Error code 476 | // 477 | // Comments : 478 | // hSpnegoToken must reference a token of type NegTokenInit. The 479 | // function will copy data from the ContextFlags element into the 480 | // location pucContextFlags points to. Note that the function will 481 | // fail if the actual ContextFlags data appears invalid. 482 | // 483 | //////////////////////////////////////////////////////////////////////////// 484 | 485 | int spnegoGetContextFlags( SPNEGO_TOKEN_HANDLE hSpnegoToken, unsigned char* pucContextFlags ) 486 | { 487 | int nReturn = SPNEGO_E_INVALID_PARAMETER; 488 | SPNEGO_TOKEN* pSpnegoToken = (SPNEGO_TOKEN*) hSpnegoToken; 489 | 490 | // Check parameters 491 | if ( IsValidSpnegoToken( pSpnegoToken ) && 492 | NULL != pucContextFlags && 493 | SPNEGO_TOKEN_INIT == pSpnegoToken->ucTokenType ) 494 | { 495 | 496 | // Check if ContextFlags is available 497 | if ( pSpnegoToken->aElementArray[SPNEGO_INIT_REQFLAGS_ELEMENT].iElementPresent 498 | == SPNEGO_TOKEN_ELEMENT_AVAILABLE ) 499 | { 500 | // The length should be two, the value should show a 1 bit difference in the difference byte, and 501 | // the value must be valid 502 | if ( pSpnegoToken->aElementArray[SPNEGO_INIT_REQFLAGS_ELEMENT].nDatalength == SPNEGO_NEGINIT_MAXLEN_REQFLAGS && 503 | pSpnegoToken->aElementArray[SPNEGO_INIT_REQFLAGS_ELEMENT].pbData[0] == SPNEGO_NEGINIT_REQFLAGS_BITDIFF && 504 | IsValidContextFlags( pSpnegoToken->aElementArray[SPNEGO_INIT_REQFLAGS_ELEMENT].pbData[1] ) ) 505 | { 506 | *pucContextFlags = pSpnegoToken->aElementArray[SPNEGO_INIT_REQFLAGS_ELEMENT].pbData[1]; 507 | nReturn = SPNEGO_E_SUCCESS; 508 | } 509 | else 510 | { 511 | nReturn = SPNEGO_E_INVALID_ELEMENT; 512 | } 513 | 514 | } 515 | else 516 | { 517 | nReturn = SPNEGO_E_ELEMENT_UNAVAILABLE; 518 | } 519 | 520 | } // IF parameters OK 521 | 522 | LOG(("spnegoGetContextFlags returned %d\n",nReturn)); 523 | return nReturn;; 524 | } 525 | 526 | ///////////////////////////////////////////////////////////////////////////// 527 | // 528 | // Function: 529 | // spnegoGetNegotiationResult 530 | // 531 | // Parameters: 532 | // [in] hSpnegoToken - Initialized SPNEGO_TOKEN_HANDLE 533 | // [out] pnegResult - Filled out with NegResult value. 534 | // 535 | // Returns: 536 | // int Success - SPNEGO_E_SUCCESS 537 | // Failure - SPNEGO API Error code 538 | // 539 | // Comments : 540 | // hSpnegoToken must reference a token of type NegTokenTarg. The 541 | // function will copy data from the NegResult element into the 542 | // location pointed to by pnegResult. Note that the function will 543 | // fail if the actual NegResult data appears invalid. 544 | // 545 | //////////////////////////////////////////////////////////////////////////// 546 | 547 | int spnegoGetNegotiationResult( SPNEGO_TOKEN_HANDLE hSpnegoToken, SPNEGO_NEGRESULT* pnegResult ) 548 | { 549 | int nReturn = SPNEGO_E_INVALID_PARAMETER; 550 | SPNEGO_TOKEN* pSpnegoToken = (SPNEGO_TOKEN*) hSpnegoToken; 551 | 552 | // Check parameters 553 | if ( IsValidSpnegoToken( pSpnegoToken ) && 554 | NULL != pnegResult && 555 | SPNEGO_TOKEN_TARG == pSpnegoToken->ucTokenType ) 556 | { 557 | 558 | // Check if NegResult is available 559 | if ( pSpnegoToken->aElementArray[SPNEGO_TARG_NEGRESULT_ELEMENT].iElementPresent 560 | == SPNEGO_TOKEN_ELEMENT_AVAILABLE ) 561 | { 562 | // Must be 1 byte long and a valid value 563 | if ( pSpnegoToken->aElementArray[SPNEGO_TARG_NEGRESULT_ELEMENT].nDatalength == SPNEGO_NEGTARG_MAXLEN_NEGRESULT && 564 | IsValidNegResult( *pSpnegoToken->aElementArray[SPNEGO_TARG_NEGRESULT_ELEMENT].pbData ) ) 565 | { 566 | *pnegResult = *pSpnegoToken->aElementArray[SPNEGO_TARG_NEGRESULT_ELEMENT].pbData; 567 | nReturn = SPNEGO_E_SUCCESS; 568 | } 569 | else 570 | { 571 | nReturn = SPNEGO_E_INVALID_ELEMENT; 572 | } 573 | } 574 | else 575 | { 576 | nReturn = SPNEGO_E_ELEMENT_UNAVAILABLE; 577 | } 578 | 579 | } // IF parameters OK 580 | 581 | LOG(("spnegoGetNegotiationResult returned %d\n",nReturn)); 582 | return nReturn;; 583 | } 584 | 585 | ///////////////////////////////////////////////////////////////////////////// 586 | // 587 | // Function: 588 | // spnegoGetSupportedMechType 589 | // 590 | // Parameters: 591 | // [in] hSpnegoToken - Initialized SPNEGO_TOKEN_HANDLE 592 | // [out] pMechOID - Filled out with Supported MechType value. 593 | // 594 | // Returns: 595 | // int Success - SPNEGO_E_SUCCESS 596 | // Failure - SPNEGO API Error code 597 | // 598 | // Comments : 599 | // hSpnegoToken must reference a token of type NegTokenTarg. The 600 | // function will check the Supported MechType element, and if it 601 | // corresponds to a supported MechType ( spnego_mech_oid_Kerberos_V5_Legacy 602 | // or spnego_mech_oid_Kerberos_V5 ), will set the location pointed 603 | // to by pMechOID equal to the appropriate value. 604 | // 605 | //////////////////////////////////////////////////////////////////////////// 606 | 607 | int spnegoGetSupportedMechType( SPNEGO_TOKEN_HANDLE hSpnegoToken, SPNEGO_MECH_OID* pMechOID ) 608 | { 609 | int nReturn = SPNEGO_E_INVALID_PARAMETER; 610 | int nCtr = 0L; 611 | long nLength = 0L; 612 | SPNEGO_TOKEN* pSpnegoToken = (SPNEGO_TOKEN*) hSpnegoToken; 613 | 614 | // Check parameters 615 | if ( IsValidSpnegoToken( pSpnegoToken ) && 616 | NULL != pMechOID && 617 | SPNEGO_TOKEN_TARG == pSpnegoToken->ucTokenType ) 618 | { 619 | 620 | // Check if MechList is available 621 | if ( pSpnegoToken->aElementArray[SPNEGO_TARG_SUPPMECH_ELEMENT].iElementPresent 622 | == SPNEGO_TOKEN_ELEMENT_AVAILABLE ) 623 | { 624 | 625 | for ( nCtr = 0; 626 | nReturn != SPNEGO_E_SUCCESS && 627 | g_stcMechOIDList[nCtr].eMechanismOID != spnego_mech_oid_NotUsed; 628 | nCtr++ ) 629 | { 630 | 631 | if ( ( nReturn = ASNDerCheckOID( 632 | pSpnegoToken->aElementArray[SPNEGO_TARG_SUPPMECH_ELEMENT].pbData, 633 | nCtr, 634 | pSpnegoToken->aElementArray[SPNEGO_TARG_SUPPMECH_ELEMENT].nDatalength, 635 | &nLength ) ) == SPNEGO_E_SUCCESS ) 636 | { 637 | *pMechOID = nCtr; 638 | } 639 | 640 | } // For enum MechOIDs 641 | 642 | 643 | } 644 | else 645 | { 646 | nReturn = SPNEGO_E_ELEMENT_UNAVAILABLE; 647 | } 648 | 649 | } // IF parameters OK 650 | 651 | LOG(("spnegoGetSupportedMechType returned %d\n",nReturn)); 652 | return nReturn;; 653 | } 654 | 655 | ///////////////////////////////////////////////////////////////////////////// 656 | // 657 | // Function: 658 | // spnegoTokenGetMechToken 659 | // 660 | // Parameters: 661 | // [in] hSpnegoToken - Initialized SPNEGO_TOKEN_HANDLE 662 | // [out] pbTokenData - Buffer to copy MechToken into 663 | // [in/out] pulDataLen - Length of pbTokenData buffer, filled out 664 | // with actual size used upon function return. 665 | // 666 | // Returns: 667 | // int Success - SPNEGO_E_SUCCESS 668 | // Failure - SPNEGO API Error code 669 | // 670 | // Comments : 671 | // hSpnegoToken can point to either NegTokenInit or a NegTokenTarg token. 672 | // The function will copy the MechToken (the initial MechToken if 673 | // NegTokenInit, the response MechToken if NegTokenTarg) from the 674 | // underlying token into the buffer pointed to by pbTokenData. If 675 | // pbTokenData is NULL, or the value in pulDataLen is too small, the 676 | // function will return SPNEGO_E_BUFFER_TOO_SMALL and fill out pulDataLen 677 | // with the minimum required buffer size. The token can then be passed 678 | // to a GSS-API function for processing. 679 | // 680 | //////////////////////////////////////////////////////////////////////////// 681 | 682 | int spnegoGetMechToken( SPNEGO_TOKEN_HANDLE hSpnegoToken, unsigned char* pbTokenData, unsigned long* pulDataLen ) 683 | { 684 | int nReturn = SPNEGO_E_INVALID_PARAMETER; 685 | SPNEGO_TOKEN* pSpnegoToken = (SPNEGO_TOKEN*) hSpnegoToken; 686 | SPNEGO_ELEMENT* pSpnegoElement = NULL; 687 | 688 | // Check parameters 689 | if ( IsValidSpnegoToken( pSpnegoToken ) && 690 | NULL != pulDataLen ) 691 | { 692 | 693 | // Point at the proper Element 694 | if ( SPNEGO_TOKEN_INIT == pSpnegoToken->ucTokenType ) 695 | { 696 | pSpnegoElement = &pSpnegoToken->aElementArray[SPNEGO_INIT_MECHTOKEN_ELEMENT]; 697 | } 698 | else 699 | { 700 | pSpnegoElement = &pSpnegoToken->aElementArray[SPNEGO_TARG_RESPTOKEN_ELEMENT]; 701 | } 702 | 703 | // Check if MechType is available 704 | if ( SPNEGO_TOKEN_ELEMENT_AVAILABLE == pSpnegoElement->iElementPresent ) 705 | { 706 | // Check for Buffer too small conditions 707 | if ( NULL == pbTokenData || 708 | pSpnegoElement->nDatalength > *pulDataLen ) 709 | { 710 | *pulDataLen = pSpnegoElement->nDatalength; 711 | nReturn = SPNEGO_E_BUFFER_TOO_SMALL; 712 | } 713 | else 714 | { 715 | // Copy Memory 716 | memcpy( pbTokenData, pSpnegoElement->pbData, pSpnegoElement->nDatalength ); 717 | *pulDataLen = pSpnegoElement->nDatalength; 718 | nReturn = SPNEGO_E_SUCCESS; 719 | } 720 | } 721 | else 722 | { 723 | nReturn = SPNEGO_E_ELEMENT_UNAVAILABLE; 724 | } 725 | 726 | } // IF parameters OK 727 | 728 | LOG(("spnegoGetMechToken returned %d\n",nReturn)); 729 | return nReturn;; 730 | } 731 | 732 | ///////////////////////////////////////////////////////////////////////////// 733 | // 734 | // Function: 735 | // spnegoTokenGetMechListMIC 736 | // 737 | // Parameters: 738 | // [in] hSpnegoToken - Initialized SPNEGO_TOKEN_HANDLE 739 | // [out] pbTokenData - Buffer to copy MechListMIC data into 740 | // [in/out] pulDataLen - Length of pbTokenData buffer, filled out 741 | // with actual size used upon function return. 742 | // 743 | // Returns: 744 | // int Success - SPNEGO_E_SUCCESS 745 | // Failure - SPNEGO API Error code 746 | // 747 | // Comments : 748 | // hSpnegoToken can point to either NegTokenInit or a NegTokenTarg token. 749 | // The function will copy the MechListMIC data from the underlying token 750 | // into the buffer pointed to by pbTokenData. If pbTokenData is NULL, 751 | // or the value in pulDataLen is too small, the function will return 752 | // SPNEGO_E_BUFFER_TOO_SMALL and fill out pulDataLen with the minimum 753 | // required buffer size. 754 | // 755 | //////////////////////////////////////////////////////////////////////////// 756 | 757 | int spnegoGetMechListMIC( SPNEGO_TOKEN_HANDLE hSpnegoToken, unsigned char* pbMICData, unsigned long* pulDataLen ) 758 | { 759 | int nReturn = SPNEGO_E_INVALID_PARAMETER; 760 | SPNEGO_TOKEN* pSpnegoToken = (SPNEGO_TOKEN*) hSpnegoToken; 761 | SPNEGO_ELEMENT* pSpnegoElement = NULL; 762 | 763 | // Check parameters 764 | if ( IsValidSpnegoToken( pSpnegoToken ) && 765 | NULL != pulDataLen ) 766 | { 767 | 768 | // Point at the proper Element 769 | if ( SPNEGO_TOKEN_INIT == pSpnegoToken->ucTokenType ) 770 | { 771 | pSpnegoElement = &pSpnegoToken->aElementArray[SPNEGO_INIT_MECHLISTMIC_ELEMENT]; 772 | } 773 | else 774 | { 775 | pSpnegoElement = &pSpnegoToken->aElementArray[SPNEGO_TARG_MECHLISTMIC_ELEMENT]; 776 | } 777 | 778 | // Check if MechType is available 779 | if ( SPNEGO_TOKEN_ELEMENT_AVAILABLE == pSpnegoElement->iElementPresent ) 780 | { 781 | // Check for Buffer too small conditions 782 | if ( NULL == pbMICData || 783 | pSpnegoElement->nDatalength > *pulDataLen ) 784 | { 785 | *pulDataLen = pSpnegoElement->nDatalength; 786 | nReturn = SPNEGO_E_BUFFER_TOO_SMALL; 787 | } 788 | else 789 | { 790 | // Copy Memory 791 | memcpy( pbMICData, pSpnegoElement->pbData, pSpnegoElement->nDatalength ); 792 | *pulDataLen = pSpnegoElement->nDatalength; 793 | nReturn = SPNEGO_E_SUCCESS; 794 | } 795 | } 796 | else 797 | { 798 | nReturn = SPNEGO_E_ELEMENT_UNAVAILABLE; 799 | } 800 | 801 | } // IF parameters OK 802 | 803 | LOG(("spnegoGetMechListMIC returned %d\n",nReturn)); 804 | return nReturn;; 805 | } 806 | 807 | -------------------------------------------------------------------------------- /spnegohelp/spnego.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2002 Microsoft Corporation 2 | // All rights reserved. 3 | // 4 | // THIS CODE AND INFORMATION IS PROVIDED "AS IS" 5 | // WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 6 | // OR IMPLIED, INCLUDING BUT NOT LIMITED 7 | // TO THE IMPLIED WARRANTIES OF MERCHANTIBILITY 8 | // AND/OR FITNESS FOR A PARTICULAR PURPOSE. 9 | // 10 | // Date - 10/08/2002 11 | // Author - Sanj Surati 12 | 13 | ///////////////////////////////////////////////////////////// 14 | // 15 | // SPNEGO.H 16 | // 17 | // SPNEGO Token Handler Header File 18 | // 19 | // Contains the definitions required to interpret and create 20 | // SPNEGO tokens so that Kerberos GSS tokens can be 21 | // Unpackaged/packaged. 22 | // 23 | ///////////////////////////////////////////////////////////// 24 | 25 | #ifndef __SPNEGO_H__ 26 | #define __SPNEGO_H__ 27 | 28 | // C++ Specific 29 | #if defined(__cplusplus) 30 | extern "C" 31 | { 32 | #endif 33 | 34 | // Type Definitions 35 | 36 | // 37 | // Users of SPNEGO Token Handler API will request 38 | // these as well as free them, 39 | // 40 | typedef void* SPNEGO_TOKEN_HANDLE; 41 | 42 | // 43 | // Defines the element types that are found 44 | // in each of the tokens. 45 | // 46 | 47 | typedef enum spnego_element_type 48 | { 49 | spnego_element_min, // Lower bound 50 | 51 | // Init token elements 52 | spnego_init_mechtypes, 53 | spnego_init_reqFlags, 54 | spnego_init_mechToken, 55 | spnego_init_mechListMIC, 56 | 57 | // Targ token elements 58 | spnego_targ_negResult, 59 | spnego_targ_supportedMech, 60 | spnego_targ_responseToken, 61 | spnego_targ_mechListMIC, 62 | 63 | spnego_element_max // Upper bound 64 | 65 | } SPNEGO_ELEMENT_TYPE; 66 | 67 | // 68 | // Token Element Availability. Elements in both 69 | // token types are optional. Since there are only 70 | // 4 elements in each Token, we will allocate space 71 | // to hold the information, but we need a way to 72 | // indicate whether or not an element is available 73 | // 74 | 75 | #define SPNEGO_TOKEN_ELEMENT_UNAVAILABLE 0 76 | #define SPNEGO_TOKEN_ELEMENT_AVAILABLE 1 77 | 78 | // 79 | // Token type values. SPNEGO has 2 token types: 80 | // NegTokenInit and NegTokenTarg 81 | // 82 | 83 | #define SPNEGO_TOKEN_INIT 0 84 | #define SPNEGO_TOKEN_TARG 1 85 | 86 | // 87 | // GSS Mechanism OID enumeration. We only really handle 88 | // 3 different OIDs. These are stored in an array structure 89 | // defined in the parsing code. 90 | // 91 | 92 | typedef enum spnego_mech_oid 93 | { 94 | // Init token elements 95 | spnego_mech_oid_Kerberos_V5_Legacy, // Really V5, but OID off by 1 bit 96 | spnego_mech_oid_Kerberos_V5, 97 | spnego_mech_oid_Spnego, 98 | spnego_mech_oid_NotUsed = -1 99 | 100 | } SPNEGO_MECH_OID; 101 | 102 | // 103 | // Defines the negResult values. 104 | // 105 | 106 | typedef enum spnego_negResult 107 | { 108 | spnego_negresult_success, 109 | spnego_negresult_incomplete, 110 | spnego_negresult_rejected, 111 | spnego_negresult_NotUsed = -1 112 | } SPNEGO_NEGRESULT; 113 | 114 | // 115 | // Context Flags in NegTokenInit 116 | // 117 | 118 | // 119 | // ContextFlags values MUST be zero or a combination 120 | // of the below 121 | // 122 | 123 | #define SPNEGO_NEGINIT_CONTEXT_DELEG_FLAG 0x80 124 | #define SPNEGO_NEGINIT_CONTEXT_MUTUAL_FLAG 0x40 125 | #define SPNEGO_NEGINIT_CONTEXT_REPLAY_FLAG 0x20 126 | #define SPNEGO_NEGINIT_CONTEXT_SEQUENCE_FLAG 0x10 127 | #define SPNEGO_NEGINIT_CONTEXT_ANON_FLAG 0x8 128 | #define SPNEGO_NEGINIT_CONTEXT_CONF_FLAG 0x4 129 | #define SPNEGO_NEGINIT_CONTEXT_INTEG_FLAG 0x2 130 | 131 | // 132 | // Mask to retrieve valid values. 133 | // 134 | 135 | #define SPNEGO_NEGINIT_CONTEXT_MASK 0xFE // Logical combination of above flags 136 | 137 | // 138 | // SPNEGO API return codes. 139 | // 140 | 141 | // API function was successful 142 | #define SPNEGO_E_SUCCESS 0 143 | 144 | // The supplied Token was invalid 145 | #define SPNEGO_E_INVALID_TOKEN -1 146 | 147 | // An invalid length was encountered 148 | #define SPNEGO_E_INVALID_LENGTH -2 149 | 150 | // The Token Parse failed 151 | #define SPNEGO_E_PARSE_FAILED -3 152 | 153 | // The requested value was not found 154 | #define SPNEGO_E_NOT_FOUND -4 155 | 156 | // The requested element is not available 157 | #define SPNEGO_E_ELEMENT_UNAVAILABLE -5 158 | 159 | // Out of Memory 160 | #define SPNEGO_E_OUT_OF_MEMORY -6 161 | 162 | // Not Implemented 163 | #define SPNEGO_E_NOT_IMPLEMENTED -7 164 | 165 | // Invalid Parameter 166 | #define SPNEGO_E_INVALID_PARAMETER -8 167 | 168 | // Token Handler encountered an unexpected OID 169 | #define SPNEGO_E_UNEXPECTED_OID -9 170 | 171 | // The requested token was not found 172 | #define SPNEGO_E_TOKEN_NOT_FOUND -10 173 | 174 | // An unexpected type was encountered in the encoding 175 | #define SPNEGO_E_UNEXPECTED_TYPE -11 176 | 177 | // The buffer was too small 178 | #define SPNEGO_E_BUFFER_TOO_SMALL -12 179 | 180 | // A Token Element was invalid (e.g. improper length or value) 181 | #define SPNEGO_E_INVALID_ELEMENT -13 182 | 183 | /* Miscelaneous API Functions */ 184 | 185 | // Frees opaque data 186 | void spnegoFreeData( SPNEGO_TOKEN_HANDLE hSpnegoToken ); 187 | 188 | // Initializes SPNEGO_TOKEN structure from DER encoded binary data 189 | int spnegoInitFromBinary( unsigned char* pbTokenData, unsigned long ulLength, SPNEGO_TOKEN_HANDLE* phSpnegoToken ); 190 | 191 | // Initializes SPNEGO_TOKEN structure for a NegTokenInit type using the 192 | // supplied parameters 193 | int spnegoCreateNegTokenInit( SPNEGO_MECH_OID MechType, 194 | unsigned char ucContextFlags, unsigned char* pbMechToken, 195 | unsigned long ulMechTokenLen, unsigned char* pbMechTokenMIC, 196 | unsigned long ulMechTokenMIC, SPNEGO_TOKEN_HANDLE* phSpnegoToken ); 197 | 198 | // Initializes SPNEGO_TOKEN structure for a NegTokenTarg type using the 199 | // supplied parameters 200 | int spnegoCreateNegTokenTarg( SPNEGO_MECH_OID MechType, 201 | SPNEGO_NEGRESULT spnegoNegResult, unsigned char* pbMechToken, 202 | unsigned long ulMechTokenLen, unsigned char* pbMechListMIC, 203 | unsigned long ulMechListMICLen, SPNEGO_TOKEN_HANDLE* phSpnegoToken ); 204 | 205 | // Copies binary representation of SPNEGO Data into user supplied buffer 206 | int spnegoTokenGetBinary( SPNEGO_TOKEN_HANDLE hSpnegoToken, unsigned char* pbTokenData, 207 | unsigned long * pulDataLen ); 208 | 209 | // Returns SPNEGO Token Type 210 | int spnegoGetTokenType( SPNEGO_TOKEN_HANDLE hSpnegoToken, int * piTokenType ); 211 | 212 | /* Reading an Init Token */ 213 | 214 | // Returns the Initial Mech Type in the MechList element in the NegInitToken. 215 | int spnegoIsMechTypeAvailable( SPNEGO_TOKEN_HANDLE hSpnegoToken, SPNEGO_MECH_OID MechOID, int * piMechTypeIndex ); 216 | 217 | // Returns the value from the context flags element in the NegInitToken as an unsigned long 218 | int spnegoGetContextFlags( SPNEGO_TOKEN_HANDLE hSpnegoToken, unsigned char* pucContextFlags ); 219 | 220 | /* Reading a Response Token */ 221 | 222 | // Returns the value from the negResult element (Status code of GSS call - 0,1,2) 223 | int spnegoGetNegotiationResult( SPNEGO_TOKEN_HANDLE hSpnegoToken, SPNEGO_NEGRESULT* pnegResult ); 224 | 225 | // Returns the Supported Mech Type from the NegTokenTarg. 226 | int spnegoGetSupportedMechType( SPNEGO_TOKEN_HANDLE hSpnegoToken, SPNEGO_MECH_OID* pMechOID ); 227 | 228 | /* Reading either Token Type */ 229 | 230 | // Returns the actual Mechanism data from the token (this is what is passed into GSS-API functions 231 | int spnegoGetMechToken( SPNEGO_TOKEN_HANDLE hSpnegoToken, unsigned char* pbTokenData, unsigned long* pulDataLen ); 232 | 233 | // Returns the Message Integrity BLOB in the token 234 | int spnegoGetMechListMIC( SPNEGO_TOKEN_HANDLE hSpnegoToken, unsigned char* pbMICData, unsigned long* pulDataLen ); 235 | 236 | // C++ Specific 237 | #if defined(__cplusplus) 238 | } 239 | #endif 240 | #ifdef DEBUG 241 | #define LOG(x) printf x 242 | #else 243 | #define LOG(x) 244 | #endif 245 | #endif 246 | -------------------------------------------------------------------------------- /spnegohelp/spnegohelp.c: -------------------------------------------------------------------------------- 1 | /* ----------------------------------------------------------------------------- 2 | * spnegohelp.c defines RFC 2478 SPNEGO GSS-API mechanism APIs. 3 | * 4 | * Author: Frank Balluffi 5 | * 6 | * Copyright (C) 2002-2003 All rights reserved. 7 | * ----------------------------------------------------------------------------- 8 | */ 9 | 10 | #include "spnegohelp.h" 11 | #include "spnego.h" 12 | 13 | #include 14 | 15 | int makeNegTokenTarg (const unsigned char * kerberosToken, 16 | size_t kerberosTokenLength, 17 | const unsigned char ** negTokenTarg, 18 | size_t * negTokenTargLength) 19 | { 20 | SPNEGO_TOKEN_HANDLE hSpnegoToken = NULL; 21 | int rc1 = 1; 22 | int rc2 = SPNEGO_E_SUCCESS; 23 | 24 | /* Check arguments. */ 25 | 26 | if (!kerberosToken || 27 | !negTokenTarg || 28 | !negTokenTargLength) 29 | return 10; 30 | 31 | /* Does IIS reply with 1.2.840.48018.1.2.2 or 1.2.840.113554.1.2.2? */ 32 | 33 | /* Does IIS always reply with accept_completed? */ 34 | 35 | /* IIS does not include a MIC. */ 36 | 37 | rc2 = spnegoCreateNegTokenTarg (spnego_mech_oid_Kerberos_V5_Legacy, 38 | spnego_negresult_success, 39 | (unsigned char *) kerberosToken, 40 | kerberosTokenLength, 41 | NULL, 42 | 0, 43 | &hSpnegoToken); 44 | 45 | if (rc2 != SPNEGO_E_SUCCESS) 46 | { 47 | rc1 = abs(rc2)+100; 48 | goto cleanup; 49 | } 50 | 51 | /* Get NegTokenTarg length. */ 52 | 53 | rc2 = spnegoTokenGetBinary (hSpnegoToken, 54 | NULL, 55 | (unsigned long*) negTokenTargLength); 56 | 57 | if (rc2 != SPNEGO_E_BUFFER_TOO_SMALL) 58 | { 59 | rc1 = abs(rc2)+200; 60 | goto cleanup; 61 | } 62 | 63 | *negTokenTarg = malloc (*negTokenTargLength); 64 | 65 | if (!*negTokenTarg) 66 | { 67 | rc1 = abs(rc2)+300; 68 | goto cleanup; 69 | } 70 | 71 | /* Get NegTokenTarg data. */ 72 | 73 | rc2 = spnegoTokenGetBinary (hSpnegoToken, 74 | (unsigned char *) *negTokenTarg, 75 | (unsigned long*) negTokenTargLength); 76 | 77 | 78 | if (rc2 != SPNEGO_E_SUCCESS) 79 | { 80 | rc1 = abs(rc2)+400; 81 | goto error; 82 | } 83 | 84 | rc1 = 0; 85 | 86 | goto cleanup; 87 | 88 | error: 89 | 90 | if (*negTokenTarg) 91 | { 92 | free ((unsigned char *) *negTokenTarg); 93 | *negTokenTarg = NULL; 94 | *negTokenTargLength = 0; 95 | } 96 | 97 | cleanup: 98 | 99 | if (hSpnegoToken) 100 | spnegoFreeData (hSpnegoToken); 101 | 102 | LOG(("makeNegTokenTarg returned %d\n",rc1)); 103 | return rc1; 104 | } 105 | 106 | int parseNegTokenInit (const unsigned char * negTokenInit, 107 | size_t negTokenInitLength, 108 | const unsigned char ** kerberosToken, 109 | size_t * kerberosTokenLength) 110 | { 111 | SPNEGO_TOKEN_HANDLE hSpnegoToken = NULL; 112 | int index = -1; 113 | int rc1 = 1; 114 | int rc2 = SPNEGO_E_SUCCESS; 115 | unsigned char reqFlags = 0; 116 | int tokenType = 0; 117 | 118 | /* Check arguments. */ 119 | 120 | if (!negTokenInit || 121 | !kerberosToken || 122 | !kerberosTokenLength) 123 | return 10; 124 | 125 | /* Decode SPNEGO token. */ 126 | 127 | rc2 = spnegoInitFromBinary ((unsigned char *) negTokenInit, 128 | negTokenInitLength, 129 | &hSpnegoToken); 130 | 131 | if (rc2 != SPNEGO_E_SUCCESS) 132 | { 133 | rc1 = abs(rc2)+100; 134 | goto cleanup; 135 | } 136 | 137 | /* Check for negTokenInit choice. */ 138 | 139 | rc2 = spnegoGetTokenType (hSpnegoToken, 140 | &tokenType); 141 | 142 | if (rc2 != SPNEGO_E_SUCCESS) 143 | { 144 | rc1 = abs(rc2)+200; 145 | goto cleanup; 146 | } 147 | 148 | if (tokenType != SPNEGO_TOKEN_INIT) 149 | { 150 | rc1 = abs(rc2)+300; 151 | goto cleanup; 152 | } 153 | 154 | /* 155 | Check that first mechType is 1.2.840.113554.1.2.2 or 1.2.840.48018.1.2.2. 156 | */ 157 | 158 | /* 159 | IE seems to reply with 1.2.840.48018.1.2.2 and then 1.2.840.113554.1.2.2. 160 | */ 161 | 162 | rc2 = spnegoIsMechTypeAvailable (hSpnegoToken, 163 | spnego_mech_oid_Kerberos_V5_Legacy, 164 | &index); 165 | 166 | if (rc2 != SPNEGO_E_SUCCESS || 167 | index != 0) 168 | { 169 | rc2 = spnegoIsMechTypeAvailable (hSpnegoToken, 170 | spnego_mech_oid_Kerberos_V5, 171 | &index); 172 | 173 | if (rc2 != SPNEGO_E_SUCCESS || 174 | index != 0) 175 | { 176 | rc1 = abs(rc2)+400; 177 | goto cleanup; 178 | } 179 | } 180 | 181 | /* Check for no reqFlags. */ 182 | 183 | /* Does IE ever send reqFlags? */ 184 | 185 | rc2 = spnegoGetContextFlags (hSpnegoToken, 186 | &reqFlags); 187 | 188 | if (rc2 == SPNEGO_E_SUCCESS) 189 | { 190 | rc1 = abs(rc2)+500; 191 | goto cleanup; 192 | } 193 | 194 | /* Get mechanism token length. */ 195 | 196 | rc2 = spnegoGetMechToken (hSpnegoToken, 197 | NULL, 198 | (unsigned long*) kerberosTokenLength); 199 | 200 | if (rc2 != SPNEGO_E_BUFFER_TOO_SMALL) 201 | { 202 | rc1 = abs(rc2)+600; 203 | goto cleanup; 204 | } 205 | 206 | *kerberosToken = malloc (*kerberosTokenLength); 207 | 208 | if (!*kerberosToken) 209 | { 210 | rc1 = abs(rc2)+700; 211 | goto cleanup; 212 | } 213 | 214 | /* Get mechanism token data. */ 215 | 216 | rc2 = spnegoGetMechToken (hSpnegoToken, 217 | (unsigned char *) *kerberosToken, 218 | (unsigned long*) kerberosTokenLength); 219 | 220 | if (rc2 != SPNEGO_E_SUCCESS) 221 | { 222 | rc1 = abs(rc2)+800; 223 | goto error; 224 | } 225 | 226 | /* According to Microsoft, IE does not send a MIC. */ 227 | 228 | rc1 = 0; 229 | 230 | goto cleanup; 231 | 232 | error: 233 | 234 | if (*kerberosToken) 235 | { 236 | free ((unsigned char *) *kerberosToken); 237 | *kerberosToken = NULL; 238 | *kerberosTokenLength = 0; 239 | } 240 | 241 | cleanup: 242 | 243 | if (hSpnegoToken) 244 | spnegoFreeData (hSpnegoToken); 245 | 246 | LOG(("parseNegTokenInit returned %d\n",rc1)); 247 | return rc1; 248 | } 249 | -------------------------------------------------------------------------------- /spnegohelp/spnegohelp.h: -------------------------------------------------------------------------------- 1 | /* ----------------------------------------------------------------------------- 2 | * spnegohelp.c declares RFC 2478 SPNEGO GSS-API mechanism APIs. 3 | * 4 | * Author: Frank Balluffi 5 | * 6 | * Copyright (C) 2002-2003. All rights reserved. 7 | * ----------------------------------------------------------------------------- 8 | */ 9 | 10 | #ifndef SPNEGOHELP_H 11 | #define SPNEGOHELP_H 12 | 13 | #ifdef __cplusplus 14 | extern "C" { 15 | #endif 16 | 17 | #include 18 | 19 | /* ----------------------------------------------------------------------------- 20 | * makeNegTokenTarg makes an RFC 2478 SPNEGO NegTokenTarg (token) from an 21 | * RFC 1964 Kerberos GSS-API token. 22 | * 23 | * If makeNegTokenTarg is successful, call free (*negTokenTarg) to free the 24 | * memory allocated by parseNegTokenInit. 25 | * 26 | * Returns 0 if successful, 1 otherwise. 27 | * ----------------------------------------------------------------------------- 28 | */ 29 | 30 | int makeNegTokenTarg (const unsigned char * kerberosToken, 31 | size_t kerberosTokenLength, 32 | const unsigned char ** negTokenTarg, 33 | size_t * negTokenTargLength); 34 | 35 | /* ----------------------------------------------------------------------------- 36 | * parseNegTokenInit parses an RFC 2478 SPNEGO NegTokenInit (token) to extract 37 | * an RFC 1964 Kerberos GSS-API token. 38 | * 39 | * If the NegTokenInit does cotain a Kerberos GSS-API token, parseNegTokenInit 40 | * returns an error. 41 | * 42 | * If parseNegTokenInit is successful, call free (*kerberosToken) to 43 | * free the memory allocated by parseNegTokenInit. 44 | * 45 | * Returns 0 if successful, 1 otherwise. 46 | * ----------------------------------------------------------------------------- 47 | */ 48 | 49 | int parseNegTokenInit (const unsigned char * negTokenInit, 50 | size_t negTokenInitLength, 51 | const unsigned char ** kerberosToken, 52 | size_t * kerberosTokenLength); 53 | 54 | #ifdef __cplusplus 55 | } 56 | #endif 57 | 58 | #endif /* SPNEGOHELP_H */ 59 | -------------------------------------------------------------------------------- /spnegohelp/spnegoparse.c: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2002 Microsoft Corporation 2 | // All rights reserved. 3 | // 4 | // THIS CODE AND INFORMATION IS PROVIDED "AS IS" 5 | // WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 6 | // OR IMPLIED, INCLUDING BUT NOT LIMITED 7 | // TO THE IMPLIED WARRANTIES OF MERCHANTIBILITY 8 | // AND/OR FITNESS FOR A PARTICULAR PURPOSE. 9 | // 10 | // Date - 10/08/2002 11 | // Author - Sanj Surati 12 | 13 | ///////////////////////////////////////////////////////////// 14 | // 15 | // SPNEGOPARSE.C 16 | // 17 | // SPNEGO Token Handler Source File 18 | // 19 | // Contains implementation of SPNEGO Token parsing functions. 20 | // 21 | ///////////////////////////////////////////////////////////// 22 | 23 | #include 24 | #include 25 | #include 26 | #include "spnego.h" 27 | #include "derparse.h" 28 | #include "spnegoparse.h" 29 | 30 | // 31 | // Defined in DERPARSE.C 32 | // 33 | 34 | extern MECH_OID g_stcMechOIDList []; 35 | 36 | /**********************************************************************/ 37 | /** **/ 38 | /** **/ 39 | /** **/ 40 | /** **/ 41 | /** Local SPNEGO Helper definitions **/ 42 | /** **/ 43 | /** **/ 44 | /** **/ 45 | /** **/ 46 | /**********************************************************************/ 47 | 48 | 49 | ///////////////////////////////////////////////////////////////////////////// 50 | // 51 | // Function: 52 | // CalculateMinSpnegoInitTokenSize 53 | // 54 | // Parameters: 55 | // [in] nMechTokenLength - Length of the MechToken Element 56 | // [in] nMechListMICLength - Length of the MechListMIC Element 57 | // [in] mechOID - OID for MechList 58 | // [in] nReqFlagsAvailable - Is ContextFlags element available 59 | // [out] pnTokenSize - Filled out with total size of token 60 | // [out] pnInternalTokenLength - Filled out with length minus length 61 | // for initial token. 62 | // 63 | // Returns: 64 | // int Success - SPNEGO_E_SUCCESS 65 | // Failure - SPNEGO API Error code 66 | // 67 | // Comments : 68 | // Calculates the required length for a SPNEGO NegTokenInit token based 69 | // on the supplied variable length values and which elements are present. 70 | // Note that because the lengths can be represented by an arbitrary 71 | // number of bytes in DER encodings, we actually calculate the lengths 72 | // backwards, so we always know how many bytes we will potentially be 73 | // writing out. 74 | // 75 | //////////////////////////////////////////////////////////////////////////// 76 | 77 | int CalculateMinSpnegoInitTokenSize( long nMechTokenLength, 78 | long nMechListMICLength, SPNEGO_MECH_OID mechOid, 79 | int nReqFlagsAvailable, long* pnTokenSize, 80 | long* pnInternalTokenLength ) 81 | { 82 | int nReturn = SPNEGO_E_INVALID_LENGTH; 83 | 84 | // Start at 0. 85 | long nTotalLength = 0; 86 | long nTempLength= 0L; 87 | 88 | // We will calculate this by walking the token backwards 89 | 90 | // Start with MIC Element 91 | if ( nMechListMICLength > 0L ) 92 | { 93 | nTempLength = ASNDerCalcElementLength( nMechListMICLength, NULL ); 94 | 95 | // Check for rollover error 96 | if ( nTempLength < nMechListMICLength ) 97 | { 98 | goto xEndTokenInitLength; 99 | } 100 | 101 | nTotalLength += nTempLength; 102 | } 103 | 104 | // Next is the MechToken 105 | if ( nMechTokenLength > 0L ) 106 | { 107 | nTempLength += ASNDerCalcElementLength( nMechTokenLength, NULL ); 108 | 109 | // Check for rollover error 110 | if ( nTempLength < nTotalLength ) 111 | { 112 | goto xEndTokenInitLength; 113 | } 114 | 115 | nTotalLength = nTempLength; 116 | } 117 | 118 | // Next is the ReqFlags 119 | if ( nReqFlagsAvailable ) 120 | { 121 | nTempLength += ASNDerCalcElementLength( SPNEGO_NEGINIT_MAXLEN_REQFLAGS, NULL ); 122 | 123 | // Check for rollover error 124 | if ( nTempLength < nTotalLength ) 125 | { 126 | goto xEndTokenInitLength; 127 | } 128 | 129 | nTotalLength = nTempLength; 130 | } 131 | 132 | // Next is the MechList - This is REQUIRED 133 | nTempLength += ASNDerCalcMechListLength( mechOid, NULL ); 134 | 135 | // Check for rollover error 136 | if ( nTempLength < nTotalLength ) 137 | { 138 | goto xEndTokenInitLength; 139 | } 140 | 141 | nTotalLength = nTempLength; 142 | 143 | // Following four fields are the basic header tokens 144 | 145 | // Sequence Token 146 | nTempLength += ASNDerCalcTokenLength( nTotalLength, 0L ); 147 | 148 | // Check for rollover error 149 | if ( nTempLength < nTotalLength ) 150 | { 151 | goto xEndTokenInitLength; 152 | } 153 | 154 | nTotalLength = nTempLength; 155 | 156 | // Neg Token Identifier Token 157 | nTempLength += ASNDerCalcTokenLength( nTotalLength, 0L ); 158 | 159 | // Check for rollover error 160 | if ( nTempLength < nTotalLength ) 161 | { 162 | goto xEndTokenInitLength; 163 | } 164 | 165 | nTotalLength = nTempLength; 166 | 167 | // SPNEGO OID Token 168 | nTempLength += g_stcMechOIDList[spnego_mech_oid_Spnego].iLen; 169 | 170 | // Check for rollover error 171 | if ( nTempLength < nTotalLength ) 172 | { 173 | goto xEndTokenInitLength; 174 | } 175 | 176 | nTotalLength = nTempLength; 177 | 178 | // App Constructed Token 179 | nTempLength += ASNDerCalcTokenLength( nTotalLength, 0L ); 180 | 181 | // Check for rollover error 182 | if ( nTempLength < nTotalLength ) 183 | { 184 | goto xEndTokenInitLength; 185 | } 186 | 187 | // The internal length doesn't include the number of bytes 188 | // for the initial token 189 | *pnInternalTokenLength = nTotalLength; 190 | nTotalLength = nTempLength; 191 | 192 | // We're done 193 | *pnTokenSize = nTotalLength; 194 | nReturn = SPNEGO_E_SUCCESS; 195 | 196 | xEndTokenInitLength: 197 | 198 | LOG(("CalculateMinSpnegoInitTokenSize returned %d\n",nReturn)); 199 | return nReturn; 200 | 201 | } 202 | 203 | ///////////////////////////////////////////////////////////////////////////// 204 | // 205 | // Function: 206 | // CreateSpnegoInitToken 207 | // 208 | // Parameters: 209 | // [in] MechType - OID in MechList 210 | // [in] ucContextFlags - ContextFlags value 211 | // [in] pbMechToken - Mech Token Binary Data 212 | // [in] ulMechTokenLen - Length of Mech Token 213 | // [in] pbMechListMIC - MechListMIC Binary Data 214 | // [in] ulMechListMICn - Length of MechListMIC 215 | // [out] pbTokenData - Buffer to write token into. 216 | // [in] nTokenLength - Length of pbTokenData buffer 217 | // [in] nInternalTokenLength - Length of full token without leading 218 | // token bytes. 219 | // 220 | // Returns: 221 | // int Success - SPNEGO_E_SUCCESS 222 | // Failure - SPNEGO API Error code 223 | // 224 | // Comments : 225 | // Uses DER to fill out pbTokenData with a SPNEGO NegTokenInit Token 226 | // Note that because the lengths can be represented by an arbitrary 227 | // number of bytes in DER encodings, we actually calculate the lengths 228 | // backwards, so we always know how many bytes we will potentially be 229 | // writing out. 230 | // 231 | //////////////////////////////////////////////////////////////////////////// 232 | 233 | int CreateSpnegoInitToken( SPNEGO_MECH_OID MechType, 234 | unsigned char ucContextFlags, unsigned char* pbMechToken, 235 | unsigned long ulMechTokenLen, unsigned char* pbMechListMIC, 236 | unsigned long ulMechListMICLen, unsigned char* pbTokenData, 237 | long nTokenLength, long nInternalTokenLength ) 238 | { 239 | int nReturn = SPNEGO_E_INVALID_LENGTH; 240 | 241 | // Start at 0. 242 | long nTempLength= 0L; 243 | long nTotalBytesWritten = 0L; 244 | long nInternalLength = 0L; 245 | 246 | unsigned char* pbWriteTokenData = pbTokenData + nTokenLength; 247 | 248 | // Temporary buffer to hold the REQ Flags as BIT String Data 249 | unsigned char abTempReqFlags[SPNEGO_NEGINIT_MAXLEN_REQFLAGS]; 250 | 251 | 252 | // We will write the token out backwards to properly handle the cases 253 | // where the length bytes become adjustable 254 | 255 | // Start with MIC Element 256 | if ( ulMechListMICLen > 0L ) 257 | { 258 | nTempLength = ASNDerCalcElementLength( ulMechListMICLen, &nInternalLength ); 259 | 260 | // Decrease the pbWriteTokenData, now we know the length and 261 | // write it out. 262 | 263 | pbWriteTokenData -= nTempLength; 264 | nTempLength = ASNDerWriteElement( pbWriteTokenData, SPNEGO_NEGINIT_ELEMENT_MECHLISTMIC, 265 | OCTETSTRING, pbMechListMIC, ulMechListMICLen ); 266 | 267 | // Adjust Values and sanity check 268 | nTotalBytesWritten += nTempLength; 269 | nInternalTokenLength -= nTempLength; 270 | 271 | if ( nTotalBytesWritten > nTokenLength || nInternalTokenLength < 0 ) 272 | { 273 | goto xEndWriteNegTokenInit; 274 | } 275 | 276 | } // IF MechListMIC is present 277 | 278 | // Next is the MechToken 279 | if ( ulMechTokenLen > 0L ) 280 | { 281 | nTempLength = ASNDerCalcElementLength( ulMechTokenLen, &nInternalLength ); 282 | 283 | // Decrease the pbWriteTokenData, now we know the length and 284 | // write it out. 285 | pbWriteTokenData -= nTempLength; 286 | nTempLength = ASNDerWriteElement( pbWriteTokenData, SPNEGO_NEGINIT_ELEMENT_MECHTOKEN, 287 | OCTETSTRING, pbMechToken, ulMechTokenLen ); 288 | // Adjust Values and sanity check 289 | nTotalBytesWritten += nTempLength; 290 | nInternalTokenLength -= nTempLength; 291 | 292 | if ( nTotalBytesWritten > nTokenLength || nInternalTokenLength < 0 ) 293 | { 294 | goto xEndWriteNegTokenInit; 295 | } 296 | 297 | } // IF MechToken Length is present 298 | 299 | // Next is the ReqFlags 300 | if ( ucContextFlags > 0L ) 301 | { 302 | 303 | nTempLength = ASNDerCalcElementLength( SPNEGO_NEGINIT_MAXLEN_REQFLAGS, &nInternalLength ); 304 | 305 | // We need a byte that indicates how many bits difference between the number 306 | // of bits used in final octet (we only have one) and the max (8) 307 | 308 | abTempReqFlags[0] = SPNEGO_NEGINIT_REQFLAGS_BITDIFF; 309 | abTempReqFlags[1] = ucContextFlags; 310 | 311 | // Decrease the pbWriteTokenData, now we know the length and 312 | // write it out. 313 | pbWriteTokenData -= nTempLength; 314 | nTempLength = ASNDerWriteElement( pbWriteTokenData, SPNEGO_NEGINIT_ELEMENT_REQFLAGS, 315 | BITSTRING, abTempReqFlags, SPNEGO_NEGINIT_MAXLEN_REQFLAGS ); 316 | 317 | // Adjust Values and sanity check 318 | nTotalBytesWritten += nTempLength; 319 | nInternalTokenLength -= nTempLength; 320 | 321 | if ( nTotalBytesWritten > nTokenLength || nInternalTokenLength < 0 ) 322 | { 323 | goto xEndWriteNegTokenInit; 324 | } 325 | 326 | } // IF ContextFlags 327 | 328 | // Next is the MechList - This is REQUIRED 329 | nTempLength = ASNDerCalcMechListLength( MechType, &nInternalLength ); 330 | 331 | // Decrease the pbWriteTokenData, now we know the length and 332 | // write it out. 333 | pbWriteTokenData -= nTempLength; 334 | nTempLength = ASNDerWriteMechList( pbWriteTokenData, MechType ); 335 | 336 | // Adjust Values and sanity check 337 | nTotalBytesWritten += nTempLength; 338 | nInternalTokenLength -= nTempLength; 339 | 340 | if ( nTotalBytesWritten > nTokenLength || nInternalTokenLength < 0 ) 341 | { 342 | goto xEndWriteNegTokenInit; 343 | } 344 | 345 | // The next tokens we're writing out reflect the total number of bytes 346 | // we have actually written out. 347 | 348 | // Sequence Token 349 | nTempLength = ASNDerCalcTokenLength( nTotalBytesWritten, 0L ); 350 | 351 | // Decrease the pbWriteTokenData, now we know the length and 352 | // write it out. 353 | pbWriteTokenData -= nTempLength; 354 | nTempLength = ASNDerWriteToken( pbWriteTokenData, SPNEGO_CONSTRUCTED_SEQUENCE, 355 | NULL, nTotalBytesWritten ); 356 | 357 | // Adjust Values and sanity check 358 | nTotalBytesWritten += nTempLength; 359 | nInternalTokenLength -= nTempLength; 360 | 361 | if ( nTotalBytesWritten > nTokenLength || nInternalTokenLength < 0 ) 362 | { 363 | goto xEndWriteNegTokenInit; 364 | } 365 | 366 | // Neg Init Token Identifier Token 367 | nTempLength = ASNDerCalcTokenLength( nTotalBytesWritten, 0L ); 368 | 369 | // Decrease the pbWriteTokenData, now we know the length and 370 | // write it out. 371 | pbWriteTokenData -= nTempLength; 372 | nTempLength = ASNDerWriteToken( pbWriteTokenData, SPNEGO_NEGINIT_TOKEN_IDENTIFIER, 373 | NULL, nTotalBytesWritten ); 374 | 375 | // Adjust Values and sanity check 376 | nTotalBytesWritten += nTempLength; 377 | nInternalTokenLength -= nTempLength; 378 | 379 | if ( nTotalBytesWritten > nTokenLength || nInternalTokenLength < 0 ) 380 | { 381 | goto xEndWriteNegTokenInit; 382 | } 383 | 384 | // SPNEGO OID Token 385 | nTempLength = g_stcMechOIDList[spnego_mech_oid_Spnego].iLen; 386 | 387 | // Decrease the pbWriteTokenData, now we know the length and 388 | // write it out. 389 | pbWriteTokenData -= nTempLength; 390 | nTempLength = ASNDerWriteOID( pbWriteTokenData, spnego_mech_oid_Spnego ); 391 | 392 | // Adjust Values and sanity check 393 | nTotalBytesWritten += nTempLength; 394 | nInternalTokenLength -= nTempLength; 395 | 396 | if ( nTotalBytesWritten > nTokenLength || nInternalTokenLength < 0 ) 397 | { 398 | goto xEndWriteNegTokenInit; 399 | } 400 | 401 | // App Constructed Token 402 | nTempLength = ASNDerCalcTokenLength( nTotalBytesWritten, 0L ); 403 | 404 | // Decrease the pbWriteTokenData, now we know the length and 405 | // write it out. 406 | pbWriteTokenData -= nTempLength; 407 | nTempLength = ASNDerWriteToken( pbWriteTokenData, SPNEGO_NEGINIT_APP_CONSTRUCT, 408 | NULL, nTotalBytesWritten ); 409 | 410 | // Adjust Values and sanity check 411 | nTotalBytesWritten += nTempLength; 412 | 413 | // Don't adjust the internal token length here, it doesn't account 414 | // the initial bytes written out (we really don't need to keep 415 | // a running count here, but for debugging, it helps to be able 416 | // to see the total number of bytes written out as well as the 417 | // number of bytes left to write). 418 | 419 | if ( nTotalBytesWritten == nTokenLength && nInternalTokenLength == 0 && 420 | pbWriteTokenData == pbTokenData ) 421 | { 422 | nReturn = SPNEGO_E_SUCCESS; 423 | } 424 | 425 | xEndWriteNegTokenInit: 426 | 427 | LOG(("CreateSpnegoInitToken returned %d\n",nReturn)); 428 | return nReturn; 429 | 430 | } 431 | 432 | ///////////////////////////////////////////////////////////////////////////// 433 | // 434 | // Function: 435 | // CalculateMinSpnegoTargTokenSize 436 | // 437 | // Parameters: 438 | // [in] MechType - Supported MechType 439 | // [in] spnegoNegResult - Neg Result 440 | // [in] nMechTokenLength - Length of the MechToken Element 441 | // [in] nMechListMICLength - Length of the MechListMIC Element 442 | // [out] pnTokenSize - Filled out with total size of token 443 | // [out] pnInternalTokenLength - Filled out with length minus length 444 | // for initial token. 445 | // 446 | // Returns: 447 | // int Success - SPNEGO_E_SUCCESS 448 | // Failure - SPNEGO API Error code 449 | // 450 | // Comments : 451 | // Calculates the required length for a SPNEGO NegTokenTarg token based 452 | // on the supplied variable length values and which elements are present. 453 | // Note that because the lengths can be represented by an arbitrary 454 | // number of bytes in DER encodings, we actually calculate the lengths 455 | // backwards, so we always know how many bytes we will potentially be 456 | // writing out. 457 | // 458 | //////////////////////////////////////////////////////////////////////////// 459 | 460 | int CalculateMinSpnegoTargTokenSize( SPNEGO_MECH_OID MechType, 461 | SPNEGO_NEGRESULT spnegoNegResult, long nMechTokenLen, 462 | long nMechListMICLen, long* pnTokenSize, 463 | long* pnInternalTokenLength ) 464 | { 465 | int nReturn = SPNEGO_E_INVALID_LENGTH; 466 | 467 | // Start at 0. 468 | long nTotalLength = 0; 469 | long nTempLength= 0L; 470 | 471 | // We will calculate this by walking the token backwards 472 | 473 | // Start with MIC Element 474 | if ( nMechListMICLen > 0L ) 475 | { 476 | nTempLength = ASNDerCalcElementLength( nMechListMICLen, NULL ); 477 | 478 | // Check for rollover error 479 | if ( nTempLength < nMechListMICLen ) 480 | { 481 | goto xEndTokenTargLength; 482 | } 483 | 484 | nTotalLength += nTempLength; 485 | } 486 | 487 | // Next is the MechToken 488 | if ( nMechTokenLen > 0L ) 489 | { 490 | nTempLength += ASNDerCalcElementLength( nMechTokenLen, NULL ); 491 | 492 | // Check for rollover error 493 | if ( nTempLength < nTotalLength ) 494 | { 495 | goto xEndTokenTargLength; 496 | } 497 | 498 | nTotalLength = nTempLength; 499 | } 500 | 501 | // Supported MechType 502 | if ( spnego_mech_oid_NotUsed != MechType ) 503 | { 504 | // Supported MechOID element - we use the token function since 505 | // we already know the size of the OID token and value 506 | nTempLength += ASNDerCalcElementLength( g_stcMechOIDList[MechType].iActualDataLen, 507 | NULL ); 508 | 509 | // Check for rollover error 510 | if ( nTempLength < nTotalLength ) 511 | { 512 | goto xEndTokenTargLength; 513 | } 514 | 515 | nTotalLength = nTempLength; 516 | 517 | } // IF MechType is available 518 | 519 | // NegResult Element 520 | if ( spnego_negresult_NotUsed != spnegoNegResult ) 521 | { 522 | nTempLength += ASNDerCalcElementLength( SPNEGO_NEGTARG_MAXLEN_NEGRESULT, NULL ); 523 | 524 | // Check for rollover error 525 | if ( nTempLength < nTotalLength ) 526 | { 527 | goto xEndTokenTargLength; 528 | } 529 | 530 | nTotalLength = nTempLength; 531 | 532 | } // IF negResult is available 533 | 534 | // Following two fields are the basic header tokens 535 | 536 | // Sequence Token 537 | nTempLength += ASNDerCalcTokenLength( nTotalLength, 0L ); 538 | 539 | // Check for rollover error 540 | if ( nTempLength < nTotalLength ) 541 | { 542 | goto xEndTokenTargLength; 543 | } 544 | 545 | nTotalLength = nTempLength; 546 | 547 | // Neg Token Identifier Token 548 | nTempLength += ASNDerCalcTokenLength( nTotalLength, 0L ); 549 | 550 | // Check for rollover error 551 | if ( nTempLength < nTotalLength ) 552 | { 553 | goto xEndTokenTargLength; 554 | } 555 | 556 | // The internal length doesn't include the number of bytes 557 | // for the initial token 558 | *pnInternalTokenLength = nTotalLength; 559 | nTotalLength = nTempLength; 560 | 561 | // We're done 562 | *pnTokenSize = nTotalLength; 563 | nReturn = SPNEGO_E_SUCCESS; 564 | 565 | xEndTokenTargLength: 566 | 567 | LOG(("CalculateMinSpnegoTargTokenSize returned %d\n",nReturn)); 568 | return nReturn; 569 | 570 | } 571 | 572 | ///////////////////////////////////////////////////////////////////////////// 573 | // 574 | // Function: 575 | // CreateSpnegoTargToken 576 | // 577 | // Parameters: 578 | // [in] MechType - Supported MechType 579 | // [in] eNegResult - NegResult value 580 | // [in] pbMechToken - Mech Token Binary Data 581 | // [in] ulMechTokenLen - Length of Mech Token 582 | // [in] pbMechListMIC - MechListMIC Binary Data 583 | // [in] ulMechListMICn - Length of MechListMIC 584 | // [out] pbTokenData - Buffer to write token into. 585 | // [in] nTokenLength - Length of pbTokenData buffer 586 | // [in] nInternalTokenLength - Length of full token without leading 587 | // token bytes. 588 | // 589 | // Returns: 590 | // int Success - SPNEGO_E_SUCCESS 591 | // Failure - SPNEGO API Error code 592 | // 593 | // Comments : 594 | // Uses DER to fill out pbTokenData with a SPNEGO NegTokenTarg Token 595 | // Note that because the lengths can be represented by an arbitrary 596 | // number of bytes in DER encodings, we actually calculate the lengths 597 | // backwards, so we always know how many bytes we will potentially be 598 | // writing out. 599 | // 600 | //////////////////////////////////////////////////////////////////////////// 601 | 602 | int CreateSpnegoTargToken( SPNEGO_MECH_OID MechType, 603 | SPNEGO_NEGRESULT eNegResult, unsigned char* pbMechToken, 604 | unsigned long ulMechTokenLen, unsigned char* pbMechListMIC, 605 | unsigned long ulMechListMICLen, unsigned char* pbTokenData, 606 | long nTokenLength, long nInternalTokenLength ) 607 | { 608 | int nReturn = SPNEGO_E_INVALID_LENGTH; 609 | 610 | // Start at 0. 611 | long nTempLength= 0L; 612 | long nTotalBytesWritten = 0L; 613 | long nInternalLength = 0L; 614 | 615 | unsigned char ucTemp = 0; 616 | 617 | // We will write the token out backwards to properly handle the cases 618 | // where the length bytes become adjustable, so the write location 619 | // is initialized to point *just* past the end of the buffer. 620 | 621 | unsigned char* pbWriteTokenData = pbTokenData + nTokenLength; 622 | 623 | 624 | // Start with MIC Element 625 | if ( ulMechListMICLen > 0L ) 626 | { 627 | nTempLength = ASNDerCalcElementLength( ulMechListMICLen, &nInternalLength ); 628 | 629 | // Decrease the pbWriteTokenData, now we know the length and 630 | // write it out. 631 | 632 | pbWriteTokenData -= nTempLength; 633 | nTempLength = ASNDerWriteElement( pbWriteTokenData, SPNEGO_NEGTARG_ELEMENT_MECHLISTMIC, 634 | OCTETSTRING, pbMechListMIC, ulMechListMICLen ); 635 | 636 | // Adjust Values and sanity check 637 | nTotalBytesWritten += nTempLength; 638 | nInternalTokenLength -= nTempLength; 639 | 640 | if ( nTotalBytesWritten > nTokenLength || nInternalTokenLength < 0 ) 641 | { 642 | goto xEndWriteNegTokenTarg; 643 | } 644 | 645 | } // IF MechListMIC is present 646 | 647 | // Next is the MechToken 648 | if ( ulMechTokenLen > 0L ) 649 | { 650 | nTempLength = ASNDerCalcElementLength( ulMechTokenLen, &nInternalLength ); 651 | 652 | // Decrease the pbWriteTokenData, now we know the length and 653 | // write it out. 654 | pbWriteTokenData -= nTempLength; 655 | nTempLength = ASNDerWriteElement( pbWriteTokenData, SPNEGO_NEGTARG_ELEMENT_RESPONSETOKEN, 656 | OCTETSTRING, pbMechToken, ulMechTokenLen ); 657 | // Adjust Values and sanity check 658 | nTotalBytesWritten += nTempLength; 659 | nInternalTokenLength -= nTempLength; 660 | 661 | if ( nTotalBytesWritten > nTokenLength || nInternalTokenLength < 0 ) 662 | { 663 | goto xEndWriteNegTokenTarg; 664 | } 665 | 666 | } // IF MechToken Length is present 667 | 668 | // Supported Mech Type 669 | if ( spnego_mech_oid_NotUsed != MechType ) 670 | { 671 | 672 | nTempLength = ASNDerCalcElementLength( g_stcMechOIDList[MechType].iActualDataLen, 673 | &nInternalLength ); 674 | 675 | // Decrease the pbWriteTokenData, now we know the length and 676 | // write it out. 677 | pbWriteTokenData -= nTempLength; 678 | nTempLength = ASNDerWriteToken( pbWriteTokenData, SPNEGO_NEGTARG_ELEMENT_SUPPORTEDMECH, 679 | g_stcMechOIDList[MechType].ucOid, 680 | g_stcMechOIDList[MechType].iLen ); 681 | 682 | // Adjust Values and sanity check 683 | nTotalBytesWritten += nTempLength; 684 | nInternalTokenLength -= nTempLength; 685 | 686 | if ( nTotalBytesWritten > nTokenLength || nInternalTokenLength < 0 ) 687 | { 688 | goto xEndWriteNegTokenTarg; 689 | } 690 | 691 | } // IF MechType is present 692 | 693 | // Neg Result 694 | // NegResult Element 695 | if ( spnego_negresult_NotUsed != eNegResult ) 696 | { 697 | ucTemp = (unsigned char) eNegResult; 698 | 699 | nTempLength = ASNDerCalcElementLength( SPNEGO_NEGTARG_MAXLEN_NEGRESULT, &nInternalLength ); 700 | 701 | // Decrease the pbWriteTokenData, now we know the length and 702 | // write it out. 703 | pbWriteTokenData -= nTempLength; 704 | nTempLength = ASNDerWriteElement( pbWriteTokenData, SPNEGO_NEGTARG_ELEMENT_NEGRESULT, 705 | ENUMERATED, &ucTemp, SPNEGO_NEGTARG_MAXLEN_NEGRESULT ); 706 | 707 | // Adjust Values and sanity check 708 | nTotalBytesWritten += nTempLength; 709 | nInternalTokenLength -= nTempLength; 710 | 711 | if ( nTotalBytesWritten > nTokenLength || nInternalTokenLength < 0 ) 712 | { 713 | goto xEndWriteNegTokenTarg; 714 | } 715 | 716 | } // If eNegResult is available 717 | 718 | // The next tokens we're writing out reflect the total number of bytes 719 | // we have actually written out. 720 | 721 | // Sequence Token 722 | nTempLength = ASNDerCalcTokenLength( nTotalBytesWritten, 0L ); 723 | 724 | // Decrease the pbWriteTokenData, now we know the length and 725 | // write it out. 726 | pbWriteTokenData -= nTempLength; 727 | nTempLength = ASNDerWriteToken( pbWriteTokenData, SPNEGO_CONSTRUCTED_SEQUENCE, 728 | NULL, nTotalBytesWritten ); 729 | 730 | // Adjust Values and sanity check 731 | nTotalBytesWritten += nTempLength; 732 | nInternalTokenLength -= nTempLength; 733 | 734 | if ( nTotalBytesWritten > nTokenLength || nInternalTokenLength < 0 ) 735 | { 736 | goto xEndWriteNegTokenTarg; 737 | } 738 | 739 | // Neg Targ Token Identifier Token 740 | nTempLength = ASNDerCalcTokenLength( nTotalBytesWritten, 0L ); 741 | 742 | // Decrease the pbWriteTokenData, now we know the length and 743 | // write it out. 744 | pbWriteTokenData -= nTempLength; 745 | nTempLength = ASNDerWriteToken( pbWriteTokenData, SPNEGO_NEGTARG_TOKEN_IDENTIFIER, 746 | NULL, nTotalBytesWritten ); 747 | 748 | // Adjust Values and sanity check 749 | nTotalBytesWritten += nTempLength; 750 | 751 | // Don't adjust the internal token length here, it doesn't account 752 | // the initial bytes written out (we really don't need to keep 753 | // a running count here, but for debugging, it helps to be able 754 | // to see the total number of bytes written out as well as the 755 | // number of bytes left to write). 756 | 757 | if ( nTotalBytesWritten == nTokenLength && nInternalTokenLength == 0 && 758 | pbWriteTokenData == pbTokenData ) 759 | { 760 | nReturn = SPNEGO_E_SUCCESS; 761 | } 762 | 763 | 764 | xEndWriteNegTokenTarg: 765 | 766 | LOG(("CreateSpnegoTargToken returned %d\n",nReturn)); 767 | return nReturn; 768 | 769 | 770 | } 771 | 772 | 773 | ///////////////////////////////////////////////////////////////////////////// 774 | // 775 | // Function: 776 | // AllocEmptySpnegoToken 777 | // 778 | // Parameters: 779 | // [in] ucCopyData - Flag to copy data or pointer. 780 | // [in] ulFlags - Flags for SPNEGO_TOKEN data member. 781 | // [in] pbTokenData - Binary token data. 782 | // [in] ulTokenSize - Size of pbTokenData. 783 | // 784 | // Returns: 785 | // SPNEGO_TOKEN* Success - Pointer to initialized SPNEGO_TOKEN struct 786 | // Failure - NULL 787 | // 788 | // Comments : 789 | // Allocates a SPNEGO_TOKEN data structure and initializes it. Based on 790 | // the value of ucCopyData, if non-zero, we copy the data into a buffer 791 | // we allocate in this function, otherwise, we copy the data pointer 792 | // direcly. 793 | // 794 | //////////////////////////////////////////////////////////////////////////// 795 | 796 | SPNEGO_TOKEN* AllocEmptySpnegoToken( unsigned char ucCopyData, unsigned long ulFlags, 797 | unsigned char * pbTokenData, unsigned long ulTokenSize ) 798 | { 799 | SPNEGO_TOKEN* pSpnegoToken = (SPNEGO_TOKEN*) calloc( 1, sizeof(SPNEGO_TOKEN) ); 800 | 801 | if ( NULL != pSpnegoToken ) 802 | { 803 | // Set the token size 804 | pSpnegoToken->nStructSize = SPNEGO_TOKEN_SIZE; 805 | 806 | // Initialize the element array 807 | InitSpnegoTokenElementArray( pSpnegoToken ); 808 | 809 | // Assign the flags value 810 | pSpnegoToken->ulFlags = ulFlags; 811 | 812 | // 813 | // IF ucCopyData is TRUE, we will allocate a buffer and copy data into it. 814 | // Otherwise, we will just copy the pointer and the length. This is so we 815 | // can cut out additional allocations for performance reasons 816 | // 817 | 818 | if ( SPNEGO_TOKEN_INTERNAL_FLAGS_FREEDATA == ucCopyData ) 819 | { 820 | // Alloc the internal buffer. Cleanup on failure. 821 | pSpnegoToken->pbBinaryData = (unsigned char*) calloc( ulTokenSize, sizeof(unsigned char) ); 822 | 823 | if ( NULL != pSpnegoToken->pbBinaryData ) 824 | { 825 | // We must ALWAYS free this buffer 826 | pSpnegoToken->ulFlags |= SPNEGO_TOKEN_INTERNAL_FLAGS_FREEDATA; 827 | 828 | // Copy the data locally 829 | memcpy( pSpnegoToken->pbBinaryData, pbTokenData, ulTokenSize ); 830 | pSpnegoToken->ulBinaryDataLen = ulTokenSize; 831 | } 832 | else 833 | { 834 | free( pSpnegoToken ); 835 | pSpnegoToken = NULL; 836 | } 837 | 838 | } // IF ucCopyData 839 | else 840 | { 841 | // Copy the pointer and the length directly - ulFlags will control whether or not 842 | // we are allowed to free the value 843 | 844 | pSpnegoToken->pbBinaryData = pbTokenData; 845 | pSpnegoToken->ulBinaryDataLen = ulTokenSize; 846 | } 847 | 848 | } 849 | 850 | return pSpnegoToken; 851 | } 852 | 853 | ///////////////////////////////////////////////////////////////////////////// 854 | // 855 | // Function: 856 | // FreeSpnegoToken 857 | // 858 | // Parameters: 859 | // [in] pSpnegoToken - Points to SPNEGO_TOKEN to free. 860 | // 861 | // Returns: 862 | // void 863 | // 864 | // Comments : 865 | // If non-NULL, interprets pSpnegoToken, freeing any internal allocations 866 | // and finally the actual structure. 867 | // 868 | //////////////////////////////////////////////////////////////////////////// 869 | 870 | void FreeSpnegoToken( SPNEGO_TOKEN* pSpnegoToken ) 871 | { 872 | if ( NULL != pSpnegoToken ) 873 | { 874 | 875 | // Cleanup internal allocation per the flags 876 | if ( pSpnegoToken->ulFlags & SPNEGO_TOKEN_INTERNAL_FLAGS_FREEDATA && 877 | NULL != pSpnegoToken->pbBinaryData ) 878 | { 879 | free( pSpnegoToken->pbBinaryData ); 880 | pSpnegoToken->pbBinaryData = NULL; 881 | } 882 | 883 | free ( pSpnegoToken ); 884 | } 885 | } 886 | 887 | ///////////////////////////////////////////////////////////////////////////// 888 | // 889 | // Function: 890 | // InitSpnegoTokenElementArray 891 | // 892 | // Parameters: 893 | // [in] pSpnegoToken - Points to SPNEGO_TOKEN structure. 894 | // 895 | // Returns: 896 | // void 897 | // 898 | // Comments : 899 | // Initializes the element array data member of a SPNEGO_TOKEN data 900 | // structure. 901 | // 902 | //////////////////////////////////////////////////////////////////////////// 903 | 904 | void InitSpnegoTokenElementArray( SPNEGO_TOKEN* pSpnegoToken ) 905 | { 906 | int nCtr; 907 | 908 | // Set the number of elemnts 909 | pSpnegoToken->nNumElements = MAX_NUM_TOKEN_ELEMENTS; 910 | 911 | // 912 | // Initially, all elements are unavailable 913 | // 914 | 915 | for ( nCtr = 0; nCtr < MAX_NUM_TOKEN_ELEMENTS; nCtr++ ) 916 | { 917 | // Set the element size as well 918 | pSpnegoToken->aElementArray[ nCtr ].nStructSize = SPNEGO_ELEMENT_SIZE; 919 | pSpnegoToken->aElementArray[ nCtr ].iElementPresent = SPNEGO_TOKEN_ELEMENT_UNAVAILABLE; 920 | } 921 | 922 | } 923 | 924 | ///////////////////////////////////////////////////////////////////////////// 925 | // 926 | // Function: 927 | // InitSpnegoTokenType 928 | // 929 | // Parameters: 930 | // [in] pSpnegoToken - Points to SPNEGO_TOKEN structure. 931 | // [out] pnTokenLength - Filled out with total token length 932 | // [out] pnRemainingTokenLength - Filled out with remaining length 933 | // after header is parsed 934 | // [out] ppbFirstElement - Filled out with pointer to first 935 | // element after header info. 936 | // 937 | // Returns: 938 | // int Success - SPNEGO_E_SUCCESS 939 | // Failure - SPNEGO API Error code 940 | // 941 | // Comments : 942 | // Walks the underlying binary data for a SPNEGO_TOKEN data structure 943 | // and determines the type of the underlying token based on token header 944 | // information. 945 | // 946 | //////////////////////////////////////////////////////////////////////////// 947 | 948 | int InitSpnegoTokenType( SPNEGO_TOKEN* pSpnegoToken, long* pnTokenLength, 949 | long* pnRemainingTokenLength, unsigned char** ppbFirstElement ) 950 | { 951 | int nReturn = SPNEGO_E_INVALID_TOKEN; 952 | long nActualTokenLength = 0L; 953 | long nBoundaryLength = pSpnegoToken->ulBinaryDataLen; 954 | unsigned char* pbTokenData = pSpnegoToken->pbBinaryData; 955 | 956 | // 957 | // First byte MUST be either an APP_CONSTRUCT or the NEGTARG_TOKEN_TARG 958 | // 959 | 960 | if ( SPNEGO_NEGINIT_APP_CONSTRUCT == *pbTokenData ) 961 | { 962 | // Validate the above token - this will tell us the actual length of the token 963 | // per the encoding (minus the actual token bytes) 964 | if ( ( nReturn = ASNDerCheckToken( pbTokenData, SPNEGO_NEGINIT_APP_CONSTRUCT, 0L, nBoundaryLength, 965 | pnTokenLength, &nActualTokenLength ) ) 966 | == SPNEGO_E_SUCCESS ) 967 | { 968 | // Initialize the remaining token length value. This will be used 969 | // to tell the caller how much token there is left once we've parsed 970 | // the header (they could calculate it from the other values, but this 971 | // is a bit friendlier) 972 | *pnRemainingTokenLength = *pnTokenLength; 973 | 974 | // Make adjustments to next token 975 | pbTokenData += nActualTokenLength; 976 | nBoundaryLength -= nActualTokenLength; 977 | 978 | // The next token should be an OID 979 | if ( ( nReturn = ASNDerCheckOID( pbTokenData, spnego_mech_oid_Spnego, nBoundaryLength, 980 | &nActualTokenLength ) ) == SPNEGO_E_SUCCESS ) 981 | { 982 | // Make adjustments to next token 983 | pbTokenData += nActualTokenLength; 984 | nBoundaryLength -= nActualTokenLength; 985 | *pnRemainingTokenLength -= nActualTokenLength; 986 | 987 | // The next token should specify the NegTokenInit 988 | if ( ( nReturn = ASNDerCheckToken( pbTokenData, SPNEGO_NEGINIT_TOKEN_IDENTIFIER, 989 | *pnRemainingTokenLength, nBoundaryLength, pnTokenLength, 990 | &nActualTokenLength ) ) 991 | == SPNEGO_E_SUCCESS ) 992 | { 993 | // Make adjustments to next token 994 | pbTokenData += nActualTokenLength; 995 | nBoundaryLength -= nActualTokenLength; 996 | *pnRemainingTokenLength -= nActualTokenLength; 997 | 998 | // The next token should specify the start of a sequence 999 | if ( ( nReturn = ASNDerCheckToken( pbTokenData, SPNEGO_CONSTRUCTED_SEQUENCE, 1000 | *pnRemainingTokenLength, nBoundaryLength, pnTokenLength, 1001 | &nActualTokenLength ) ) 1002 | == SPNEGO_E_SUCCESS ) 1003 | { 1004 | // NegTokenInit header is now checked out! 1005 | 1006 | // Make adjustments to next token 1007 | *pnRemainingTokenLength -= nActualTokenLength; 1008 | 1009 | // Store pointer to first element 1010 | *ppbFirstElement = pbTokenData + nActualTokenLength; 1011 | pSpnegoToken->ucTokenType = SPNEGO_TOKEN_INIT; 1012 | } // IF Check Sequence Token 1013 | 1014 | } // IF Check NegTokenInit token 1015 | 1016 | 1017 | } // IF Check for SPNEGO OID 1018 | 1019 | 1020 | } // IF check app construct token 1021 | 1022 | } 1023 | else if ( SPNEGO_NEGTARG_TOKEN_IDENTIFIER == *pbTokenData ) 1024 | { 1025 | 1026 | // The next token should specify the NegTokenInit 1027 | if ( ( nReturn = ASNDerCheckToken( pbTokenData, SPNEGO_NEGTARG_TOKEN_IDENTIFIER, 1028 | *pnRemainingTokenLength, nBoundaryLength, pnTokenLength, 1029 | &nActualTokenLength ) ) 1030 | == SPNEGO_E_SUCCESS ) 1031 | { 1032 | // Initialize the remaining token length value. This will be used 1033 | // to tell the caller how much token there is left once we've parsed 1034 | // the header (they could calculate it from the other values, but this 1035 | // is a bit friendlier) 1036 | *pnRemainingTokenLength = *pnTokenLength; 1037 | 1038 | // Make adjustments to next token 1039 | pbTokenData += nActualTokenLength; 1040 | nBoundaryLength -= nActualTokenLength; 1041 | 1042 | // The next token should specify the start of a sequence 1043 | if ( ( nReturn = ASNDerCheckToken( pbTokenData, SPNEGO_CONSTRUCTED_SEQUENCE, 1044 | *pnRemainingTokenLength, nBoundaryLength, pnTokenLength, 1045 | &nActualTokenLength ) ) 1046 | == SPNEGO_E_SUCCESS ) 1047 | { 1048 | // NegTokenInit header is now checked out! 1049 | 1050 | // Make adjustments to next token 1051 | *pnRemainingTokenLength -= nActualTokenLength; 1052 | 1053 | // Store pointer to first element 1054 | *ppbFirstElement = pbTokenData + nActualTokenLength; 1055 | pSpnegoToken->ucTokenType = SPNEGO_TOKEN_TARG; 1056 | } // IF Check Sequence Token 1057 | 1058 | } // IF Check NegTokenInit token 1059 | 1060 | } // ELSE IF it's a NegTokenTarg 1061 | 1062 | LOG(("InitSpnegoTokenType returned %d\n",nReturn)); 1063 | return nReturn; 1064 | } 1065 | 1066 | 1067 | ///////////////////////////////////////////////////////////////////////////// 1068 | // 1069 | // Function: 1070 | // GetSpnegoInitTokenMechList 1071 | // 1072 | // Parameters: 1073 | // [in] pbTokenData - Points to binary MechList element 1074 | // in NegTokenInit. 1075 | // [in] nMechListLength - Length of the MechList 1076 | // [out] pSpnegoElement - Filled out with MechList Element 1077 | // data. 1078 | // 1079 | // Returns: 1080 | // int Success - SPNEGO_E_SUCCESS 1081 | // Failure - SPNEGO API Error code 1082 | // 1083 | // Comments : 1084 | // Checks that pbTokenData is pointing at something that at least 1085 | // *looks* like a MechList and then fills out the supplied 1086 | // SPNEGO_ELEMENT structure. 1087 | // 1088 | //////////////////////////////////////////////////////////////////////////// 1089 | 1090 | int GetSpnegoInitTokenMechList( unsigned char* pbTokenData, int nMechListLength, 1091 | SPNEGO_ELEMENT* pSpnegoElement ) 1092 | { 1093 | int nReturn = SPNEGO_E_INVALID_TOKEN; 1094 | long nLength = 0L; 1095 | long nActualTokenLength = 0L; 1096 | 1097 | // Actual MechList is prepended by a Constructed Sequence Token 1098 | if ( ( nReturn = ASNDerCheckToken( pbTokenData, SPNEGO_CONSTRUCTED_SEQUENCE, 1099 | nMechListLength, nMechListLength, 1100 | &nLength, &nActualTokenLength ) ) 1101 | == SPNEGO_E_SUCCESS ) 1102 | { 1103 | // Adjust for this token 1104 | nMechListLength -= nActualTokenLength; 1105 | pbTokenData += nActualTokenLength; 1106 | 1107 | // Perform simple validation of the actual MechList (i.e. ensure that 1108 | // the OIDs in the MechList are reasonable). 1109 | 1110 | if ( ( nReturn = ValidateMechList( pbTokenData, nLength ) ) == SPNEGO_E_SUCCESS ) 1111 | { 1112 | // Initialize the element now 1113 | pSpnegoElement->eElementType = spnego_init_mechtypes; 1114 | pSpnegoElement->iElementPresent = SPNEGO_TOKEN_ELEMENT_AVAILABLE; 1115 | pSpnegoElement->type = SPNEGO_MECHLIST_TYPE; 1116 | pSpnegoElement->nDatalength = nLength; 1117 | pSpnegoElement->pbData = pbTokenData; 1118 | } 1119 | 1120 | } // IF Check Token 1121 | 1122 | LOG(("GetSpnegoInitTokenMechList returned %d\n",nReturn)); 1123 | return nReturn; 1124 | } 1125 | 1126 | ///////////////////////////////////////////////////////////////////////////// 1127 | // 1128 | // Function: 1129 | // InitSpnegoTokenElementFromBasicType 1130 | // 1131 | // Parameters: 1132 | // [in] pbTokenData - Points to binary element data in 1133 | // a SPNEGO token. 1134 | // [in] nElementLength - Length of the element 1135 | // [in] ucExpectedType - Expected DER type. 1136 | // [in] spnegoElementType - Which element is this? 1137 | // [out] pSpnegoElement - Filled out with element data. 1138 | // 1139 | // Returns: 1140 | // int Success - SPNEGO_E_SUCCESS 1141 | // Failure - SPNEGO API Error code 1142 | // 1143 | // Comments : 1144 | // Checks that pbTokenData is pointing at the specified DER type. If so, 1145 | // then we verify that lengths are proper and then fill out the 1146 | // SPNEGO_ELEMENT data structure. 1147 | // 1148 | //////////////////////////////////////////////////////////////////////////// 1149 | 1150 | int InitSpnegoTokenElementFromBasicType( unsigned char* pbTokenData, int nElementLength, 1151 | unsigned char ucExpectedType, 1152 | SPNEGO_ELEMENT_TYPE spnegoElementType, 1153 | SPNEGO_ELEMENT* pSpnegoElement ) 1154 | { 1155 | int nReturn = SPNEGO_E_UNEXPECTED_TYPE; 1156 | long nLength = 0L; 1157 | long nActualTokenLength = 0L; 1158 | 1159 | // The type BYTE must match our token data or something is badly wrong 1160 | if ( *pbTokenData == ucExpectedType ) 1161 | { 1162 | 1163 | // Check that we are pointing at the specified type 1164 | if ( ( nReturn = ASNDerCheckToken( pbTokenData, ucExpectedType, 1165 | nElementLength, nElementLength, 1166 | &nLength, &nActualTokenLength ) ) 1167 | == SPNEGO_E_SUCCESS ) 1168 | { 1169 | // Adjust for this token 1170 | nElementLength -= nActualTokenLength; 1171 | pbTokenData += nActualTokenLength; 1172 | 1173 | // Initialize the element now 1174 | pSpnegoElement->eElementType = spnegoElementType; 1175 | pSpnegoElement->iElementPresent = SPNEGO_TOKEN_ELEMENT_AVAILABLE; 1176 | pSpnegoElement->type = ucExpectedType; 1177 | pSpnegoElement->nDatalength = nLength; 1178 | pSpnegoElement->pbData = pbTokenData; 1179 | } 1180 | 1181 | } // IF type makes sense 1182 | 1183 | LOG(("InitSpnegoTokenElementFromBasicType returned %d\n",nReturn)); 1184 | return nReturn; 1185 | } 1186 | 1187 | 1188 | ///////////////////////////////////////////////////////////////////////////// 1189 | // 1190 | // Function: 1191 | // InitSpnegoTokenElementFromOID 1192 | // 1193 | // Parameters: 1194 | // [in] pbTokenData - Points to binary element data in 1195 | // a SPNEGO token. 1196 | // [in] nElementLength - Length of the element 1197 | // [in] spnegoElementType - Which element is this? 1198 | // [out] pSpnegoElement - Filled out with element data. 1199 | // 1200 | // Returns: 1201 | // int Success - SPNEGO_E_SUCCESS 1202 | // Failure - SPNEGO API Error code 1203 | // 1204 | // Comments : 1205 | // Initializes a SpnegoElement from an OID - normally, this would have 1206 | // used the Basic Type function above, but since we do binary compares 1207 | // on the OIDs against the DER information as well as the OID, we need 1208 | // to account for that. 1209 | // 1210 | //////////////////////////////////////////////////////////////////////////// 1211 | 1212 | int InitSpnegoTokenElementFromOID( unsigned char* pbTokenData, int nElementLength, 1213 | SPNEGO_ELEMENT_TYPE spnegoElementType, 1214 | SPNEGO_ELEMENT* pSpnegoElement ) 1215 | { 1216 | int nReturn = SPNEGO_E_UNEXPECTED_TYPE; 1217 | long nLength = 0L; 1218 | long nActualTokenLength = 0L; 1219 | 1220 | // The type BYTE must match our token data or something is badly wrong 1221 | if ( *pbTokenData == OID ) 1222 | { 1223 | 1224 | // Check that we are pointing at an OID type 1225 | if ( ( nReturn = ASNDerCheckToken( pbTokenData, OID, 1226 | nElementLength, nElementLength, 1227 | &nLength, &nActualTokenLength ) ) 1228 | == SPNEGO_E_SUCCESS ) 1229 | { 1230 | // Don't adjust any values for this function 1231 | 1232 | // Initialize the element now 1233 | pSpnegoElement->eElementType = spnegoElementType; 1234 | pSpnegoElement->iElementPresent = SPNEGO_TOKEN_ELEMENT_AVAILABLE; 1235 | pSpnegoElement->type = OID; 1236 | pSpnegoElement->nDatalength = nElementLength; 1237 | pSpnegoElement->pbData = pbTokenData; 1238 | } 1239 | 1240 | } // IF type makes sense 1241 | 1242 | LOG(("InitSpnegoTokenElementFromBasicType returned %d\n",nReturn)); 1243 | return nReturn; 1244 | } 1245 | 1246 | 1247 | ///////////////////////////////////////////////////////////////////////////// 1248 | // 1249 | // Function: 1250 | // InitSpnegoTokenElements 1251 | // 1252 | // Parameters: 1253 | // [in] pSpnegoToken - Points to SPNEGO_TOKEN struct 1254 | // [in] pbTokenData - Points to initial binary element 1255 | // data in a SPNEGO token. 1256 | // [in] nRemainingTokenLength - Length remaining past header 1257 | // 1258 | // Returns: 1259 | // int Success - SPNEGO_E_SUCCESS 1260 | // Failure - SPNEGO API Error code 1261 | // 1262 | // Comments : 1263 | // Interprets the data at pbTokenData based on the TokenType in 1264 | // pSpnegoToken. Since some elements are optional (technically all are 1265 | // but the token becomes quite useless if this is so), we check if 1266 | // an element exists before filling out the element in the array. 1267 | // 1268 | //////////////////////////////////////////////////////////////////////////// 1269 | 1270 | int InitSpnegoTokenElements( SPNEGO_TOKEN* pSpnegoToken, unsigned char* pbTokenData, 1271 | long nRemainingTokenLength ) 1272 | { 1273 | // 1274 | // The following arrays contain the token identifiers for the elements 1275 | // comprising the actual token. All values are optional, and there are 1276 | // no defaults. 1277 | // 1278 | 1279 | static unsigned char abNegTokenInitElements[] = 1280 | { SPNEGO_NEGINIT_ELEMENT_MECHTYPES, SPNEGO_NEGINIT_ELEMENT_REQFLAGS, 1281 | SPNEGO_NEGINIT_ELEMENT_MECHTOKEN, SPNEGO_NEGINIT_ELEMENT_MECHLISTMIC }; 1282 | 1283 | static unsigned char abNegTokenTargElements[] = 1284 | { SPNEGO_NEGTARG_ELEMENT_NEGRESULT, SPNEGO_NEGTARG_ELEMENT_SUPPORTEDMECH, 1285 | SPNEGO_NEGTARG_ELEMENT_RESPONSETOKEN, SPNEGO_NEGTARG_ELEMENT_MECHLISTMIC }; 1286 | 1287 | int nReturn = SPNEGO_E_SUCCESS; 1288 | int nCtr = 0L; 1289 | long nElementLength = 0L; 1290 | long nActualTokenLength = 0L; 1291 | unsigned char* pbElements = NULL; 1292 | 1293 | // Point to the correct array 1294 | switch( pSpnegoToken->ucTokenType ) 1295 | { 1296 | case SPNEGO_TOKEN_INIT: 1297 | { 1298 | pbElements = abNegTokenInitElements; 1299 | } 1300 | break; 1301 | 1302 | case SPNEGO_TOKEN_TARG: 1303 | { 1304 | pbElements = abNegTokenTargElements; 1305 | } 1306 | break; 1307 | 1308 | } // SWITCH tokentype 1309 | 1310 | // 1311 | // Enumerate the element arrays and look for the tokens at our current location 1312 | // 1313 | 1314 | for ( nCtr = 0L; 1315 | SPNEGO_E_SUCCESS == nReturn && 1316 | nCtr < MAX_NUM_TOKEN_ELEMENTS && 1317 | nRemainingTokenLength > 0L; 1318 | nCtr++ ) 1319 | { 1320 | 1321 | // Check if the token exists 1322 | if ( ( nReturn = ASNDerCheckToken( pbTokenData, pbElements[nCtr], 1323 | 0L, nRemainingTokenLength, 1324 | &nElementLength, &nActualTokenLength ) ) 1325 | == SPNEGO_E_SUCCESS ) 1326 | { 1327 | 1328 | // Token data should skip over the sequence token and then 1329 | // call the appropriate function to initialize the element 1330 | pbTokenData += nActualTokenLength; 1331 | 1332 | // Lengths in the elements should NOT go beyond the element 1333 | // length 1334 | 1335 | // Different tokens mean different elements 1336 | if ( SPNEGO_TOKEN_INIT == pSpnegoToken->ucTokenType ) 1337 | { 1338 | 1339 | // Handle each element as appropriate 1340 | switch( pbElements[nCtr] ) 1341 | { 1342 | 1343 | case SPNEGO_NEGINIT_ELEMENT_MECHTYPES: 1344 | { 1345 | // 1346 | // This is a Mech List that specifies which OIDs the 1347 | // originator of the Init Token supports. 1348 | // 1349 | 1350 | nReturn = GetSpnegoInitTokenMechList( pbTokenData, nElementLength, 1351 | &pSpnegoToken->aElementArray[nCtr] ); 1352 | 1353 | } 1354 | break; 1355 | 1356 | case SPNEGO_NEGINIT_ELEMENT_REQFLAGS: 1357 | { 1358 | // 1359 | // This is a BITSTRING which specifies the flags that the receiver 1360 | // pass to the gss_accept_sec_context() function. 1361 | // 1362 | 1363 | nReturn = InitSpnegoTokenElementFromBasicType( pbTokenData, nElementLength, 1364 | BITSTRING, spnego_init_reqFlags, 1365 | &pSpnegoToken->aElementArray[nCtr] ); 1366 | } 1367 | break; 1368 | 1369 | case SPNEGO_NEGINIT_ELEMENT_MECHTOKEN: 1370 | { 1371 | // 1372 | // This is an OCTETSTRING which contains a GSSAPI token corresponding 1373 | // to the first OID in the MechList. 1374 | // 1375 | 1376 | nReturn = InitSpnegoTokenElementFromBasicType( pbTokenData, nElementLength, 1377 | OCTETSTRING, spnego_init_mechToken, 1378 | &pSpnegoToken->aElementArray[nCtr] ); 1379 | } 1380 | break; 1381 | 1382 | case SPNEGO_NEGINIT_ELEMENT_MECHLISTMIC: 1383 | { 1384 | // 1385 | // This is an OCTETSTRING which contains a message integrity BLOB. 1386 | // 1387 | 1388 | nReturn = InitSpnegoTokenElementFromBasicType( pbTokenData, nElementLength, 1389 | OCTETSTRING, spnego_init_mechListMIC, 1390 | &pSpnegoToken->aElementArray[nCtr] ); 1391 | } 1392 | break; 1393 | 1394 | } // SWITCH Element 1395 | } 1396 | else 1397 | { 1398 | 1399 | switch( pbElements[nCtr] ) 1400 | { 1401 | 1402 | case SPNEGO_NEGTARG_ELEMENT_NEGRESULT: 1403 | { 1404 | // 1405 | // This is an ENUMERATION which specifies result of the last GSS 1406 | // token negotiation call. 1407 | // 1408 | 1409 | nReturn = InitSpnegoTokenElementFromBasicType( pbTokenData, nElementLength, 1410 | ENUMERATED, spnego_targ_negResult, 1411 | &pSpnegoToken->aElementArray[nCtr] ); 1412 | } 1413 | break; 1414 | 1415 | case SPNEGO_NEGTARG_ELEMENT_SUPPORTEDMECH: 1416 | { 1417 | // 1418 | // This is an OID which specifies a supported mechanism. 1419 | // 1420 | 1421 | nReturn = InitSpnegoTokenElementFromOID( pbTokenData, nElementLength, 1422 | spnego_targ_mechListMIC, 1423 | &pSpnegoToken->aElementArray[nCtr] ); 1424 | } 1425 | break; 1426 | 1427 | case SPNEGO_NEGTARG_ELEMENT_RESPONSETOKEN: 1428 | { 1429 | // 1430 | // This is an OCTETSTRING which specifies results of the last GSS 1431 | // token negotiation call. 1432 | // 1433 | 1434 | nReturn = InitSpnegoTokenElementFromBasicType( pbTokenData, nElementLength, 1435 | OCTETSTRING, spnego_targ_responseToken, 1436 | &pSpnegoToken->aElementArray[nCtr] ); 1437 | } 1438 | break; 1439 | 1440 | case SPNEGO_NEGTARG_ELEMENT_MECHLISTMIC: 1441 | { 1442 | // 1443 | // This is an OCTETSTRING which specifies a message integrity BLOB. 1444 | // 1445 | 1446 | nReturn = InitSpnegoTokenElementFromBasicType( pbTokenData, nElementLength, 1447 | OCTETSTRING, spnego_targ_mechListMIC, 1448 | &pSpnegoToken->aElementArray[nCtr] ); 1449 | } 1450 | break; 1451 | 1452 | } // SWITCH Element 1453 | 1454 | } // ELSE !NegTokenInit 1455 | 1456 | // Account for the entire token and following data 1457 | nRemainingTokenLength -= ( nActualTokenLength + nElementLength ); 1458 | 1459 | // Token data should skip past the element length now 1460 | pbTokenData += nElementLength; 1461 | 1462 | } // IF Token found 1463 | else if ( SPNEGO_E_TOKEN_NOT_FOUND == nReturn ) 1464 | { 1465 | // For now, this is a benign error (remember, all elements are optional, so 1466 | // if we don't find one, it's okay). 1467 | 1468 | nReturn = SPNEGO_E_SUCCESS; 1469 | } 1470 | 1471 | } // FOR enum elements 1472 | 1473 | // 1474 | // We should always run down to 0 remaining bytes in the token. If not, we've got 1475 | // a bad token. 1476 | // 1477 | 1478 | if ( SPNEGO_E_SUCCESS == nReturn && nRemainingTokenLength != 0L ) 1479 | { 1480 | nReturn = SPNEGO_E_INVALID_TOKEN; 1481 | } 1482 | 1483 | LOG(("InitSpnegoTokenElements returned %d\n",nReturn)); 1484 | return nReturn; 1485 | } 1486 | 1487 | 1488 | ///////////////////////////////////////////////////////////////////////////// 1489 | // 1490 | // Function: 1491 | // FindMechOIDInMechList 1492 | // 1493 | // Parameters: 1494 | // [in] pSpnegoElement - SPNEGO_ELEMENT for MechList 1495 | // [in] MechOID - OID we're looking for. 1496 | // [out] piMechTypeIndex - Index in the list where OID was 1497 | // found 1498 | // 1499 | // Returns: 1500 | // int Success - SPNEGO_E_SUCCESS 1501 | // Failure - SPNEGO API Error code 1502 | // 1503 | // Comments : 1504 | // Walks the MechList for MechOID. When it is found, the index in the 1505 | // list is written to piMechTypeIndex. 1506 | // 1507 | //////////////////////////////////////////////////////////////////////////// 1508 | 1509 | int FindMechOIDInMechList( SPNEGO_ELEMENT* pSpnegoElement, SPNEGO_MECH_OID MechOID, 1510 | int * piMechTypeIndex ) 1511 | { 1512 | int nReturn = SPNEGO_E_NOT_FOUND; 1513 | int nCtr = 0; 1514 | long nLength = 0L; 1515 | long nBoundaryLength = pSpnegoElement->nDatalength; 1516 | unsigned char* pbMechListData = pSpnegoElement->pbData; 1517 | 1518 | while( SPNEGO_E_SUCCESS != nReturn && nBoundaryLength > 0L ) 1519 | { 1520 | 1521 | // Use the helper function to check the OID 1522 | if ( ( nReturn = ASNDerCheckOID( pbMechListData, MechOID, nBoundaryLength, &nLength ) ) 1523 | == SPNEGO_E_SUCCESS ) 1524 | { 1525 | *piMechTypeIndex = nCtr; 1526 | } 1527 | 1528 | // Adjust for the current OID 1529 | pbMechListData += nLength; 1530 | nBoundaryLength -= nLength; 1531 | nCtr++; 1532 | 1533 | } // WHILE enuming OIDs 1534 | 1535 | LOG(("FindMechOIDInMechList returned %d\n",nReturn)); 1536 | return nReturn; 1537 | 1538 | } 1539 | 1540 | 1541 | ///////////////////////////////////////////////////////////////////////////// 1542 | // 1543 | // Function: 1544 | // ValidateMechList 1545 | // 1546 | // Parameters: 1547 | // [in] pbMechListData - Pointer to binary MechList data 1548 | // [in] nBoundaryLength - Length we must not exceed 1549 | // 1550 | // Returns: 1551 | // int Success - SPNEGO_E_SUCCESS 1552 | // Failure - SPNEGO API Error code 1553 | // 1554 | // Comments : 1555 | // Checks the data at pbMechListData to see if it looks like a MechList. 1556 | // As part of this, we walk the list and ensure that none of the OIDs 1557 | // have a length that takes us outside of nBoundaryLength. 1558 | // 1559 | //////////////////////////////////////////////////////////////////////////// 1560 | 1561 | int ValidateMechList( unsigned char* pbMechListData, long nBoundaryLength ) 1562 | { 1563 | int nReturn = SPNEGO_E_SUCCESS; 1564 | long nLength = 0L; 1565 | long nTokenLength = 0L; 1566 | 1567 | while( SPNEGO_E_SUCCESS == nReturn && nBoundaryLength > 0L ) 1568 | { 1569 | // Verify that we have something that at least *looks* like an OID - in other 1570 | // words it has an OID identifier and specifies a length that doesn't go beyond 1571 | // the size of the list. 1572 | nReturn = ASNDerCheckToken( pbMechListData, OID, 0L, nBoundaryLength, 1573 | &nLength, &nTokenLength ); 1574 | 1575 | // Adjust for the current OID 1576 | pbMechListData += ( nLength + nTokenLength ); 1577 | nBoundaryLength -= ( nLength + nTokenLength ); 1578 | 1579 | } // WHILE enuming OIDs 1580 | 1581 | LOG(("ValidateMechList returned %d\n",nReturn)); 1582 | return nReturn; 1583 | 1584 | } 1585 | 1586 | ///////////////////////////////////////////////////////////////////////////// 1587 | // 1588 | // Function: 1589 | // IsValidMechOid 1590 | // 1591 | // Parameters: 1592 | // [in] mechOid - mechOID id enumeration 1593 | // 1594 | // Returns: 1595 | // int Success - 1 1596 | // Failure - 0 1597 | // 1598 | // Comments : 1599 | // Checks for a valid mechOid value. 1600 | // 1601 | //////////////////////////////////////////////////////////////////////////// 1602 | 1603 | int IsValidMechOid( SPNEGO_MECH_OID mechOid ) 1604 | { 1605 | LOG(("IsValidMechOid returned %d\n",mechOid >= spnego_mech_oid_Kerberos_V5_Legacy && 1606 | mechOid <= spnego_mech_oid_Spnego)); 1607 | return ( mechOid >= spnego_mech_oid_Kerberos_V5_Legacy && 1608 | mechOid <= spnego_mech_oid_Spnego ); 1609 | } 1610 | 1611 | ///////////////////////////////////////////////////////////////////////////// 1612 | // 1613 | // Function: 1614 | // IsValidContextFlags 1615 | // 1616 | // Parameters: 1617 | // [in] ucContextFlags - ContextFlags value 1618 | // 1619 | // Returns: 1620 | // int Success - 1 1621 | // Failure - 0 1622 | // 1623 | // Comments : 1624 | // Checks for a valid ContextFlags value. 1625 | // 1626 | //////////////////////////////////////////////////////////////////////////// 1627 | 1628 | int IsValidContextFlags( unsigned char ucContextFlags ) 1629 | { 1630 | // Mask out our valid bits. If there is anything leftover, this 1631 | // is not a valid value for Context Flags 1632 | LOG(("IsValidContextFlags returned %d\n",(( ucContextFlags & ~SPNEGO_NEGINIT_CONTEXT_MASK ) == 0)); 1633 | return ( ( ucContextFlags & ~SPNEGO_NEGINIT_CONTEXT_MASK ) == 0 )); 1634 | } 1635 | 1636 | ///////////////////////////////////////////////////////////////////////////// 1637 | // 1638 | // Function: 1639 | // IsValidNegResult 1640 | // 1641 | // Parameters: 1642 | // [in] negResult - NegResult value 1643 | // 1644 | // Returns: 1645 | // int Success - 1 1646 | // Failure - 0 1647 | // 1648 | // Comments : 1649 | // Checks for a valid NegResult value. 1650 | // 1651 | //////////////////////////////////////////////////////////////////////////// 1652 | 1653 | int IsValidNegResult( SPNEGO_NEGRESULT negResult ) 1654 | { 1655 | LOG(("IsValidNegResult returned %d\n",negResult >= spnego_negresult_success && 1656 | negResult <= spnego_negresult_rejected )); 1657 | return ( negResult >= spnego_negresult_success && 1658 | negResult <= spnego_negresult_rejected ); 1659 | } 1660 | 1661 | ///////////////////////////////////////////////////////////////////////////// 1662 | // 1663 | // Function: 1664 | // IsValidSpnegoToken 1665 | // 1666 | // Parameters: 1667 | // [in] pSpnegoToken - Points to SPNEGO_TOKEN data structure 1668 | // 1669 | // Returns: 1670 | // int Success - 1 1671 | // Failure - 0 1672 | // 1673 | // Comments : 1674 | // Performs simple heuristic on location pointed to by pSpnegoToken. 1675 | // 1676 | //////////////////////////////////////////////////////////////////////////// 1677 | 1678 | int IsValidSpnegoToken( SPNEGO_TOKEN* pSpnegoToken ) 1679 | { 1680 | int nReturn = 0; 1681 | 1682 | // Parameter should be non-NULL 1683 | if ( NULL != pSpnegoToken ) 1684 | { 1685 | // Length should be at least the size defined in the header 1686 | if ( pSpnegoToken->nStructSize >= SPNEGO_TOKEN_SIZE ) 1687 | { 1688 | // Number of elements should be >= our maximum - if it's greater, that's 1689 | // okay, since we'll only be accessing the elements up to MAX_NUM_TOKEN_ELEMENTS 1690 | if ( pSpnegoToken->nNumElements >= MAX_NUM_TOKEN_ELEMENTS ) 1691 | { 1692 | // Check for proper token type 1693 | if ( SPNEGO_TOKEN_INIT == pSpnegoToken->ucTokenType || 1694 | SPNEGO_TOKEN_TARG == pSpnegoToken->ucTokenType ) 1695 | { 1696 | nReturn = 1; 1697 | } 1698 | } 1699 | 1700 | } // IF struct size makes sense 1701 | 1702 | } // IF non-NULL spnego Token 1703 | 1704 | LOG(("IsValidSpnegoToken returned %d\n",nReturn)); 1705 | return nReturn; 1706 | } 1707 | 1708 | ///////////////////////////////////////////////////////////////////////////// 1709 | // 1710 | // Function: 1711 | // IsValidSpnegoElement 1712 | // 1713 | // Parameters: 1714 | // [in] pSpnegoToken - Points to SPNEGO_TOKEN data structure 1715 | // [in] spnegoElement - spnegoElement Type from enumeration 1716 | // 1717 | // Returns: 1718 | // int Success - 1 1719 | // Failure - 0 1720 | // 1721 | // Comments : 1722 | // Checks that spnegoElement has a valid value and is appropriate for 1723 | // the SPNEGO token encapsulated by pSpnegoToken. 1724 | // 1725 | //////////////////////////////////////////////////////////////////////////// 1726 | 1727 | int IsValidSpnegoElement( SPNEGO_TOKEN* pSpnegoToken,SPNEGO_ELEMENT_TYPE spnegoElement ) 1728 | { 1729 | int nReturn = 0; 1730 | 1731 | // Check boundaries 1732 | if ( spnegoElement > spnego_element_min && 1733 | spnegoElement < spnego_element_max ) 1734 | { 1735 | 1736 | // Check for appropriateness to token type 1737 | if ( SPNEGO_TOKEN_INIT == pSpnegoToken->ucTokenType ) 1738 | { 1739 | nReturn = ( spnegoElement >= spnego_init_mechtypes && 1740 | spnegoElement <= spnego_init_mechListMIC ); 1741 | } 1742 | else 1743 | { 1744 | nReturn = ( spnegoElement >= spnego_targ_negResult && 1745 | spnegoElement <= spnego_targ_mechListMIC ); 1746 | } 1747 | 1748 | } // IF boundary conditions are met 1749 | 1750 | LOG(("IsValidSpnegoElement returned %d\n",nReturn)); 1751 | return nReturn; 1752 | } 1753 | 1754 | ///////////////////////////////////////////////////////////////////////////// 1755 | // 1756 | // Function: 1757 | // CalculateElementArrayIndex 1758 | // 1759 | // Parameters: 1760 | // [in] pSpnegoToken - Points to SPNEGO_TOKEN data structure 1761 | // [in] spnegoElement - spnegoElement Type from enumeration 1762 | // 1763 | // Returns: 1764 | // int index in the SPNEGO_TOKEN element array that the element can 1765 | // can be found 1766 | // 1767 | // Comments : 1768 | // Based on the Token Type, calculates the index in the element array 1769 | // at which the specified element can be found. 1770 | // 1771 | //////////////////////////////////////////////////////////////////////////// 1772 | 1773 | int CalculateElementArrayIndex( SPNEGO_TOKEN* pSpnegoToken,SPNEGO_ELEMENT_TYPE spnegoElement ) 1774 | { 1775 | int nReturn = 0; 1776 | 1777 | // Offset is difference between value and initial element identifier 1778 | // (these differ based on ucTokenType) 1779 | 1780 | if ( SPNEGO_TOKEN_INIT == pSpnegoToken->ucTokenType ) 1781 | { 1782 | nReturn = spnegoElement - spnego_init_mechtypes; 1783 | } 1784 | else 1785 | { 1786 | nReturn = spnegoElement - spnego_targ_negResult; 1787 | } 1788 | 1789 | LOG(("CalculateElementArrayIndex returned %d\n",nReturn)); 1790 | return nReturn; 1791 | } 1792 | 1793 | ///////////////////////////////////////////////////////////////////////////// 1794 | // 1795 | // Function: 1796 | // InitTokenFromBinary 1797 | // 1798 | // Parameters: 1799 | // [in] ucCopyData - Flag indicating if data should be copied 1800 | // [in] ulFlags - Flags value for structure 1801 | // [in] pnTokenData - Binary Token Data 1802 | // [in] ulLength - Length of the data 1803 | // [out] ppSpnegoToken - Pointer to call allocated SPNEGO Token 1804 | // data structure 1805 | // 1806 | // Returns: 1807 | // int Success - SPNEGO_E_SUCCESS 1808 | // Failure - SPNEGO API Error code 1809 | // 1810 | // Comments : 1811 | // Allocates a SPNEGO_TOKEN data structure and fills it out as 1812 | // appropriate based in the flags passed into the function. 1813 | // 1814 | //////////////////////////////////////////////////////////////////////////// 1815 | 1816 | 1817 | // Initializes SPNEGO_TOKEN structure from DER encoded binary data 1818 | int InitTokenFromBinary( unsigned char ucCopyData, unsigned long ulFlags, 1819 | unsigned char* pbTokenData, unsigned long ulLength, 1820 | SPNEGO_TOKEN** ppSpnegoToken ) 1821 | { 1822 | int nReturn = SPNEGO_E_INVALID_PARAMETER; 1823 | SPNEGO_TOKEN* pSpnegoToken = NULL; 1824 | unsigned char* pbFirstElement = NULL; 1825 | long nTokenLength = 0L; 1826 | long nRemainingTokenLength = 0L; 1827 | 1828 | // Basic Parameter Validation 1829 | 1830 | if ( NULL != pbTokenData && 1831 | NULL != ppSpnegoToken && 1832 | 0L != ulLength ) 1833 | { 1834 | 1835 | // 1836 | // Allocate the empty token, then initialize the data structure. 1837 | // 1838 | 1839 | pSpnegoToken = AllocEmptySpnegoToken( ucCopyData, ulFlags, pbTokenData, ulLength ); 1840 | 1841 | if ( NULL != pSpnegoToken ) 1842 | { 1843 | 1844 | // Copy the binary data locally 1845 | 1846 | 1847 | // Initialize the token type 1848 | if ( ( nReturn = InitSpnegoTokenType( pSpnegoToken, &nTokenLength, 1849 | &nRemainingTokenLength, &pbFirstElement ) ) 1850 | == SPNEGO_E_SUCCESS ) 1851 | { 1852 | 1853 | // Initialize the element array 1854 | if ( ( nReturn = InitSpnegoTokenElements( pSpnegoToken, pbFirstElement, 1855 | nRemainingTokenLength ) ) 1856 | == SPNEGO_E_SUCCESS ) 1857 | { 1858 | *ppSpnegoToken = pSpnegoToken; 1859 | } 1860 | 1861 | } // IF Init Token Type 1862 | 1863 | // Cleanup on error condition 1864 | if ( SPNEGO_E_SUCCESS != nReturn ) 1865 | { 1866 | spnegoFreeData( pSpnegoToken ); 1867 | } 1868 | 1869 | } 1870 | else 1871 | { 1872 | nReturn = SPNEGO_E_OUT_OF_MEMORY; 1873 | } 1874 | 1875 | } // IF Valid parameters 1876 | 1877 | 1878 | LOG(("InitTokenFromBinary returned %d\n",nReturn)); 1879 | return nReturn; 1880 | } 1881 | -------------------------------------------------------------------------------- /spnegohelp/spnegoparse.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2002 Microsoft Corporation 2 | // All rights reserved. 3 | // 4 | // THIS CODE AND INFORMATION IS PROVIDED "AS IS" 5 | // WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 6 | // OR IMPLIED, INCLUDING BUT NOT LIMITED 7 | // TO THE IMPLIED WARRANTIES OF MERCHANTIBILITY 8 | // AND/OR FITNESS FOR A PARTICULAR PURPOSE. 9 | // 10 | // Date - 10/08/2002 11 | // Author - Sanj Surati 12 | 13 | ///////////////////////////////////////////////////////////// 14 | // 15 | // SPNEGOPARSE.H 16 | // 17 | // SPNEGO Token Parser Header File 18 | // 19 | // Contains the definitions required to properly parse a 20 | // SPNEGO token using ASN.1 DER helpers. 21 | // 22 | ///////////////////////////////////////////////////////////// 23 | 24 | #ifndef __SPNEGOPARSE_H__ 25 | #define __SPNEGOPARSE_H__ 26 | 27 | // C++ Specific 28 | #if defined(__cplusplus) 29 | extern "C" 30 | { 31 | #endif 32 | 33 | // Indicates if we copy data when creating a SPNEGO_TOKEN structure or not 34 | #define SPNEGO_TOKEN_INTERNAL_COPYPTR 0 35 | #define SPNEGO_TOKEN_INTERNAL_COPYDATA 0x1 36 | 37 | // Internal flag dictates whether or not we will free the binary data when 38 | // the SPNEG_TOKEN structure is destroyed 39 | #define SPNEGO_TOKEN_INTERNAL_FLAGS_FREEDATA 0x1 40 | 41 | // 42 | // Each SPNEGO Token Type can be broken down into a 43 | // maximum of 4 separate elements. 44 | // 45 | 46 | #define MAX_NUM_TOKEN_ELEMENTS 4 47 | 48 | // 49 | // Element offsets in the array 50 | // 51 | 52 | // INIT elements 53 | #define SPNEGO_INIT_MECHTYPES_ELEMENT 0 54 | #define SPNEGO_INIT_REQFLAGS_ELEMENT 1 55 | #define SPNEGO_INIT_MECHTOKEN_ELEMENT 2 56 | #define SPNEGO_INIT_MECHLISTMIC_ELEMENT 3 57 | 58 | // Response elements 59 | #define SPNEGO_TARG_NEGRESULT_ELEMENT 0 60 | #define SPNEGO_TARG_SUPPMECH_ELEMENT 1 61 | #define SPNEGO_TARG_RESPTOKEN_ELEMENT 2 62 | #define SPNEGO_TARG_MECHLISTMIC_ELEMENT 3 63 | 64 | // 65 | // Defines an individual SPNEGO Token Element. 66 | // 67 | 68 | typedef struct SpnegoElement 69 | { 70 | size_t nStructSize; // Size of the element structure 71 | int iElementPresent; // Is the field present? Must be either 72 | // SPNEGO_TOKEN_ELEMENT_UNAVAILABLE or 73 | // SPNEGO_TOKEN_ELEMENT_AVAILABLE 74 | 75 | SPNEGO_ELEMENT_TYPE eElementType; // The Element Type 76 | 77 | unsigned char type; // Data Type 78 | 79 | unsigned char* pbData; // Points to actual Data 80 | 81 | unsigned long nDatalength; // Actual Data Length 82 | 83 | } SPNEGO_ELEMENT; 84 | 85 | // Structure size in case we later choose to extend the structure 86 | #define SPNEGO_ELEMENT_SIZE sizeof(SPNEGO_ELEMENT) 87 | 88 | // 89 | // Packages a SPNEGO Token Encoding. There are two types of 90 | // encodings: NegTokenInit and NegTokenTarg. Each encoding can 91 | // contain up to four distinct, optional elements. 92 | // 93 | 94 | typedef struct SpnegoToken 95 | { 96 | size_t nStructSize; // Size of the Token structure 97 | unsigned long ulFlags; // Internal Structure Flags - Reserved! 98 | int ucTokenType; // Token Type - Must be 99 | // SPNEGO_TOKEN_INIT or 100 | // SPNEGO_TOKEN_TARG 101 | 102 | unsigned char* pbBinaryData; // Points to binary token data 103 | 104 | unsigned long ulBinaryDataLen; // Length of the actual binary data 105 | int nNumElements; // Number of elements 106 | SPNEGO_ELEMENT aElementArray [MAX_NUM_TOKEN_ELEMENTS]; // Holds the elements for the token 107 | } SPNEGO_TOKEN; 108 | 109 | // Structure size in case we later choose to extend the structure 110 | #define SPNEGO_TOKEN_SIZE sizeof(SPNEGO_TOKEN) 111 | 112 | // 113 | // Function definitions 114 | // 115 | 116 | SPNEGO_TOKEN* AllocEmptySpnegoToken( unsigned char ucCopyData, unsigned long ulFlags, 117 | unsigned char * pbTokenData, unsigned long ulTokenSize ); 118 | void FreeSpnegoToken( SPNEGO_TOKEN* pSpnegoToken ); 119 | void InitSpnegoTokenElementArray( SPNEGO_TOKEN* pSpnegoToken ); 120 | int InitSpnegoTokenType( SPNEGO_TOKEN* pSpnegoToken, long* pnTokenLength, 121 | long* pnRemainingTokenLength, unsigned char** ppbFirstElement ); 122 | int InitSpnegoTokenElements( SPNEGO_TOKEN* pSpnegoToken, unsigned char* pbTokenData, 123 | long nRemainingTokenLength ); 124 | int GetSpnegoInitTokenMechList( unsigned char* pbTokenData, int nMechListLength, 125 | SPNEGO_ELEMENT* pSpnegoElement ); 126 | int InitSpnegoTokenElementFromBasicType( unsigned char* pbTokenData, int nElementLength, 127 | unsigned char ucExpectedType, 128 | SPNEGO_ELEMENT_TYPE spnegoElementType, 129 | SPNEGO_ELEMENT* pSpnegoElement ); 130 | int InitSpnegoTokenElementFromOID( unsigned char* pbTokenData, int nElementLength, 131 | SPNEGO_ELEMENT_TYPE spnegoElementType, 132 | SPNEGO_ELEMENT* pSpnegoElement ); 133 | int FindMechOIDInMechList( SPNEGO_ELEMENT* pSpnegoElement, SPNEGO_MECH_OID MechOID, 134 | int * piMechTypeIndex ); 135 | int ValidateMechList( unsigned char* pbMechListData, long nBoundaryLength ); 136 | int CalculateMinSpnegoInitTokenSize( long nMechTokenLength, long nMechListMICLength, 137 | SPNEGO_MECH_OID mechOid, int nReqFlagsAvailable, 138 | long* plTokenSize, long* plInternalLength ); 139 | int CalculateMinSpnegoTargTokenSize( SPNEGO_MECH_OID MechType, SPNEGO_NEGRESULT spnegoNegResult, 140 | long nMechTokenLen, 141 | long nMechTokenMIC, long* pnTokenSize, 142 | long* pnInternalTokenLength ); 143 | int CreateSpnegoInitToken( SPNEGO_MECH_OID MechType, 144 | unsigned char ucContextFlags, unsigned char* pbMechToken, 145 | unsigned long ulMechTokenLen, unsigned char* pbMechListMIC, 146 | unsigned long ulMechListMICLen, unsigned char* pbTokenData, 147 | long nTokenLength, long nInternalTokenLength ); 148 | int CreateSpnegoTargToken( SPNEGO_MECH_OID MechType, 149 | SPNEGO_NEGRESULT eNegResult, unsigned char* pbMechToken, 150 | unsigned long ulMechTokenLen, unsigned char* pbMechListMIC, 151 | unsigned long ulMechListMICLen, unsigned char* pbTokenData, 152 | long nTokenLength, long nInternalTokenLength ); 153 | int IsValidMechOid( SPNEGO_MECH_OID mechOid ); 154 | int IsValidContextFlags( unsigned char ucContextFlags ); 155 | int IsValidNegResult( SPNEGO_NEGRESULT negResult ); 156 | int IsValidSpnegoToken( SPNEGO_TOKEN* pSpnegoToken ); 157 | int IsValidSpnegoElement( SPNEGO_TOKEN* pSpnegoToken,SPNEGO_ELEMENT_TYPE spnegoElement ); 158 | int CalculateElementArrayIndex( SPNEGO_TOKEN* pSpnegoToken,SPNEGO_ELEMENT_TYPE spnegoElement ); 159 | int InitTokenFromBinary( unsigned char ucCopyData, unsigned long ulFlags, 160 | unsigned char* pbTokenData, unsigned long ulLength, 161 | SPNEGO_TOKEN** ppSpnegoToken ); 162 | 163 | // C++ Specific 164 | #if defined(__cplusplus) 165 | } 166 | #endif 167 | 168 | #endif 169 | 170 | --------------------------------------------------------------------------------