From d93166972447594f3431edfa3ef2db946bfcc24e Mon Sep 17 00:00:00 2001 From: Greg Hudson Date: Thu, 26 Apr 2012 21:47:02 +0000 Subject: [PATCH] Convert SAM-2 preauth mechanism to a module Move the last real preauth mechanism, SAM-2, from the pa_types table to a built-in module. git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@25831 dc483132-0cff-0310-8789-dd5450dbe970 --- src/lib/krb5/krb/Makefile.in | 1 + src/lib/krb5/krb/int-proto.h | 4 + src/lib/krb5/krb/preauth2.c | 373 +-------------------------- src/lib/krb5/krb/preauth_sam2.c | 435 ++++++++++++++++++++++++++++++++ 4 files changed, 442 insertions(+), 371 deletions(-) create mode 100644 src/lib/krb5/krb/preauth_sam2.c diff --git a/src/lib/krb5/krb/Makefile.in b/src/lib/krb5/krb/Makefile.in index fe55c24ee..933edd09a 100644 --- a/src/lib/krb5/krb/Makefile.in +++ b/src/lib/krb5/krb/Makefile.in @@ -79,6 +79,7 @@ STLIBOBJS= \ preauth2.o \ preauth_ec.o \ preauth_encts.o \ + preauth_sam2.o \ gic_opt_set_pa.o \ princ_comp.o \ privsafe.o \ diff --git a/src/lib/krb5/krb/int-proto.h b/src/lib/krb5/krb/int-proto.h index 899579f75..3b93532af 100644 --- a/src/lib/krb5/krb/int-proto.h +++ b/src/lib/krb5/krb/int-proto.h @@ -63,6 +63,10 @@ krb5_error_code clpreauth_encrypted_timestamp_initvt(krb5_context context, int maj_ver, int min_ver, krb5_plugin_vtable vtable); +krb5_error_code +clpreauth_sam2_initvt(krb5_context context, int maj_ver, int min_ver, + krb5_plugin_vtable vtable); + krb5_error_code krb5int_construct_matching_creds(krb5_context context, krb5_flags options, krb5_creds *in_creds, krb5_creds *mcreds, diff --git a/src/lib/krb5/krb/preauth2.c b/src/lib/krb5/krb/preauth2.c index 81c508d53..25fa581d1 100644 --- a/src/lib/krb5/krb/preauth2.c +++ b/src/lib/krb5/krb/preauth2.c @@ -125,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)) @@ -600,372 +602,6 @@ pa_fx_cookie(krb5_context context, krb5_kdc_req *request, return 0; } -/* 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, @@ -1025,11 +661,6 @@ static const pa_types_t pa_types[] = { pa_salt, PA_INFO, }, - { - KRB5_PADATA_SAM_CHALLENGE_2, - pa_sam_2, - PA_REAL, - }, { KRB5_PADATA_FX_COOKIE, pa_fx_cookie, diff --git a/src/lib/krb5/krb/preauth_sam2.c b/src/lib/krb5/krb/preauth_sam2.c new file mode 100644 index 000000000..fd8028af5 --- /dev/null +++ b/src/lib/krb5/krb/preauth_sam2.c @@ -0,0 +1,435 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* lib/krb5/krb/preauth_sam2.c - SAM-2 clpreauth module */ +/* + * 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 + * require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + * + */ + +#include +#include +#include "int-proto.h" + +static int +sam2_flags(krb5_context context, krb5_preauthtype pa_type) +{ + return PA_REAL; +} + +/* 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 +sam2_process(krb5_context context, krb5_clpreauth_moddata moddata, + krb5_clpreauth_modreq modreq, krb5_get_init_creds_opt *opt, + krb5_clpreauth_callbacks cb, krb5_clpreauth_rock rock, + krb5_kdc_req *request, krb5_data *encoded_request_body, + krb5_data *encoded_previous_request, krb5_pa_data *padata, + krb5_prompter_fct prompter, void *prompter_data, + krb5_pa_data ***out_padata) +{ + 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, *salt; + 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 = padata->length; + tmp_data.data = (char *)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 = (*rock->gak_fct)(context, request->client, sc2b->sam_etype, + prompter, prompter_data, rock->salt, + rock->s2kparams, rock->as_key, + *rock->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() */ + salt = rock->salt; + 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 (rock->as_key->length) { + krb5_free_keyblock_contents(context, rock->as_key); + rock->as_key->length = 0; + } + + /* generate a key using the supplied password */ + retval = krb5_c_string_to_key(context, sc2b->sam_etype, + (krb5_data *)*rock->gak_data, salt, + rock->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, rock->as_key, &tmp_kb, + rock->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 (rock->as_key->length) { + krb5_free_keyblock_contents(context, rock->as_key); + rock->as_key->length = 0; + } + + /* generate a key using the supplied password */ + retval = krb5_c_string_to_key(context, sc2b->sam_etype, + &response_data, salt, rock->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, rock->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, rock->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, rock->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(2 * sizeof(*sam_padata)); + if (sam_padata == NULL) { + krb5_free_data(context, scratch); + return(ENOMEM); + } + sam_padata[0] = malloc(sizeof(krb5_pa_data)); + if (sam_padata[0] == NULL) { + krb5_free_data(context, scratch); + free(sam_padata); + return(ENOMEM); + } + + sam_padata[0]->magic = KV5M_PA_DATA; + sam_padata[0]->pa_type = KRB5_PADATA_SAM_RESPONSE_2; + sam_padata[0]->length = scratch->length; + sam_padata[0]->contents = (krb5_octet *) scratch->data; + free(scratch); + sam_padata[1] = NULL; + + *out_padata = sam_padata; + + return(0); +} + +static krb5_preauthtype sam2_pa_types[] = { + KRB5_PADATA_SAM_CHALLENGE_2, 0}; + +krb5_error_code +clpreauth_sam2_initvt(krb5_context context, int maj_ver, int min_ver, + krb5_plugin_vtable vtable) +{ + krb5_clpreauth_vtable vt; + + if (maj_ver != 1) + return KRB5_PLUGIN_VER_NOTSUPP; + vt = (krb5_clpreauth_vtable)vtable; + vt->name = "sam2"; + vt->pa_type_list = sam2_pa_types; + vt->flags = sam2_flags; + vt->process = sam2_process; + return 0; +} -- 2.26.2