2 * Copyright (c) 2004-2008 Apple Inc. All Rights Reserved.
4 * Export of this software from the United States of America may require
5 * a specific license from the United States Government. It is the
6 * responsibility of any person or organization contemplating export to
7 * obtain such a license before exporting.
9 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
10 * distribute this software and its documentation for any purpose and
11 * without fee is hereby granted, provided that the above copyright
12 * notice appear in all copies and that both that copyright notice and
13 * this permission notice appear in supporting documentation, and that
14 * the name of Apple Inc. not be used in advertising or publicity pertaining
15 * to distribution of the software without specific, written prior
16 * permission. Apple Inc. makes no representations about the suitability of
17 * this software for any purpose. It is provided "as is" without express
18 * or implied warranty.
20 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
21 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
22 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
27 * pkinit_apple_server.c - Server side routines for PKINIT, Mac OS X version
29 * Created 21 May 2004 by Doug Mitchell at Apple.
34 #include "pkinit_server.h"
35 #include "pkinit_asn1.h"
36 #include "pkinit_cms.h"
39 #define PKINIT_DEBUG 0
41 #define pkiDebug(args...) printf(args)
43 #define pkiDebug(args...)
47 * Parse PA-PK-AS-REQ message. Optionally evaluates the message's certificate chain.
48 * Optionally returns various components.
50 krb5_error_code krb5int_pkinit_as_req_parse(
52 const krb5_data *as_req,
53 krb5_timestamp *kctime, /* optionally RETURNED */
54 krb5_ui_4 *cusec, /* microseconds, optionally RETURNED */
55 krb5_ui_4 *nonce, /* optionally RETURNED */
56 krb5_checksum *pa_cksum, /* optional, contents mallocd and RETURNED */
57 krb5int_cert_sig_status *cert_status,/* optionally RETURNED */
58 krb5_ui_4 *num_cms_types, /* optionally RETURNED */
59 krb5int_algorithm_id **cms_types, /* optionally mallocd and RETURNED */
62 * Cert fields, all optionally RETURNED.
64 * signer_cert is the full X.509 leaf cert from the incoming SignedData.
65 * all_certs is an array of all of the certs in the incoming SignedData,
68 krb5_data *signer_cert, /* content mallocd */
69 krb5_ui_4 *num_all_certs, /* sizeof *all_certs */
70 krb5_data **all_certs, /* krb5_data's and their content mallocd */
73 * Array of trustedCertifiers, optionally RETURNED. These are DER-encoded
74 * issuer/serial numbers.
76 krb5_ui_4 *num_trusted_CAs, /* sizeof *trusted_CAs */
77 krb5_data **trusted_CAs, /* krb5_data's and their content mallocd */
79 /* KDC cert specified by client as kdcPkId. DER-encoded issuer/serial number. */
83 krb5_data signed_auth_pack = {0, 0, NULL};
84 krb5_data raw_auth_pack = {0, 0, NULL};
85 krb5_data *raw_auth_pack_p = NULL;
86 krb5_boolean proceed = FALSE;
87 krb5_boolean need_auth_pack = FALSE;
88 krb5int_cms_content_type content_type;
89 krb5_pkinit_cert_db_t cert_db = NULL;
90 krb5_boolean is_signed;
91 krb5_boolean is_encrypted;
93 assert(as_req != NULL);
96 * We always have to decode the top-level AS-REQ...
98 krtn = krb5int_pkinit_pa_pk_as_req_decode(as_req, &signed_auth_pack,
99 num_trusted_CAs, trusted_CAs, /* optional */
100 kdc_cert); /* optional */
102 pkiDebug("krb5int_pkinit_pa_pk_as_req_decode returned %d\n", (int)krtn);
106 /* Do we need info about or from the ContentInto or AuthPack? */
107 if ((kctime != NULL) || (cusec != NULL) || (nonce != NULL) ||
108 (pa_cksum != NULL) || (cms_types != NULL)) {
109 need_auth_pack = TRUE;
110 raw_auth_pack_p = &raw_auth_pack;
112 if (need_auth_pack || (cert_status != NULL) ||
113 (signer_cert != NULL) || (all_certs != NULL)) {
121 /* Parse and possibly verify the ContentInfo */
122 krtn = krb5_pkinit_get_kdc_cert_db(&cert_db);
124 pkiDebug("pa_pk_as_req_parse: error in krb5_pkinit_get_kdc_cert_db\n");
127 krtn = krb5int_pkinit_parse_cms_msg(&signed_auth_pack, cert_db, TRUE,
128 &is_signed, &is_encrypted,
129 raw_auth_pack_p, &content_type, signer_cert, cert_status,
130 num_all_certs, all_certs);
132 pkiDebug("krb5int_pkinit_parse_content_info returned %d\n", (int)krtn);
136 if (is_encrypted || !is_signed) {
137 pkiDebug("pkinit_parse_content_info: is_encrypted %s is_signed %s!\n",
138 is_encrypted ? "true" :"false",
139 is_signed ? "true" : "false");
140 krtn = KRB5KDC_ERR_PREAUTH_FAILED;
143 if (content_type != ECT_PkAuthData) {
144 pkiDebug("authPack eContentType %d!\n", (int)content_type);
145 krtn = KRB5KDC_ERR_PREAUTH_FAILED;
149 /* optionally parse contents of authPack */
150 if (need_auth_pack) {
151 krtn = krb5int_pkinit_auth_pack_decode(&raw_auth_pack, kctime,
152 cusec, nonce, pa_cksum,
153 cms_types, num_cms_types);
155 pkiDebug("krb5int_pkinit_auth_pack_decode returned %d\n", (int)krtn);
161 /* free temp mallocd data that we didn't pass back to caller */
162 if(signed_auth_pack.data) {
163 free(signed_auth_pack.data);
165 if(raw_auth_pack.data) {
166 free(raw_auth_pack.data);
169 krb5_pkinit_release_cert_db(cert_db);
175 * Create a PA-PK-AS-REP message, public key (no Diffie Hellman) version.
177 * PA-PK-AS-REP is based on ReplyKeyPack like so:
179 * PA-PK-AS-REP ::= EnvelopedData(SignedData(ReplyKeyPack))
181 krb5_error_code krb5int_pkinit_as_rep_create(
182 krb5_context context,
183 const krb5_keyblock *key_block,
184 const krb5_checksum *checksum, /* checksum of corresponding AS-REQ */
185 krb5_pkinit_signing_cert_t signer_cert, /* server's cert */
186 krb5_boolean include_server_cert,/* include signer_cert in SignerInfo */
187 const krb5_data *recipient_cert, /* client's cert */
190 * These correspond to the same out-parameters from
191 * krb5int_pkinit_as_req_parse(). All are optional.
193 krb5_ui_4 num_cms_types,
194 const krb5int_algorithm_id *cms_types,
195 krb5_ui_4 num_trusted_CAs,
196 krb5_data *trusted_CAs,
199 krb5_data *as_rep) /* mallocd and RETURNED */
201 krb5_data reply_key_pack = {0, 0, NULL};
202 krb5_error_code krtn;
203 krb5_data enc_key_pack = {0, 0, NULL};
205 /* innermost content = ReplyKeyPack */
206 krtn = krb5int_pkinit_reply_key_pack_encode(key_block, checksum,
213 * Put that in an EnvelopedData(SignedData)
214 * -- SignedData.EncapsulatedData.ContentType = id-pkinit-rkeyData
216 krtn = krb5int_pkinit_create_cms_msg(&reply_key_pack,
220 num_cms_types, cms_types,
227 * Finally, wrap that inside of PA-PK-AS-REP
229 krtn = krb5int_pkinit_pa_pk_as_rep_encode(NULL, &enc_key_pack, as_rep);
232 if (reply_key_pack.data) {
233 free(reply_key_pack.data);
235 if (enc_key_pack.data) {
236 free(enc_key_pack.data);
241 #endif /* APPLE_PKINIT */