summaryrefslogtreecommitdiffstats
path: root/src/lib/krb5/krb/preauth2.c
diff options
context:
space:
mode:
authorAlexandra Ellwood <lxs@mit.edu>2008-05-30 20:47:03 +0000
committerAlexandra Ellwood <lxs@mit.edu>2008-05-30 20:47:03 +0000
commit7ee1ef1a8a7d8424faa3cf7df88b184b0f911b3a (patch)
tree7cbcc11e0de0af794f9c2f16d03a6b1505e20a1d /src/lib/krb5/krb/preauth2.c
parent8505824cad8ed0b6e8b96a5103cd43373c266996 (diff)
downloadkrb5-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.c301
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;
}