1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
3 * COPYRIGHT (C) 2006,2007
4 * THE REGENTS OF THE UNIVERSITY OF MICHIGAN
7 * Permission is granted to use, copy, create derivative works
8 * and redistribute this software and such derivative works
9 * for any purpose, so long as the name of The University of
10 * Michigan is not used in any advertising or publicity
11 * pertaining to the use of distribution of this software
12 * without specific, written prior authorization. If the
13 * above copyright notice or any other identification of the
14 * University of Michigan is included in any copy of any
15 * portion of this software, then the disclaimer below must
18 * THIS SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION
19 * FROM THE UNIVERSITY OF MICHIGAN AS TO ITS FITNESS FOR ANY
20 * PURPOSE, AND WITHOUT WARRANTY BY THE UNIVERSITY OF
21 * MICHIGAN OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
22 * WITHOUT LIMITATION THE IMPLIED WARRANTIES OF
23 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
24 * REGENTS OF THE UNIVERSITY OF MICHIGAN SHALL NOT BE LIABLE
25 * FOR ANY DAMAGES, INCLUDING SPECIAL, INDIRECT, INCIDENTAL, OR
26 * CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM ARISING
27 * OUT OF OR IN CONNECTION WITH THE USE OF THE SOFTWARE, EVEN
28 * IF IT HAS BEEN OR IS HEREAFTER ADVISED OF THE POSSIBILITY OF
39 /* Remove when FAST PKINIT is settled. */
40 #include "../fast_factor.h"
42 static krb5_error_code
43 pkinit_init_kdc_req_context(krb5_context, pkinit_kdc_req_context *blob);
46 pkinit_fini_kdc_req_context(krb5_context context, void *blob);
49 pkinit_server_plugin_fini_realm(krb5_context context,
50 pkinit_kdc_context plgctx);
53 pkinit_server_plugin_fini(krb5_context context,
54 krb5_kdcpreauth_moddata moddata);
56 static pkinit_kdc_context
57 pkinit_find_realm_context(krb5_context context,
58 krb5_kdcpreauth_moddata moddata,
59 krb5_principal princ);
61 static krb5_error_code
62 pkinit_create_edata(krb5_context context,
63 pkinit_plg_crypto_context plg_cryptoctx,
64 pkinit_req_crypto_context req_cryptoctx,
65 pkinit_identity_crypto_context id_cryptoctx,
66 pkinit_plg_opts *opts,
67 krb5_error_code err_code,
70 krb5_error_code retval = KRB5KRB_ERR_GENERIC;
72 pkiDebug("pkinit_create_edata: creating edata for error %d (%s)\n",
73 err_code, error_message(err_code));
75 case KRB5KDC_ERR_CANT_VERIFY_CERTIFICATE:
76 retval = pkinit_create_td_trusted_certifiers(context,
77 plg_cryptoctx, req_cryptoctx, id_cryptoctx, e_data);
79 case KRB5KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED:
80 retval = pkinit_create_td_dh_parameters(context, plg_cryptoctx,
81 req_cryptoctx, id_cryptoctx, opts, e_data);
83 case KRB5KDC_ERR_INVALID_CERTIFICATE:
84 case KRB5KDC_ERR_REVOKED_CERTIFICATE:
85 retval = pkinit_create_td_invalid_certificate(context,
86 plg_cryptoctx, req_cryptoctx, id_cryptoctx, e_data);
89 pkiDebug("no edata needed for error %d (%s)\n",
90 err_code, error_message(err_code));
100 static krb5_error_code
101 pkinit_server_get_edata(krb5_context context,
102 krb5_kdc_req *request,
103 struct _krb5_db_entry_new *client,
104 struct _krb5_db_entry_new *server,
105 krb5_kdcpreauth_get_data_fn server_get_entry_data,
106 krb5_kdcpreauth_moddata moddata,
109 krb5_error_code retval = 0;
110 pkinit_kdc_context plgctx = NULL;
111 krb5_keyblock *armor_key = NULL;
113 pkiDebug("pkinit_server_get_edata: entered!\n");
115 /* Remove (along with armor_key) when FAST PKINIT is settled. */
116 retval = fast_kdc_get_armor_key(context, server_get_entry_data, request,
118 if (retval == 0 && armor_key != NULL) {
119 /* Don't advertise PKINIT if the client used FAST. */
120 krb5_free_keyblock(context, armor_key);
125 * If we don't have a realm context for the given realm,
126 * don't tell the client that we support pkinit!
128 plgctx = pkinit_find_realm_context(context, moddata, request->server);
135 static krb5_error_code
136 verify_client_san(krb5_context context,
137 pkinit_kdc_context plgctx,
138 pkinit_kdc_req_context reqctx,
139 krb5_principal client,
142 krb5_error_code retval;
143 krb5_principal *princs = NULL;
144 krb5_principal *upns = NULL;
146 #ifdef DEBUG_SAN_INFO
147 char *client_string = NULL, *san_string;
150 retval = crypto_retrieve_cert_sans(context, plgctx->cryptoctx,
151 reqctx->cryptoctx, plgctx->idctx,
153 plgctx->opts->allow_upn ? &upns : NULL,
156 pkiDebug("%s: error from retrieve_certificate_sans()\n", __FUNCTION__);
157 retval = KRB5KDC_ERR_CLIENT_NAME_MISMATCH;
160 /* XXX Verify this is consistent with client side XXX */
162 retval = call_san_checking_plugins(context, plgctx, reqctx, princs,
163 upns, NULL, &plugin_decision, &ignore);
164 pkiDebug("%s: call_san_checking_plugins() returned retval %d\n",
167 retval = KRB5KDC_ERR_CLIENT_NAME_MISMATCH;
170 pkiDebug("%s: call_san_checking_plugins() returned decision %d\n",
171 __FUNCTION__, plugin_decision);
172 if (plugin_decision != NO_DECISION) {
173 retval = plugin_decision;
178 #ifdef DEBUG_SAN_INFO
179 krb5_unparse_name(context, client, &client_string);
181 pkiDebug("%s: Checking pkinit sans\n", __FUNCTION__);
182 for (i = 0; princs != NULL && princs[i] != NULL; i++) {
183 #ifdef DEBUG_SAN_INFO
184 krb5_unparse_name(context, princs[i], &san_string);
185 pkiDebug("%s: Comparing client '%s' to pkinit san value '%s'\n",
186 __FUNCTION__, client_string, san_string);
187 krb5_free_unparsed_name(context, san_string);
189 if (krb5_principal_compare(context, princs[i], client)) {
190 pkiDebug("%s: pkinit san match found\n", __FUNCTION__);
196 pkiDebug("%s: no pkinit san match found\n", __FUNCTION__);
198 * XXX if cert has names but none match, should we
199 * be returning KRB5KDC_ERR_CLIENT_NAME_MISMATCH here?
203 pkiDebug("%s: no upn sans (or we wouldn't accept them anyway)\n",
205 retval = KRB5KDC_ERR_CLIENT_NAME_MISMATCH;
209 pkiDebug("%s: Checking upn sans\n", __FUNCTION__);
210 for (i = 0; upns[i] != NULL; i++) {
211 #ifdef DEBUG_SAN_INFO
212 krb5_unparse_name(context, upns[i], &san_string);
213 pkiDebug("%s: Comparing client '%s' to upn san value '%s'\n",
214 __FUNCTION__, client_string, san_string);
215 krb5_free_unparsed_name(context, san_string);
217 if (krb5_principal_compare(context, upns[i], client)) {
218 pkiDebug("%s: upn san match found\n", __FUNCTION__);
224 pkiDebug("%s: no upn san match found\n", __FUNCTION__);
226 /* We found no match */
227 if (princs != NULL || upns != NULL) {
229 /* XXX ??? If there was one or more name in the cert, but
230 * none matched the client name, then return mismatch? */
231 retval = KRB5KDC_ERR_CLIENT_NAME_MISMATCH;
236 if (princs != NULL) {
237 for (i = 0; princs[i] != NULL; i++)
238 krb5_free_principal(context, princs[i]);
242 for (i = 0; upns[i] != NULL; i++)
243 krb5_free_principal(context, upns[i]);
246 #ifdef DEBUG_SAN_INFO
247 if (client_string != NULL)
248 krb5_free_unparsed_name(context, client_string);
250 pkiDebug("%s: returning retval %d, valid_san %d\n",
251 __FUNCTION__, retval, *valid_san);
255 static krb5_error_code
256 verify_client_eku(krb5_context context,
257 pkinit_kdc_context plgctx,
258 pkinit_kdc_req_context reqctx,
261 krb5_error_code retval;
265 if (plgctx->opts->require_eku == 0) {
266 pkiDebug("%s: configuration requests no EKU checking\n", __FUNCTION__);
272 retval = crypto_check_cert_eku(context, plgctx->cryptoctx,
273 reqctx->cryptoctx, plgctx->idctx,
275 plgctx->opts->accept_secondary_eku,
278 pkiDebug("%s: Error from crypto_check_cert_eku %d (%s)\n",
279 __FUNCTION__, retval, error_message(retval));
284 pkiDebug("%s: returning retval %d, eku_accepted %d\n",
285 __FUNCTION__, retval, *eku_accepted);
289 static krb5_error_code
290 pkinit_server_verify_padata(krb5_context context,
291 struct _krb5_db_entry_new * client,
293 krb5_kdc_req * request,
294 krb5_enc_tkt_part * enc_tkt_reply,
296 krb5_kdcpreauth_get_data_fn server_get_entry_data,
297 krb5_kdcpreauth_moddata moddata,
298 krb5_kdcpreauth_modreq *modreq_out,
300 krb5_authdata ***authz_data)
302 krb5_error_code retval = 0;
303 krb5_octet_data authp_data = {0, 0, NULL}, krb5_authz = {0, 0, NULL};
304 krb5_pa_pk_as_req *reqp = NULL;
305 krb5_pa_pk_as_req_draft9 *reqp9 = NULL;
306 krb5_auth_pack *auth_pack = NULL;
307 krb5_auth_pack_draft9 *auth_pack9 = NULL;
308 pkinit_kdc_context plgctx = NULL;
309 pkinit_kdc_req_context reqctx = NULL;
310 krb5_preauthtype pa_type;
311 krb5_checksum cksum = {0, 0, 0, NULL};
312 krb5_data *der_req = NULL;
313 int valid_eku = 0, valid_san = 0;
314 krb5_kdc_req *tmp_as_req = NULL;
317 krb5_keyblock *armor_key;
319 pkiDebug("pkinit_verify_padata: entered!\n");
320 if (data == NULL || data->length <= 0 || data->contents == NULL)
323 /* Remove (along with armor_key) when FAST PKINIT is settled. */
324 retval = fast_kdc_get_armor_key(context, server_get_entry_data, request,
326 if (retval == 0 && armor_key != NULL) {
327 /* Don't allow PKINIT if the client used FAST. */
328 krb5_free_keyblock(context, armor_key);
332 if (moddata == NULL || e_data == NULL)
335 plgctx = pkinit_find_realm_context(context, moddata, request->server);
340 print_buffer_bin(data->contents, data->length, "/tmp/kdc_as_req");
342 /* create a per-request context */
343 retval = pkinit_init_kdc_req_context(context, &reqctx);
346 reqctx->pa_type = data->pa_type;
348 PADATA_TO_KRB5DATA(data, &k5data);
350 switch ((int)data->pa_type) {
351 case KRB5_PADATA_PK_AS_REQ:
352 pkiDebug("processing KRB5_PADATA_PK_AS_REQ\n");
353 pa_type = (int)data->pa_type;
354 retval = k5int_decode_krb5_pa_pk_as_req(&k5data, &reqp);
356 pkiDebug("decode_krb5_pa_pk_as_req failed\n");
360 print_buffer_bin(reqp->signedAuthPack.data,
361 reqp->signedAuthPack.length,
362 "/tmp/kdc_signed_data");
364 retval = cms_signeddata_verify(context, plgctx->cryptoctx,
365 reqctx->cryptoctx, plgctx->idctx, CMS_SIGN_CLIENT,
366 plgctx->opts->require_crl_checking,
367 reqp->signedAuthPack.data, reqp->signedAuthPack.length,
368 &authp_data.data, &authp_data.length, &krb5_authz.data,
369 &krb5_authz.length, &is_signed);
371 case KRB5_PADATA_PK_AS_REP_OLD:
372 case KRB5_PADATA_PK_AS_REQ_OLD:
373 pkiDebug("processing KRB5_PADATA_PK_AS_REQ_OLD\n");
374 pa_type = KRB5_PADATA_PK_AS_REQ_OLD;
375 retval = k5int_decode_krb5_pa_pk_as_req_draft9(&k5data, &reqp9);
377 pkiDebug("decode_krb5_pa_pk_as_req_draft9 failed\n");
381 print_buffer_bin(reqp9->signedAuthPack.data,
382 reqp9->signedAuthPack.length,
383 "/tmp/kdc_signed_data_draft9");
386 retval = cms_signeddata_verify(context, plgctx->cryptoctx,
387 reqctx->cryptoctx, plgctx->idctx, CMS_SIGN_DRAFT9,
388 plgctx->opts->require_crl_checking,
389 reqp9->signedAuthPack.data, reqp9->signedAuthPack.length,
390 &authp_data.data, &authp_data.length, &krb5_authz.data,
391 &krb5_authz.length, NULL);
394 pkiDebug("unrecognized pa_type = %d\n", data->pa_type);
399 pkiDebug("pkcs7_signeddata_verify failed\n");
404 retval = verify_client_san(context, plgctx, reqctx, request->client,
409 pkiDebug("%s: did not find an acceptable SAN in user "
410 "certificate\n", __FUNCTION__);
411 retval = KRB5KDC_ERR_CLIENT_NAME_MISMATCH;
414 retval = verify_client_eku(context, plgctx, reqctx, &valid_eku);
419 pkiDebug("%s: did not find an acceptable EKU in user "
420 "certificate\n", __FUNCTION__);
421 retval = KRB5KDC_ERR_INCONSISTENT_KEY_PURPOSE;
424 } else { /* !is_signed */
425 if (!krb5_principal_compare(context, request->client,
426 krb5_anonymous_principal())) {
427 retval = KRB5KDC_ERR_PREAUTH_FAILED;
428 krb5_set_error_message(context, retval,
429 _("Pkinit request not signed, but client "
435 print_buffer_bin(authp_data.data, authp_data.length, "/tmp/kdc_auth_pack");
438 OCTETDATA_TO_KRB5DATA(&authp_data, &k5data);
439 switch ((int)data->pa_type) {
440 case KRB5_PADATA_PK_AS_REQ:
441 retval = k5int_decode_krb5_auth_pack(&k5data, &auth_pack);
443 pkiDebug("failed to decode krb5_auth_pack\n");
447 /* check dh parameters */
448 if (auth_pack->clientPublicValue != NULL) {
449 retval = server_check_dh(context, plgctx->cryptoctx,
450 reqctx->cryptoctx, plgctx->idctx,
451 &auth_pack->clientPublicValue->algorithm.parameters,
452 plgctx->opts->dh_min_bits);
455 pkiDebug("bad dh parameters\n");
458 } else if (!is_signed) {
459 /*Anonymous pkinit requires DH*/
460 retval = KRB5KDC_ERR_PREAUTH_FAILED;
461 krb5_set_error_message(context, retval,
462 _("Anonymous pkinit without DH public "
463 "value not supported."));
467 * The KDC may have modified the request after decoding it.
468 * We need to compute the checksum on the data that
469 * came from the client. Therefore, we use the original
472 retval = k5int_decode_krb5_as_req(req_pkt, &tmp_as_req);
474 pkiDebug("decode_krb5_as_req returned %d\n", (int)retval);
478 retval = k5int_encode_krb5_kdc_req_body(tmp_as_req, &der_req);
480 pkiDebug("encode_krb5_kdc_req_body returned %d\n", (int) retval);
483 retval = krb5_c_make_checksum(context, CKSUMTYPE_NIST_SHA, NULL,
486 pkiDebug("unable to calculate AS REQ checksum\n");
489 if (cksum.length != auth_pack->pkAuthenticator.paChecksum.length ||
490 memcmp(cksum.contents,
491 auth_pack->pkAuthenticator.paChecksum.contents,
493 pkiDebug("failed to match the checksum\n");
495 pkiDebug("calculating checksum on buf size (%d)\n",
497 print_buffer(req_pkt->data, req_pkt->length);
498 pkiDebug("received checksum type=%d size=%d ",
499 auth_pack->pkAuthenticator.paChecksum.checksum_type,
500 auth_pack->pkAuthenticator.paChecksum.length);
501 print_buffer(auth_pack->pkAuthenticator.paChecksum.contents,
502 auth_pack->pkAuthenticator.paChecksum.length);
503 pkiDebug("expected checksum type=%d size=%d ",
504 cksum.checksum_type, cksum.length);
505 print_buffer(cksum.contents, cksum.length);
508 retval = KRB5KDC_ERR_PA_CHECKSUM_MUST_BE_INCLUDED;
512 /* check if kdcPkId present and match KDC's subjectIdentifier */
513 if (reqp->kdcPkId.data != NULL) {
514 int valid_kdcPkId = 0;
515 retval = pkinit_check_kdc_pkid(context, plgctx->cryptoctx,
516 reqctx->cryptoctx, plgctx->idctx,
517 reqp->kdcPkId.data, reqp->kdcPkId.length, &valid_kdcPkId);
521 pkiDebug("kdcPkId in AS_REQ does not match KDC's cert"
522 "RFC says to ignore and proceed\n");
525 /* remember the decoded auth_pack for verify_padata routine */
526 reqctx->rcv_auth_pack = auth_pack;
529 case KRB5_PADATA_PK_AS_REP_OLD:
530 case KRB5_PADATA_PK_AS_REQ_OLD:
531 retval = k5int_decode_krb5_auth_pack_draft9(&k5data, &auth_pack9);
533 pkiDebug("failed to decode krb5_auth_pack_draft9\n");
536 if (auth_pack9->clientPublicValue != NULL) {
537 retval = server_check_dh(context, plgctx->cryptoctx,
538 reqctx->cryptoctx, plgctx->idctx,
539 &auth_pack9->clientPublicValue->algorithm.parameters,
540 plgctx->opts->dh_min_bits);
543 pkiDebug("bad dh parameters\n");
547 /* remember the decoded auth_pack for verify_padata routine */
548 reqctx->rcv_auth_pack9 = auth_pack9;
554 * This code used to generate ad-initial-verified-cas authorization data.
555 * However that has been removed until the ad-kdc-issued discussion can
556 * happen in the working group. Dec 2009
558 /* return authorization data to be included in the ticket */
559 switch ((int)data->pa_type) {
563 /* remember to set the PREAUTH flag in the reply */
564 enc_tkt_reply->flags |= TKT_FLG_PRE_AUTH;
565 *modreq_out = (krb5_kdcpreauth_modreq)reqctx;
569 if (retval && data->pa_type == KRB5_PADATA_PK_AS_REQ) {
570 pkiDebug("pkinit_verify_padata failed: creating e-data\n");
571 if (pkinit_create_edata(context, plgctx->cryptoctx, reqctx->cryptoctx,
572 plgctx->idctx, plgctx->opts, retval, e_data))
573 pkiDebug("pkinit_create_edata failed\n");
576 switch ((int)data->pa_type) {
577 case KRB5_PADATA_PK_AS_REQ:
578 free_krb5_pa_pk_as_req(&reqp);
579 free(cksum.contents);
581 krb5_free_data(context, der_req);
583 case KRB5_PADATA_PK_AS_REP_OLD:
584 case KRB5_PADATA_PK_AS_REQ_OLD:
585 free_krb5_pa_pk_as_req_draft9(&reqp9);
587 if (tmp_as_req != NULL)
588 k5int_krb5_free_kdc_req(context, tmp_as_req);
589 free(authp_data.data);
590 free(krb5_authz.data);
592 pkinit_fini_kdc_req_context(context, reqctx);
593 if (auth_pack != NULL)
594 free_krb5_auth_pack(&auth_pack);
595 if (auth_pack9 != NULL)
596 free_krb5_auth_pack_draft9(context, &auth_pack9);
600 static krb5_error_code
601 return_pkinit_kx(krb5_context context, krb5_kdc_req *request,
602 krb5_kdc_rep *reply, krb5_keyblock *encrypting_key,
603 krb5_pa_data **out_padata)
605 krb5_error_code ret = 0;
606 krb5_keyblock *session = reply->ticket->enc_part2->session;
607 krb5_keyblock *new_session = NULL;
608 krb5_pa_data *pa = NULL;
610 krb5_data *scratch = NULL;
613 enc.ciphertext.data = NULL;
614 if (!krb5_principal_compare(context, request->client,
615 krb5_anonymous_principal()))
618 * The KDC contribution key needs to be a fresh key of an enctype supported
619 * by the client and server. The existing session key meets these
620 * requirements so we use it.
622 ret = krb5_c_fx_cf2_simple(context, session, "PKINIT",
623 encrypting_key, "KEYEXCHANGE",
627 ret = encode_krb5_encryption_key( session, &scratch);
630 ret = krb5_encrypt_helper(context, encrypting_key,
631 KRB5_KEYUSAGE_PA_PKINIT_KX, scratch, &enc);
634 memset(scratch->data, 0, scratch->length);
635 krb5_free_data(context, scratch);
637 ret = encode_krb5_enc_data(&enc, &scratch);
640 pa = malloc(sizeof(krb5_pa_data));
645 pa->pa_type = KRB5_PADATA_PKINIT_KX;
646 pa->length = scratch->length;
647 pa->contents = (krb5_octet *) scratch->data;
649 scratch->data = NULL;
650 memset(session->contents, 0, session->length);
651 krb5_free_keyblock_contents(context, session);
652 *session = *new_session;
653 new_session->contents = NULL;
655 krb5_free_data_contents(context, &enc.ciphertext);
656 krb5_free_keyblock(context, new_session);
657 krb5_free_data(context, scratch);
661 static krb5_error_code
662 pkinit_server_return_padata(krb5_context context,
663 krb5_pa_data * padata,
664 struct _krb5_db_entry_new * client,
666 krb5_kdc_req * request,
667 krb5_kdc_rep * reply,
668 struct _krb5_key_data * client_key,
669 krb5_keyblock * encrypting_key,
670 krb5_pa_data ** send_pa,
671 krb5_kdcpreauth_get_data_fn server_get_entry_data,
672 krb5_kdcpreauth_moddata moddata,
673 krb5_kdcpreauth_modreq modreq)
675 krb5_error_code retval = 0;
676 krb5_data scratch = {0, 0, NULL};
677 krb5_pa_pk_as_req *reqp = NULL;
678 krb5_pa_pk_as_req_draft9 *reqp9 = NULL;
681 unsigned char *subjectPublicKey = NULL;
682 unsigned char *dh_pubkey = NULL, *server_key = NULL;
683 unsigned int subjectPublicKey_len = 0;
684 unsigned int server_key_len = 0, dh_pubkey_len = 0;
686 krb5_kdc_dh_key_info dhkey_info;
687 krb5_data *encoded_dhkey_info = NULL;
688 krb5_pa_pk_as_rep *rep = NULL;
689 krb5_pa_pk_as_rep_draft9 *rep9 = NULL;
690 krb5_data *out_data = NULL;
692 krb5_enctype enctype = -1;
694 krb5_reply_key_pack *key_pack = NULL;
695 krb5_reply_key_pack_draft9 *key_pack9 = NULL;
696 krb5_data *encoded_key_pack = NULL;
698 pkinit_kdc_context plgctx;
699 pkinit_kdc_req_context reqctx;
701 int fixed_keypack = 0;
704 if (padata->pa_type == KRB5_PADATA_PKINIT_KX) {
705 return return_pkinit_kx(context, request, reply,
706 encrypting_key, send_pa);
708 if (padata->length <= 0 || padata->contents == NULL)
711 if (modreq == NULL) {
712 pkiDebug("missing request context \n");
716 plgctx = pkinit_find_realm_context(context, moddata, request->server);
717 if (plgctx == NULL) {
718 pkiDebug("Unable to locate correct realm context\n");
722 pkiDebug("pkinit_return_padata: entered!\n");
723 reqctx = (pkinit_kdc_req_context)modreq;
725 if (encrypting_key->contents) {
726 free(encrypting_key->contents);
727 encrypting_key->length = 0;
728 encrypting_key->contents = NULL;
731 for(i = 0; i < request->nktypes; i++) {
732 enctype = request->ktype[i];
733 if (!krb5_c_valid_enctype(enctype))
736 pkiDebug("KDC picked etype = %d\n", enctype);
741 if (i == request->nktypes) {
742 retval = KRB5KDC_ERR_ETYPE_NOSUPP;
746 switch((int)reqctx->pa_type) {
747 case KRB5_PADATA_PK_AS_REQ:
748 init_krb5_pa_pk_as_rep(&rep);
753 /* let's assume it's RSA. we'll reset it to DH if needed */
754 rep->choice = choice_pa_pk_as_rep_encKeyPack;
756 case KRB5_PADATA_PK_AS_REP_OLD:
757 case KRB5_PADATA_PK_AS_REQ_OLD:
758 init_krb5_pa_pk_as_rep_draft9(&rep9);
763 rep9->choice = choice_pa_pk_as_rep_draft9_encKeyPack;
766 retval = KRB5KDC_ERR_PREAUTH_FAILED;
770 if (reqctx->rcv_auth_pack != NULL &&
771 reqctx->rcv_auth_pack->clientPublicValue != NULL) {
773 reqctx->rcv_auth_pack->clientPublicValue->subjectPublicKey.data;
774 subjectPublicKey_len =
775 reqctx->rcv_auth_pack->clientPublicValue->subjectPublicKey.length;
776 rep->choice = choice_pa_pk_as_rep_dhInfo;
777 } else if (reqctx->rcv_auth_pack9 != NULL &&
778 reqctx->rcv_auth_pack9->clientPublicValue != NULL) {
780 reqctx->rcv_auth_pack9->clientPublicValue->subjectPublicKey.data;
781 subjectPublicKey_len =
782 reqctx->rcv_auth_pack9->clientPublicValue->subjectPublicKey.length;
783 rep9->choice = choice_pa_pk_as_rep_draft9_dhSignedData;
786 /* if this DH, then process finish computing DH key */
787 if (rep != NULL && (rep->choice == choice_pa_pk_as_rep_dhInfo ||
788 rep->choice == choice_pa_pk_as_rep_draft9_dhSignedData)) {
789 pkiDebug("received DH key delivery AS REQ\n");
790 retval = server_process_dh(context, plgctx->cryptoctx,
791 reqctx->cryptoctx, plgctx->idctx, subjectPublicKey,
792 subjectPublicKey_len, &dh_pubkey, &dh_pubkey_len,
793 &server_key, &server_key_len);
795 pkiDebug("failed to process/create dh paramters\n");
801 rep9->choice == choice_pa_pk_as_rep_draft9_dhSignedData) ||
802 (rep != NULL && rep->choice == choice_pa_pk_as_rep_dhInfo)) {
803 retval = pkinit_octetstring2key(context, enctype, server_key,
804 server_key_len, encrypting_key);
806 pkiDebug("pkinit_octetstring2key failed: %s\n",
807 error_message(retval));
811 dhkey_info.subjectPublicKey.length = dh_pubkey_len;
812 dhkey_info.subjectPublicKey.data = dh_pubkey;
813 dhkey_info.nonce = request->nonce;
814 dhkey_info.dhKeyExpiration = 0;
816 retval = k5int_encode_krb5_kdc_dh_key_info(&dhkey_info,
817 &encoded_dhkey_info);
819 pkiDebug("encode_krb5_kdc_dh_key_info failed\n");
823 print_buffer_bin((unsigned char *)encoded_dhkey_info->data,
824 encoded_dhkey_info->length,
825 "/tmp/kdc_dh_key_info");
828 switch ((int)padata->pa_type) {
829 case KRB5_PADATA_PK_AS_REQ:
830 retval = cms_signeddata_create(context, plgctx->cryptoctx,
831 reqctx->cryptoctx, plgctx->idctx, CMS_SIGN_SERVER, 1,
832 (unsigned char *)encoded_dhkey_info->data,
833 encoded_dhkey_info->length,
834 &rep->u.dh_Info.dhSignedData.data,
835 &rep->u.dh_Info.dhSignedData.length);
837 pkiDebug("failed to create pkcs7 signed data\n");
841 case KRB5_PADATA_PK_AS_REP_OLD:
842 case KRB5_PADATA_PK_AS_REQ_OLD:
843 retval = cms_signeddata_create(context, plgctx->cryptoctx,
844 reqctx->cryptoctx, plgctx->idctx, CMS_SIGN_DRAFT9, 1,
845 (unsigned char *)encoded_dhkey_info->data,
846 encoded_dhkey_info->length,
847 &rep9->u.dhSignedData.data,
848 &rep9->u.dhSignedData.length);
850 pkiDebug("failed to create pkcs7 signed data\n");
856 pkiDebug("received RSA key delivery AS REQ\n");
858 retval = krb5_c_make_random_key(context, enctype, encrypting_key);
860 pkiDebug("unable to make a session key\n");
864 /* check if PA_TYPE of 132 is present which means the client is
865 * requesting that a checksum is send back instead of the nonce
867 for (i = 0; request->padata[i] != NULL; i++) {
868 pkiDebug("%s: Checking pa_type 0x%08x\n",
869 __FUNCTION__, request->padata[i]->pa_type);
870 if (request->padata[i]->pa_type == 132)
873 pkiDebug("%s: return checksum instead of nonce = %d\n",
874 __FUNCTION__, fixed_keypack);
876 /* if this is an RFC reply or draft9 client requested a checksum
877 * in the reply instead of the nonce, create an RFC-style keypack
879 if ((int)padata->pa_type == KRB5_PADATA_PK_AS_REQ || fixed_keypack) {
880 init_krb5_reply_key_pack(&key_pack);
881 if (key_pack == NULL) {
886 retval = krb5_c_make_checksum(context, 0,
887 encrypting_key, KRB5_KEYUSAGE_TGS_REQ_AUTH_CKSUM,
888 req_pkt, &key_pack->asChecksum);
890 pkiDebug("unable to calculate AS REQ checksum\n");
894 pkiDebug("calculating checksum on buf size = %d\n", req_pkt->length);
895 print_buffer(req_pkt->data, req_pkt->length);
896 pkiDebug("checksum size = %d\n", key_pack->asChecksum.length);
897 print_buffer(key_pack->asChecksum.contents,
898 key_pack->asChecksum.length);
899 pkiDebug("encrypting key (%d)\n", encrypting_key->length);
900 print_buffer(encrypting_key->contents, encrypting_key->length);
903 krb5_copy_keyblock_contents(context, encrypting_key,
904 &key_pack->replyKey);
906 retval = k5int_encode_krb5_reply_key_pack(key_pack,
909 pkiDebug("failed to encode reply_key_pack\n");
914 switch ((int)padata->pa_type) {
915 case KRB5_PADATA_PK_AS_REQ:
916 rep->choice = choice_pa_pk_as_rep_encKeyPack;
917 retval = cms_envelopeddata_create(context, plgctx->cryptoctx,
918 reqctx->cryptoctx, plgctx->idctx, padata->pa_type, 1,
919 (unsigned char *)encoded_key_pack->data,
920 encoded_key_pack->length,
921 &rep->u.encKeyPack.data, &rep->u.encKeyPack.length);
923 case KRB5_PADATA_PK_AS_REP_OLD:
924 case KRB5_PADATA_PK_AS_REQ_OLD:
925 /* if the request is from the broken draft9 client that
926 * expects back a nonce, create it now
928 if (!fixed_keypack) {
929 init_krb5_reply_key_pack_draft9(&key_pack9);
930 if (key_pack9 == NULL) {
934 key_pack9->nonce = reqctx->rcv_auth_pack9->pkAuthenticator.nonce;
935 krb5_copy_keyblock_contents(context, encrypting_key,
936 &key_pack9->replyKey);
938 retval = k5int_encode_krb5_reply_key_pack_draft9(key_pack9,
941 pkiDebug("failed to encode reply_key_pack\n");
946 rep9->choice = choice_pa_pk_as_rep_draft9_encKeyPack;
947 retval = cms_envelopeddata_create(context, plgctx->cryptoctx,
948 reqctx->cryptoctx, plgctx->idctx, padata->pa_type, 1,
949 (unsigned char *)encoded_key_pack->data,
950 encoded_key_pack->length,
951 &rep9->u.encKeyPack.data, &rep9->u.encKeyPack.length);
955 pkiDebug("failed to create pkcs7 enveloped data: %s\n",
956 error_message(retval));
960 print_buffer_bin((unsigned char *)encoded_key_pack->data,
961 encoded_key_pack->length,
962 "/tmp/kdc_key_pack");
963 switch ((int)padata->pa_type) {
964 case KRB5_PADATA_PK_AS_REQ:
965 print_buffer_bin(rep->u.encKeyPack.data,
966 rep->u.encKeyPack.length,
967 "/tmp/kdc_enc_key_pack");
969 case KRB5_PADATA_PK_AS_REP_OLD:
970 case KRB5_PADATA_PK_AS_REQ_OLD:
971 print_buffer_bin(rep9->u.encKeyPack.data,
972 rep9->u.encKeyPack.length,
973 "/tmp/kdc_enc_key_pack");
979 switch ((int)padata->pa_type) {
980 case KRB5_PADATA_PK_AS_REQ:
981 retval = k5int_encode_krb5_pa_pk_as_rep(rep, &out_data);
983 case KRB5_PADATA_PK_AS_REP_OLD:
984 case KRB5_PADATA_PK_AS_REQ_OLD:
985 retval = k5int_encode_krb5_pa_pk_as_rep_draft9(rep9, &out_data);
989 pkiDebug("failed to encode AS_REP\n");
993 if (out_data != NULL)
994 print_buffer_bin((unsigned char *)out_data->data, out_data->length,
998 *send_pa = malloc(sizeof(krb5_pa_data));
999 if (*send_pa == NULL) {
1001 free(out_data->data);
1006 (*send_pa)->magic = KV5M_PA_DATA;
1007 switch ((int)padata->pa_type) {
1008 case KRB5_PADATA_PK_AS_REQ:
1009 (*send_pa)->pa_type = KRB5_PADATA_PK_AS_REP;
1011 case KRB5_PADATA_PK_AS_REQ_OLD:
1012 case KRB5_PADATA_PK_AS_REP_OLD:
1013 (*send_pa)->pa_type = KRB5_PADATA_PK_AS_REP_OLD;
1016 (*send_pa)->length = out_data->length;
1017 (*send_pa)->contents = (krb5_octet *) out_data->data;
1021 pkinit_fini_kdc_req_context(context, reqctx);
1024 if (encoded_dhkey_info != NULL)
1025 krb5_free_data(context, encoded_dhkey_info);
1026 if (encoded_key_pack != NULL)
1027 krb5_free_data(context, encoded_key_pack);
1031 switch ((int)padata->pa_type) {
1032 case KRB5_PADATA_PK_AS_REQ:
1033 free_krb5_pa_pk_as_req(&reqp);
1034 free_krb5_pa_pk_as_rep(&rep);
1035 free_krb5_reply_key_pack(&key_pack);
1037 case KRB5_PADATA_PK_AS_REP_OLD:
1038 case KRB5_PADATA_PK_AS_REQ_OLD:
1039 free_krb5_pa_pk_as_req_draft9(&reqp9);
1040 free_krb5_pa_pk_as_rep_draft9(&rep9);
1042 free_krb5_reply_key_pack_draft9(&key_pack9);
1044 free_krb5_reply_key_pack(&key_pack);
1049 pkiDebug("pkinit_verify_padata failure");
1055 pkinit_server_get_flags(krb5_context kcontext, krb5_preauthtype patype)
1057 if (patype == KRB5_PADATA_PKINIT_KX)
1059 return PA_SUFFICIENT | PA_REPLACES_KEY;
1062 static krb5_preauthtype supported_server_pa_types[] = {
1063 KRB5_PADATA_PK_AS_REQ,
1064 KRB5_PADATA_PK_AS_REQ_OLD,
1065 KRB5_PADATA_PK_AS_REP_OLD,
1066 KRB5_PADATA_PKINIT_KX,
1071 pkinit_fini_kdc_profile(krb5_context context, pkinit_kdc_context plgctx)
1074 * There is nothing currently allocated by pkinit_init_kdc_profile()
1075 * which needs to be freed here.
1079 static krb5_error_code
1080 pkinit_init_kdc_profile(krb5_context context, pkinit_kdc_context plgctx)
1082 krb5_error_code retval;
1083 char *eku_string = NULL;
1085 pkiDebug("%s: entered for realm %s\n", __FUNCTION__, plgctx->realmname);
1086 retval = pkinit_kdcdefault_string(context, plgctx->realmname,
1087 KRB5_CONF_PKINIT_IDENTITY,
1088 &plgctx->idopts->identity);
1089 if (retval != 0 || NULL == plgctx->idopts->identity) {
1091 krb5_set_error_message(context, retval,
1092 _("No pkinit_identity supplied for realm %s"),
1097 retval = pkinit_kdcdefault_strings(context, plgctx->realmname,
1098 KRB5_CONF_PKINIT_ANCHORS,
1099 &plgctx->idopts->anchors);
1100 if (retval != 0 || NULL == plgctx->idopts->anchors) {
1102 krb5_set_error_message(context, retval,
1103 _("No pkinit_anchors supplied for realm %s"),
1108 pkinit_kdcdefault_strings(context, plgctx->realmname,
1109 KRB5_CONF_PKINIT_POOL,
1110 &plgctx->idopts->intermediates);
1112 pkinit_kdcdefault_strings(context, plgctx->realmname,
1113 KRB5_CONF_PKINIT_REVOKE,
1114 &plgctx->idopts->crls);
1116 pkinit_kdcdefault_string(context, plgctx->realmname,
1117 KRB5_CONF_PKINIT_KDC_OCSP,
1118 &plgctx->idopts->ocsp);
1120 pkinit_kdcdefault_string(context, plgctx->realmname,
1121 KRB5_CONF_PKINIT_MAPPING_FILE,
1122 &plgctx->idopts->dn_mapping_file);
1124 pkinit_kdcdefault_integer(context, plgctx->realmname,
1125 KRB5_CONF_PKINIT_DH_MIN_BITS,
1126 PKINIT_DEFAULT_DH_MIN_BITS,
1127 &plgctx->opts->dh_min_bits);
1128 if (plgctx->opts->dh_min_bits < PKINIT_DEFAULT_DH_MIN_BITS) {
1129 pkiDebug("%s: invalid value (%d) for pkinit_dh_min_bits, "
1130 "using default value (%d) instead\n", __FUNCTION__,
1131 plgctx->opts->dh_min_bits, PKINIT_DEFAULT_DH_MIN_BITS);
1132 plgctx->opts->dh_min_bits = PKINIT_DEFAULT_DH_MIN_BITS;
1135 pkinit_kdcdefault_boolean(context, plgctx->realmname,
1136 KRB5_CONF_PKINIT_ALLOW_UPN,
1137 0, &plgctx->opts->allow_upn);
1139 pkinit_kdcdefault_boolean(context, plgctx->realmname,
1140 KRB5_CONF_PKINIT_REQUIRE_CRL_CHECKING,
1141 0, &plgctx->opts->require_crl_checking);
1143 pkinit_kdcdefault_string(context, plgctx->realmname,
1144 KRB5_CONF_PKINIT_EKU_CHECKING,
1146 if (eku_string != NULL) {
1147 if (strcasecmp(eku_string, "kpClientAuth") == 0) {
1148 plgctx->opts->require_eku = 1;
1149 plgctx->opts->accept_secondary_eku = 0;
1150 } else if (strcasecmp(eku_string, "scLogin") == 0) {
1151 plgctx->opts->require_eku = 1;
1152 plgctx->opts->accept_secondary_eku = 1;
1153 } else if (strcasecmp(eku_string, "none") == 0) {
1154 plgctx->opts->require_eku = 0;
1155 plgctx->opts->accept_secondary_eku = 0;
1157 pkiDebug("%s: Invalid value for pkinit_eku_checking: '%s'\n",
1158 __FUNCTION__, eku_string);
1166 pkinit_fini_kdc_profile(context, plgctx);
1170 static pkinit_kdc_context
1171 pkinit_find_realm_context(krb5_context context,
1172 krb5_kdcpreauth_moddata moddata,
1173 krb5_principal princ)
1176 pkinit_kdc_context *realm_contexts = (pkinit_kdc_context *)moddata;
1178 if (moddata == NULL)
1181 for (i = 0; realm_contexts[i] != NULL; i++) {
1182 pkinit_kdc_context p = realm_contexts[i];
1184 if ((p->realmname_len == princ->realm.length) &&
1185 (strncmp(p->realmname, princ->realm.data, p->realmname_len) == 0)) {
1186 pkiDebug("%s: returning context at %p for realm '%s'\n",
1187 __FUNCTION__, p, p->realmname);
1191 pkiDebug("%s: unable to find realm context for realm '%.*s'\n",
1192 __FUNCTION__, princ->realm.length, princ->realm.data);
1197 pkinit_server_plugin_init_realm(krb5_context context, const char *realmname,
1198 pkinit_kdc_context *pplgctx)
1200 krb5_error_code retval = ENOMEM;
1201 pkinit_kdc_context plgctx = NULL;
1205 plgctx = calloc(1, sizeof(*plgctx));
1209 pkiDebug("%s: initializing context at %p for realm '%s'\n",
1210 __FUNCTION__, plgctx, realmname);
1211 memset(plgctx, 0, sizeof(*plgctx));
1212 plgctx->magic = PKINIT_CTX_MAGIC;
1214 plgctx->realmname = strdup(realmname);
1215 if (plgctx->realmname == NULL)
1217 plgctx->realmname_len = strlen(plgctx->realmname);
1219 retval = pkinit_init_plg_crypto(&plgctx->cryptoctx);
1223 retval = pkinit_init_plg_opts(&plgctx->opts);
1227 retval = pkinit_init_identity_crypto(&plgctx->idctx);
1231 retval = pkinit_init_identity_opts(&plgctx->idopts);
1235 retval = pkinit_init_kdc_profile(context, plgctx);
1239 retval = pkinit_identity_initialize(context, plgctx->cryptoctx, NULL,
1240 plgctx->idopts, plgctx->idctx, 0, NULL);
1244 pkiDebug("%s: returning context at %p for realm '%s'\n",
1245 __FUNCTION__, plgctx, realmname);
1251 pkinit_server_plugin_fini_realm(context, plgctx);
1257 pkinit_server_plugin_init(krb5_context context,
1258 krb5_kdcpreauth_moddata *moddata_out,
1259 const char **realmnames)
1261 krb5_error_code retval = ENOMEM;
1262 pkinit_kdc_context plgctx, *realm_contexts = NULL;
1266 retval = pkinit_accessor_init();
1270 /* Determine how many realms we may need to support */
1271 for (i = 0; realmnames[i] != NULL; i++) {};
1274 realm_contexts = calloc(numrealms+1, sizeof(pkinit_kdc_context));
1275 if (realm_contexts == NULL)
1278 for (i = 0, j = 0; i < numrealms; i++) {
1279 pkiDebug("%s: processing realm '%s'\n", __FUNCTION__, realmnames[i]);
1280 retval = pkinit_server_plugin_init_realm(context, realmnames[i], &plgctx);
1281 if (retval == 0 && plgctx != NULL)
1282 realm_contexts[j++] = plgctx;
1287 krb5_set_error_message(context, retval,
1288 _("No realms configured correctly for pkinit "
1293 *moddata_out = (krb5_kdcpreauth_moddata)realm_contexts;
1295 pkiDebug("%s: returning context at %p\n", __FUNCTION__, realm_contexts);
1299 pkinit_server_plugin_fini(context,
1300 (krb5_kdcpreauth_moddata)realm_contexts);
1307 pkinit_server_plugin_fini_realm(krb5_context context, pkinit_kdc_context plgctx)
1312 pkinit_fini_kdc_profile(context, plgctx);
1313 pkinit_fini_identity_opts(plgctx->idopts);
1314 pkinit_fini_identity_crypto(plgctx->idctx);
1315 pkinit_fini_plg_crypto(plgctx->cryptoctx);
1316 pkinit_fini_plg_opts(plgctx->opts);
1317 free(plgctx->realmname);
1322 pkinit_server_plugin_fini(krb5_context context,
1323 krb5_kdcpreauth_moddata moddata)
1325 pkinit_kdc_context *realm_contexts = (pkinit_kdc_context *)moddata;
1328 if (realm_contexts == NULL)
1331 for (i = 0; realm_contexts[i] != NULL; i++) {
1332 pkinit_server_plugin_fini_realm(context, realm_contexts[i]);
1334 pkiDebug("%s: freeing context at %p\n", __FUNCTION__, realm_contexts);
1335 free(realm_contexts);
1338 static krb5_error_code
1339 pkinit_init_kdc_req_context(krb5_context context, pkinit_kdc_req_context *ctx)
1341 krb5_error_code retval = ENOMEM;
1342 pkinit_kdc_req_context reqctx = NULL;
1344 reqctx = malloc(sizeof(*reqctx));
1347 memset(reqctx, 0, sizeof(*reqctx));
1348 reqctx->magic = PKINIT_CTX_MAGIC;
1350 retval = pkinit_init_req_crypto(&reqctx->cryptoctx);
1353 reqctx->rcv_auth_pack = NULL;
1354 reqctx->rcv_auth_pack9 = NULL;
1356 pkiDebug("%s: returning reqctx at %p\n", __FUNCTION__, reqctx);
1361 pkinit_fini_kdc_req_context(context, reqctx);
1367 pkinit_fini_kdc_req_context(krb5_context context, void *ctx)
1369 pkinit_kdc_req_context reqctx = (pkinit_kdc_req_context)ctx;
1371 if (reqctx == NULL || reqctx->magic != PKINIT_CTX_MAGIC) {
1372 pkiDebug("pkinit_fini_kdc_req_context: got bad reqctx (%p)!\n", reqctx);
1375 pkiDebug("%s: freeing reqctx at %p\n", __FUNCTION__, reqctx);
1377 pkinit_fini_req_crypto(reqctx->cryptoctx);
1378 if (reqctx->rcv_auth_pack != NULL)
1379 free_krb5_auth_pack(&reqctx->rcv_auth_pack);
1380 if (reqctx->rcv_auth_pack9 != NULL)
1381 free_krb5_auth_pack_draft9(context, &reqctx->rcv_auth_pack9);
1387 kdcpreauth_pkinit_initvt(krb5_context context, int maj_ver, int min_ver,
1388 krb5_plugin_vtable vtable);
1391 kdcpreauth_pkinit_initvt(krb5_context context, int maj_ver, int min_ver,
1392 krb5_plugin_vtable vtable)
1394 krb5_kdcpreauth_vtable vt;
1397 return KRB5_PLUGIN_VER_NOTSUPP;
1398 vt = (krb5_kdcpreauth_vtable)vtable;
1399 vt->name = "pkinit";
1400 vt->pa_type_list = supported_server_pa_types;
1401 vt->init = pkinit_server_plugin_init;
1402 vt->fini = pkinit_server_plugin_fini;
1403 vt->flags = pkinit_server_get_flags;
1404 vt->edata = pkinit_server_get_edata;
1405 vt->verify = pkinit_server_verify_padata;
1406 vt->return_padata = pkinit_server_return_padata;