summaryrefslogtreecommitdiffstats
path: root/src/kdc
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/kdc
parent8505824cad8ed0b6e8b96a5103cd43373c266996 (diff)
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/kdc')
-rw-r--r--src/kdc/do_as_req.c19
-rw-r--r--src/kdc/kdc_preauth.c502
-rw-r--r--src/kdc/pkinit_apple_server.c241
-rw-r--r--src/kdc/pkinit_server.h112
4 files changed, 874 insertions, 0 deletions
diff --git a/src/kdc/do_as_req.c b/src/kdc/do_as_req.c
index 4c2a09b95..5dbf7acc2 100644
--- a/src/kdc/do_as_req.c
+++ b/src/kdc/do_as_req.c
@@ -45,6 +45,15 @@
#include "adm_proto.h"
#include "extern.h"
+#if APPLE_PKINIT
+#define AS_REQ_DEBUG 0
+#if AS_REQ_DEBUG
+#define asReqDebug(args...) printf(args)
+#else
+#define asReqDebug(args...)
+#endif
+#endif /* APPLE_PKINIT */
+
static krb5_error_code prepare_error_as (krb5_kdc_req *, int, krb5_data *,
krb5_data **, const char *);
@@ -80,6 +89,11 @@ process_as_req(krb5_kdc_req *request, krb5_data *req_pkt,
char fromstringbuf[70];
void *pa_context = NULL;
+#if APPLE_PKINIT
+ asReqDebug("process_as_req top realm %s name %s\n",
+ request->client->realm.data, request->client->data->data);
+#endif /* APPLE_PKINIT */
+
ticket_reply.enc_part.ciphertext.data = 0;
e_data.data = 0;
encrypting_key.contents = 0;
@@ -391,6 +405,11 @@ process_as_req(krb5_kdc_req *request, krb5_data *req_pkt,
goto errout;
}
+#if APPLE_PKINIT
+ asReqDebug("process_as_req reply realm %s name %s\n",
+ reply.client->realm.data, reply.client->data->data);
+#endif /* APPLE_PKINIT */
+
/* now encode/encrypt the response */
reply.enc_part.enctype = encrypting_key.enctype;
diff --git a/src/kdc/kdc_preauth.c b/src/kdc/kdc_preauth.c
index 67764b22e..a250cf4ec 100644
--- a/src/kdc/kdc_preauth.c
+++ b/src/kdc/kdc_preauth.c
@@ -57,6 +57,11 @@
#include "extern.h"
#include <stdio.h>
#include "adm_proto.h"
+#if APPLE_PKINIT
+#include "pkinit_server.h"
+#include "pkinit_cert_store.h"
+#endif /* APPLE_PKINIT */
+
#include <syslog.h>
#include <assert.h>
@@ -188,7 +193,58 @@ static krb5_error_code return_sam_data
void *pa_module_context,
void **pa_request_context);
+#if APPLE_PKINIT
+/* PKINIT preauth support */
+static krb5_error_code get_pkinit_edata(
+ krb5_context context,
+ krb5_kdc_req *request,
+ krb5_db_entry *client,
+ krb5_db_entry *server,
+ preauth_get_entry_data_proc get_entry_data,
+ void *pa_module_context,
+ krb5_pa_data *pa_data);
+static krb5_error_code verify_pkinit_request(
+ krb5_context context,
+ krb5_db_entry *client,
+ krb5_data *req_pkt,
+ krb5_kdc_req *request,
+ krb5_enc_tkt_part *enc_tkt_reply,
+ krb5_pa_data *data,
+ preauth_get_entry_data_proc get_entry_data,
+ void *pa_module_context,
+ void **pa_request_context,
+ krb5_data **e_data,
+ krb5_authdata ***authz_data);
+static krb5_error_code return_pkinit_response(
+ krb5_context context,
+ krb5_pa_data * padata,
+ krb5_db_entry *client,
+ krb5_data *req_pkt,
+ krb5_kdc_req *request,
+ krb5_kdc_rep *reply,
+ krb5_key_data *client_key,
+ krb5_keyblock *encrypting_key,
+ krb5_pa_data **send_pa,
+ preauth_get_entry_data_proc get_entry_data,
+ void *pa_module_context,
+ void **pa_request_context);
+#endif /* APPLE_PKINIT */
+
static krb5_preauth_systems static_preauth_systems[] = {
+#if APPLE_PKINIT
+ {
+ "pkinit",
+ KRB5_PADATA_PK_AS_REQ,
+ PA_SUFFICIENT,
+ NULL, // pa_sys_context
+ NULL, // init
+ NULL, // fini
+ get_pkinit_edata,
+ verify_pkinit_request,
+ return_pkinit_response,
+ NULL // free_pa_request_context
+ },
+#endif /* APPLE_PKINIT */
{
"timestamp",
KRB5_PADATA_ENC_TIMESTAMP,
@@ -2350,3 +2406,449 @@ verify_sam_response(krb5_context context, krb5_db_entry *client,
return retval;
}
+
+#if APPLE_PKINIT
+/* PKINIT preauth support */
+#define PKINIT_DEBUG 0
+#if PKINIT_DEBUG
+#define kdcPkinitDebug(args...) printf(args)
+#else
+#define kdcPkinitDebug(args...)
+#endif
+
+/*
+ * get_edata() - our only job is to determine whether this KDC is capable of
+ * performing PKINIT. We infer that from the presence or absence of any
+ * KDC signing cert.
+ */
+static krb5_error_code get_pkinit_edata(
+ krb5_context context,
+ krb5_kdc_req *request,
+ krb5_db_entry *client,
+ krb5_db_entry *server,
+ preauth_get_entry_data_proc pkinit_get_entry_data,
+ void *pa_module_context,
+ krb5_pa_data *pa_data)
+{
+ krb5_pkinit_signing_cert_t cert = NULL;
+ krb5_error_code err = krb5_pkinit_get_kdc_cert(0, NULL, NULL, &cert);
+
+ kdcPkinitDebug("get_pkinit_edata: kdc cert %s\n", err ? "NOT FOUND" : "FOUND");
+ if(cert) {
+ krb5_pkinit_release_cert(cert);
+ }
+ return err;
+}
+
+/*
+ * This is 0 only for testing until the KDC DB contains
+ * the hash of the client cert
+ */
+#define REQUIRE_CLIENT_CERT_MATCH 1
+
+static krb5_error_code verify_pkinit_request(
+ krb5_context context,
+ krb5_db_entry *client,
+ krb5_data *req_pkt,
+ krb5_kdc_req *request,
+ krb5_enc_tkt_part *enc_tkt_reply,
+ krb5_pa_data *data,
+ preauth_get_entry_data_proc pkinit_get_entry_data,
+ void *pa_module_context,
+ void **pa_request_context,
+ krb5_data **e_data,
+ krb5_authdata ***authz_data)
+{
+ krb5_error_code krtn;
+ krb5_data pa_data;
+ krb5_data *der_req = NULL;
+ krb5_boolean valid_cksum;
+ char *cert_hash = NULL;
+ unsigned cert_hash_len;
+ unsigned key_dex;
+ unsigned cert_match = 0;
+ krb5_keyblock decrypted_key;
+
+ /* the data we get from the AS-REQ */
+ krb5_timestamp client_ctime = 0;
+ krb5_ui_4 client_cusec = 0;
+ krb5_timestamp kdc_ctime = 0;
+ krb5_int32 kdc_cusec = 0;
+ krb5_ui_4 nonce = 0;
+ krb5_checksum pa_cksum;
+ krb5int_cert_sig_status cert_sig_status;
+ krb5_data client_cert = {0, 0, NULL};
+
+ krb5_kdc_req *tmp_as_req = NULL;
+
+ kdcPkinitDebug("verify_pkinit_request\n");
+
+ decrypted_key.contents = NULL;
+ pa_data.data = (char *)data->contents;
+ pa_data.length = data->length;
+ krtn = krb5int_pkinit_as_req_parse(context, &pa_data,
+ &client_ctime, &client_cusec,
+ &nonce, &pa_cksum,
+ &cert_sig_status,
+ NULL, NULL, /* num_cms_types, cms_types */
+ &client_cert, /* signer_cert */
+ /* remaining fields unused (for now) */
+ NULL, NULL, /* num_all_certs, all_certs */
+ NULL, NULL, /* num_trusted_CAs, trusted_CAs */
+ NULL); /* kdc_cert */
+ if(krtn) {
+ kdcPkinitDebug("pa_pk_as_req_parse returned %d; PKINIT aborting.\n",
+ (int)krtn);
+ return krtn;
+ }
+ #if PKINIT_DEBUG
+ if(cert_sig_status != pki_cs_good) {
+ kdcPkinitDebug("verify_pkinit_request: cert_sig_status %d\n",
+ (int)cert_sig_status);
+ }
+ #endif /* PKINIT_DEBUG */
+
+ /*
+ * Verify signature and cert.
+ * FIXME: The spec calls for an e-data with error-specific type to be
+ * returned on error here. TD_TRUSTED_CERTIFIERS
+ * to be returned to the client here. There is no way for a preauth
+ * module to pass back e-data to process_as_req at this time. We
+ * might want to add such capability via an out param to check_padata
+ * and to its callees.
+ */
+ switch(cert_sig_status) {
+ case pki_cs_good:
+ break;
+ case pki_cs_sig_verify_fail:
+ /* no e-data */
+ krtn = KDC_ERR_INVALID_SIG;
+ goto cleanup;
+ case pki_cs_no_root:
+ case pki_cs_unknown_root:
+ case pki_cs_untrusted:
+ /*
+ * Can't verify to known root.
+ * e-data TD_TRUSTED_CERTIFIERS
+ */
+ kdcPkinitDebug("verify_pkinit_request: KDC_ERR_CANT_VERIFY_CERTIFICATE\n");
+ krtn = KDC_ERR_CANT_VERIFY_CERTIFICATE;
+ goto cleanup;
+ case pki_cs_bad_leaf:
+ case pki_cs_expired:
+ case pki_cs_not_valid_yet:
+ /*
+ * Problems with client cert itself.
+ * e-data type TD_INVALID_CERTIFICATES
+ */
+ krtn = KDC_ERR_INVALID_CERTIFICATE;
+ goto cleanup;
+ case pki_cs_revoked:
+ /* e-data type TD-INVALID-CERTIFICATES */
+ krtn = KDC_ERR_REVOKED_CERTIFICATE;
+ goto cleanup;
+ case pki_bad_key_use:
+ krtn = KDC_ERR_INCONSISTENT_KEY_PURPOSE;
+ /* no e-data */
+ goto cleanup;
+ case pki_bad_digest:
+ /* undefined (explicitly!) e-data */
+ krtn = KDC_ERR_DIGEST_IN_SIGNED_DATA_NOT_ACCEPTED;
+ goto cleanup;
+ case pki_bad_cms:
+ case pki_cs_other_err:
+ default:
+ krtn = KRB5KDC_ERR_PREAUTH_FAILED;
+ goto cleanup;
+ }
+
+ krtn = krb5_us_timeofday(context, &kdc_ctime, &kdc_cusec);
+ if(krtn) {
+ goto cleanup;
+ }
+ if (labs(kdc_ctime - client_ctime) > context->clockskew) {
+ kdcPkinitDebug("verify_pkinit_request: clock skew violation client %d svr %d\n",
+ (int)client_ctime, (int)kdc_ctime);
+ krtn = KRB5KRB_AP_ERR_SKEW;
+ goto cleanup;
+ }
+
+ /*
+ * The KDC may have modified the request after decoding it.
+ * We need to compute the checksum on the data that
+ * came from the client. Therefore, we use the original
+ * packet contents.
+ */
+ krtn = decode_krb5_as_req(req_pkt, &tmp_as_req);
+ if(krtn) {
+ kdcPkinitDebug("decode_krb5_as_req returned %d\n", (int)krtn);
+ goto cleanup;
+ }
+
+ /* calculate and compare checksum */
+ krtn = encode_krb5_kdc_req_body(tmp_as_req, &der_req);
+ if(krtn) {
+ kdcPkinitDebug("encode_krb5_kdc_req_body returned %d\n", (int)krtn);
+ goto cleanup;
+ }
+ krtn = krb5_c_verify_checksum(context, NULL, 0, der_req,
+ &pa_cksum, &valid_cksum);
+ if(krtn) {
+ kdcPkinitDebug("krb5_c_verify_checksum returned %d\n", (int)krtn);
+ goto cleanup;
+ }
+ if(!valid_cksum) {
+ kdcPkinitDebug("verify_pkinit_request: checksum error\n");
+ krtn = KRB5KRB_AP_ERR_BAD_INTEGRITY;
+ goto cleanup;
+ }
+
+ #if REQUIRE_CLIENT_CERT_MATCH
+ /* look up in the KDB to ensure correct client/cert binding */
+ cert_hash = krb5_pkinit_cert_hash_str(&client_cert);
+ if(cert_hash == NULL) {
+ krtn = ENOMEM;
+ goto cleanup;
+ }
+ cert_hash_len = strlen(cert_hash);
+ for(key_dex=0; key_dex<client->n_key_data; key_dex++) {
+ krb5_key_data *key_data = &client->key_data[key_dex];
+ kdcPkinitDebug("--- key %u type[0] %u length[0] %u type[1] %u length[1] %u\n",
+ key_dex,
+ key_data->key_data_type[0], key_data->key_data_length[0],
+ key_data->key_data_type[1], key_data->key_data_length[1]);
+ if(key_data->key_data_type[1] != KRB5_KDB_SALTTYPE_CERTHASH) {
+ continue;
+ }
+
+ /*
+ * Unfortunately this key is stored encrypted even though it's
+ * not sensitive...
+ */
+ krtn = krb5_dbekd_decrypt_key_data(context, &master_keyblock,
+ key_data, &decrypted_key, NULL);
+ if(krtn) {
+ kdcPkinitDebug("verify_pkinit_request: error decrypting cert hash block\n");
+ break;
+ }
+ if((decrypted_key.contents != NULL) &&
+ (cert_hash_len == decrypted_key.length) &&
+ !memcmp(decrypted_key.contents, cert_hash, cert_hash_len)) {
+ cert_match = 1;
+ break;
+ }
+ }
+ if(decrypted_key.contents) {
+ krb5_free_keyblock_contents(context, &decrypted_key);
+ }
+ if(!cert_match) {
+ kdcPkinitDebug("verify_pkinit_request: client cert does not match\n");
+ krtn = KDC_ERR_CLIENT_NOT_TRUSTED;
+ goto cleanup;
+ }
+ #endif /* REQUIRE_CLIENT_CERT_MATCH */
+ krtn = 0;
+ setflag(enc_tkt_reply->flags, TKT_FLG_PRE_AUTH);
+
+cleanup:
+ if(pa_cksum.contents) {
+ free(pa_cksum.contents);
+ }
+ if (tmp_as_req) {
+ krb5_free_kdc_req(context, tmp_as_req);
+ }
+ if (der_req) {
+ krb5_free_data(context, der_req);
+ }
+ if(cert_hash) {
+ free(cert_hash);
+ }
+ if(client_cert.data) {
+ free(client_cert.data);
+ }
+ kdcPkinitDebug("verify_pkinit_request: returning %d\n", (int)krtn);
+ return krtn;
+}
+
+static krb5_error_code return_pkinit_response(
+ krb5_context context,
+ krb5_pa_data * padata,
+ krb5_db_entry *client,
+ krb5_data *req_pkt,
+ krb5_kdc_req *request,
+ krb5_kdc_rep *reply,
+ krb5_key_data *client_key,
+ krb5_keyblock *encrypting_key,
+ krb5_pa_data **send_pa,
+ preauth_get_entry_data_proc pkinit_get_entry_data,
+ void *pa_module_context,
+ void **pa_request_context)
+{
+ krb5_error_code krtn;
+ krb5_data pa_data;
+ krb5_pkinit_signing_cert_t signing_cert = NULL;
+ krb5_checksum as_req_checksum = {0};
+ krb5_data *encoded_as_req = NULL;
+ krb5int_algorithm_id *cms_types = NULL;
+ krb5_ui_4 num_cms_types = 0;
+
+ /* the data we get from the AS-REQ */
+ krb5_ui_4 nonce = 0;
+ krb5_data client_cert = {0};
+
+ /*
+ * Trusted CA list and specific KC cert optionally obtained via
+ * krb5int_pkinit_as_req_parse(). All are DER-encoded
+ * issuerAndSerialNumbers.
+ */
+ krb5_data *trusted_CAs = NULL;
+ krb5_ui_4 num_trusted_CAs;
+ krb5_data kdc_cert = {0};
+
+ if (padata == NULL) {
+ /* Client has to send us something */
+ return 0;
+ }
+
+ kdcPkinitDebug("return_pkinit_response\n");
+ pa_data.data = (char *)padata->contents;
+ pa_data.length = padata->length;
+
+ /*
+ * We've already verified; just obtain the fields we need to create a response
+ */
+ krtn = krb5int_pkinit_as_req_parse(context,
+ &pa_data,
+ NULL, NULL, &nonce, /* ctime, cusec, nonce */
+ NULL, NULL, /* pa_cksum, cert_status */
+ &num_cms_types, &cms_types,
+ &client_cert, /* signer_cert: we encrypt for this */
+ /* remaining fields unused (for now) */
+ NULL, NULL, /* num_all_certs, all_certs */
+ &num_trusted_CAs, &trusted_CAs,
+ &kdc_cert);
+ if(krtn) {
+ kdcPkinitDebug("pa_pk_as_req_parse returned %d; PKINIT aborting.\n", (int)krtn);
+ goto cleanup;
+ }
+ if(client_cert.data == NULL) {
+ kdcPkinitDebug("pa_pk_as_req_parse failed to give a client_cert; aborting.\n");
+ krtn = KRB5KDC_ERR_PREAUTH_FAILED;
+ goto cleanup;
+ }
+
+ if(krb5_pkinit_get_kdc_cert(num_trusted_CAs, trusted_CAs,
+ (kdc_cert.data ? &kdc_cert : NULL),
+ &signing_cert)) {
+ /*
+ * Since get_pkinit_edata was able to obtain *some* KDC cert,
+ * this means that we can't satisfy the client's requirement.
+ * FIXME - particular error status for this?
+ */
+ kdcPkinitDebug("return_pkinit_response: NO appropriate signing cert!\n");
+ krtn = KRB5KDC_ERR_PREAUTH_FAILED;
+ goto cleanup;
+ }
+
+ /*
+ * Cook up keyblock for caller and for outgoing AS-REP.
+ * FIXME how much is known to be valid about encrypting_key?
+ * Will encrypting_key->enctype always be valid here? Seems that
+ * if we allow for clients without a shared secret (i.e. preauth
+ * by PKINIT only) there won't be a valid encrypting_key set up
+ * here for us.
+ */
+ krb5_free_keyblock_contents(context, encrypting_key);
+ krb5_c_make_random_key(context, encrypting_key->enctype, encrypting_key);
+
+ /* calculate checksum of incoming AS-REQ */
+ krtn = encode_krb5_as_req(request, &encoded_as_req);
+ if(krtn) {
+ kdcPkinitDebug("encode_krb5_as_req returned %d; PKINIT aborting.\n", (int)krtn);
+ goto cleanup;
+ }
+ krtn = krb5_c_make_checksum(context, context->kdc_req_sumtype,
+ encrypting_key, KRB5_KEYUSAGE_TGS_REQ_AUTH_CKSUM,
+ encoded_as_req, &as_req_checksum);
+ if(krtn) {
+ goto cleanup;
+ }
+
+ /*
+ * FIXME: here we assume that the client has one cert - the one that
+ * signed the AuthPack in the request (and that we therefore obtained from
+ * krb5int_pkinit_as_req_parse()), and the one we're using to encrypt the
+ * ReplyKeyPack with here. This may need rethinking.
+ */
+ krtn = krb5int_pkinit_as_rep_create(context,
+ encrypting_key, &as_req_checksum, signing_cert, TRUE,
+ &client_cert,
+ num_cms_types, cms_types,
+ num_trusted_CAs, trusted_CAs,
+ (kdc_cert.data ? &kdc_cert : NULL),
+ &pa_data);
+ if(krtn) {
+ kdcPkinitDebug("pa_pk_as_rep_create returned %d; PKINIT aborting.\n", (int)krtn);
+ goto cleanup;
+ }
+
+ *send_pa = (krb5_pa_data *)malloc(sizeof(krb5_pa_data));
+ if(*send_pa == NULL) {
+ krtn = ENOMEM;
+ free(pa_data.data);
+ goto cleanup;
+ }
+ (*send_pa)->magic = KV5M_PA_DATA;
+ (*send_pa)->pa_type = KRB5_PADATA_PK_AS_REP;
+ (*send_pa)->length = pa_data.length;
+ (*send_pa)->contents = (krb5_octet *)pa_data.data;
+ krtn = 0;
+
+ #if PKINIT_DEBUG
+ fprintf(stderr, "return_pkinit_response: SUCCESS\n");
+ fprintf(stderr, "nonce 0x%x enctype %d keydata %02x %02x %02x %02x...\n",
+ (int)nonce, (int)encrypting_key->enctype,
+ encrypting_key->contents[0], encrypting_key->contents[1],
+ encrypting_key->contents[2], encrypting_key->contents[3]);
+ #endif
+
+cleanup:
+ /* all of this was allocd by krb5int_pkinit_as_req_parse() */
+ if(signing_cert) {
+ krb5_pkinit_release_cert(signing_cert);
+ }
+ if(cms_types) {
+ unsigned dex;
+ krb5int_algorithm_id *alg_id;
+
+ for(dex=0; dex<num_cms_types; dex++) {
+ alg_id = &cms_types[dex];
+ if(alg_id->algorithm.data) {
+ free(alg_id->algorithm.data);
+ }
+ if(alg_id->parameters.data) {
+ free(alg_id->parameters.data);
+ }
+ }
+ free(cms_types);
+ }
+ if(trusted_CAs) {
+ unsigned dex;
+ for(dex=0; dex<num_trusted_CAs; dex++) {
+ free(trusted_CAs[dex].data);
+ }
+ free(trusted_CAs);
+ }
+ if(kdc_cert.data) {
+ free(kdc_cert.data);
+ }
+ if(client_cert.data) {
+ free(client_cert.data);
+ }
+ if(encoded_as_req) {
+ krb5_free_data(context, encoded_as_req);
+ }
+ return krtn;
+}
+
+#endif /* APPLE_PKINIT */
diff --git a/src/kdc/pkinit_apple_server.c b/src/kdc/pkinit_apple_server.c
new file mode 100644
index 000000000..b86c63444
--- /dev/null
+++ b/src/kdc/pkinit_apple_server.c
@@ -0,0 +1,241 @@
+/*
+ * Copyright (c) 2004-2008 Apple Inc. 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 Apple Inc. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. Apple Inc. makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ */
+
+/*
+ * pkinit_apple_server.c - Server side routines for PKINIT, Mac OS X version
+ *
+ * Created 21 May 2004 by Doug Mitchell at Apple.
+ */
+
+#if APPLE_PKINIT
+
+#include "pkinit_server.h"
+#include "pkinit_asn1.h"
+#include "pkinit_cms.h"
+#include <assert.h>
+
+#define PKINIT_DEBUG 0
+#if PKINIT_DEBUG
+#define pkiDebug(args...) printf(args)
+#else
+#define pkiDebug(args...)
+#endif
+
+/*
+ * Parse PA-PK-AS-REQ message. Optionally evaluates the message's certificate chain.
+ * Optionally returns various components.
+ */
+krb5_error_code krb5int_pkinit_as_req_parse(
+ krb5_context context,
+ const krb5_data *as_req,
+ krb5_timestamp *kctime, /* optionally RETURNED */
+ krb5_ui_4 *cusec, /* microseconds, optionally RETURNED */
+ krb5_ui_4 *nonce, /* optionally RETURNED */
+ krb5_checksum *pa_cksum, /* optional, contents mallocd and RETURNED */
+ krb5int_cert_sig_status *cert_status,/* optionally RETURNED */
+ krb5_ui_4 *num_cms_types, /* optionally RETURNED */
+ krb5int_algorithm_id **cms_types, /* optionally mallocd and RETURNED */
+
+ /*
+ * Cert fields, all optionally RETURNED.
+ *
+ * signer_cert is the full X.509 leaf cert from the incoming SignedData.
+ * all_certs is an array of all of the certs in the incoming SignedData,
+ * in full X.509 form.
+ */
+ krb5_data *signer_cert, /* content mallocd */
+ krb5_ui_4 *num_all_certs, /* sizeof *all_certs */
+ krb5_data **all_certs, /* krb5_data's and their content mallocd */
+
+ /*
+ * Array of trustedCertifiers, optionally RETURNED. These are DER-encoded
+ * issuer/serial numbers.
+ */
+ krb5_ui_4 *num_trusted_CAs, /* sizeof *trusted_CAs */
+ krb5_data **trusted_CAs, /* krb5_data's and their content mallocd */
+
+ /* KDC cert specified by client as kdcPkId. DER-encoded issuer/serial number. */
+ krb5_data *kdc_cert)
+{
+ krb5_error_code krtn;
+ krb5_data signed_auth_pack = {0, 0, NULL};
+ krb5_data raw_auth_pack = {0, 0, NULL};
+ krb5_data *raw_auth_pack_p = NULL;
+ krb5_boolean proceed = FALSE;
+ krb5_boolean need_auth_pack = FALSE;
+ krb5int_cms_content_type content_type;
+ krb5_pkinit_cert_db_t cert_db = NULL;
+ krb5_boolean is_signed;
+ krb5_boolean is_encrypted;
+
+ assert(as_req != NULL);
+
+ /*
+ * We always have to decode the top-level AS-REQ...
+ */
+ krtn = krb5int_pkinit_pa_pk_as_req_decode(as_req, &signed_auth_pack,
+ num_trusted_CAs, trusted_CAs, /* optional */
+ kdc_cert); /* optional */
+ if (krtn) {
+ pkiDebug("krb5int_pkinit_pa_pk_as_req_decode returned %d\n", (int)krtn);
+ return krtn;
+ }
+
+ /* Do we need info about or from the ContentInto or AuthPack? */
+ if ((kctime != NULL) || (cusec != NULL) || (nonce != NULL) ||
+ (pa_cksum != NULL) || (cms_types != NULL)) {
+ need_auth_pack = TRUE;
+ raw_auth_pack_p = &raw_auth_pack;
+ }
+ if (need_auth_pack || (cert_status != NULL) ||
+ (signer_cert != NULL) || (all_certs != NULL)) {
+ proceed = TRUE;
+ }
+ if (!proceed) {
+ krtn = 0;
+ goto err_out;
+ }
+
+ /* Parse and possibly verify the ContentInfo */
+ krtn = krb5_pkinit_get_kdc_cert_db(&cert_db);
+ if (krtn) {
+ pkiDebug("pa_pk_as_req_parse: error in krb5_pkinit_get_kdc_cert_db\n");
+ goto err_out;
+ }
+ krtn = krb5int_pkinit_parse_cms_msg(&signed_auth_pack, cert_db, TRUE,
+ &is_signed, &is_encrypted,
+ raw_auth_pack_p, &content_type, signer_cert, cert_status,
+ num_all_certs, all_certs);
+ if (krtn) {
+ pkiDebug("krb5int_pkinit_parse_content_info returned %d\n", (int)krtn);
+ goto err_out;
+ }
+
+ if (is_encrypted || !is_signed) {
+ pkiDebug("pkinit_parse_content_info: is_encrypted %s is_signed %s!\n",
+ is_encrypted ? "true" :"false",
+ is_signed ? "true" : "false");
+ krtn = KRB5KDC_ERR_PREAUTH_FAILED;
+ goto err_out;
+ }
+ if (content_type != ECT_PkAuthData) {
+ pkiDebug("authPack eContentType %d!\n", (int)content_type);
+ krtn = KRB5KDC_ERR_PREAUTH_FAILED;
+ goto err_out;
+ }
+
+ /* optionally parse contents of authPack */
+ if (need_auth_pack) {
+ krtn = krb5int_pkinit_auth_pack_decode(&raw_auth_pack, kctime,
+ cusec, nonce, pa_cksum,
+ cms_types, num_cms_types);
+ if(krtn) {
+ pkiDebug("krb5int_pkinit_auth_pack_decode returned %d\n", (int)krtn);
+ goto err_out;
+ }
+ }
+
+err_out:
+ /* free temp mallocd data that we didn't pass back to caller */
+ if(signed_auth_pack.data) {
+ free(signed_auth_pack.data);
+ }
+ if(raw_auth_pack.data) {
+ free(raw_auth_pack.data);
+ }
+ if(cert_db) {
+ krb5_pkinit_release_cert_db(cert_db);
+ }
+ return krtn;
+}
+
+/*
+ * Create a PA-PK-AS-REP message, public key (no Diffie Hellman) version.
+ *
+ * PA-PK-AS-REP is based on ReplyKeyPack like so:
+ *
+ * PA-PK-AS-REP ::= EnvelopedData(SignedData(ReplyKeyPack))
+ */
+krb5_error_code krb5int_pkinit_as_rep_create(
+ krb5_context context,
+ const krb5_keyblock *key_block,
+ const krb5_checksum *checksum, /* checksum of corresponding AS-REQ */
+ krb5_pkinit_signing_cert_t signer_cert, /* server's cert */
+ krb5_boolean include_server_cert,/* include signer_cert in SignerInfo */
+ const krb5_data *recipient_cert, /* client's cert */
+
+ /*
+ * These correspond to the same out-parameters from
+ * krb5int_pkinit_as_req_parse(). All are optional.
+ */
+ krb5_ui_4 num_cms_types,
+ const krb5int_algorithm_id *cms_types,
+ krb5_ui_4 num_trusted_CAs,
+ krb5_data *trusted_CAs,
+ krb5_data *kdc_cert,
+
+ krb5_data *as_rep) /* mallocd and RETURNED */
+{
+ krb5_data reply_key_pack = {0, 0, NULL};
+ krb5_error_code krtn;
+ krb5_data enc_key_pack = {0, 0, NULL};
+
+ /* innermost content = ReplyKeyPack */
+ krtn = krb5int_pkinit_reply_key_pack_encode(key_block, checksum,
+ &reply_key_pack);
+ if (krtn) {
+ return krtn;
+ }
+
+ /*
+ * Put that in an EnvelopedData(SignedData)
+ * -- SignedData.EncapsulatedData.ContentType = id-pkinit-rkeyData
+ */
+ krtn = krb5int_pkinit_create_cms_msg(&reply_key_pack,
+ signer_cert,
+ recipient_cert,
+ ECT_PkReplyKeyKata,
+ num_cms_types, cms_types,
+ &enc_key_pack);
+ if (krtn) {
+ goto err_out;
+ }
+
+ /*
+ * Finally, wrap that inside of PA-PK-AS-REP
+ */
+ krtn = krb5int_pkinit_pa_pk_as_rep_encode(NULL, &enc_key_pack, as_rep);
+
+err_out:
+ if (reply_key_pack.data) {
+ free(reply_key_pack.data);
+ }
+ if (enc_key_pack.data) {
+ free(enc_key_pack.data);
+ }
+ return krtn;
+}
+
+#endif /* APPLE_PKINIT */
diff --git a/src/kdc/pkinit_server.h b/src/kdc/pkinit_server.h
new file mode 100644
index 000000000..773b497e0
--- /dev/null
+++ b/src/kdc/pkinit_server.h
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2004-2008 Apple Inc. 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 Apple Inc. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. Apple Inc. makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ */
+
+/*
+ * pkinit_server.h - Server side routines for PKINIT
+ *
+ * Created 21 May 2004 by Doug Mitchell at Apple.
+ */
+
+#ifndef _PKINIT_SERVER_H_
+#define _PKINIT_SERVER_H_
+
+#include "krb5.h"
+#include "pkinit_cms.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/*
+ * Parse PA-PK-AS-REQ message. Optionally evaluates the message's certificate chain
+ * if cert_status is non-NULL. Optionally returns various components.
+ */
+krb5_error_code krb5int_pkinit_as_req_parse(
+ krb5_context context,
+ const krb5_data *as_req,
+ krb5_timestamp *kctime, /* optionally RETURNED */
+ krb5_ui_4 *cusec, /* microseconds, optionally RETURNED */
+ krb5_ui_4 *nonce, /* optionally RETURNED */
+ krb5_checksum *pa_cksum, /* optional, contents mallocd and RETURNED */
+ krb5int_cert_sig_status *cert_status, /* optionally RETURNED */
+ krb5_ui_4 *num_cms_types, /* optionally RETURNED */
+ krb5int_algorithm_id **cms_types, /* optionally mallocd and RETURNED */
+
+ /*
+ * Cert fields, all optionally RETURNED.
+ *
+ * signer_cert is the full X.509 leaf cert from the incoming SignedData.
+ * all_certs is an array of all of the certs in the incoming SignedData,
+ * in full X.509 form.
+ */
+ krb5_data *signer_cert, /* content mallocd */
+ krb5_ui_4 *num_all_certs, /* sizeof *all_certs */
+ krb5_data **all_certs, /* krb5_data's and their content mallocd */
+
+ /*
+ * Array of trustedCertifiers, optionally RETURNED. These are DER-encoded
+ * issuer/serial numbers.
+ */
+ krb5_ui_4 *num_trusted_CAs, /* sizeof *trustedCAs */
+ krb5_data **trusted_CAs, /* krb5_data's and their content mallocd */
+
+ /* KDC cert specified by client as kdcPkId. DER-encoded issuer/serial number. */
+ krb5_data *kdc_cert);
+
+
+/*
+ * Create a PA-PK-AS-REP message, public key (no Diffie Hellman) version.
+ *
+ * PA-PK-AS-REP is based on ReplyKeyPack like so:
+ *
+ * PA-PK-AS-REP ::= EnvelopedData(SignedData(ReplyKeyPack))
+ */
+krb5_error_code krb5int_pkinit_as_rep_create(
+ krb5_context context,
+ const krb5_keyblock *key_block,
+ const krb5_checksum *checksum, /* checksum of corresponding AS-REQ */
+ krb5_pkinit_signing_cert_t signer_cert, /* server's cert */
+ krb5_boolean include_server_cert, /* include signer_cert in SignerInfo */
+ const krb5_data *recipient_cert, /* client's cert */
+
+ /*
+ * These correspond to the same out-parameters from
+ * krb5int_pkinit_as_req_parse(). All are optional.
+ */
+ krb5_ui_4 num_cms_types,
+ const krb5int_algorithm_id *cms_types,
+ krb5_ui_4 num_trusted_CAs,
+ krb5_data *trusted_CAs,
+ krb5_data *kdc_cert,
+
+ /* result here, mallocd and RETURNED */
+ krb5_data *as_rep);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _PKINIT_SERVER_H_ */