static OM_uint32 spnego_initial (OM_uint32 * minor_status, gssspnego_cred cred, gss_ctx_id_t * context_handle, const gss_name_t target_name, const gss_OID mech_type, OM_uint32 req_flags, OM_uint32 time_req, const gss_channel_bindings_t input_chan_bindings, const gss_buffer_t input_token, gss_OID * actual_mech_type, gss_buffer_t output_token, OM_uint32 * ret_flags, OM_uint32 * time_rec ) { NegTokenInit ni; int ret; OM_uint32 sub, minor; gss_buffer_desc mech_token; u_char *buf; size_t buf_size, buf_len; gss_buffer_desc data; size_t ni_len; gss_ctx_id_t context; gssspnego_ctx ctx; spnego_name name = (spnego_name)target_name; *minor_status = 0; memset (&ni, 0, sizeof(ni)); *context_handle = GSS_C_NO_CONTEXT; if (target_name == GSS_C_NO_NAME) return GSS_S_BAD_NAME; sub = _gss_spnego_alloc_sec_context(&minor, &context); if (GSS_ERROR(sub)) { *minor_status = minor; return sub; } ctx = (gssspnego_ctx)context; HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex); ctx->local = 1; sub = gss_import_name(&minor, &name->value, &name->type, &ctx->target_name); if (GSS_ERROR(sub)) { *minor_status = minor; _gss_spnego_internal_delete_sec_context(&minor, &context, GSS_C_NO_BUFFER); return sub; } sub = _gss_spnego_indicate_mechtypelist(&minor, ctx->target_name, initiator_approved, 0, cred, &ni.mechTypes, &ctx->preferred_mech_type); if (GSS_ERROR(sub)) { *minor_status = minor; _gss_spnego_internal_delete_sec_context(&minor, &context, GSS_C_NO_BUFFER); return sub; } ni.reqFlags = NULL; /* * If we have a credential handle, use it to select the mechanism * that we will use */ /* generate optimistic token */ sub = gss_init_sec_context(&minor, (cred != NULL) ? cred->negotiated_cred_id : GSS_C_NO_CREDENTIAL, &ctx->negotiated_ctx_id, ctx->target_name, ctx->preferred_mech_type, req_flags, time_req, input_chan_bindings, input_token, &ctx->negotiated_mech_type, &mech_token, &ctx->mech_flags, &ctx->mech_time_rec); if (GSS_ERROR(sub)) { free_NegTokenInit(&ni); *minor_status = minor; gss_mg_collect_error(ctx->preferred_mech_type, sub, minor); _gss_spnego_internal_delete_sec_context(&minor, &context, GSS_C_NO_BUFFER); return sub; } if (sub == GSS_S_COMPLETE) ctx->maybe_open = 1; if (mech_token.length != 0) { ALLOC(ni.mechToken, 1); if (ni.mechToken == NULL) { free_NegTokenInit(&ni); gss_release_buffer(&minor, &mech_token); _gss_spnego_internal_delete_sec_context(&minor, &context, GSS_C_NO_BUFFER); *minor_status = ENOMEM; return GSS_S_FAILURE; } ni.mechToken->length = mech_token.length; ni.mechToken->data = malloc(mech_token.length); if (ni.mechToken->data == NULL && mech_token.length != 0) { free_NegTokenInit(&ni); gss_release_buffer(&minor, &mech_token); *minor_status = ENOMEM; _gss_spnego_internal_delete_sec_context(&minor, &context, GSS_C_NO_BUFFER); return GSS_S_FAILURE; } memcpy(ni.mechToken->data, mech_token.value, mech_token.length); gss_release_buffer(&minor, &mech_token); } else ni.mechToken = NULL; ni.mechListMIC = NULL; ni_len = length_NegTokenInit(&ni); buf_size = 1 + der_length_len(ni_len) + ni_len; buf = malloc(buf_size); if (buf == NULL) { free_NegTokenInit(&ni); *minor_status = ENOMEM; _gss_spnego_internal_delete_sec_context(&minor, &context, GSS_C_NO_BUFFER); return GSS_S_FAILURE; } ret = encode_NegTokenInit(buf + buf_size - 1, ni_len, &ni, &buf_len); if (ret == 0 && ni_len != buf_len) abort(); if (ret == 0) { size_t tmp; ret = der_put_length_and_tag(buf + buf_size - buf_len - 1, buf_size - buf_len, buf_len, ASN1_C_CONTEXT, CONS, 0, &tmp); if (ret == 0 && tmp + buf_len != buf_size) abort(); } if (ret) { *minor_status = ret; free(buf); free_NegTokenInit(&ni); _gss_spnego_internal_delete_sec_context(&minor, &context, GSS_C_NO_BUFFER); return GSS_S_FAILURE; } data.value = buf; data.length = buf_size; ctx->initiator_mech_types.len = ni.mechTypes.len; ctx->initiator_mech_types.val = ni.mechTypes.val; ni.mechTypes.len = 0; ni.mechTypes.val = NULL; free_NegTokenInit(&ni); sub = gss_encapsulate_token(&data, GSS_SPNEGO_MECHANISM, output_token); free (buf); if (sub) { _gss_spnego_internal_delete_sec_context(&minor, &context, GSS_C_NO_BUFFER); return sub; } if (actual_mech_type) *actual_mech_type = ctx->negotiated_mech_type; if (ret_flags) *ret_flags = ctx->mech_flags; if (time_rec) *time_rec = ctx->mech_time_rec; HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); *context_handle = context; return GSS_S_CONTINUE_NEEDED; }
OM_uint32 KRB5_LIB_FUNCTION gss_init_sec_context_spnego( OM_uint32 * minor_status, const gss_cred_id_t initiator_cred_handle, gss_ctx_id_t * context_handle, const gss_name_t target_name, const gss_OID mech_type, OM_uint32 req_flags, OM_uint32 time_req, const gss_channel_bindings_t input_chan_bindings, const gss_buffer_t input_token, gss_OID * actual_mech_type, gss_buffer_t output_token, OM_uint32 * ret_flags, OM_uint32 * time_rec) { NegTokenInit token_init; OM_uint32 major_status, minor_status2; gss_buffer_desc krb5_output_token = GSS_C_EMPTY_BUFFER; unsigned char *buf = NULL; size_t buf_size; size_t len; int ret; memset(&token_init, 0, sizeof(token_init)); ALLOC(token_init.mechTypes); if (token_init.mechTypes == NULL) { *minor_status = ENOMEM; return GSS_S_FAILURE; } ret = add_mech(token_init.mechTypes, GSS_KRB5_MECH); if (ret) { *minor_status = ret; ret = GSS_S_FAILURE; goto end; } #if 0 ALLOC(token_init.reqFlags); if (token_init.reqFlags == NULL) { *minor_status = ENOMEM; ret = GSS_S_FAILURE; goto end; } set_context_flags(req_flags, token_init.reqFlags); #endif major_status = gss_init_sec_context(minor_status, initiator_cred_handle, context_handle, target_name, GSS_KRB5_MECH, req_flags, time_req, input_chan_bindings, input_token, actual_mech_type, &krb5_output_token, ret_flags, time_rec); if (GSS_ERROR(major_status)) { ret = major_status; goto end; } if (krb5_output_token.length > 0) { printf("\nOK COUCOU INITSECCONTEXT L:117\n\n"); ALLOC(token_init.mechToken); if (token_init.mechToken == NULL) { *minor_status = ENOMEM; ret = GSS_S_FAILURE; goto end; } token_init.mechToken->data = krb5_output_token.value; token_init.mechToken->length = krb5_output_token.length; krb5_output_token.length = 0; /* don't free it later */ } printf("\nOK COUCOU INITSECCONTEXT L:128\n\n"); /* The MS implementation of SPNEGO seems to not like the mechListMIC field, * so we omit it (it's optional anyway) */ buf_size = 1024; buf = malloc(buf_size); do { ret = encode_NegTokenInit(buf + buf_size -1, buf_size, &token_init, &len); if (ret == 0) { size_t tmp; ret = der_put_length_and_tag(buf + buf_size - len - 1, buf_size - len, len, KERB_CTXT, CONS, 0, &tmp); if (ret == 0) len += tmp; printf("\nOK COUCOU INITSECCONTEXT L:150\n\n"); } if (ret) { if (ret == ASN1_OVERFLOW) { u_char *tmp; buf_size *= 2; tmp = realloc (buf, buf_size); if (tmp == NULL) { *minor_status = ENOMEM; ret = GSS_S_FAILURE; goto end; } buf = tmp; printf("\nOK COUCOU INITSECCONTEXT L:163\n\n"); } else { *minor_status = ret; printf("\nOK COUCOU INITSECCONTEXT L:167\n\n"); ret = GSS_S_FAILURE; goto end; } } } while (ret == ASN1_OVERFLOW); ret = gssapi_spnego_encapsulate(minor_status, buf + buf_size - len, len, output_token, GSS_SPNEGO_MECH); ret = major_status; end: free_NegTokenInit(&token_init); if (krb5_output_token.length > 0) gss_release_buffer(&minor_status2, &krb5_output_token); if (buf) free(buf); return ret; }