OM_uint32 _gss_spnego_verify_mechtypes_mic(OM_uint32 *minor_status, gssspnego_ctx ctx, heim_octet_string *mic) { gss_buffer_desc mic_buf; OM_uint32 major_status; if (ctx->flags.verified_mic) { /* This doesn't make sense, we've already verified it? */ *minor_status = 0; return GSS_S_DUPLICATE_TOKEN; } mic_buf.length = mic->length; mic_buf.value = mic->data; major_status = gss_verify_mic(minor_status, ctx->negotiated_ctx_id, &ctx->NegTokenInit_mech_types, &mic_buf, NULL); if (major_status == GSS_S_UNAVAILABLE) { _gss_mg_log(10, "mech doesn't support MIC, allowing anyway"); } else if (major_status) { return gss_mg_set_error_string(GSS_SPNEGO_MECHANISM, GSS_S_DEFECTIVE_TOKEN, *minor_status, "SPNEGO peer sent invalid mechListMIC"); } ctx->flags.verified_mic = 1; *minor_status = 0; return GSS_S_COMPLETE; }
OM_uint32 _gss_scram_init_sec_context(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) { OM_uint32 major; heim_scram_data in, out; scram_id_t ctx; int ret; *minor_status = 0; if (ret_flags) *ret_flags = 0; if (time_rec) *time_rec = 0; if (actual_mech_type) *actual_mech_type = GSS_C_NO_OID; ctx = (scram_id_t) *context_handle; if (actual_mech_type) *actual_mech_type = GSS_SCRAM_MECHANISM; if (ret_flags) *ret_flags = 0; if (ctx == NULL) { gss_buffer_desc outraw; if (initiator_cred_handle == GSS_C_NO_CREDENTIAL) return GSS_S_FAILURE; major = _gss_scram_have_cred(minor_status, (char *)initiator_cred_handle, NULL); if (major != GSS_S_COMPLETE) return major; ctx = calloc(1, sizeof(*ctx)); if (ctx == NULL) { *minor_status = ENOMEM; return gss_mg_set_error_string(GSS_SCRAM_MECHANISM, GSS_S_FAILURE, ENOMEM, "out of memory"); } *context_handle = (gss_ctx_id_t) ctx; ctx->client = strdup((char *)initiator_cred_handle); ret = heim_scram_client1(ctx->client, NULL, HEIM_SCRAM_DIGEST_SHA1, &ctx->scram, &out); if (ret) { _gss_scram_delete_sec_context(minor_status, context_handle, NULL); *minor_status = ret; return GSS_S_FAILURE; } outraw.value = out.data; outraw.length = out.length; major = gss_encapsulate_token(&outraw, GSS_SCRAM_MECHANISM, output_token); if (major) { _gss_scram_delete_sec_context(minor_status, context_handle, NULL); *minor_status = ENOMEM; return GSS_S_FAILURE; } ctx->flags = req_flags; _gss_mg_log(1, "scram-isc-client1: %s", ctx->client); ctx->state = CLIENT2; major = GSS_S_CONTINUE_NEEDED; } else if (ctx->state == CLIENT2) { in.data = input_token->value; in.length = input_token->length; ret = heim_scram_client2(&in, &scram_client, ctx, ctx->scram, &out); if (ret) { _gss_scram_delete_sec_context(minor_status, context_handle, NULL); *minor_status = ret; return gss_mg_set_error_string(GSS_SCRAM_MECHANISM, GSS_S_FAILURE, ret, "failed to create scram response"); } output_token->length = out.length; output_token->value = malloc(out.length); memcpy(output_token->value, out.data, out.length); ctx->state = CLIENT3; /* * Here we have session key, pull it out and set prot ready */ major = GSS_S_CONTINUE_NEEDED; } else if (ctx->state == CLIENT3) { in.data = input_token->value; in.length = input_token->length; ret = heim_scram_client3(&in, ctx->scram); if (ret) { _gss_scram_delete_sec_context(minor_status, context_handle, NULL); *minor_status = ret; return gss_mg_set_error_string(GSS_SCRAM_MECHANISM, GSS_S_FAILURE, ret, "failed to verify server response"); } output_token->length = 0; output_token->value = NULL; ctx->status |= STATUS_OPEN; major = GSS_S_COMPLETE; } else { abort(); } if (ret_flags) *ret_flags = ctx->flags; if (time_rec) *time_rec = GSS_C_INDEFINITE; return major; }
OM_uint32 _gssiakerb_acquire_cred(OM_uint32 * minor_status, const gss_name_t desired_name, OM_uint32 time_req, const gss_OID_set desired_mechs, gss_cred_usage_t cred_usage, gss_cred_id_t * output_cred_handle, gss_OID_set * actual_mechs, OM_uint32 * time_rec) { krb5_principal princ = (krb5_principal)desired_name; OM_uint32 major_status, junk; krb5_context context; krb5_error_code ret; gsskrb5_cred handle; krb5_data data; int iakerb = 0; GSSAPI_KRB5_INIT(&context); *minor_status = 0; *output_cred_handle = NULL; if (cred_usage != GSS_C_INITIATE && cred_usage != GSS_C_BOTH) return GSS_S_FAILURE; if (princ == NULL) return GSS_S_FAILURE; handle = calloc(1, sizeof(*handle)); if (handle == NULL) return GSS_S_FAILURE; HEIMDAL_MUTEX_init(&handle->cred_id_mutex); major_status = _acquire_uuid_name(minor_status, context, princ, &iakerb, handle); if (major_status) return major_status; if (!iakerb) return GSS_S_BAD_NAME; if ((ret = krb5_cc_get_config(context, handle->ccache, NULL, "password", &data)) == 0) { ret = asprintf(&handle->password, "%.*s", (int)data.length, (char *)data.data); memset(data.data, 0, data.length); krb5_data_free(&data); if (ret <= 0 || handle->password == NULL) { _gsskrb5_release_cred(&junk, (gss_cred_id_t *)&handle); *minor_status = ENOMEM; return GSS_S_FAILURE; } #ifdef PKINIT } else if ((ret = krb5_cc_get_config(context, handle->ccache, NULL, "certificate-ref", &data)) == 0) { hx509_certs certs; hx509_query *q; ret = hx509_certs_init(context->hx509ctx, "KEYCHAIN:", 0, NULL, &certs); if (ret) { krb5_data_free(&data); hx509_certs_free(&certs); _gsskrb5_release_cred(&junk, (gss_cred_id_t *)&handle); *minor_status = ret; return GSS_S_FAILURE; } ret = hx509_query_alloc(context->hx509ctx, &q); if (ret) { krb5_data_free(&data); hx509_certs_free(&certs); _gsskrb5_release_cred(&junk, (gss_cred_id_t *)&handle); *minor_status = ret; return GSS_S_FAILURE; } hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY); hx509_query_match_option(q, HX509_QUERY_OPTION_KU_DIGITALSIGNATURE); hx509_query_match_persistent(q, &data); ret = _krb5_pk_find_cert(context, 1, certs, q, &handle->cert); krb5_data_free(&data); hx509_certs_free(&certs); hx509_query_free(context->hx509ctx, q); if (ret != 0) { _gss_mg_log(1, "gss-krb5: failed to find certificate ref %d", ret); _gsskrb5_release_cred(&junk, (gss_cred_id_t *)&handle); *minor_status = ret; return GSS_S_FAILURE; } #endif } else if ((ret = krb5_cc_get_config(context, handle->ccache, NULL, "iakerb", &data)) == 0) { handle->cred_flags |= GSS_CF_IAKERB_RESOLVED; krb5_data_free(&data); } else { _gsskrb5_release_cred(&junk, (gss_cred_id_t *)&handle); *minor_status = 0; return GSS_S_FAILURE; } handle->usage = GSS_C_INITIATE; handle->endtime = INT_MAX; *output_cred_handle = (gss_cred_id_t)handle; *minor_status = 0; return GSS_S_COMPLETE; }
static OM_uint32 gsskrb5_acceptor_start(OM_uint32 * minor_status, gsskrb5_ctx ctx, krb5_context context, 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) { krb5_error_code kret; OM_uint32 ret = GSS_S_COMPLETE; krb5_data indata; krb5_flags ap_options; krb5_keytab keytab = NULL; int is_cfx = 0; const gsskrb5_cred acceptor_cred = (gsskrb5_cred)acceptor_cred_handle; krb5_boolean is_hostbased_service = FALSE; /* * We may, or may not, have an escapsulation. */ ret = _gsskrb5_decapsulate (minor_status, input_token_buffer, &indata, "\x01\x00", ctx->mech); if (ret) { /* Assume that there is no OID wrapping. */ indata.length = input_token_buffer->length; indata.data = input_token_buffer->value; } /* * We need to get our keytab */ if (acceptor_cred == NULL) { if (_gsskrb5_keytab != NULL) keytab = _gsskrb5_keytab; } else if (acceptor_cred->keytab != NULL) { keytab = acceptor_cred->keytab; } is_hostbased_service = (acceptor_cred && acceptor_cred->principal && krb5_principal_is_gss_hostbased_service(context, acceptor_cred->principal)); /* * We need to check the ticket and create the AP-REP packet */ { krb5_rd_req_in_ctx in = NULL; krb5_rd_req_out_ctx out = NULL; krb5_principal server = NULL; if (acceptor_cred && !is_hostbased_service) server = acceptor_cred->principal; kret = krb5_rd_req_in_ctx_alloc(context, &in); if (kret == 0) kret = krb5_rd_req_in_set_keytab(context, in, keytab); if (kret) { if (in) krb5_rd_req_in_ctx_free(context, in); *minor_status = kret; return GSS_S_FAILURE; } kret = krb5_rd_req_ctx(context, &ctx->auth_context, &indata, server, in, &out); krb5_rd_req_in_ctx_free(context, in); if (ret && _gss_mg_log_level(5)) { const char *e = krb5_get_error_message(context, ret); char *s = NULL; if (server) (void)krb5_unparse_name(context, server, &s); _gss_mg_log(5, "gss-asc: rd_req (server: %s) failed with: %d: %s", s ? s : "<not specified>", ret, e); krb5_free_error_message(context, e); if (s) krb5_xfree(s); } switch (kret) { case 0: break; case KRB5KRB_AP_ERR_SKEW: case KRB5KRB_AP_ERR_TKT_NYV: /* * No reply in non-MUTUAL mode, but we don't know that its * non-MUTUAL mode yet, thats inside the 8003 checksum, so * lets only send the error token on clock skew, that * limit when send error token for non-MUTUAL. */ return send_error_token(minor_status, context, kret, server, &indata, ctx->mech, output_token); case KRB5KRB_AP_ERR_MODIFIED: case KRB5_KT_NOTFOUND: case KRB5_KT_END: /* * If the error is on the keytab entry missing or bad * decryption, lets assume that the keytab version was * wrong and tell the client that. */ return send_error_token(minor_status, context, KRB5KRB_AP_ERR_MODIFIED, server, NULL, ctx->mech, output_token); default: *minor_status = kret; return GSS_S_FAILURE; } /* * we need to remember some data on the context_handle. */ kret = krb5_rd_req_out_get_ap_req_options(context, out, &ap_options); if (kret == 0) kret = krb5_rd_req_out_get_ticket(context, out, &ctx->ticket); if (kret == 0) kret = krb5_rd_req_out_get_keyblock(context, out, &ctx->service_keyblock); if (kret == 0) { int flags; flags = krb5_rd_req_out_get_flags(context, out); if (flags & KRB5_RD_REQ_OUT_PAC_VALID) ctx->more_flags |= PAC_VALID; } if (kret == 0 && is_hostbased_service) { krb5_principal sp = ctx->ticket->server; if (sp->name.name_string.len < 1 || strcmp(sp->name.name_string.val[0], acceptor_cred->principal->name.name_string.val[0]) != 0) { kret = KRB5KRB_AP_WRONG_PRINC; krb5_set_error_message(context, ret, "Expecting service %s but got %s", acceptor_cred->principal->name.name_string.val[0], sp->name.name_string.val[0]); } } ctx->endtime = ctx->ticket->ticket.endtime; krb5_rd_req_out_ctx_free(context, out); if (kret) { ret = GSS_S_FAILURE; *minor_status = kret; return ret; } } /* * We need to copy the principal names to the context and the * calling layer. */ kret = krb5_copy_principal(context, ctx->ticket->client, &ctx->source); if (kret) { *minor_status = kret; return GSS_S_FAILURE; } kret = krb5_copy_principal(context, ctx->ticket->server, &ctx->target); if (kret) { ret = GSS_S_FAILURE; *minor_status = kret; return ret; } /* * We need to setup some compat stuff, this assumes that * context_handle->target is already set. */ ret = _gss_DES3_get_mic_compat(minor_status, ctx, context); if (ret) return ret; if (src_name != NULL) { kret = krb5_copy_principal (context, ctx->ticket->client, (gsskrb5_name*)src_name); if (kret) { ret = GSS_S_FAILURE; *minor_status = kret; return ret; } } /* * We need to get the flags out of the 8003 checksum. */ { krb5_authenticator authenticator; kret = krb5_auth_con_getauthenticator(context, ctx->auth_context, &authenticator); if(kret) { ret = GSS_S_FAILURE; *minor_status = kret; return ret; } if (authenticator->cksum == NULL) { krb5_free_authenticator(context, &authenticator); *minor_status = 0; return GSS_S_BAD_BINDINGS; } if (authenticator->cksum->cksumtype == CKSUMTYPE_GSSAPI) { krb5_data finished_data; krb5_crypto crypto = NULL; if (ctx->auth_context->remote_subkey) { kret = krb5_crypto_init(context, ctx->auth_context->remote_subkey, 0, &crypto); if (kret) { *minor_status = kret; return GSS_S_FAILURE; } } krb5_data_zero(&finished_data); ret = _gsskrb5_verify_8003_checksum(minor_status, context, crypto, input_chan_bindings, authenticator->cksum, &ctx->flags, &ctx->fwd_data, &finished_data); krb5_free_authenticator(context, &authenticator); if (ret) { krb5_crypto_destroy(context, crypto); return ret; } if (finished_data.length) { GSS_KRB5_FINISHED finished; krb5_data pkt; memset(&finished, 0, sizeof(finished)); if (ctx->messages == NULL) { krb5_crypto_destroy(context, crypto); krb5_data_free(&finished_data); *minor_status = 0; return GSS_S_BAD_SIG; } kret = krb5_storage_to_data(ctx->messages, &pkt); if (kret) { krb5_crypto_destroy(context, crypto); krb5_data_free(&finished_data); *minor_status = kret; return GSS_S_FAILURE; } if (ctx->auth_context->remote_subkey == NULL) { krb5_crypto_destroy(context, crypto); krb5_data_free(&finished_data); krb5_data_free(&pkt); *minor_status = 0; return GSS_S_BAD_SIG; } kret = decode_GSS_KRB5_FINISHED(finished_data.data, finished_data.length, &finished, NULL); krb5_data_free(&finished_data); if (kret) { krb5_crypto_destroy(context, crypto); krb5_data_free(&pkt); *minor_status = kret; return GSS_S_FAILURE; } kret = krb5_verify_checksum(context, crypto, KRB5_KU_FINISHED, pkt.data, pkt.length, &finished.gss_mic); free_GSS_KRB5_FINISHED(&finished); krb5_data_free(&pkt); if (kret) { krb5_crypto_destroy(context, crypto); *minor_status = kret; return GSS_S_FAILURE; } } krb5_crypto_destroy(context, crypto); } else { krb5_crypto crypto; kret = krb5_crypto_init(context, ctx->auth_context->keyblock, 0, &crypto); if(kret) { krb5_free_authenticator(context, &authenticator); ret = GSS_S_FAILURE; *minor_status = kret; return ret; } /* * Windows accepts Samba3's use of a kerberos, rather than * GSSAPI checksum here */ kret = krb5_verify_checksum(context, crypto, KRB5_KU_AP_REQ_AUTH_CKSUM, NULL, 0, authenticator->cksum); krb5_free_authenticator(context, &authenticator); krb5_crypto_destroy(context, crypto); if(kret) { ret = GSS_S_BAD_SIG; *minor_status = kret; return ret; } /* * Samba style get some flags (but not DCE-STYLE), use * ap_options to guess the mutual flag. */ ctx->flags = GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG; if (ap_options & AP_OPTS_MUTUAL_REQUIRED) ctx->flags |= GSS_C_MUTUAL_FLAG; } } if(ctx->flags & GSS_C_MUTUAL_FLAG) { krb5_data outbuf; int use_subkey = 0; _gsskrb5i_is_cfx(context, ctx, 1); is_cfx = (ctx->more_flags & IS_CFX); if (is_cfx || (ap_options & AP_OPTS_USE_SUBKEY)) { use_subkey = 1; } else { krb5_keyblock *rkey; /* * If there is a initiator subkey, copy that to acceptor * subkey to match Windows behavior */ kret = krb5_auth_con_getremotesubkey(context, ctx->auth_context, &rkey); if (kret == 0) { kret = krb5_auth_con_setlocalsubkey(context, ctx->auth_context, rkey); if (kret == 0) use_subkey = 1; krb5_free_keyblock(context, rkey); } } if (use_subkey) { ctx->gk5c.flags |= GK5C_ACCEPTOR_SUBKEY; krb5_auth_con_addflags(context, ctx->auth_context, KRB5_AUTH_CONTEXT_USE_SUBKEY, NULL); } kret = krb5_mk_rep(context, ctx->auth_context, &outbuf); if (kret) { *minor_status = kret; return GSS_S_FAILURE; } if (IS_DCE_STYLE(ctx)) { output_token->length = outbuf.length; output_token->value = outbuf.data; } else { ret = _gsskrb5_encapsulate(minor_status, &outbuf, output_token, "\x02\x00", ctx->mech); krb5_data_free (&outbuf); if (ret) return ret; } } ctx->flags |= GSS_C_TRANS_FLAG; /* Remember the flags */ ctx->endtime = ctx->ticket->ticket.endtime; ctx->more_flags |= OPEN; if (mech_type) *mech_type = ctx->mech; if (time_rec) { ret = _gsskrb5_lifetime_left(minor_status, context, ctx->endtime, time_rec); if (ret) { return ret; } } /* * When GSS_C_DCE_STYLE is in use, we need ask for a AP-REP from * the client. */ if (IS_DCE_STYLE(ctx)) { /* * Return flags to caller, but we haven't processed * delgations yet */ if (ret_flags) *ret_flags = (ctx->flags & ~GSS_C_DELEG_FLAG); ctx->acceptor_state = acceptor_wait_for_dcestyle; return GSS_S_CONTINUE_NEEDED; } ret = gsskrb5_acceptor_ready(minor_status, ctx, context, delegated_cred_handle); if (ret_flags) *ret_flags = ctx->flags; return ret; }