1178825Sdfr/* 2233294Sstas * Copyright (c) 1997 - 2006 Kungliga Tekniska H��gskolan 3233294Sstas * (Royal Institute of Technology, Stockholm, Sweden). 4178825Sdfr * Portions Copyright (c) 2004 PADL Software Pty Ltd. 5178825Sdfr * 6233294Sstas * Redistribution and use in source and binary forms, with or without 7233294Sstas * modification, are permitted provided that the following conditions 8233294Sstas * are met: 9178825Sdfr * 10233294Sstas * 1. Redistributions of source code must retain the above copyright 11233294Sstas * notice, this list of conditions and the following disclaimer. 12178825Sdfr * 13233294Sstas * 2. Redistributions in binary form must reproduce the above copyright 14233294Sstas * notice, this list of conditions and the following disclaimer in the 15233294Sstas * documentation and/or other materials provided with the distribution. 16178825Sdfr * 17233294Sstas * 3. Neither the name of the Institute nor the names of its contributors 18233294Sstas * may be used to endorse or promote products derived from this software 19233294Sstas * without specific prior written permission. 20178825Sdfr * 21233294Sstas * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 22233294Sstas * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23233294Sstas * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24233294Sstas * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 25233294Sstas * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26233294Sstas * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27233294Sstas * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28233294Sstas * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29233294Sstas * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30233294Sstas * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31233294Sstas * SUCH DAMAGE. 32178825Sdfr */ 33178825Sdfr 34233294Sstas#include "spnego_locl.h" 35178825Sdfr 36178825Sdfrstatic OM_uint32 37178825Sdfrsend_reject (OM_uint32 *minor_status, 38178825Sdfr gss_buffer_t output_token) 39178825Sdfr{ 40178825Sdfr NegotiationToken nt; 41178825Sdfr size_t size; 42178825Sdfr 43178825Sdfr nt.element = choice_NegotiationToken_negTokenResp; 44178825Sdfr 45178825Sdfr ALLOC(nt.u.negTokenResp.negResult, 1); 46178825Sdfr if (nt.u.negTokenResp.negResult == NULL) { 47178825Sdfr *minor_status = ENOMEM; 48178825Sdfr return GSS_S_FAILURE; 49178825Sdfr } 50178825Sdfr *(nt.u.negTokenResp.negResult) = reject; 51178825Sdfr nt.u.negTokenResp.supportedMech = NULL; 52178825Sdfr nt.u.negTokenResp.responseToken = NULL; 53178825Sdfr nt.u.negTokenResp.mechListMIC = NULL; 54233294Sstas 55178825Sdfr ASN1_MALLOC_ENCODE(NegotiationToken, 56178825Sdfr output_token->value, output_token->length, &nt, 57178825Sdfr &size, *minor_status); 58178825Sdfr free_NegotiationToken(&nt); 59178825Sdfr if (*minor_status != 0) 60178825Sdfr return GSS_S_FAILURE; 61178825Sdfr 62178825Sdfr return GSS_S_BAD_MECH; 63178825Sdfr} 64178825Sdfr 65178825Sdfrstatic OM_uint32 66178825Sdfracceptor_approved(gss_name_t target_name, gss_OID mech) 67178825Sdfr{ 68178825Sdfr gss_cred_id_t cred = GSS_C_NO_CREDENTIAL; 69178825Sdfr gss_OID_set oidset; 70178825Sdfr OM_uint32 junk, ret; 71178825Sdfr 72178825Sdfr if (target_name == GSS_C_NO_NAME) 73178825Sdfr return GSS_S_COMPLETE; 74178825Sdfr 75178825Sdfr gss_create_empty_oid_set(&junk, &oidset); 76178825Sdfr gss_add_oid_set_member(&junk, mech, &oidset); 77233294Sstas 78178825Sdfr ret = gss_acquire_cred(&junk, target_name, GSS_C_INDEFINITE, oidset, 79178825Sdfr GSS_C_ACCEPT, &cred, NULL, NULL); 80178825Sdfr gss_release_oid_set(&junk, &oidset); 81178825Sdfr if (ret != GSS_S_COMPLETE) 82178825Sdfr return ret; 83178825Sdfr gss_release_cred(&junk, &cred); 84233294Sstas 85178825Sdfr return GSS_S_COMPLETE; 86178825Sdfr} 87178825Sdfr 88178825Sdfrstatic OM_uint32 89178825Sdfrsend_supported_mechs (OM_uint32 *minor_status, 90178825Sdfr gss_buffer_t output_token) 91178825Sdfr{ 92178825Sdfr NegotiationTokenWin nt; 93233294Sstas size_t buf_len = 0; 94178825Sdfr gss_buffer_desc data; 95178825Sdfr OM_uint32 ret; 96178825Sdfr 97178825Sdfr memset(&nt, 0, sizeof(nt)); 98178825Sdfr 99178825Sdfr nt.element = choice_NegotiationTokenWin_negTokenInit; 100178825Sdfr nt.u.negTokenInit.reqFlags = NULL; 101178825Sdfr nt.u.negTokenInit.mechToken = NULL; 102178825Sdfr nt.u.negTokenInit.negHints = NULL; 103178825Sdfr 104178825Sdfr ret = _gss_spnego_indicate_mechtypelist(minor_status, GSS_C_NO_NAME, 105178825Sdfr acceptor_approved, 1, NULL, 106178825Sdfr &nt.u.negTokenInit.mechTypes, NULL); 107178825Sdfr if (ret != GSS_S_COMPLETE) { 108178825Sdfr return ret; 109178825Sdfr } 110178825Sdfr 111178825Sdfr ALLOC(nt.u.negTokenInit.negHints, 1); 112178825Sdfr if (nt.u.negTokenInit.negHints == NULL) { 113178825Sdfr *minor_status = ENOMEM; 114178825Sdfr free_NegotiationTokenWin(&nt); 115178825Sdfr return GSS_S_FAILURE; 116178825Sdfr } 117178825Sdfr 118178825Sdfr ALLOC(nt.u.negTokenInit.negHints->hintName, 1); 119178825Sdfr if (nt.u.negTokenInit.negHints->hintName == NULL) { 120178825Sdfr *minor_status = ENOMEM; 121178825Sdfr free_NegotiationTokenWin(&nt); 122178825Sdfr return GSS_S_FAILURE; 123178825Sdfr } 124178825Sdfr 125233294Sstas *nt.u.negTokenInit.negHints->hintName = strdup("not_defined_in_RFC4178@please_ignore"); 126178825Sdfr nt.u.negTokenInit.negHints->hintAddress = NULL; 127178825Sdfr 128233294Sstas ASN1_MALLOC_ENCODE(NegotiationTokenWin, 129178825Sdfr data.value, data.length, &nt, &buf_len, ret); 130178825Sdfr free_NegotiationTokenWin(&nt); 131178825Sdfr if (ret) { 132233294Sstas *minor_status = ret; 133233294Sstas return GSS_S_FAILURE; 134178825Sdfr } 135233294Sstas if (data.length != buf_len) { 136178825Sdfr abort(); 137233294Sstas UNREACHABLE(return GSS_S_FAILURE); 138233294Sstas } 139178825Sdfr 140178825Sdfr ret = gss_encapsulate_token(&data, GSS_SPNEGO_MECHANISM, output_token); 141178825Sdfr 142178825Sdfr free (data.value); 143178825Sdfr 144178825Sdfr if (ret != GSS_S_COMPLETE) 145178825Sdfr return ret; 146178825Sdfr 147178825Sdfr *minor_status = 0; 148178825Sdfr 149178825Sdfr return GSS_S_CONTINUE_NEEDED; 150178825Sdfr} 151178825Sdfr 152178825Sdfrstatic OM_uint32 153178825Sdfrsend_accept (OM_uint32 *minor_status, 154178825Sdfr gssspnego_ctx context_handle, 155178825Sdfr gss_buffer_t mech_token, 156178825Sdfr int initial_response, 157178825Sdfr gss_buffer_t mech_buf, 158178825Sdfr gss_buffer_t output_token) 159178825Sdfr{ 160178825Sdfr NegotiationToken nt; 161178825Sdfr OM_uint32 ret; 162178825Sdfr gss_buffer_desc mech_mic_buf; 163178825Sdfr size_t size; 164178825Sdfr 165178825Sdfr memset(&nt, 0, sizeof(nt)); 166178825Sdfr 167178825Sdfr nt.element = choice_NegotiationToken_negTokenResp; 168178825Sdfr 169178825Sdfr ALLOC(nt.u.negTokenResp.negResult, 1); 170178825Sdfr if (nt.u.negTokenResp.negResult == NULL) { 171178825Sdfr *minor_status = ENOMEM; 172178825Sdfr return GSS_S_FAILURE; 173178825Sdfr } 174178825Sdfr 175178825Sdfr if (context_handle->open) { 176178825Sdfr if (mech_token != GSS_C_NO_BUFFER 177178825Sdfr && mech_token->length != 0 178178825Sdfr && mech_buf != GSS_C_NO_BUFFER) 179178825Sdfr *(nt.u.negTokenResp.negResult) = accept_incomplete; 180178825Sdfr else 181178825Sdfr *(nt.u.negTokenResp.negResult) = accept_completed; 182178825Sdfr } else { 183178825Sdfr if (initial_response && context_handle->require_mic) 184178825Sdfr *(nt.u.negTokenResp.negResult) = request_mic; 185178825Sdfr else 186178825Sdfr *(nt.u.negTokenResp.negResult) = accept_incomplete; 187178825Sdfr } 188178825Sdfr 189178825Sdfr if (initial_response) { 190178825Sdfr ALLOC(nt.u.negTokenResp.supportedMech, 1); 191178825Sdfr if (nt.u.negTokenResp.supportedMech == NULL) { 192178825Sdfr free_NegotiationToken(&nt); 193178825Sdfr *minor_status = ENOMEM; 194178825Sdfr return GSS_S_FAILURE; 195178825Sdfr } 196178825Sdfr 197178825Sdfr ret = der_get_oid(context_handle->preferred_mech_type->elements, 198178825Sdfr context_handle->preferred_mech_type->length, 199178825Sdfr nt.u.negTokenResp.supportedMech, 200178825Sdfr NULL); 201178825Sdfr if (ret) { 202178825Sdfr free_NegotiationToken(&nt); 203178825Sdfr *minor_status = ENOMEM; 204178825Sdfr return GSS_S_FAILURE; 205178825Sdfr } 206178825Sdfr } else { 207178825Sdfr nt.u.negTokenResp.supportedMech = NULL; 208178825Sdfr } 209178825Sdfr 210178825Sdfr if (mech_token != GSS_C_NO_BUFFER && mech_token->length != 0) { 211178825Sdfr ALLOC(nt.u.negTokenResp.responseToken, 1); 212178825Sdfr if (nt.u.negTokenResp.responseToken == NULL) { 213178825Sdfr free_NegotiationToken(&nt); 214178825Sdfr *minor_status = ENOMEM; 215178825Sdfr return GSS_S_FAILURE; 216178825Sdfr } 217178825Sdfr nt.u.negTokenResp.responseToken->length = mech_token->length; 218178825Sdfr nt.u.negTokenResp.responseToken->data = mech_token->value; 219178825Sdfr mech_token->length = 0; 220178825Sdfr mech_token->value = NULL; 221178825Sdfr } else { 222178825Sdfr nt.u.negTokenResp.responseToken = NULL; 223178825Sdfr } 224178825Sdfr 225178825Sdfr if (mech_buf != GSS_C_NO_BUFFER) { 226178825Sdfr ret = gss_get_mic(minor_status, 227178825Sdfr context_handle->negotiated_ctx_id, 228178825Sdfr 0, 229178825Sdfr mech_buf, 230178825Sdfr &mech_mic_buf); 231178825Sdfr if (ret == GSS_S_COMPLETE) { 232178825Sdfr ALLOC(nt.u.negTokenResp.mechListMIC, 1); 233178825Sdfr if (nt.u.negTokenResp.mechListMIC == NULL) { 234178825Sdfr gss_release_buffer(minor_status, &mech_mic_buf); 235178825Sdfr free_NegotiationToken(&nt); 236178825Sdfr *minor_status = ENOMEM; 237178825Sdfr return GSS_S_FAILURE; 238178825Sdfr } 239178825Sdfr nt.u.negTokenResp.mechListMIC->length = mech_mic_buf.length; 240178825Sdfr nt.u.negTokenResp.mechListMIC->data = mech_mic_buf.value; 241178825Sdfr } else if (ret == GSS_S_UNAVAILABLE) { 242178825Sdfr nt.u.negTokenResp.mechListMIC = NULL; 243178825Sdfr } else { 244178825Sdfr free_NegotiationToken(&nt); 245178825Sdfr return ret; 246178825Sdfr } 247178825Sdfr 248178825Sdfr } else 249178825Sdfr nt.u.negTokenResp.mechListMIC = NULL; 250233294Sstas 251178825Sdfr ASN1_MALLOC_ENCODE(NegotiationToken, 252178825Sdfr output_token->value, output_token->length, 253178825Sdfr &nt, &size, ret); 254178825Sdfr if (ret) { 255178825Sdfr free_NegotiationToken(&nt); 256178825Sdfr *minor_status = ret; 257178825Sdfr return GSS_S_FAILURE; 258178825Sdfr } 259178825Sdfr 260178825Sdfr /* 261178825Sdfr * The response should not be encapsulated, because 262178825Sdfr * it is a SubsequentContextToken (note though RFC 1964 263178825Sdfr * specifies encapsulation for all _Kerberos_ tokens). 264178825Sdfr */ 265178825Sdfr 266178825Sdfr if (*(nt.u.negTokenResp.negResult) == accept_completed) 267178825Sdfr ret = GSS_S_COMPLETE; 268178825Sdfr else 269178825Sdfr ret = GSS_S_CONTINUE_NEEDED; 270178825Sdfr free_NegotiationToken(&nt); 271178825Sdfr return ret; 272178825Sdfr} 273178825Sdfr 274178825Sdfr 275178825Sdfrstatic OM_uint32 276178825Sdfrverify_mechlist_mic 277178825Sdfr (OM_uint32 *minor_status, 278178825Sdfr gssspnego_ctx context_handle, 279178825Sdfr gss_buffer_t mech_buf, 280178825Sdfr heim_octet_string *mechListMIC 281178825Sdfr ) 282178825Sdfr{ 283178825Sdfr OM_uint32 ret; 284178825Sdfr gss_buffer_desc mic_buf; 285178825Sdfr 286178825Sdfr if (context_handle->verified_mic) { 287178825Sdfr /* This doesn't make sense, we've already verified it? */ 288178825Sdfr *minor_status = 0; 289178825Sdfr return GSS_S_DUPLICATE_TOKEN; 290178825Sdfr } 291178825Sdfr 292178825Sdfr if (mechListMIC == NULL) { 293178825Sdfr *minor_status = 0; 294178825Sdfr return GSS_S_DEFECTIVE_TOKEN; 295178825Sdfr } 296178825Sdfr 297178825Sdfr mic_buf.length = mechListMIC->length; 298178825Sdfr mic_buf.value = mechListMIC->data; 299178825Sdfr 300178825Sdfr ret = gss_verify_mic(minor_status, 301178825Sdfr context_handle->negotiated_ctx_id, 302178825Sdfr mech_buf, 303178825Sdfr &mic_buf, 304178825Sdfr NULL); 305178825Sdfr 306178825Sdfr if (ret != GSS_S_COMPLETE) 307178825Sdfr ret = GSS_S_DEFECTIVE_TOKEN; 308178825Sdfr 309178825Sdfr return ret; 310178825Sdfr} 311178825Sdfr 312178825Sdfrstatic OM_uint32 313178825Sdfrselect_mech(OM_uint32 *minor_status, MechType *mechType, int verify_p, 314178825Sdfr gss_OID *mech_p) 315178825Sdfr{ 316178825Sdfr char mechbuf[64]; 317178825Sdfr size_t mech_len; 318178825Sdfr gss_OID_desc oid; 319178828Sdfr gss_OID oidp; 320178828Sdfr gss_OID_set mechs; 321233294Sstas size_t i; 322178825Sdfr OM_uint32 ret, junk; 323178825Sdfr 324178825Sdfr ret = der_put_oid ((unsigned char *)mechbuf + sizeof(mechbuf) - 1, 325178825Sdfr sizeof(mechbuf), 326178825Sdfr mechType, 327178825Sdfr &mech_len); 328178825Sdfr if (ret) { 329178825Sdfr return GSS_S_DEFECTIVE_TOKEN; 330178825Sdfr } 331178825Sdfr 332178825Sdfr oid.length = mech_len; 333178825Sdfr oid.elements = mechbuf + sizeof(mechbuf) - mech_len; 334178825Sdfr 335178825Sdfr if (gss_oid_equal(&oid, GSS_SPNEGO_MECHANISM)) { 336178825Sdfr return GSS_S_BAD_MECH; 337178825Sdfr } 338178825Sdfr 339178825Sdfr *minor_status = 0; 340178825Sdfr 341178825Sdfr /* Translate broken MS Kebreros OID */ 342178828Sdfr if (gss_oid_equal(&oid, &_gss_spnego_mskrb_mechanism_oid_desc)) 343178828Sdfr oidp = &_gss_spnego_krb5_mechanism_oid_desc; 344178828Sdfr else 345178828Sdfr oidp = &oid; 346178825Sdfr 347178825Sdfr 348178828Sdfr ret = gss_indicate_mechs(&junk, &mechs); 349178828Sdfr if (ret) 350178828Sdfr return (ret); 351178825Sdfr 352178828Sdfr for (i = 0; i < mechs->count; i++) 353178828Sdfr if (gss_oid_equal(&mechs->elements[i], oidp)) 354178828Sdfr break; 355178828Sdfr 356178828Sdfr if (i == mechs->count) { 357178828Sdfr gss_release_oid_set(&junk, &mechs); 358178825Sdfr return GSS_S_BAD_MECH; 359178825Sdfr } 360178828Sdfr gss_release_oid_set(&junk, &mechs); 361178825Sdfr 362178828Sdfr ret = gss_duplicate_oid(minor_status, 363178828Sdfr &oid, /* possibly this should be oidp */ 364178828Sdfr mech_p); 365178828Sdfr 366178825Sdfr if (verify_p) { 367178825Sdfr gss_name_t name = GSS_C_NO_NAME; 368178825Sdfr gss_buffer_desc namebuf; 369178825Sdfr char *str = NULL, *host, hostname[MAXHOSTNAMELEN]; 370178825Sdfr 371178825Sdfr host = getenv("GSSAPI_SPNEGO_NAME"); 372178825Sdfr if (host == NULL || issuid()) { 373233294Sstas int rv; 374178825Sdfr if (gethostname(hostname, sizeof(hostname)) != 0) { 375178825Sdfr *minor_status = errno; 376178825Sdfr return GSS_S_FAILURE; 377178825Sdfr } 378233294Sstas rv = asprintf(&str, "host@%s", hostname); 379233294Sstas if (rv < 0 || str == NULL) { 380233294Sstas *minor_status = ENOMEM; 381233294Sstas return GSS_S_FAILURE; 382233294Sstas } 383178825Sdfr host = str; 384178825Sdfr } 385178825Sdfr 386178825Sdfr namebuf.length = strlen(host); 387178825Sdfr namebuf.value = host; 388178825Sdfr 389178825Sdfr ret = gss_import_name(minor_status, &namebuf, 390178825Sdfr GSS_C_NT_HOSTBASED_SERVICE, &name); 391178825Sdfr if (str) 392178825Sdfr free(str); 393178825Sdfr if (ret != GSS_S_COMPLETE) 394178825Sdfr return ret; 395178825Sdfr 396178825Sdfr ret = acceptor_approved(name, *mech_p); 397178825Sdfr gss_release_name(&junk, &name); 398178825Sdfr } 399178825Sdfr 400178825Sdfr return ret; 401178825Sdfr} 402178825Sdfr 403178825Sdfr 404178825Sdfrstatic OM_uint32 405178825Sdfracceptor_complete(OM_uint32 * minor_status, 406178825Sdfr gssspnego_ctx ctx, 407178825Sdfr int *get_mic, 408178825Sdfr gss_buffer_t mech_buf, 409178825Sdfr gss_buffer_t mech_input_token, 410178825Sdfr gss_buffer_t mech_output_token, 411178825Sdfr heim_octet_string *mic, 412178825Sdfr gss_buffer_t output_token) 413178825Sdfr{ 414178825Sdfr OM_uint32 ret; 415178825Sdfr int require_mic, verify_mic; 416178825Sdfr 417178825Sdfr ret = _gss_spnego_require_mechlist_mic(minor_status, ctx, &require_mic); 418178825Sdfr if (ret) 419178825Sdfr return ret; 420233294Sstas 421178825Sdfr ctx->require_mic = require_mic; 422178825Sdfr 423178825Sdfr if (mic != NULL) 424178825Sdfr require_mic = 1; 425233294Sstas 426178825Sdfr if (ctx->open && require_mic) { 427178825Sdfr if (mech_input_token == GSS_C_NO_BUFFER) { /* Even/One */ 428178825Sdfr verify_mic = 1; 429178825Sdfr *get_mic = 0; 430178825Sdfr } else if (mech_output_token != GSS_C_NO_BUFFER && 431178825Sdfr mech_output_token->length == 0) { /* Odd */ 432178825Sdfr *get_mic = verify_mic = 1; 433178825Sdfr } else { /* Even/One */ 434178825Sdfr verify_mic = 0; 435178825Sdfr *get_mic = 1; 436178825Sdfr } 437233294Sstas 438233294Sstas if (verify_mic || *get_mic) { 439178825Sdfr int eret; 440233294Sstas size_t buf_len = 0; 441233294Sstas 442233294Sstas ASN1_MALLOC_ENCODE(MechTypeList, 443178825Sdfr mech_buf->value, mech_buf->length, 444178825Sdfr &ctx->initiator_mech_types, &buf_len, eret); 445178825Sdfr if (eret) { 446178825Sdfr *minor_status = eret; 447178825Sdfr return GSS_S_FAILURE; 448178825Sdfr } 449233294Sstas heim_assert(mech_buf->length == buf_len, "Internal ASN.1 error"); 450233294Sstas UNREACHABLE(return GSS_S_FAILURE); 451178825Sdfr } 452233294Sstas 453178825Sdfr if (verify_mic) { 454178825Sdfr ret = verify_mechlist_mic(minor_status, ctx, mech_buf, mic); 455178825Sdfr if (ret) { 456233294Sstas if (*get_mic) 457178825Sdfr send_reject (minor_status, output_token); 458178825Sdfr return ret; 459178825Sdfr } 460178825Sdfr ctx->verified_mic = 1; 461178825Sdfr } 462233294Sstas } else 463233294Sstas *get_mic = 0; 464178825Sdfr 465178825Sdfr return GSS_S_COMPLETE; 466178825Sdfr} 467178825Sdfr 468178825Sdfr 469233294Sstasstatic OM_uint32 GSSAPI_CALLCONV 470178825Sdfracceptor_start 471178825Sdfr (OM_uint32 * minor_status, 472178825Sdfr gss_ctx_id_t * context_handle, 473178825Sdfr const gss_cred_id_t acceptor_cred_handle, 474178825Sdfr const gss_buffer_t input_token_buffer, 475178825Sdfr const gss_channel_bindings_t input_chan_bindings, 476178825Sdfr gss_name_t * src_name, 477178825Sdfr gss_OID * mech_type, 478178825Sdfr gss_buffer_t output_token, 479178825Sdfr OM_uint32 * ret_flags, 480178825Sdfr OM_uint32 * time_rec, 481178825Sdfr gss_cred_id_t *delegated_cred_handle 482178825Sdfr ) 483178825Sdfr{ 484233294Sstas OM_uint32 ret, junk; 485178825Sdfr NegotiationToken nt; 486178825Sdfr size_t nt_len; 487178825Sdfr NegTokenInit *ni; 488178825Sdfr gss_buffer_desc data; 489178825Sdfr gss_buffer_t mech_input_token = GSS_C_NO_BUFFER; 490178825Sdfr gss_buffer_desc mech_output_token; 491178825Sdfr gss_buffer_desc mech_buf; 492178825Sdfr gss_OID preferred_mech_type = GSS_C_NO_OID; 493178825Sdfr gssspnego_ctx ctx; 494178825Sdfr int get_mic = 0; 495178825Sdfr int first_ok = 0; 496178825Sdfr 497178825Sdfr mech_output_token.value = NULL; 498178825Sdfr mech_output_token.length = 0; 499178825Sdfr mech_buf.value = NULL; 500178825Sdfr 501178825Sdfr if (input_token_buffer->length == 0) 502178825Sdfr return send_supported_mechs (minor_status, output_token); 503233294Sstas 504178825Sdfr ret = _gss_spnego_alloc_sec_context(minor_status, context_handle); 505178825Sdfr if (ret != GSS_S_COMPLETE) 506178825Sdfr return ret; 507178825Sdfr 508178825Sdfr ctx = (gssspnego_ctx)*context_handle; 509178825Sdfr 510178825Sdfr /* 511178825Sdfr * The GSS-API encapsulation is only present on the initial 512178825Sdfr * context token (negTokenInit). 513178825Sdfr */ 514178825Sdfr ret = gss_decapsulate_token (input_token_buffer, 515178825Sdfr GSS_SPNEGO_MECHANISM, 516178825Sdfr &data); 517178825Sdfr if (ret) 518178825Sdfr return ret; 519178825Sdfr 520178825Sdfr ret = decode_NegotiationToken(data.value, data.length, &nt, &nt_len); 521178825Sdfr gss_release_buffer(minor_status, &data); 522178825Sdfr if (ret) { 523178825Sdfr *minor_status = ret; 524178825Sdfr return GSS_S_DEFECTIVE_TOKEN; 525178825Sdfr } 526178825Sdfr if (nt.element != choice_NegotiationToken_negTokenInit) { 527178825Sdfr *minor_status = 0; 528178825Sdfr return GSS_S_DEFECTIVE_TOKEN; 529178825Sdfr } 530178825Sdfr ni = &nt.u.negTokenInit; 531178825Sdfr 532178825Sdfr if (ni->mechTypes.len < 1) { 533178825Sdfr free_NegotiationToken(&nt); 534178825Sdfr *minor_status = 0; 535178825Sdfr return GSS_S_DEFECTIVE_TOKEN; 536178825Sdfr } 537178825Sdfr 538178825Sdfr HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex); 539178825Sdfr 540178825Sdfr ret = copy_MechTypeList(&ni->mechTypes, &ctx->initiator_mech_types); 541178825Sdfr if (ret) { 542178825Sdfr HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); 543178825Sdfr free_NegotiationToken(&nt); 544178825Sdfr *minor_status = ret; 545178825Sdfr return GSS_S_FAILURE; 546178825Sdfr } 547178825Sdfr 548178825Sdfr /* 549178825Sdfr * First we try the opportunistic token if we have support for it, 550178825Sdfr * don't try to verify we have credential for the token, 551233294Sstas * gss_accept_sec_context() will (hopefully) tell us that. 552233294Sstas * If that failes, 553178825Sdfr */ 554178825Sdfr 555178825Sdfr ret = select_mech(minor_status, 556233294Sstas &ni->mechTypes.val[0], 557178825Sdfr 0, 558178825Sdfr &preferred_mech_type); 559178825Sdfr 560178825Sdfr if (ret == 0 && ni->mechToken != NULL) { 561178825Sdfr gss_buffer_desc ibuf; 562178825Sdfr 563178825Sdfr ibuf.length = ni->mechToken->length; 564178825Sdfr ibuf.value = ni->mechToken->data; 565178825Sdfr mech_input_token = &ibuf; 566178825Sdfr 567178825Sdfr if (ctx->mech_src_name != GSS_C_NO_NAME) 568233294Sstas gss_release_name(&junk, &ctx->mech_src_name); 569233294Sstas 570233294Sstas ret = gss_accept_sec_context(minor_status, 571178825Sdfr &ctx->negotiated_ctx_id, 572233294Sstas acceptor_cred_handle, 573178825Sdfr mech_input_token, 574178825Sdfr input_chan_bindings, 575178825Sdfr &ctx->mech_src_name, 576178825Sdfr &ctx->negotiated_mech_type, 577178825Sdfr &mech_output_token, 578178825Sdfr &ctx->mech_flags, 579178825Sdfr &ctx->mech_time_rec, 580233294Sstas delegated_cred_handle); 581233294Sstas 582178825Sdfr if (ret == GSS_S_COMPLETE || ret == GSS_S_CONTINUE_NEEDED) { 583178825Sdfr ctx->preferred_mech_type = preferred_mech_type; 584178825Sdfr if (ret == GSS_S_COMPLETE) 585178825Sdfr ctx->open = 1; 586178825Sdfr 587178825Sdfr ret = acceptor_complete(minor_status, 588178825Sdfr ctx, 589178825Sdfr &get_mic, 590178825Sdfr &mech_buf, 591178825Sdfr mech_input_token, 592178825Sdfr &mech_output_token, 593178825Sdfr ni->mechListMIC, 594178825Sdfr output_token); 595178825Sdfr if (ret != GSS_S_COMPLETE) 596178825Sdfr goto out; 597178825Sdfr 598178825Sdfr first_ok = 1; 599233294Sstas } else { 600233294Sstas gss_mg_collect_error(preferred_mech_type, ret, *minor_status); 601178825Sdfr } 602178825Sdfr } 603178825Sdfr 604178825Sdfr /* 605178825Sdfr * If opportunistic token failed, lets try the other mechs. 606178825Sdfr */ 607178825Sdfr 608233294Sstas if (!first_ok && ni->mechToken != NULL) { 609233294Sstas size_t j; 610178825Sdfr 611233294Sstas preferred_mech_type = GSS_C_NO_OID; 612233294Sstas 613178825Sdfr /* Call glue layer to find first mech we support */ 614233294Sstas for (j = 1; j < ni->mechTypes.len; ++j) { 615178825Sdfr ret = select_mech(minor_status, 616233294Sstas &ni->mechTypes.val[j], 617178825Sdfr 1, 618178825Sdfr &preferred_mech_type); 619178825Sdfr if (ret == 0) 620178825Sdfr break; 621178825Sdfr } 622178825Sdfr if (preferred_mech_type == GSS_C_NO_OID) { 623178825Sdfr HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); 624178825Sdfr free_NegotiationToken(&nt); 625233294Sstas return ret; 626178825Sdfr } 627178825Sdfr 628178825Sdfr ctx->preferred_mech_type = preferred_mech_type; 629178825Sdfr } 630178825Sdfr 631178825Sdfr /* 632178825Sdfr * The initial token always have a response 633178825Sdfr */ 634178825Sdfr 635178825Sdfr ret = send_accept (minor_status, 636178825Sdfr ctx, 637178825Sdfr &mech_output_token, 638178825Sdfr 1, 639178825Sdfr get_mic ? &mech_buf : NULL, 640178825Sdfr output_token); 641178825Sdfr if (ret) 642178825Sdfr goto out; 643233294Sstas 644178825Sdfrout: 645178825Sdfr if (mech_output_token.value != NULL) 646233294Sstas gss_release_buffer(&junk, &mech_output_token); 647178825Sdfr if (mech_buf.value != NULL) { 648178825Sdfr free(mech_buf.value); 649178825Sdfr mech_buf.value = NULL; 650178825Sdfr } 651178825Sdfr free_NegotiationToken(&nt); 652178825Sdfr 653178825Sdfr 654178825Sdfr if (ret == GSS_S_COMPLETE) { 655178825Sdfr if (src_name != NULL && ctx->mech_src_name != NULL) { 656178825Sdfr spnego_name name; 657178825Sdfr 658178825Sdfr name = calloc(1, sizeof(*name)); 659178825Sdfr if (name) { 660178825Sdfr name->mech = ctx->mech_src_name; 661178825Sdfr ctx->mech_src_name = NULL; 662178825Sdfr *src_name = (gss_name_t)name; 663178825Sdfr } 664178825Sdfr } 665178825Sdfr } 666233294Sstas 667178825Sdfr if (mech_type != NULL) 668178825Sdfr *mech_type = ctx->negotiated_mech_type; 669178825Sdfr if (ret_flags != NULL) 670178825Sdfr *ret_flags = ctx->mech_flags; 671178825Sdfr if (time_rec != NULL) 672178825Sdfr *time_rec = ctx->mech_time_rec; 673178825Sdfr 674178825Sdfr if (ret == GSS_S_COMPLETE || ret == GSS_S_CONTINUE_NEEDED) { 675178825Sdfr HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); 676178825Sdfr return ret; 677178825Sdfr } 678178825Sdfr 679233294Sstas _gss_spnego_internal_delete_sec_context(&junk, context_handle, 680178825Sdfr GSS_C_NO_BUFFER); 681233294Sstas 682178825Sdfr return ret; 683178825Sdfr} 684178825Sdfr 685178825Sdfr 686233294Sstasstatic OM_uint32 GSSAPI_CALLCONV 687178825Sdfracceptor_continue 688178825Sdfr (OM_uint32 * minor_status, 689178825Sdfr gss_ctx_id_t * context_handle, 690178825Sdfr const gss_cred_id_t acceptor_cred_handle, 691178825Sdfr const gss_buffer_t input_token_buffer, 692178825Sdfr const gss_channel_bindings_t input_chan_bindings, 693178825Sdfr gss_name_t * src_name, 694178825Sdfr gss_OID * mech_type, 695178825Sdfr gss_buffer_t output_token, 696178825Sdfr OM_uint32 * ret_flags, 697178825Sdfr OM_uint32 * time_rec, 698178825Sdfr gss_cred_id_t *delegated_cred_handle 699178825Sdfr ) 700178825Sdfr{ 701178825Sdfr OM_uint32 ret, ret2, minor; 702178825Sdfr NegotiationToken nt; 703178825Sdfr size_t nt_len; 704178825Sdfr NegTokenResp *na; 705178825Sdfr unsigned int negResult = accept_incomplete; 706178825Sdfr gss_buffer_t mech_input_token = GSS_C_NO_BUFFER; 707178825Sdfr gss_buffer_t mech_output_token = GSS_C_NO_BUFFER; 708178825Sdfr gss_buffer_desc mech_buf; 709178825Sdfr gssspnego_ctx ctx; 710178825Sdfr 711178825Sdfr mech_buf.value = NULL; 712178825Sdfr 713178825Sdfr ctx = (gssspnego_ctx)*context_handle; 714178825Sdfr 715178825Sdfr /* 716178825Sdfr * The GSS-API encapsulation is only present on the initial 717178825Sdfr * context token (negTokenInit). 718178825Sdfr */ 719178825Sdfr 720233294Sstas ret = decode_NegotiationToken(input_token_buffer->value, 721178825Sdfr input_token_buffer->length, 722178825Sdfr &nt, &nt_len); 723178825Sdfr if (ret) { 724178825Sdfr *minor_status = ret; 725178825Sdfr return GSS_S_DEFECTIVE_TOKEN; 726178825Sdfr } 727178825Sdfr if (nt.element != choice_NegotiationToken_negTokenResp) { 728178825Sdfr *minor_status = 0; 729178825Sdfr return GSS_S_DEFECTIVE_TOKEN; 730178825Sdfr } 731178825Sdfr na = &nt.u.negTokenResp; 732178825Sdfr 733178825Sdfr if (na->negResult != NULL) { 734178825Sdfr negResult = *(na->negResult); 735178825Sdfr } 736178825Sdfr 737178825Sdfr HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex); 738178825Sdfr 739178825Sdfr { 740178825Sdfr gss_buffer_desc ibuf, obuf; 741178825Sdfr int require_mic, get_mic = 0; 742178825Sdfr int require_response; 743178825Sdfr heim_octet_string *mic; 744178825Sdfr 745178825Sdfr if (na->responseToken != NULL) { 746178825Sdfr ibuf.length = na->responseToken->length; 747178825Sdfr ibuf.value = na->responseToken->data; 748178825Sdfr mech_input_token = &ibuf; 749178825Sdfr } else { 750178825Sdfr ibuf.value = NULL; 751178825Sdfr ibuf.length = 0; 752178825Sdfr } 753178825Sdfr 754178825Sdfr if (mech_input_token != GSS_C_NO_BUFFER) { 755178825Sdfr 756178825Sdfr if (ctx->mech_src_name != GSS_C_NO_NAME) 757178825Sdfr gss_release_name(&minor, &ctx->mech_src_name); 758178825Sdfr 759178825Sdfr ret = gss_accept_sec_context(&minor, 760178825Sdfr &ctx->negotiated_ctx_id, 761233294Sstas acceptor_cred_handle, 762178825Sdfr mech_input_token, 763178825Sdfr input_chan_bindings, 764178825Sdfr &ctx->mech_src_name, 765178825Sdfr &ctx->negotiated_mech_type, 766178825Sdfr &obuf, 767178825Sdfr &ctx->mech_flags, 768178825Sdfr &ctx->mech_time_rec, 769233294Sstas delegated_cred_handle); 770233294Sstas 771178825Sdfr if (ret == GSS_S_COMPLETE || ret == GSS_S_CONTINUE_NEEDED) { 772178825Sdfr mech_output_token = &obuf; 773178825Sdfr } 774178825Sdfr if (ret != GSS_S_COMPLETE && ret != GSS_S_CONTINUE_NEEDED) { 775178825Sdfr free_NegotiationToken(&nt); 776233294Sstas gss_mg_collect_error(ctx->negotiated_mech_type, ret, minor); 777178825Sdfr send_reject (minor_status, output_token); 778178825Sdfr HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); 779178825Sdfr return ret; 780178825Sdfr } 781178825Sdfr if (ret == GSS_S_COMPLETE) 782178825Sdfr ctx->open = 1; 783178825Sdfr } else 784178825Sdfr ret = GSS_S_COMPLETE; 785178825Sdfr 786233294Sstas ret2 = _gss_spnego_require_mechlist_mic(minor_status, 787178825Sdfr ctx, 788178825Sdfr &require_mic); 789178825Sdfr if (ret2) 790178825Sdfr goto out; 791178825Sdfr 792178825Sdfr ctx->require_mic = require_mic; 793178825Sdfr 794178825Sdfr mic = na->mechListMIC; 795178825Sdfr if (mic != NULL) 796178825Sdfr require_mic = 1; 797178825Sdfr 798178825Sdfr if (ret == GSS_S_COMPLETE) 799178825Sdfr ret = acceptor_complete(minor_status, 800178825Sdfr ctx, 801178825Sdfr &get_mic, 802178825Sdfr &mech_buf, 803178825Sdfr mech_input_token, 804178825Sdfr mech_output_token, 805178825Sdfr na->mechListMIC, 806178825Sdfr output_token); 807178825Sdfr 808178825Sdfr if (ctx->mech_flags & GSS_C_DCE_STYLE) 809178825Sdfr require_response = (negResult != accept_completed); 810178825Sdfr else 811178825Sdfr require_response = 0; 812178825Sdfr 813178825Sdfr /* 814178825Sdfr * Check whether we need to send a result: there should be only 815178825Sdfr * one accept_completed response sent in the entire negotiation 816178825Sdfr */ 817178825Sdfr if ((mech_output_token != GSS_C_NO_BUFFER && 818178825Sdfr mech_output_token->length != 0) 819178825Sdfr || (ctx->open && negResult == accept_incomplete) 820178825Sdfr || require_response 821178825Sdfr || get_mic) { 822178825Sdfr ret2 = send_accept (minor_status, 823178825Sdfr ctx, 824178825Sdfr mech_output_token, 825178825Sdfr 0, 826178825Sdfr get_mic ? &mech_buf : NULL, 827178825Sdfr output_token); 828178825Sdfr if (ret2) 829178825Sdfr goto out; 830178825Sdfr } 831178825Sdfr 832178825Sdfr out: 833178825Sdfr if (ret2 != GSS_S_COMPLETE) 834178825Sdfr ret = ret2; 835178825Sdfr if (mech_output_token != NULL) 836178825Sdfr gss_release_buffer(&minor, mech_output_token); 837178825Sdfr if (mech_buf.value != NULL) 838178825Sdfr free(mech_buf.value); 839178825Sdfr free_NegotiationToken(&nt); 840178825Sdfr } 841178825Sdfr 842178825Sdfr if (ret == GSS_S_COMPLETE) { 843178825Sdfr if (src_name != NULL && ctx->mech_src_name != NULL) { 844178825Sdfr spnego_name name; 845178825Sdfr 846178825Sdfr name = calloc(1, sizeof(*name)); 847178825Sdfr if (name) { 848178825Sdfr name->mech = ctx->mech_src_name; 849178825Sdfr ctx->mech_src_name = NULL; 850178825Sdfr *src_name = (gss_name_t)name; 851178825Sdfr } 852178825Sdfr } 853178825Sdfr } 854178825Sdfr 855178825Sdfr if (mech_type != NULL) 856178825Sdfr *mech_type = ctx->negotiated_mech_type; 857178825Sdfr if (ret_flags != NULL) 858178825Sdfr *ret_flags = ctx->mech_flags; 859178825Sdfr if (time_rec != NULL) 860178825Sdfr *time_rec = ctx->mech_time_rec; 861178825Sdfr 862178825Sdfr if (ret == GSS_S_COMPLETE || ret == GSS_S_CONTINUE_NEEDED) { 863178825Sdfr HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); 864178825Sdfr return ret; 865178825Sdfr } 866178825Sdfr 867178825Sdfr _gss_spnego_internal_delete_sec_context(&minor, context_handle, 868178825Sdfr GSS_C_NO_BUFFER); 869178825Sdfr 870178825Sdfr return ret; 871178825Sdfr} 872178825Sdfr 873233294SstasOM_uint32 GSSAPI_CALLCONV 874178825Sdfr_gss_spnego_accept_sec_context 875178825Sdfr (OM_uint32 * minor_status, 876178825Sdfr gss_ctx_id_t * context_handle, 877178825Sdfr const gss_cred_id_t acceptor_cred_handle, 878178825Sdfr const gss_buffer_t input_token_buffer, 879178825Sdfr const gss_channel_bindings_t input_chan_bindings, 880178825Sdfr gss_name_t * src_name, 881178825Sdfr gss_OID * mech_type, 882178825Sdfr gss_buffer_t output_token, 883178825Sdfr OM_uint32 * ret_flags, 884178825Sdfr OM_uint32 * time_rec, 885178825Sdfr gss_cred_id_t *delegated_cred_handle 886178825Sdfr ) 887178825Sdfr{ 888178825Sdfr _gss_accept_sec_context_t *func; 889178825Sdfr 890178825Sdfr *minor_status = 0; 891178825Sdfr 892178825Sdfr output_token->length = 0; 893178825Sdfr output_token->value = NULL; 894178825Sdfr 895178825Sdfr if (src_name != NULL) 896178825Sdfr *src_name = GSS_C_NO_NAME; 897178825Sdfr if (mech_type != NULL) 898178825Sdfr *mech_type = GSS_C_NO_OID; 899178825Sdfr if (ret_flags != NULL) 900178825Sdfr *ret_flags = 0; 901178825Sdfr if (time_rec != NULL) 902178825Sdfr *time_rec = 0; 903178825Sdfr if (delegated_cred_handle != NULL) 904178825Sdfr *delegated_cred_handle = GSS_C_NO_CREDENTIAL; 905178825Sdfr 906178825Sdfr 907233294Sstas if (*context_handle == GSS_C_NO_CONTEXT) 908178825Sdfr func = acceptor_start; 909178825Sdfr else 910178825Sdfr func = acceptor_continue; 911178825Sdfr 912233294Sstas 913178825Sdfr return (*func)(minor_status, context_handle, acceptor_cred_handle, 914178825Sdfr input_token_buffer, input_chan_bindings, 915178825Sdfr src_name, mech_type, output_token, ret_flags, 916178825Sdfr time_rec, delegated_cred_handle); 917178825Sdfr} 918