/* -*- 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
*/
#include "k5-int.h"
-#if APPLE_PKINIT
-#include "pkinit_client.h"
-#include "pkinit_cert_store.h"
-#endif /* APPLE_PKINIT */
#include "osconf.h"
#include <krb5/preauth_plugin.h>
#include "int-proto.h"
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))
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);
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
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,
&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. */
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
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; udex<num_trusted_CAs; udex++) {
- free(trusted_CAs[udex].data);
- }
- free(trusted_CAs);
- }
- if(kdc_cert.data) {
- free(kdc_cert.data);
- }
- return krtn;
-
-}
-
-/* If and only if the realm is that of a Local KDC, accept
- * the KDC certificate as valid if its hash matches the
- * realm.
- */
-static krb5_boolean
-local_kdc_cert_match(krb5_context context,
- krb5_data *signer_cert,
- krb5_principal client)
-{
- static const char lkdcprefix[] = "LKDC:SHA1.";
- krb5_boolean match = FALSE;
- size_t cert_hash_len;
- char *cert_hash;
- const char *realm_hash;
- size_t realm_hash_len;
-
- if (client->realm.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,
/* 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,
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)
{
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;
}
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);
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;
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) &&
#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,
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,
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);
}