diff options
author | Alexandra Ellwood <lxs@mit.edu> | 2008-05-30 20:47:03 +0000 |
---|---|---|
committer | Alexandra Ellwood <lxs@mit.edu> | 2008-05-30 20:47:03 +0000 |
commit | 7ee1ef1a8a7d8424faa3cf7df88b184b0f911b3a (patch) | |
tree | 7cbcc11e0de0af794f9c2f16d03a6b1505e20a1d /src/lib/krb5/krb/preauth2.c | |
parent | 8505824cad8ed0b6e8b96a5103cd43373c266996 (diff) | |
download | krb5-7ee1ef1a8a7d8424faa3cf7df88b184b0f911b3a.tar.gz krb5-7ee1ef1a8a7d8424faa3cf7df88b184b0f911b3a.tar.xz krb5-7ee1ef1a8a7d8424faa3cf7df88b184b0f911b3a.zip |
Apple PKINIT patch commit
Commit of Apple PKINIT patches under "APPLE_PKINIT" preprocessor symbol.
Long term goal is to merge these patches with the pkinit preauth plugin which
does not currently have support for Mac OS X crypto libraries or the exported
functions used by Back To My Mac.
ticket: new
status: open
git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@20346 dc483132-0cff-0310-8789-dd5450dbe970
Diffstat (limited to 'src/lib/krb5/krb/preauth2.c')
-rw-r--r-- | src/lib/krb5/krb/preauth2.c | 301 |
1 files changed, 301 insertions, 0 deletions
diff --git a/src/lib/krb5/krb/preauth2.c b/src/lib/krb5/krb/preauth2.c index dbd00bf259..96df2db042 100644 --- a/src/lib/krb5/krb/preauth2.c +++ b/src/lib/krb5/krb/preauth2.c @@ -30,6 +30,10 @@ */ #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" @@ -991,6 +995,279 @@ krb5_error_code pa_sam(krb5_context context, 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; + +} + +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; + + *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, + /* don't care about returned certs - do we? */ + NULL, NULL, NULL); + if(krtn) { + kdcPkinitDebug("pkinit_as_rep_parse returned %d\n", (int)krtn); + return krtn; + } + switch(sig_status) { + case pki_cs_good: + break; + 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(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 */ + static krb5_error_code pa_sam_2(krb5_context context, krb5_kdc_req *request, @@ -1320,6 +1597,7 @@ krb5_error_code pa_sam_2(krb5_context context, return(0); } +/* FIXME - order significant? */ static const pa_types_t pa_types[] = { { KRB5_PADATA_PW_SALT, @@ -1331,6 +1609,18 @@ static const pa_types_t pa_types[] = { 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_ENC_TIMESTAMP, pa_enc_timestamp, @@ -1596,6 +1886,17 @@ krb5_do_preauth(krb5_context context, salt, s2kparams, etype, as_key, prompter, prompter_data, gak_fct, gak_data)))) { + if (paorder[h] == PA_INFO) { +#ifdef DEBUG + fprintf (stderr, + "internal function for type %d, flag %d " + "failed with err %d\n", + in_padata[i]->pa_type, paorder[h], ret); +#endif + ret = 0; + continue; /* PA_INFO type failed, ignore */ + } + goto cleanup; } |