static OM_uint32 initiator_approved(gss_name_t target_name, gss_OID mech) { OM_uint32 min_stat, maj_stat; gss_ctx_id_t ctx = GSS_C_NO_CONTEXT; gss_buffer_desc out; maj_stat = gss_init_sec_context(&min_stat, GSS_C_NO_CREDENTIAL, &ctx, target_name, mech, 0, GSS_C_INDEFINITE, GSS_C_NO_CHANNEL_BINDINGS, GSS_C_NO_BUFFER, NULL, &out, NULL, NULL); if (GSS_ERROR(maj_stat)) { gss_mg_collect_error(mech, maj_stat, min_stat); return GSS_S_BAD_MECH; } gss_release_buffer(&min_stat, &out); gss_delete_sec_context(&min_stat, &ctx, NULL); return GSS_S_COMPLETE; }
static OM_uint32 spnego_reply (OM_uint32 * minor_status, const 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 ) { OM_uint32 ret, minor; NegotiationToken resp; gss_OID_desc mech; int require_mic; size_t buf_len; gss_buffer_desc mic_buf, mech_buf; gss_buffer_desc mech_output_token; gssspnego_ctx ctx; *minor_status = 0; ctx = (gssspnego_ctx)*context_handle; output_token->length = 0; output_token->value = NULL; mech_output_token.length = 0; mech_output_token.value = NULL; mech_buf.value = NULL; mech_buf.length = 0; ret = decode_NegotiationToken(input_token->value, input_token->length, &resp, NULL); if (ret) return ret; if (resp.element != choice_NegotiationToken_negTokenResp) { free_NegotiationToken(&resp); *minor_status = 0; return GSS_S_BAD_MECH; } if (resp.u.negTokenResp.negResult == NULL || *(resp.u.negTokenResp.negResult) == reject /* || resp.u.negTokenResp.supportedMech == NULL */ ) { free_NegotiationToken(&resp); return GSS_S_BAD_MECH; } /* * Pick up the mechanism that the acceptor selected, only allow it * to be sent in packet. */ HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex); if (resp.u.negTokenResp.supportedMech) { if (ctx->oidlen) { free_NegotiationToken(&resp); HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); return GSS_S_BAD_MECH; } ret = der_put_oid(ctx->oidbuf + sizeof(ctx->oidbuf) - 1, sizeof(ctx->oidbuf), resp.u.negTokenResp.supportedMech, &ctx->oidlen); /* Avoid recursively embedded SPNEGO */ if (ret || (ctx->oidlen == GSS_SPNEGO_MECHANISM->length && memcmp(ctx->oidbuf + sizeof(ctx->oidbuf) - ctx->oidlen, GSS_SPNEGO_MECHANISM->elements, ctx->oidlen) == 0)) { free_NegotiationToken(&resp); HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); return GSS_S_BAD_MECH; } /* check if the acceptor took our optimistic token */ if (ctx->oidlen != ctx->preferred_mech_type->length || memcmp(ctx->oidbuf + sizeof(ctx->oidbuf) - ctx->oidlen, ctx->preferred_mech_type->elements, ctx->oidlen) != 0) { gss_delete_sec_context(&minor, &ctx->negotiated_ctx_id, GSS_C_NO_BUFFER); ctx->negotiated_ctx_id = GSS_C_NO_CONTEXT; } } else if (ctx->oidlen == 0) { free_NegotiationToken(&resp); HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); return GSS_S_BAD_MECH; } /* if a token (of non zero length), or no context, pass to underlaying mech */ if ((resp.u.negTokenResp.responseToken != NULL && resp.u.negTokenResp.responseToken->length) || ctx->negotiated_ctx_id == GSS_C_NO_CONTEXT) { gss_buffer_desc mech_input_token; if (resp.u.negTokenResp.responseToken) { mech_input_token.length = resp.u.negTokenResp.responseToken->length; mech_input_token.value = resp.u.negTokenResp.responseToken->data; } else { mech_input_token.length = 0; mech_input_token.value = NULL; } mech.length = ctx->oidlen; mech.elements = ctx->oidbuf + sizeof(ctx->oidbuf) - ctx->oidlen; /* Fall through as if the negotiated mechanism was requested explicitly */ ret = gss_init_sec_context(&minor, (cred != NULL) ? cred->negotiated_cred_id : GSS_C_NO_CREDENTIAL, &ctx->negotiated_ctx_id, ctx->target_name, &mech, req_flags, time_req, input_chan_bindings, &mech_input_token, &ctx->negotiated_mech_type, &mech_output_token, &ctx->mech_flags, &ctx->mech_time_rec); if (GSS_ERROR(ret)) { HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); free_NegotiationToken(&resp); gss_mg_collect_error(&mech, ret, minor); *minor_status = minor; return ret; } if (ret == GSS_S_COMPLETE) { ctx->open = 1; } } else if (*(resp.u.negTokenResp.negResult) == accept_completed) { if (ctx->maybe_open) ctx->open = 1; } if (*(resp.u.negTokenResp.negResult) == request_mic) { ctx->require_mic = 1; } if (ctx->open) { /* * Verify the mechListMIC if one was provided or CFX was * used and a non-preferred mechanism was selected */ if (resp.u.negTokenResp.mechListMIC != NULL) { require_mic = 1; } else { ret = _gss_spnego_require_mechlist_mic(minor_status, ctx, &require_mic); if (ret) { HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); free_NegotiationToken(&resp); gss_release_buffer(&minor, &mech_output_token); return ret; } } } else { require_mic = 0; } if (require_mic) { ASN1_MALLOC_ENCODE(MechTypeList, mech_buf.value, mech_buf.length, &ctx->initiator_mech_types, &buf_len, ret); if (ret) { HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); free_NegotiationToken(&resp); gss_release_buffer(&minor, &mech_output_token); *minor_status = ret; return GSS_S_FAILURE; } if (mech_buf.length != buf_len) abort(); if (resp.u.negTokenResp.mechListMIC == NULL) { HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); free(mech_buf.value); free_NegotiationToken(&resp); *minor_status = 0; return GSS_S_DEFECTIVE_TOKEN; } mic_buf.length = resp.u.negTokenResp.mechListMIC->length; mic_buf.value = resp.u.negTokenResp.mechListMIC->data; if (mech_output_token.length == 0) { ret = gss_verify_mic(minor_status, ctx->negotiated_ctx_id, &mech_buf, &mic_buf, NULL); if (ret) { HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); free(mech_buf.value); gss_release_buffer(&minor, &mech_output_token); free_NegotiationToken(&resp); return GSS_S_DEFECTIVE_TOKEN; } ctx->verified_mic = 1; } } ret = spnego_reply_internal(minor_status, ctx, require_mic ? &mech_buf : NULL, &mech_output_token, output_token); if (mech_buf.value != NULL) free(mech_buf.value); free_NegotiationToken(&resp); gss_release_buffer(&minor, &mech_output_token); 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); return ret; }
static OM_uint32 GSSAPI_CALLCONV acceptor_continue (OM_uint32 * minor_status, gss_ctx_id_t * context_handle, const gss_cred_id_t acceptor_cred_handle, const gss_buffer_t input_token_buffer, const gss_channel_bindings_t input_chan_bindings, gss_name_t * src_name, gss_OID * mech_type, gss_buffer_t output_token, OM_uint32 * ret_flags, OM_uint32 * time_rec, gss_cred_id_t *delegated_cred_handle ) { OM_uint32 ret, ret2, minor; NegotiationToken nt; size_t nt_len; NegTokenResp *na; unsigned int negResult = accept_incomplete; gss_buffer_t mech_input_token = GSS_C_NO_BUFFER; gss_buffer_t mech_output_token = GSS_C_NO_BUFFER; gss_buffer_desc mech_buf; gssspnego_ctx ctx; mech_buf.value = NULL; ctx = (gssspnego_ctx)*context_handle; /* * The GSS-API encapsulation is only present on the initial * context token (negTokenInit). */ ret = decode_NegotiationToken(input_token_buffer->value, input_token_buffer->length, &nt, &nt_len); if (ret) { *minor_status = ret; return GSS_S_DEFECTIVE_TOKEN; } if (nt.element != choice_NegotiationToken_negTokenResp) { *minor_status = 0; return GSS_S_DEFECTIVE_TOKEN; } na = &nt.u.negTokenResp; if (na->negResult != NULL) { negResult = *(na->negResult); } HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex); { gss_buffer_desc ibuf, obuf; int require_mic, get_mic = 0; int require_response; heim_octet_string *mic; if (na->responseToken != NULL) { ibuf.length = na->responseToken->length; ibuf.value = na->responseToken->data; mech_input_token = &ibuf; } else { ibuf.value = NULL; ibuf.length = 0; } if (mech_input_token != GSS_C_NO_BUFFER) { if (ctx->mech_src_name != GSS_C_NO_NAME) gss_release_name(&minor, &ctx->mech_src_name); ret = gss_accept_sec_context(&minor, &ctx->negotiated_ctx_id, acceptor_cred_handle, mech_input_token, input_chan_bindings, &ctx->mech_src_name, &ctx->negotiated_mech_type, &obuf, &ctx->mech_flags, &ctx->mech_time_rec, delegated_cred_handle); if (ret == GSS_S_COMPLETE || ret == GSS_S_CONTINUE_NEEDED) { mech_output_token = &obuf; } if (ret != GSS_S_COMPLETE && ret != GSS_S_CONTINUE_NEEDED) { free_NegotiationToken(&nt); gss_mg_collect_error(ctx->negotiated_mech_type, ret, minor); send_reject (minor_status, output_token); HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); return ret; } if (ret == GSS_S_COMPLETE) ctx->open = 1; } else ret = GSS_S_COMPLETE; ret2 = _gss_spnego_require_mechlist_mic(minor_status, ctx, &require_mic); if (ret2) goto out; ctx->require_mic = require_mic; mic = na->mechListMIC; if (mic != NULL) require_mic = 1; if (ret == GSS_S_COMPLETE) ret = acceptor_complete(minor_status, ctx, &get_mic, &mech_buf, mech_input_token, mech_output_token, na->mechListMIC, output_token); if (ctx->mech_flags & GSS_C_DCE_STYLE) require_response = (negResult != accept_completed); else require_response = 0; /* * Check whether we need to send a result: there should be only * one accept_completed response sent in the entire negotiation */ if ((mech_output_token != GSS_C_NO_BUFFER && mech_output_token->length != 0) || (ctx->open && negResult == accept_incomplete) || require_response || get_mic) { ret2 = send_accept (minor_status, ctx, mech_output_token, 0, get_mic ? &mech_buf : NULL, output_token); if (ret2) goto out; } out: if (ret2 != GSS_S_COMPLETE) ret = ret2; if (mech_output_token != NULL) gss_release_buffer(&minor, mech_output_token); if (mech_buf.value != NULL) free(mech_buf.value); free_NegotiationToken(&nt); } if (ret == GSS_S_COMPLETE) { if (src_name != NULL && ctx->mech_src_name != NULL) { spnego_name name; name = calloc(1, sizeof(*name)); if (name) { name->mech = ctx->mech_src_name; ctx->mech_src_name = NULL; *src_name = (gss_name_t)name; } } } if (mech_type != NULL) *mech_type = ctx->negotiated_mech_type; if (ret_flags != NULL) *ret_flags = ctx->mech_flags; if (time_rec != NULL) *time_rec = ctx->mech_time_rec; if (ret == GSS_S_COMPLETE || ret == GSS_S_CONTINUE_NEEDED) { HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); return ret; } _gss_spnego_internal_delete_sec_context(&minor, context_handle, GSS_C_NO_BUFFER); return ret; }
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; }
static OM_uint32 GSSAPI_CALLCONV acceptor_start (OM_uint32 * minor_status, gss_ctx_id_t * context_handle, const gss_cred_id_t acceptor_cred_handle, const gss_buffer_t input_token_buffer, const gss_channel_bindings_t input_chan_bindings, gss_name_t * src_name, gss_OID * mech_type, gss_buffer_t output_token, OM_uint32 * ret_flags, OM_uint32 * time_rec, gss_cred_id_t *delegated_cred_handle ) { OM_uint32 ret, junk; NegotiationToken nt; size_t nt_len; NegTokenInit *ni; int i; gss_buffer_desc data; gss_buffer_t mech_input_token = GSS_C_NO_BUFFER; gss_buffer_desc mech_output_token; gss_buffer_desc mech_buf; gss_OID preferred_mech_type = GSS_C_NO_OID; gssspnego_ctx ctx; int get_mic = 0; int first_ok = 0; mech_output_token.value = NULL; mech_output_token.length = 0; mech_buf.value = NULL; if (input_token_buffer->length == 0) return send_supported_mechs (minor_status, output_token); ret = _gss_spnego_alloc_sec_context(minor_status, context_handle); if (ret != GSS_S_COMPLETE) return ret; ctx = (gssspnego_ctx)*context_handle; /* * The GSS-API encapsulation is only present on the initial * context token (negTokenInit). */ ret = gss_decapsulate_token (input_token_buffer, GSS_SPNEGO_MECHANISM, &data); if (ret) return ret; ret = decode_NegotiationToken(data.value, data.length, &nt, &nt_len); gss_release_buffer(minor_status, &data); if (ret) { *minor_status = ret; return GSS_S_DEFECTIVE_TOKEN; } if (nt.element != choice_NegotiationToken_negTokenInit) { *minor_status = 0; return GSS_S_DEFECTIVE_TOKEN; } ni = &nt.u.negTokenInit; if (ni->mechTypes.len < 1) { free_NegotiationToken(&nt); *minor_status = 0; return GSS_S_DEFECTIVE_TOKEN; } HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex); ret = copy_MechTypeList(&ni->mechTypes, &ctx->initiator_mech_types); if (ret) { HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); free_NegotiationToken(&nt); *minor_status = ret; return GSS_S_FAILURE; } /* * First we try the opportunistic token if we have support for it, * don't try to verify we have credential for the token, * gss_accept_sec_context() will (hopefully) tell us that. * If that failes, */ ret = select_mech(minor_status, &ni->mechTypes.val[0], 0, &preferred_mech_type); if (ret == 0 && ni->mechToken != NULL) { gss_buffer_desc ibuf; ibuf.length = ni->mechToken->length; ibuf.value = ni->mechToken->data; mech_input_token = &ibuf; if (ctx->mech_src_name != GSS_C_NO_NAME) gss_release_name(&junk, &ctx->mech_src_name); ret = gss_accept_sec_context(minor_status, &ctx->negotiated_ctx_id, acceptor_cred_handle, mech_input_token, input_chan_bindings, &ctx->mech_src_name, &ctx->negotiated_mech_type, &mech_output_token, &ctx->mech_flags, &ctx->mech_time_rec, delegated_cred_handle); if (ret == GSS_S_COMPLETE || ret == GSS_S_CONTINUE_NEEDED) { ctx->preferred_mech_type = preferred_mech_type; if (ret == GSS_S_COMPLETE) ctx->open = 1; ret = acceptor_complete(minor_status, ctx, &get_mic, &mech_buf, mech_input_token, &mech_output_token, ni->mechListMIC, output_token); if (ret != GSS_S_COMPLETE) goto out; first_ok = 1; } else { gss_mg_collect_error(preferred_mech_type, ret, *minor_status); } } /* * If opportunistic token failed, lets try the other mechs. */ if (!first_ok && ni->mechToken != NULL) { preferred_mech_type = GSS_C_NO_OID; /* Call glue layer to find first mech we support */ for (i = 1; i < ni->mechTypes.len; ++i) { ret = select_mech(minor_status, &ni->mechTypes.val[i], 1, &preferred_mech_type); if (ret == 0) break; } if (preferred_mech_type == GSS_C_NO_OID) { HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); free_NegotiationToken(&nt); return ret; } ctx->preferred_mech_type = preferred_mech_type; } /* * The initial token always have a response */ ret = send_accept (minor_status, ctx, &mech_output_token, 1, get_mic ? &mech_buf : NULL, output_token); if (ret) goto out; out: if (mech_output_token.value != NULL) gss_release_buffer(&junk, &mech_output_token); if (mech_buf.value != NULL) { free(mech_buf.value); mech_buf.value = NULL; } free_NegotiationToken(&nt); if (ret == GSS_S_COMPLETE) { if (src_name != NULL && ctx->mech_src_name != NULL) { spnego_name name; name = calloc(1, sizeof(*name)); if (name) { name->mech = ctx->mech_src_name; ctx->mech_src_name = NULL; *src_name = (gss_name_t)name; } } } if (mech_type != NULL) *mech_type = ctx->negotiated_mech_type; if (ret_flags != NULL) *ret_flags = ctx->mech_flags; if (time_rec != NULL) *time_rec = ctx->mech_time_rec; if (ret == GSS_S_COMPLETE || ret == GSS_S_CONTINUE_NEEDED) { HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); return ret; } _gss_spnego_internal_delete_sec_context(&junk, context_handle, GSS_C_NO_BUFFER); return ret; }