X-Git-Url: http://git.tremily.us/?a=blobdiff_plain;f=src%2Flib%2Fkrb5%2Fkrb%2Fpreauth2.c;h=7c5452790218ae0128732bd4d4db72231a1fc05b;hb=bc096a77ffdab283d77c2e0fc1fdd15b9f77eb41;hp=f2ead9361cdd4f2e6e58635c95e7017d0aafb1f3;hpb=ca3ec7f3fd59baa0d0eedcb61c7009165ea2730c;p=krb5.git diff --git a/src/lib/krb5/krb/preauth2.c b/src/lib/krb5/krb/preauth2.c index f2ead9361..7c5452790 100644 --- a/src/lib/krb5/krb/preauth2.c +++ b/src/lib/krb5/krb/preauth2.c @@ -1,6 +1,6 @@ /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ /* - * Copyright 1995, 2003, 2008 by the Massachusetts Institute of Technology. All + * Copyright 1995, 2003, 2008, 2012 by the Massachusetts Institute of Technology. All * Rights Reserved. * * Export of this software from the United States of America may @@ -31,10 +31,6 @@ */ #include "k5-int.h" -#if APPLE_PKINIT -#include "pkinit_client.h" -#include "pkinit_cert_store.h" -#endif /* APPLE_PKINIT */ #include "osconf.h" #include #include "int-proto.h" @@ -129,6 +125,8 @@ krb5_init_preauth_context(krb5_context kcontext) k5_plugin_register(kcontext, PLUGIN_INTERFACE_CLPREAUTH, "encrypted_timestamp", clpreauth_encrypted_timestamp_initvt); + k5_plugin_register(kcontext, PLUGIN_INTERFACE_CLPREAUTH, "sam2", + clpreauth_sam2_initvt); /* Get all available clpreauth vtables. */ if (k5_plugin_load_all(kcontext, PLUGIN_INTERFACE_CLPREAUTH, &plugins)) @@ -281,6 +279,7 @@ krb5_preauth_request_context_init(krb5_context context) if (context->preauth_context == NULL) return; for (i = 0; i < context->preauth_context->n_modules; i++) { + context->preauth_context->modules[i].use_count = 0; mod = &context->preauth_context->modules[i]; if (mod->client_req_init != NULL) mod->client_req_init(context, mod->moddata, mod->modreq_p); @@ -387,10 +386,58 @@ fast_armor(krb5_context context, krb5_clpreauth_rock rock) return rock->fast_state->armor_key; } +static krb5_error_code +get_as_key(krb5_context context, krb5_clpreauth_rock rock, + krb5_keyblock **keyblock) +{ + krb5_error_code ret; + krb5_data *salt; + + if (rock->as_key->length == 0) { + salt = (*rock->default_salt) ? NULL : rock->salt; + ret = (*rock->gak_fct)(context, rock->client, *rock->etype, + rock->prompter, rock->prompter_data, salt, + rock->s2kparams, rock->as_key, *rock->gak_data); + if (ret) + return ret; + } + *keyblock = rock->as_key; + return 0; +} + +static krb5_error_code +set_as_key(krb5_context context, krb5_clpreauth_rock rock, + const krb5_keyblock *keyblock) +{ + krb5_free_keyblock_contents(context, rock->as_key); + return krb5_copy_keyblock_contents(context, keyblock, rock->as_key); +} + +static krb5_error_code +get_preauth_time(krb5_context context, krb5_clpreauth_rock rock, + krb5_boolean allow_unauth_time, krb5_timestamp *time_out, + krb5_int32 *usec_out) +{ + if (rock->pa_offset_state != NO_OFFSET && + (allow_unauth_time || rock->pa_offset_state == AUTH_OFFSET) && + (context->library_options & KRB5_LIBOPT_SYNC_KDCTIME)) { + /* Use the offset we got from the preauth-required error. */ + return k5_time_with_offset(rock->pa_offset, rock->pa_offset_usec, + time_out, usec_out); + + } else { + /* Use the time offset from the context, or no offset. */ + return krb5_us_timeofday(context, time_out, usec_out); + } +} + static struct krb5_clpreauth_callbacks_st callbacks = { - 1, + 2, get_etype, - fast_armor + fast_armor, + get_as_key, + set_as_key, + get_preauth_time }; /* Tweak the request body, for now adding any enctypes which the module claims @@ -432,12 +479,7 @@ run_preauth_plugins(krb5_context kcontext, krb5_pa_data *in_padata, krb5_prompter_fct prompter, void *prompter_data, - krb5_clpreauth_get_as_key_fn gak_fct, - krb5_data *salt, - krb5_data *s2kparams, - void *gak_data, krb5_clpreauth_rock preauth_rock, - krb5_keyblock *as_key, krb5_pa_data ***out_pa_list, int *out_pa_list_size, int *module_ret, @@ -481,9 +523,7 @@ run_preauth_plugins(krb5_context kcontext, &callbacks, preauth_rock, request, encoded_request_body, encoded_previous_request, in_padata, - prompter, prompter_data, gak_fct, - gak_data, salt, s2kparams, as_key, - &out_pa_data); + prompter, prompter_data, &out_pa_data); TRACE_PREAUTH_PROCESS(kcontext, module->name, module->pa_type, module->flags, ret); /* Make note of the module's flags and status. */ @@ -516,26 +556,103 @@ padata2data(krb5_pa_data p) return d; } +/* Set etype info parameters in rock based on padata. */ static krb5_error_code -pa_salt(krb5_context context, krb5_kdc_req *request, krb5_pa_data *in_padata, - krb5_pa_data **out_padata, krb5_data *salt, krb5_data *s2kparams, - krb5_enctype *etype, krb5_keyblock *as_key, krb5_prompter_fct prompter, - void *prompter_data, krb5_gic_get_as_key_fct gak_fct, void *gak_data) +get_etype_info(krb5_context context, krb5_pa_data **padata, + krb5_kdc_req *request, krb5_clpreauth_rock rock) { - krb5_data tmp; - krb5_error_code retval; + krb5_error_code ret = 0; + krb5_pa_data *pa; + krb5_data d; + krb5_etype_info etype_info = NULL, e; + krb5_etype_info_entry *entry; + krb5_boolean valid_found; + const char *p; + int i; - tmp = padata2data(*in_padata); - krb5_free_data_contents(context, salt); - retval = krb5int_copy_data_contents(context, &tmp, salt); - if (retval) - return retval; + /* Find an etype-info2 or etype-info element in padata. */ + pa = krb5int_find_pa_data(context, padata, KRB5_PADATA_ETYPE_INFO2); + if (pa != NULL) { + d = padata2data(*pa); + ret = decode_krb5_etype_info2(&d, &etype_info); + } else { + pa = krb5int_find_pa_data(context, padata, KRB5_PADATA_ETYPE_INFO2); + if (pa != NULL) { + d = padata2data(*pa); + ret = decode_krb5_etype_info2(&d, &etype_info); + } + } - TRACE_PREAUTH_SALT(context, salt, in_padata->pa_type); - if (in_padata->pa_type == KRB5_PADATA_AFS3_SALT) - salt->length = SALT_TYPE_AFS_LENGTH; + if (etype_info != NULL) { + /* Search entries in order of the requests's enctype preference. */ + entry = NULL; + valid_found = FALSE; + for (i = 0; i < request->nktypes && entry == NULL; i++) { + for (e = etype_info; *e != NULL && entry == NULL; e++) { + if ((*e)->etype == request->ktype[i]) + entry = *e; + if (krb5_c_valid_enctype((*e)->etype)) + valid_found = TRUE; + } + } + if (entry == NULL) { + ret = (valid_found) ? KRB5_CONFIG_ETYPE_NOSUPP : + KRB5_PROG_ETYPE_NOSUPP; + goto cleanup; + } - return(0); + /* Set rock fields based on the entry we selected. */ + *rock->etype = entry->etype; + krb5_free_data_contents(context, rock->salt); + if (entry->length != KRB5_ETYPE_NO_SALT) { + *rock->salt = make_data(entry->salt, entry->length); + entry->salt = NULL; + *rock->default_salt = FALSE; + } else { + *rock->salt = empty_data(); + *rock->default_salt = TRUE; + } + krb5_free_data_contents(context, rock->s2kparams); + *rock->s2kparams = entry->s2kparams; + entry->s2kparams = empty_data(); + TRACE_PREAUTH_ETYPE_INFO(context, *rock->etype, rock->salt, + rock->s2kparams); + } else { + /* Look for a pw-salt or afs3-salt element. */ + pa = krb5int_find_pa_data(context, padata, KRB5_PADATA_PW_SALT); + if (pa == NULL) + pa = krb5int_find_pa_data(context, padata, KRB5_PADATA_AFS3_SALT); + if (pa != NULL) { + /* Set rock->salt based on the element we found. */ + krb5_free_data_contents(context, rock->salt); + d = padata2data(*pa); + ret = krb5int_copy_data_contents(context, &d, rock->salt); + if (ret) + goto cleanup; + if (pa->pa_type == KRB5_PADATA_AFS3_SALT) { + /* Work around a (possible) old Heimdal KDC foible. */ + p = memchr(rock->salt->data, '@', rock->salt->length); + if (p != NULL) + rock->salt->length = p - rock->salt->data; + /* Tolerate extra null in MIT KDC afs3-salt value. */ + if (rock->salt->length > 0 && + rock->salt->data[rock->salt->length - 1] == '\0') + rock->salt->length--; + /* Set an s2kparams value to indicate AFS string-to-key. */ + krb5_free_data_contents(context, rock->s2kparams); + ret = alloc_data(rock->s2kparams, 1); + if (ret) + goto cleanup; + rock->s2kparams->data[0] = '\1'; + } + *rock->default_salt = FALSE; + TRACE_PREAUTH_SALT(context, rock->salt, pa->pa_type); + } + } + +cleanup: + krb5_free_etype_info(context, etype_info); + return ret; } static krb5_error_code @@ -564,686 +681,6 @@ pa_fx_cookie(krb5_context context, krb5_kdc_req *request, return 0; } -#if APPLE_PKINIT -/* - * PKINIT. One function to generate AS-REQ, one to parse AS-REP - */ -#define PKINIT_DEBUG 0 -#if PKINIT_DEBUG -#define kdcPkinitDebug(args...) printf(args) -#else -#define kdcPkinitDebug(args...) -#endif - -static krb5_error_code -pa_pkinit_gen_req(krb5_context context, - krb5_kdc_req *request, - krb5_pa_data *in_padata, - krb5_pa_data **out_padata, - krb5_data *salt, - krb5_data *s2kparams, - krb5_enctype *etype, - krb5_keyblock *as_key, - krb5_prompter_fct prompter, - void *prompter_data, - krb5_gic_get_as_key_fct gak_fct, - void *gak_data) -{ - krb5_error_code krtn; - krb5_data out_data = {0, 0, NULL}; - krb5_timestamp kctime = 0; - krb5_int32 cusec = 0; - krb5_ui_4 nonce = 0; - krb5_checksum cksum; - krb5_pkinit_signing_cert_t client_cert; - krb5_data *der_req = NULL; - char *client_principal = NULL; - char *server_principal = NULL; - unsigned char nonce_bytes[4]; - krb5_data nonce_data = {0, 4, (char *)nonce_bytes}; - int dex; - - /* - * Trusted CA list and specific KC cert optionally obtained via - * krb5_pkinit_get_server_certs(). All are DER-encoded certs. - */ - krb5_data *trusted_CAs = NULL; - krb5_ui_4 num_trusted_CAs; - krb5_data kdc_cert = {0}; - - kdcPkinitDebug("pa_pkinit_gen_req\n"); - - /* If we don't have a client cert, we're done */ - if(request->client == NULL) { - kdcPkinitDebug("No request->client; aborting PKINIT\n"); - return KRB5KDC_ERR_PREAUTH_FAILED; - } - krtn = krb5_unparse_name(context, request->client, &client_principal); - if(krtn) { - return krtn; - } - krtn = krb5_pkinit_get_client_cert(client_principal, &client_cert); - free(client_principal); - if(krtn) { - kdcPkinitDebug("No client cert; aborting PKINIT\n"); - return krtn; - } - - /* optional platform-dependent CA list and KDC cert */ - krtn = krb5_unparse_name(context, request->server, &server_principal); - if(krtn) { - goto cleanup; - } - krtn = krb5_pkinit_get_server_certs(client_principal, server_principal, - &trusted_CAs, &num_trusted_CAs, &kdc_cert); - if(krtn) { - goto cleanup; - } - - /* checksum of the encoded KDC-REQ-BODY */ - krtn = encode_krb5_kdc_req_body(request, &der_req); - if(krtn) { - kdcPkinitDebug("encode_krb5_kdc_req_body returned %d\n", (int)krtn); - goto cleanup; - } - krtn = krb5_c_make_checksum(context, CKSUMTYPE_NIST_SHA, NULL, 0, der_req, &cksum); - if(krtn) { - goto cleanup; - } - - krtn = krb5_us_timeofday(context, &kctime, &cusec); - if(krtn) { - goto cleanup; - } - - /* cook up a random 4-byte nonce */ - krtn = krb5_c_random_make_octets(context, &nonce_data); - if(krtn) { - goto cleanup; - } - for(dex=0; dex<4; dex++) { - nonce <<= 8; - nonce |= nonce_bytes[dex]; - } - - krtn = krb5int_pkinit_as_req_create(context, - kctime, cusec, nonce, &cksum, - client_cert, - trusted_CAs, num_trusted_CAs, - (kdc_cert.data ? &kdc_cert : NULL), - &out_data); - if(krtn) { - kdcPkinitDebug("error %d on pkinit_as_req_create; aborting PKINIT\n", (int)krtn); - goto cleanup; - } - *out_padata = (krb5_pa_data *)malloc(sizeof(krb5_pa_data)); - if(*out_padata == NULL) { - krtn = ENOMEM; - free(out_data.data); - goto cleanup; - } - (*out_padata)->magic = KV5M_PA_DATA; - (*out_padata)->pa_type = KRB5_PADATA_PK_AS_REQ; - (*out_padata)->length = out_data.length; - (*out_padata)->contents = (krb5_octet *)out_data.data; - krtn = 0; -cleanup: - if(client_cert) { - krb5_pkinit_release_cert(client_cert); - } - if(cksum.contents) { - free(cksum.contents); - } - if (der_req) { - krb5_free_data(context, der_req); - } - if(server_principal) { - free(server_principal); - } - /* free data mallocd by krb5_pkinit_get_server_certs() */ - if(trusted_CAs) { - unsigned udex; - for(udex=0; udexrealm.length <= sizeof(lkdcprefix) || - 0 != memcmp(lkdcprefix, client->realm.data, sizeof(lkdcprefix)-1)) - return match; - realm_hash = &client->realm.data[sizeof(lkdcprefix)-1]; - realm_hash_len = client->realm.length - sizeof(lkdcprefix) + 1; - kdcPkinitDebug("checking realm versus certificate hash\n"); - if (NULL != (cert_hash = krb5_pkinit_cert_hash_str(signer_cert))) { - kdcPkinitDebug("hash = %s\n", cert_hash); - cert_hash_len = strlen(cert_hash); - if (cert_hash_len == realm_hash_len && - 0 == memcmp(cert_hash, realm_hash, cert_hash_len)) - match = TRUE; - free(cert_hash); - } - kdcPkinitDebug("result: %s\n", match ? "matches" : "does not match"); - return match; -} - -static krb5_error_code -pa_pkinit_parse_rep(krb5_context context, - krb5_kdc_req *request, - krb5_pa_data *in_padata, - krb5_pa_data **out_padata, - krb5_data *salt, - krb5_data *s2kparams, - krb5_enctype *etype, - krb5_keyblock *as_key, - krb5_prompter_fct prompter, - void *prompter_data, - krb5_gic_get_as_key_fct gak_fct, - void *gak_data) -{ - krb5int_cert_sig_status sig_status = (krb5int_cert_sig_status)-999; - krb5_error_code krtn; - krb5_data asRep; - krb5_keyblock local_key = {0}; - krb5_pkinit_signing_cert_t client_cert; - char *princ_name = NULL; - krb5_checksum as_req_checksum_rcd = {0}; /* received checksum */ - krb5_checksum as_req_checksum_gen = {0}; /* calculated checksum */ - krb5_data *encoded_as_req = NULL; - krb5_data signer_cert = {0}; - - *out_padata = NULL; - kdcPkinitDebug("pa_pkinit_parse_rep\n"); - if((in_padata == NULL) || (in_padata->length== 0)) { - kdcPkinitDebug("pa_pkinit_parse_rep: no in_padata\n"); - return KRB5KDC_ERR_PREAUTH_FAILED; - } - - /* If we don't have a client cert, we're done */ - if(request->client == NULL) { - kdcPkinitDebug("No request->client; aborting PKINIT\n"); - return KRB5KDC_ERR_PREAUTH_FAILED; - } - krtn = krb5_unparse_name(context, request->client, &princ_name); - if(krtn) { - return krtn; - } - krtn = krb5_pkinit_get_client_cert(princ_name, &client_cert); - free(princ_name); - if(krtn) { - kdcPkinitDebug("No client cert; aborting PKINIT\n"); - return krtn; - } - - memset(&local_key, 0, sizeof(local_key)); - asRep.data = (char *)in_padata->contents; - asRep.length = in_padata->length; - krtn = krb5int_pkinit_as_rep_parse(context, &asRep, client_cert, - &local_key, &as_req_checksum_rcd, &sig_status, - &signer_cert, NULL, NULL); - if(krtn) { - kdcPkinitDebug("pkinit_as_rep_parse returned %d\n", (int)krtn); - return krtn; - } - switch(sig_status) { - case pki_cs_good: - break; - case pki_cs_unknown_root: - if (local_kdc_cert_match(context, &signer_cert, request->client)) - break; - /* FALLTHROUGH */ - default: - kdcPkinitDebug("pa_pkinit_parse_rep: bad cert/sig status %d\n", - (int)sig_status); - krtn = KRB5KDC_ERR_PREAUTH_FAILED; - goto error_out; - } - - /* calculate checksum of incoming AS-REQ using the decryption key - * we just got from the ReplyKeyPack */ - krtn = encode_krb5_as_req(request, &encoded_as_req); - if(krtn) { - goto error_out; - } - krtn = krb5_c_make_checksum(context, context->kdc_req_sumtype, - &local_key, KRB5_KEYUSAGE_TGS_REQ_AUTH_CKSUM, - encoded_as_req, &as_req_checksum_gen); - if(krtn) { - goto error_out; - } - if((as_req_checksum_gen.length != as_req_checksum_rcd.length) || - memcmp(as_req_checksum_gen.contents, - as_req_checksum_rcd.contents, - as_req_checksum_gen.length)) { - kdcPkinitDebug("pa_pkinit_parse_rep: checksum miscompare\n"); - krtn = KRB5KDC_ERR_PREAUTH_FAILED; - goto error_out; - } - - /* We have the key; transfer to caller */ - if (as_key->length) { - krb5_free_keyblock_contents(context, as_key); - } - *as_key = local_key; - -#if PKINIT_DEBUG - fprintf(stderr, "pa_pkinit_parse_rep: SUCCESS\n"); - fprintf(stderr, "enctype %d keylen %d keydata %02x %02x %02x %02x...\n", - (int)as_key->enctype, (int)as_key->length, - as_key->contents[0], as_key->contents[1], - as_key->contents[2], as_key->contents[3]); -#endif - - krtn = 0; - -error_out: - if (signer_cert.data) { - free(signer_cert.data); - } - if(as_req_checksum_rcd.contents) { - free(as_req_checksum_rcd.contents); - } - if(as_req_checksum_gen.contents) { - free(as_req_checksum_gen.contents); - } - if(encoded_as_req) { - krb5_free_data(context, encoded_as_req); - } - if(krtn && (local_key.contents != NULL)) { - krb5_free_keyblock_contents(context, &local_key); - } - return krtn; -} -#endif /* APPLE_PKINIT */ - -/* this macro expands to the int,ptr necessary for "%.*s" in an sprintf */ - -#define SAMDATA(kdata, str, maxsize) \ - (int)((kdata.length)? \ - ((((kdata.length)<=(maxsize))?(kdata.length):strlen(str))): \ - strlen(str)), \ - (kdata.length)? \ - ((((kdata.length)<=(maxsize))?(kdata.data):(str))):(str) -static char * -sam_challenge_banner(krb5_int32 sam_type) -{ - char *label; - - switch (sam_type) { - case PA_SAM_TYPE_ENIGMA: /* Enigma Logic */ - label = _("Challenge for Enigma Logic mechanism"); - break; - case PA_SAM_TYPE_DIGI_PATH: /* Digital Pathways */ - case PA_SAM_TYPE_DIGI_PATH_HEX: /* Digital Pathways */ - label = _("Challenge for Digital Pathways mechanism"); - break; - case PA_SAM_TYPE_ACTIVCARD_DEC: /* Digital Pathways */ - case PA_SAM_TYPE_ACTIVCARD_HEX: /* Digital Pathways */ - label = _("Challenge for Activcard mechanism"); - break; - case PA_SAM_TYPE_SKEY_K0: /* S/key where KDC has key 0 */ - label = _("Challenge for Enhanced S/Key mechanism"); - break; - case PA_SAM_TYPE_SKEY: /* Traditional S/Key */ - label = _("Challenge for Traditional S/Key mechanism"); - break; - case PA_SAM_TYPE_SECURID: /* Security Dynamics */ - label = _("Challenge for Security Dynamics mechanism"); - break; - case PA_SAM_TYPE_SECURID_PREDICT: /* predictive Security Dynamics */ - label = _("Challenge for Security Dynamics mechanism"); - break; - default: - label = _("Challenge from authentication server"); - break; - } - - return(label); -} - -static krb5_error_code -pa_sam_2(krb5_context context, krb5_kdc_req *request, krb5_pa_data *in_padata, - krb5_pa_data **out_padata, krb5_data *salt, krb5_data *s2kparams, - krb5_enctype *etype, krb5_keyblock *as_key, - krb5_prompter_fct prompter, void *prompter_data, - krb5_gic_get_as_key_fct gak_fct, void *gak_data) -{ - krb5_error_code retval; - krb5_sam_challenge_2 *sc2 = NULL; - krb5_sam_challenge_2_body *sc2b = NULL; - krb5_data tmp_data; - krb5_data response_data; - char name[100], banner[100], prompt[100], response[100]; - krb5_prompt kprompt; - krb5_prompt_type prompt_type; - krb5_data defsalt; - krb5_checksum **cksum; - krb5_data *scratch = NULL; - krb5_boolean valid_cksum = 0; - krb5_enc_sam_response_enc_2 enc_sam_response_enc_2; - krb5_sam_response_2 sr2; - size_t ciph_len; - krb5_pa_data *sam_padata; - - if (prompter == NULL) - return KRB5_LIBOS_CANTREADPWD; - - tmp_data.length = in_padata->length; - tmp_data.data = (char *)in_padata->contents; - - if ((retval = decode_krb5_sam_challenge_2(&tmp_data, &sc2))) - return(retval); - - retval = decode_krb5_sam_challenge_2_body(&sc2->sam_challenge_2_body, &sc2b); - - if (retval) { - krb5_free_sam_challenge_2(context, sc2); - return(retval); - } - - if (!sc2->sam_cksum || ! *sc2->sam_cksum) { - krb5_free_sam_challenge_2(context, sc2); - krb5_free_sam_challenge_2_body(context, sc2b); - return(KRB5_SAM_NO_CHECKSUM); - } - - if (sc2b->sam_flags & KRB5_SAM_MUST_PK_ENCRYPT_SAD) { - krb5_free_sam_challenge_2(context, sc2); - krb5_free_sam_challenge_2_body(context, sc2b); - return(KRB5_SAM_UNSUPPORTED); - } - - if (!krb5_c_valid_enctype(sc2b->sam_etype)) { - krb5_free_sam_challenge_2(context, sc2); - krb5_free_sam_challenge_2_body(context, sc2b); - return(KRB5_SAM_INVALID_ETYPE); - } - - /* All of the above error checks are KDC-specific, that is, they */ - /* assume a failure in the KDC reply. By returning anything other */ - /* than KRB5_KDC_UNREACH, KRB5_PREAUTH_FAILED, */ - /* KRB5_LIBOS_PWDINTR, or KRB5_REALM_CANT_RESOLVE, the client will */ - /* most likely go on to try the AS_REQ against master KDC */ - - if (!(sc2b->sam_flags & KRB5_SAM_USE_SAD_AS_KEY)) { - /* We will need the password to obtain the key used for */ - /* the checksum, and encryption of the sam_response. */ - /* Go ahead and get it now, preserving the ordering of */ - /* prompts for the user. */ - - retval = (gak_fct)(context, request->client, - sc2b->sam_etype, prompter, - prompter_data, salt, s2kparams, as_key, gak_data); - if (retval) { - krb5_free_sam_challenge_2(context, sc2); - krb5_free_sam_challenge_2_body(context, sc2b); - return(retval); - } - } - - snprintf(name, sizeof(name), "%.*s", - SAMDATA(sc2b->sam_type_name, _("SAM Authentication"), - sizeof(name) - 1)); - - snprintf(banner, sizeof(banner), "%.*s", - SAMDATA(sc2b->sam_challenge_label, - sam_challenge_banner(sc2b->sam_type), - sizeof(banner)-1)); - - snprintf(prompt, sizeof(prompt), "%s%.*s%s%.*s", - sc2b->sam_challenge.length?"Challenge is [":"", - SAMDATA(sc2b->sam_challenge, "", 20), - sc2b->sam_challenge.length?"], ":"", - SAMDATA(sc2b->sam_response_prompt, "passcode", 55)); - - response_data.data = response; - response_data.length = sizeof(response); - kprompt.prompt = prompt; - kprompt.hidden = 1; - kprompt.reply = &response_data; - - prompt_type = KRB5_PROMPT_TYPE_PREAUTH; - krb5int_set_prompt_types(context, &prompt_type); - - if ((retval = ((*prompter)(context, prompter_data, name, - banner, 1, &kprompt)))) { - krb5_free_sam_challenge_2(context, sc2); - krb5_free_sam_challenge_2_body(context, sc2b); - krb5int_set_prompt_types(context, 0); - return(retval); - } - - krb5int_set_prompt_types(context, (krb5_prompt_type *)NULL); - - /* Generate salt used by string_to_key() */ - if (((int) salt->length == -1) && (salt->data == NULL)) { - if ((retval = - krb5_principal2salt(context, request->client, &defsalt))) { - krb5_free_sam_challenge_2(context, sc2); - krb5_free_sam_challenge_2_body(context, sc2b); - return(retval); - } - salt = &defsalt; - } else { - defsalt.length = 0; - } - - /* Get encryption key to be used for checksum and sam_response */ - if (!(sc2b->sam_flags & KRB5_SAM_USE_SAD_AS_KEY)) { - /* as_key = string_to_key(password) */ - - if (as_key->length) { - krb5_free_keyblock_contents(context, as_key); - as_key->length = 0; - } - - /* generate a key using the supplied password */ - retval = krb5_c_string_to_key(context, sc2b->sam_etype, - (krb5_data *)gak_data, salt, as_key); - - if (retval) { - krb5_free_sam_challenge_2(context, sc2); - krb5_free_sam_challenge_2_body(context, sc2b); - if (defsalt.length) free(defsalt.data); - return(retval); - } - - if (!(sc2b->sam_flags & KRB5_SAM_SEND_ENCRYPTED_SAD)) { - /* as_key = combine_key (as_key, string_to_key(SAD)) */ - krb5_keyblock tmp_kb; - - retval = krb5_c_string_to_key(context, sc2b->sam_etype, - &response_data, salt, &tmp_kb); - - if (retval) { - krb5_free_sam_challenge_2(context, sc2); - krb5_free_sam_challenge_2_body(context, sc2b); - if (defsalt.length) free(defsalt.data); - return(retval); - } - - /* This should be a call to the crypto library some day */ - /* key types should already match the sam_etype */ - retval = krb5int_c_combine_keys(context, as_key, &tmp_kb, as_key); - - if (retval) { - krb5_free_sam_challenge_2(context, sc2); - krb5_free_sam_challenge_2_body(context, sc2b); - if (defsalt.length) free(defsalt.data); - return(retval); - } - krb5_free_keyblock_contents(context, &tmp_kb); - } - - if (defsalt.length) - free(defsalt.data); - - } else { - /* as_key = string_to_key(SAD) */ - - if (as_key->length) { - krb5_free_keyblock_contents(context, as_key); - as_key->length = 0; - } - - /* generate a key using the supplied password */ - retval = krb5_c_string_to_key(context, sc2b->sam_etype, - &response_data, salt, as_key); - - if (defsalt.length) - free(defsalt.data); - - if (retval) { - krb5_free_sam_challenge_2(context, sc2); - krb5_free_sam_challenge_2_body(context, sc2b); - return(retval); - } - } - - /* Now we have a key, verify the checksum on the sam_challenge */ - - cksum = sc2->sam_cksum; - - for (; *cksum; cksum++) { - if (!krb5_c_is_keyed_cksum((*cksum)->checksum_type)) - continue; - /* Check this cksum */ - retval = krb5_c_verify_checksum(context, as_key, - KRB5_KEYUSAGE_PA_SAM_CHALLENGE_CKSUM, - &sc2->sam_challenge_2_body, - *cksum, &valid_cksum); - if (retval) { - krb5_free_data(context, scratch); - krb5_free_sam_challenge_2(context, sc2); - krb5_free_sam_challenge_2_body(context, sc2b); - return(retval); - } - if (valid_cksum) - break; - } - - if (!valid_cksum) { - krb5_free_sam_challenge_2(context, sc2); - krb5_free_sam_challenge_2_body(context, sc2b); - /* - * Note: We return AP_ERR_BAD_INTEGRITY so upper-level applications - * can interpret that as "password incorrect", which is probably - * the best error we can return in this situation. - */ - return(KRB5KRB_AP_ERR_BAD_INTEGRITY); - } - - /* fill in enc_sam_response_enc_2 */ - enc_sam_response_enc_2.magic = KV5M_ENC_SAM_RESPONSE_ENC_2; - enc_sam_response_enc_2.sam_nonce = sc2b->sam_nonce; - if (sc2b->sam_flags & KRB5_SAM_SEND_ENCRYPTED_SAD) { - enc_sam_response_enc_2.sam_sad = response_data; - } else { - enc_sam_response_enc_2.sam_sad.data = NULL; - enc_sam_response_enc_2.sam_sad.length = 0; - } - - /* encode and encrypt enc_sam_response_enc_2 with as_key */ - retval = encode_krb5_enc_sam_response_enc_2(&enc_sam_response_enc_2, - &scratch); - if (retval) { - krb5_free_sam_challenge_2(context, sc2); - krb5_free_sam_challenge_2_body(context, sc2b); - return(retval); - } - - /* Fill in sam_response_2 */ - memset(&sr2, 0, sizeof(sr2)); - sr2.sam_type = sc2b->sam_type; - sr2.sam_flags = sc2b->sam_flags; - sr2.sam_track_id = sc2b->sam_track_id; - sr2.sam_nonce = sc2b->sam_nonce; - - /* Now take care of sr2.sam_enc_nonce_or_sad by encrypting encoded */ - /* enc_sam_response_enc_2 from above */ - - retval = krb5_c_encrypt_length(context, as_key->enctype, scratch->length, - &ciph_len); - if (retval) { - krb5_free_sam_challenge_2(context, sc2); - krb5_free_sam_challenge_2_body(context, sc2b); - krb5_free_data(context, scratch); - return(retval); - } - sr2.sam_enc_nonce_or_sad.ciphertext.length = ciph_len; - - sr2.sam_enc_nonce_or_sad.ciphertext.data = - (char *)malloc(sr2.sam_enc_nonce_or_sad.ciphertext.length); - - if (!sr2.sam_enc_nonce_or_sad.ciphertext.data) { - krb5_free_sam_challenge_2(context, sc2); - krb5_free_sam_challenge_2_body(context, sc2b); - krb5_free_data(context, scratch); - return(ENOMEM); - } - - retval = krb5_c_encrypt(context, as_key, KRB5_KEYUSAGE_PA_SAM_RESPONSE, - NULL, scratch, &sr2.sam_enc_nonce_or_sad); - if (retval) { - krb5_free_sam_challenge_2(context, sc2); - krb5_free_sam_challenge_2_body(context, sc2b); - krb5_free_data(context, scratch); - krb5_free_data_contents(context, &sr2.sam_enc_nonce_or_sad.ciphertext); - return(retval); - } - krb5_free_data(context, scratch); - scratch = NULL; - - /* Encode the sam_response_2 */ - retval = encode_krb5_sam_response_2(&sr2, &scratch); - krb5_free_sam_challenge_2(context, sc2); - krb5_free_sam_challenge_2_body(context, sc2b); - krb5_free_data_contents(context, &sr2.sam_enc_nonce_or_sad.ciphertext); - - if (retval) { - return (retval); - } - - /* Almost there, just need to make padata ! */ - sam_padata = malloc(sizeof(krb5_pa_data)); - if (sam_padata == NULL) { - krb5_free_data(context, scratch); - return(ENOMEM); - } - - sam_padata->magic = KV5M_PA_DATA; - sam_padata->pa_type = KRB5_PADATA_SAM_RESPONSE_2; - sam_padata->length = scratch->length; - sam_padata->contents = (krb5_octet *) scratch->data; - free(scratch); - - *out_padata = sam_padata; - - return(0); -} - static krb5_error_code pa_s4u_x509_user(krb5_context context, krb5_kdc_req *request, krb5_pa_data *in_padata, krb5_pa_data **out_padata, @@ -1293,33 +730,6 @@ pa_s4u_x509_user(krb5_context context, krb5_kdc_req *request, /* FIXME - order significant? */ static const pa_types_t pa_types[] = { - { - KRB5_PADATA_PW_SALT, - pa_salt, - PA_INFO, - }, - { - KRB5_PADATA_AFS3_SALT, - pa_salt, - PA_INFO, - }, -#if APPLE_PKINIT - { - KRB5_PADATA_PK_AS_REQ, - pa_pkinit_gen_req, - PA_INFO, - }, - { - KRB5_PADATA_PK_AS_REP, - pa_pkinit_parse_rep, - PA_REAL, - }, -#endif /* APPLE_PKINIT */ - { - KRB5_PADATA_SAM_CHALLENGE_2, - pa_sam_2, - PA_REAL, - }, { KRB5_PADATA_FX_COOKIE, pa_fx_cookie, @@ -1350,11 +760,8 @@ krb5_do_preauth_tryagain(krb5_context kcontext, krb5_pa_data **padata, krb5_pa_data ***return_padata, krb5_error *err_reply, - krb5_data *salt, krb5_data *s2kparams, - krb5_enctype *etype, - krb5_keyblock *as_key, + krb5_pa_data **err_padata, krb5_prompter_fct prompter, void *prompter_data, - krb5_gic_get_as_key_fct gak_fct, void *gak_data, krb5_clpreauth_rock preauth_rock, krb5_gic_opt_ext *opte) { @@ -1393,11 +800,9 @@ krb5_do_preauth_tryagain(krb5_context kcontext, request, encoded_request_body, encoded_previous_request, - padata[i], - err_reply, + padata[i]->pa_type, + err_reply, err_padata, prompter, prompter_data, - gak_fct, gak_data, salt, s2kparams, - as_key, &out_padata) == 0) { if (out_padata != NULL) { int k; @@ -1415,28 +820,23 @@ krb5_do_preauth_tryagain(krb5_context kcontext, } krb5_error_code KRB5_CALLCONV -krb5_do_preauth(krb5_context context, - krb5_kdc_req *request, +krb5_do_preauth(krb5_context context, krb5_kdc_req *request, krb5_data *encoded_request_body, krb5_data *encoded_previous_request, krb5_pa_data **in_padata, krb5_pa_data ***out_padata, - krb5_data *salt, krb5_data *s2kparams, - krb5_enctype *etype, - krb5_keyblock *as_key, krb5_prompter_fct prompter, void *prompter_data, - krb5_gic_get_as_key_fct gak_fct, void *gak_data, - krb5_clpreauth_rock preauth_rock, krb5_gic_opt_ext *opte) + krb5_clpreauth_rock rock, krb5_gic_opt_ext *opte, + krb5_boolean *got_real_out) { unsigned int h; int i, j, out_pa_list_size; - int seen_etype_info2 = 0; krb5_pa_data *out_pa = NULL, **out_pa_list = NULL; - krb5_data scratch; - krb5_etype_info etype_info = NULL; krb5_error_code ret; static const int paorder[] = { PA_INFO, PA_REAL }; int realdone; + *got_real_out = FALSE; + if (in_padata == NULL) { *out_padata = NULL; return(0); @@ -1444,6 +844,11 @@ krb5_do_preauth(krb5_context context, TRACE_PREAUTH_INPUT(context, in_padata); + /* Scan the padata list and process etype-info or salt elements. */ + ret = get_etype_info(context, in_padata, request, rock); + if (ret) + return ret; + out_pa_list = NULL; out_pa_list_size = 0; @@ -1452,102 +857,6 @@ krb5_do_preauth(krb5_context context, for (h=0; h<(sizeof(paorder)/sizeof(paorder[0])); h++) { realdone = 0; for (i=0; in_padata[i] && !realdone; i++) { - int k, l, etype_found, valid_etype_found; - /* - * This is really gross, but is necessary to prevent - * lossage when talking to a 1.0.x KDC, which returns an - * erroneous PA-PW-SALT when it returns a KRB-ERROR - * requiring additional preauth. - */ - switch (in_padata[i]->pa_type) { - case KRB5_PADATA_ETYPE_INFO: - case KRB5_PADATA_ETYPE_INFO2: - { - krb5_preauthtype pa_type = in_padata[i]->pa_type; - if (etype_info) { - if (seen_etype_info2 || pa_type != KRB5_PADATA_ETYPE_INFO2) - continue; - if (pa_type == KRB5_PADATA_ETYPE_INFO2) { - krb5_free_etype_info( context, etype_info); - etype_info = NULL; - } - } - - scratch.length = in_padata[i]->length; - scratch.data = (char *) in_padata[i]->contents; - if (pa_type == KRB5_PADATA_ETYPE_INFO2) { - seen_etype_info2++; - ret = decode_krb5_etype_info2(&scratch, &etype_info); - } - else ret = decode_krb5_etype_info(&scratch, &etype_info); - if (ret) { - ret = 0; /*Ignore error and etype_info element*/ - if (etype_info) - krb5_free_etype_info( context, etype_info); - etype_info = NULL; - continue; - } - if (etype_info[0] == NULL) { - krb5_free_etype_info(context, etype_info); - etype_info = NULL; - break; - } - /* - * Select first etype in our request which is also in - * etype-info (preferring client request ktype order). - */ - for (etype_found = 0, valid_etype_found = 0, k = 0; - !etype_found && k < request->nktypes; k++) { - for (l = 0; etype_info[l]; l++) { - if (etype_info[l]->etype == request->ktype[k]) { - etype_found++; - break; - } - /* check if program has support for this etype for more - * precise error reporting. - */ - if (krb5_c_valid_enctype(etype_info[l]->etype)) - valid_etype_found++; - } - } - if (!etype_found) { - if (valid_etype_found) { - /* supported enctype but not requested */ - ret = KRB5_CONFIG_ETYPE_NOSUPP; - goto cleanup; - } - else { - /* unsupported enctype */ - ret = KRB5_PROG_ETYPE_NOSUPP; - goto cleanup; - } - - } - scratch.data = (char *) etype_info[l]->salt; - scratch.length = etype_info[l]->length; - krb5_free_data_contents(context, salt); - if (scratch.length == KRB5_ETYPE_NO_SALT) - salt->data = NULL; - else - if ((ret = krb5int_copy_data_contents( context, &scratch, salt)) != 0) - goto cleanup; - *etype = etype_info[l]->etype; - krb5_free_data_contents(context, s2kparams); - if ((ret = krb5int_copy_data_contents(context, - &etype_info[l]->s2kparams, - s2kparams)) != 0) - goto cleanup; - TRACE_PREAUTH_ETYPE_INFO(context, *etype, salt, s2kparams); - break; - } - case KRB5_PADATA_PW_SALT: - case KRB5_PADATA_AFS3_SALT: - if (etype_info) - continue; - break; - default: - ; - } /* Try the internally-provided preauth type list. */ if (!realdone) for (j=0; pa_types[j].type >= 0; j++) { if ((in_padata[i]->pa_type == pa_types[j].type) && @@ -1558,11 +867,13 @@ krb5_do_preauth(krb5_context context, #endif out_pa = NULL; - if ((ret = ((*pa_types[j].fct)(context, request, - in_padata[i], &out_pa, - salt, s2kparams, etype, as_key, - prompter, prompter_data, - gak_fct, gak_data)))) { + ret = pa_types[j].fct(context, request, in_padata[i], + &out_pa, rock->salt, + rock->s2kparams, rock->etype, + rock->as_key, prompter, + prompter_data, *rock->gak_fct, + *rock->gak_data); + if (ret) { if (paorder[h] == PA_INFO) { TRACE_PREAUTH_INFO_FAIL(context, in_padata[i]->pa_type, @@ -1601,11 +912,7 @@ krb5_do_preauth(krb5_context context, in_padata[i], prompter, prompter_data, - gak_fct, - salt, s2kparams, - gak_data, - preauth_rock, - as_key, + rock, &out_pa_list, &out_pa_list_size, &module_ret, @@ -1625,17 +932,14 @@ krb5_do_preauth(krb5_context context, TRACE_PREAUTH_OUTPUT(context, out_pa_list); *out_padata = out_pa_list; - if (etype_info) - krb5_free_etype_info(context, etype_info); + *got_real_out = realdone; return(0); cleanup: if (out_pa_list) { out_pa_list[out_pa_list_size++] = NULL; krb5_free_pa_data(context, out_pa_list); } - if (etype_info) - krb5_free_etype_info(context, etype_info); return (ret); }