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/kdc | |
| parent | 8505824cad8ed0b6e8b96a5103cd43373c266996 (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.c | 19 | ||||
| -rw-r--r-- | src/kdc/kdc_preauth.c | 502 | ||||
| -rw-r--r-- | src/kdc/pkinit_apple_server.c | 241 | ||||
| -rw-r--r-- | src/kdc/pkinit_server.h | 112 |
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_ */ |
