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
45 * It is anticipated that all the special checks currently
46 * required when talking to a Longhorn server will go away
47 * by the time it is officially released and all references
48 * to the longhorn global can be removed and any code
49 * #ifdef'd with LONGHORN_BETA_COMPAT can be removed.
51 * Current testing (20070620) is against a patched Beta 3
52 * version of Longhorn. Most, if not all, problems should
53 * be fixed in SP1 of Longhorn.
55 int longhorn = 0; /* Talking to a Longhorn server? */
58 * Return true if we should use ContentInfo rather than SignedData. This
59 * happens if we are talking to what might be an old (pre-6112) MIT KDC and
60 * we're using anonymous.
63 use_content_info(krb5_context context, pkinit_req_context req,
64 krb5_principal client)
68 if (krb5_principal_compare_any_realm(context, client,
69 krb5_anonymous_principal()))
74 static krb5_error_code
75 pkinit_as_req_create(krb5_context context, pkinit_context plgctx,
76 pkinit_req_context reqctx, krb5_timestamp ctsec,
77 krb5_int32 cusec, krb5_ui_4 nonce,
78 const krb5_checksum *cksum,
79 krb5_principal client, krb5_principal server,
82 static krb5_error_code
83 pkinit_as_rep_parse(krb5_context context, pkinit_context plgctx,
84 pkinit_req_context reqctx, krb5_preauthtype pa_type,
85 krb5_kdc_req *request, const krb5_data *as_rep,
86 krb5_keyblock *key_block, krb5_enctype etype, krb5_data *);
88 static void pkinit_client_plugin_fini(krb5_context context,
89 krb5_clpreauth_moddata moddata);
91 static krb5_error_code
92 pa_pkinit_gen_req(krb5_context context,
93 pkinit_context plgctx,
94 pkinit_req_context reqctx,
95 krb5_kdc_req * request,
96 krb5_preauthtype pa_type,
97 krb5_pa_data *** out_padata,
98 krb5_prompter_fct prompter,
100 krb5_get_init_creds_opt *gic_opt)
103 krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED;
104 krb5_data *out_data = NULL;
105 krb5_timestamp ctsec = 0;
106 krb5_int32 cusec = 0;
109 krb5_data *der_req = NULL;
110 krb5_pa_data **return_pa_data = NULL;
112 cksum.contents = NULL;
113 reqctx->pa_type = pa_type;
115 pkiDebug("kdc_options = 0x%x till = %d\n",
116 request->kdc_options, request->till);
117 /* If we don't have a client, we're done */
118 if (request->client == NULL) {
119 pkiDebug("No request->client; aborting PKINIT\n");
120 return KRB5KDC_ERR_PREAUTH_FAILED;
123 retval = pkinit_get_kdc_cert(context, plgctx->cryptoctx, reqctx->cryptoctx,
124 reqctx->idctx, request->server);
126 pkiDebug("pkinit_get_kdc_cert returned %d\n", retval);
130 /* checksum of the encoded KDC-REQ-BODY */
131 retval = k5int_encode_krb5_kdc_req_body(request, &der_req);
133 pkiDebug("encode_krb5_kdc_req_body returned %d\n", (int) retval);
137 retval = krb5_c_make_checksum(context, CKSUMTYPE_NIST_SHA, NULL, 0,
141 TRACE_PKINIT_CLIENT_REQ_CHECKSUM(context, &cksum);
143 pkiDebug("calculating checksum on buf size (%d)\n", der_req->length);
144 print_buffer(der_req->data, der_req->length);
147 retval = krb5_us_timeofday(context, &ctsec, &cusec);
151 /* XXX PKINIT RFC says that nonce in PKAuthenticator doesn't have be the
152 * same as in the AS_REQ. However, if we pick a different nonce, then we
153 * need to remember that info when AS_REP is returned. I'm choosing to
154 * reuse the AS_REQ nonce.
156 nonce = request->nonce;
158 retval = pkinit_as_req_create(context, plgctx, reqctx, ctsec, cusec,
159 nonce, &cksum, request->client, request->server, &out_data);
160 if (retval || !out_data->length) {
161 pkiDebug("error %d on pkinit_as_req_create; aborting PKINIT\n",
167 * The most we'll return is two pa_data, normally just one.
168 * We need to make room for the NULL terminator.
170 return_pa_data = malloc(3 * sizeof(krb5_pa_data *));
171 if (return_pa_data == NULL)
174 return_pa_data[1] = NULL; /* in case of an early trip to cleanup */
175 return_pa_data[2] = NULL; /* Terminate the list */
177 return_pa_data[0] = malloc(sizeof(krb5_pa_data));
178 if (return_pa_data[0] == NULL)
181 return_pa_data[1] = malloc(sizeof(krb5_pa_data));
182 if (return_pa_data[1] == NULL)
185 return_pa_data[0]->magic = KV5M_PA_DATA;
187 if (pa_type == KRB5_PADATA_PK_AS_REQ_OLD)
188 return_pa_data[0]->pa_type = KRB5_PADATA_PK_AS_REP_OLD;
190 return_pa_data[0]->pa_type = pa_type;
191 return_pa_data[0]->length = out_data->length;
192 return_pa_data[0]->contents = (krb5_octet *) out_data->data;
195 * LH Beta 3 requires the extra pa-data, even for RFC requests,
196 * in order to get the Checksum rather than a Nonce in the reply.
197 * This can be removed when LH SP1 is released.
199 if ((return_pa_data[0]->pa_type == KRB5_PADATA_PK_AS_REP_OLD
200 && reqctx->opts->win2k_require_cksum) || (longhorn == 1)) {
201 return_pa_data[1]->pa_type = 132;
202 return_pa_data[1]->length = 0;
203 return_pa_data[1]->contents = NULL;
205 free(return_pa_data[1]);
206 return_pa_data[1] = NULL; /* Move the list terminator */
208 *out_padata = return_pa_data;
213 krb5_free_data(context, der_req);
217 if (return_pa_data) {
218 free(return_pa_data[0]);
219 free(return_pa_data[1]);
220 free(return_pa_data);
223 free(out_data->data);
230 static krb5_error_code
231 pkinit_as_req_create(krb5_context context,
232 pkinit_context plgctx,
233 pkinit_req_context reqctx,
234 krb5_timestamp ctsec,
237 const krb5_checksum * cksum,
238 krb5_principal client,
239 krb5_principal server,
242 krb5_error_code retval = ENOMEM;
243 krb5_subject_pk_info *info = NULL;
244 krb5_data *coded_auth_pack = NULL;
245 krb5_auth_pack *auth_pack = NULL;
246 krb5_pa_pk_as_req *req = NULL;
247 krb5_auth_pack_draft9 *auth_pack9 = NULL;
248 krb5_pa_pk_as_req_draft9 *req9 = NULL;
249 int protocol = reqctx->opts->dh_or_rsa;
251 pkiDebug("pkinit_as_req_create pa_type = %d\n", reqctx->pa_type);
253 /* Create the authpack */
254 switch((int)reqctx->pa_type) {
255 case KRB5_PADATA_PK_AS_REQ_OLD:
256 protocol = RSA_PROTOCOL;
257 init_krb5_auth_pack_draft9(&auth_pack9);
258 if (auth_pack9 == NULL)
260 auth_pack9->pkAuthenticator.ctime = ctsec;
261 auth_pack9->pkAuthenticator.cusec = cusec;
262 auth_pack9->pkAuthenticator.nonce = nonce;
263 auth_pack9->pkAuthenticator.kdcName = server;
264 free(cksum->contents);
266 case KRB5_PADATA_PK_AS_REQ:
267 init_krb5_subject_pk_info(&info);
270 init_krb5_auth_pack(&auth_pack);
271 if (auth_pack == NULL)
273 auth_pack->pkAuthenticator.ctime = ctsec;
274 auth_pack->pkAuthenticator.cusec = cusec;
275 auth_pack->pkAuthenticator.nonce = nonce;
276 auth_pack->pkAuthenticator.paChecksum = *cksum;
277 auth_pack->clientDHNonce.length = 0;
278 auth_pack->clientPublicValue = info;
279 auth_pack->supportedKDFs = (krb5_data **) supported_kdf_alg_ids;
281 /* add List of CMS algorithms */
282 retval = create_krb5_supportedCMSTypes(context, plgctx->cryptoctx,
283 reqctx->cryptoctx, reqctx->idctx,
284 &auth_pack->supportedCMSTypes);
289 pkiDebug("as_req: unrecognized pa_type = %d\n",
290 (int)reqctx->pa_type);
297 TRACE_PKINIT_CLIENT_REQ_DH(context);
298 pkiDebug("as_req: DH key transport algorithm\n");
299 retval = pkinit_copy_krb5_data(&info->algorithm.algorithm, &dh_oid);
301 pkiDebug("failed to copy dh_oid\n");
305 /* create client-side DH keys */
306 if ((retval = client_create_dh(context, plgctx->cryptoctx,
307 reqctx->cryptoctx, reqctx->idctx, reqctx->opts->dh_size,
309 &info->algorithm.parameters.data,
310 &info->algorithm.parameters.length,
312 &info->subjectPublicKey.data,
313 &info->subjectPublicKey.length)) != 0) {
314 pkiDebug("failed to create dh parameters\n");
319 TRACE_PKINIT_CLIENT_REQ_RSA(context);
320 pkiDebug("as_req: RSA key transport algorithm\n");
321 switch((int)reqctx->pa_type) {
322 case KRB5_PADATA_PK_AS_REQ_OLD:
323 auth_pack9->clientPublicValue = NULL;
325 case KRB5_PADATA_PK_AS_REQ:
326 free_krb5_subject_pk_info(&info);
327 auth_pack->clientPublicValue = NULL;
332 pkiDebug("as_req: unknown key transport protocol %d\n",
338 /* Encode the authpack */
339 switch((int)reqctx->pa_type) {
340 case KRB5_PADATA_PK_AS_REQ:
341 retval = k5int_encode_krb5_auth_pack(auth_pack, &coded_auth_pack);
343 case KRB5_PADATA_PK_AS_REQ_OLD:
344 retval = k5int_encode_krb5_auth_pack_draft9(auth_pack9,
349 pkiDebug("failed to encode the AuthPack %d\n", retval);
353 print_buffer_bin((unsigned char *)coded_auth_pack->data,
354 coded_auth_pack->length,
355 "/tmp/client_auth_pack");
358 /* create PKCS7 object from authpack */
359 switch((int)reqctx->pa_type) {
360 case KRB5_PADATA_PK_AS_REQ:
361 init_krb5_pa_pk_as_req(&req);
366 if (use_content_info(context, reqctx, client)) {
367 retval = cms_contentinfo_create(context, plgctx->cryptoctx,
368 reqctx->cryptoctx, reqctx->idctx,
371 coded_auth_pack->data,
372 coded_auth_pack->length,
374 &req->signedAuthPack.data,
375 &req->signedAuthPack.length);
377 retval = cms_signeddata_create(context, plgctx->cryptoctx,
378 reqctx->cryptoctx, reqctx->idctx,
381 coded_auth_pack->data,
382 coded_auth_pack->length,
384 &req->signedAuthPack.data,
385 &req->signedAuthPack.length);
388 print_buffer_bin((unsigned char *)req->signedAuthPack.data,
389 req->signedAuthPack.length,
390 "/tmp/client_signed_data");
393 case KRB5_PADATA_PK_AS_REQ_OLD:
394 init_krb5_pa_pk_as_req_draft9(&req9);
399 retval = cms_signeddata_create(context, plgctx->cryptoctx,
400 reqctx->cryptoctx, reqctx->idctx, CMS_SIGN_DRAFT9, 1,
401 (unsigned char *)coded_auth_pack->data,
402 coded_auth_pack->length,
404 &req9->signedAuthPack.data,
405 &req9->signedAuthPack.length);
408 print_buffer_bin((unsigned char *)req9->signedAuthPack.data,
409 req9->signedAuthPack.length,
410 "/tmp/client_signed_data_draft9");
413 krb5_free_data(context, coded_auth_pack);
415 pkiDebug("failed to create pkcs7 signed data\n");
419 /* create a list of trusted CAs */
420 switch((int)reqctx->pa_type) {
421 case KRB5_PADATA_PK_AS_REQ:
422 retval = create_krb5_trustedCertifiers(context, plgctx->cryptoctx,
423 reqctx->cryptoctx, reqctx->idctx, &req->trustedCertifiers);
426 retval = create_issuerAndSerial(context, plgctx->cryptoctx,
427 reqctx->cryptoctx, reqctx->idctx,
428 (unsigned char **)&req->kdcPkId.data,
429 &req->kdcPkId.length);
433 /* Encode the as-req */
434 retval = k5int_encode_krb5_pa_pk_as_req(req, as_req);
436 case KRB5_PADATA_PK_AS_REQ_OLD:
437 retval = create_issuerAndSerial(context, plgctx->cryptoctx,
438 reqctx->cryptoctx, reqctx->idctx,
439 (unsigned char **)&req9->kdcCert.data,
440 &req9->kdcCert.length);
443 /* Encode the as-req */
444 retval = k5int_encode_krb5_pa_pk_as_req_draft9(req9, as_req);
449 print_buffer_bin((unsigned char *)(*as_req)->data, (*as_req)->length,
450 "/tmp/client_as_req");
454 switch((int)reqctx->pa_type) {
455 case KRB5_PADATA_PK_AS_REQ:
456 auth_pack->supportedKDFs = NULL; /*alias to global constant*/
457 free_krb5_auth_pack(&auth_pack);
458 free_krb5_pa_pk_as_req(&req);
460 case KRB5_PADATA_PK_AS_REQ_OLD:
461 free_krb5_pa_pk_as_req_draft9(&req9);
467 pkiDebug("pkinit_as_req_create retval=%d\n", (int) retval);
472 static krb5_error_code
473 pa_pkinit_parse_rep(krb5_context context,
474 pkinit_context plgctx,
475 pkinit_req_context reqctx,
476 krb5_kdc_req * request,
477 krb5_pa_data * in_padata,
479 krb5_keyblock * as_key,
480 krb5_data *encoded_request)
482 krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED;
483 krb5_data asRep = { 0, 0, NULL};
486 * One way or the other - success or failure - no other PA systems can
487 * work if the server sent us a PKINIT reply, since only we know how to
490 if ((in_padata == NULL) || (in_padata->length == 0)) {
491 pkiDebug("pa_pkinit_parse_rep: no in_padata\n");
492 return KRB5KDC_ERR_PREAUTH_FAILED;
495 asRep.data = (char *) in_padata->contents;
496 asRep.length = in_padata->length;
499 pkinit_as_rep_parse(context, plgctx, reqctx, in_padata->pa_type,
500 request, &asRep, as_key, etype, encoded_request);
502 pkiDebug("pkinit_as_rep_parse returned %d (%s)\n",
503 retval, error_message(retval));
514 static krb5_error_code
515 verify_kdc_san(krb5_context context,
516 pkinit_context plgctx,
517 pkinit_req_context reqctx,
518 krb5_principal kdcprinc,
520 int *need_eku_checking)
522 krb5_error_code retval;
523 char **certhosts = NULL, **cfghosts = NULL, **hostptr;
524 krb5_principal *princs = NULL, *princptr;
525 unsigned char ***get_dns;
529 *need_eku_checking = 1;
531 retval = pkinit_libdefault_strings(context,
532 krb5_princ_realm(context, kdcprinc),
533 KRB5_CONF_PKINIT_KDC_HOSTNAME,
535 if (retval || cfghosts == NULL) {
536 pkiDebug("%s: No pkinit_kdc_hostname values found in config file\n",
540 pkiDebug("%s: pkinit_kdc_hostname values found in config file\n",
542 for (hostptr = cfghosts; *hostptr != NULL; hostptr++)
543 TRACE_PKINIT_CLIENT_SAN_CONFIG_DNSNAME(context, *hostptr);
544 get_dns = (unsigned char ***)&certhosts;
547 retval = crypto_retrieve_cert_sans(context, plgctx->cryptoctx,
548 reqctx->cryptoctx, reqctx->idctx,
549 &princs, NULL, get_dns);
551 pkiDebug("%s: error from retrieve_certificate_sans()\n", __FUNCTION__);
552 TRACE_PKINIT_CLIENT_SAN_ERR(context);
553 retval = KRB5KDC_ERR_KDC_NAME_MISMATCH;
556 for (princptr = princs; *princptr != NULL; princptr++)
557 TRACE_PKINIT_CLIENT_SAN_KDCCERT_PRINC(context, *princptr);
558 if (certhosts != NULL) {
559 for (hostptr = certhosts; *hostptr != NULL; hostptr++)
560 TRACE_PKINIT_CLIENT_SAN_KDCCERT_DNSNAME(context, *hostptr);
563 retval = call_san_checking_plugins(context, plgctx, reqctx, idctx,
564 princs, hosts, &plugin_decision,
566 pkiDebug("%s: call_san_checking_plugins() returned retval %d\n",
569 retval = KRB5KDC_ERR_KDC_NAME_MISMATCH;
572 pkiDebug("%s: call_san_checking_plugins() returned decision %d and "
573 "need_eku_checking %d\n",
574 __FUNCTION__, plugin_decision, *need_eku_checking);
575 if (plugin_decision != NO_DECISION) {
576 retval = plugin_decision;
581 pkiDebug("%s: Checking pkinit sans\n", __FUNCTION__);
582 for (i = 0; princs != NULL && princs[i] != NULL; i++) {
583 if (krb5_principal_compare(context, princs[i], kdcprinc)) {
584 TRACE_PKINIT_CLIENT_SAN_MATCH_PRINC(context, kdcprinc);
585 pkiDebug("%s: pkinit san match found\n", __FUNCTION__);
587 *need_eku_checking = 0;
592 pkiDebug("%s: no pkinit san match found\n", __FUNCTION__);
594 if (certhosts == NULL) {
595 pkiDebug("%s: no certhosts (or we wouldn't accept them anyway)\n",
597 retval = KRB5KDC_ERR_KDC_NAME_MISMATCH;
601 for (i = 0; certhosts[i] != NULL; i++) {
602 for (j = 0; cfghosts != NULL && cfghosts[j] != NULL; j++) {
603 pkiDebug("%s: comparing cert name '%s' with config name '%s'\n",
604 __FUNCTION__, certhosts[i], cfghosts[j]);
605 if (strcmp(certhosts[i], cfghosts[j]) == 0) {
606 TRACE_PKINIT_CLIENT_SAN_MATCH_DNSNAME(context, certhosts[i]);
607 pkiDebug("%s: we have a dnsName match\n", __FUNCTION__);
614 TRACE_PKINIT_CLIENT_SAN_MATCH_NONE(context);
615 pkiDebug("%s: no dnsName san match found\n", __FUNCTION__);
617 /* We found no match */
621 if (princs != NULL) {
622 for (i = 0; princs[i] != NULL; i++)
623 krb5_free_principal(context, princs[i]);
626 if (certhosts != NULL) {
627 for (i = 0; certhosts[i] != NULL; i++)
631 if (cfghosts != NULL)
632 profile_free_list(cfghosts);
634 pkiDebug("%s: returning retval %d, valid_san %d, need_eku_checking %d\n",
635 __FUNCTION__, retval, *valid_san, *need_eku_checking);
639 static krb5_error_code
640 verify_kdc_eku(krb5_context context,
641 pkinit_context plgctx,
642 pkinit_req_context reqctx,
645 krb5_error_code retval;
649 if (reqctx->opts->require_eku == 0) {
650 TRACE_PKINIT_CLIENT_EKU_SKIP(context);
651 pkiDebug("%s: configuration requests no EKU checking\n", __FUNCTION__);
656 retval = crypto_check_cert_eku(context, plgctx->cryptoctx,
657 reqctx->cryptoctx, reqctx->idctx,
659 reqctx->opts->accept_secondary_eku,
662 pkiDebug("%s: Error from crypto_check_cert_eku %d (%s)\n",
663 __FUNCTION__, retval, error_message(retval));
669 TRACE_PKINIT_CLIENT_EKU_ACCEPT(context);
671 TRACE_PKINIT_CLIENT_EKU_REJECT(context);
672 pkiDebug("%s: returning retval %d, eku_accepted %d\n",
673 __FUNCTION__, retval, *eku_accepted);
678 * Parse PA-PK-AS-REP message. Optionally evaluates the message's
680 * Optionally returns various components.
682 static krb5_error_code
683 pkinit_as_rep_parse(krb5_context context,
684 pkinit_context plgctx,
685 pkinit_req_context reqctx,
686 krb5_preauthtype pa_type,
687 krb5_kdc_req *request,
688 const krb5_data *as_rep,
689 krb5_keyblock *key_block,
691 krb5_data *encoded_request)
693 krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED;
694 krb5_principal kdc_princ = NULL;
695 krb5_pa_pk_as_rep *kdc_reply = NULL;
696 krb5_kdc_dh_key_info *kdc_dh = NULL;
697 krb5_reply_key_pack *key_pack = NULL;
698 krb5_reply_key_pack_draft9 *key_pack9 = NULL;
699 krb5_data dh_data = { 0, 0, NULL };
700 unsigned char *client_key = NULL, *kdc_hostname = NULL;
701 unsigned int client_key_len = 0;
702 krb5_checksum cksum = {0, 0, 0, NULL};
707 int need_eku_checking = 1;
709 assert((as_rep != NULL) && (key_block != NULL));
712 print_buffer_bin((unsigned char *)as_rep->data, as_rep->length,
713 "/tmp/client_as_rep");
716 if ((retval = k5int_decode_krb5_pa_pk_as_rep(as_rep, &kdc_reply))) {
717 pkiDebug("decode_krb5_as_rep failed %d\n", retval);
721 switch(kdc_reply->choice) {
722 case choice_pa_pk_as_rep_dhInfo:
723 pkiDebug("as_rep: DH key transport algorithm\n");
725 print_buffer_bin(kdc_reply->u.dh_Info.dhSignedData.data,
726 kdc_reply->u.dh_Info.dhSignedData.length, "/tmp/client_kdc_signeddata");
728 if ((retval = cms_signeddata_verify(context, plgctx->cryptoctx,
729 reqctx->cryptoctx, reqctx->idctx, CMS_SIGN_SERVER,
730 reqctx->opts->require_crl_checking,
732 kdc_reply->u.dh_Info.dhSignedData.data,
733 kdc_reply->u.dh_Info.dhSignedData.length,
734 (unsigned char **)&dh_data.data,
736 NULL, NULL, NULL)) != 0) {
737 pkiDebug("failed to verify pkcs7 signed data\n");
738 TRACE_PKINIT_CLIENT_REP_DH_FAIL(context);
741 TRACE_PKINIT_CLIENT_REP_DH(context);
743 case choice_pa_pk_as_rep_encKeyPack:
744 pkiDebug("as_rep: RSA key transport algorithm\n");
745 if ((retval = cms_envelopeddata_verify(context, plgctx->cryptoctx,
746 reqctx->cryptoctx, reqctx->idctx, pa_type,
747 reqctx->opts->require_crl_checking,
749 kdc_reply->u.encKeyPack.data,
750 kdc_reply->u.encKeyPack.length,
751 (unsigned char **)&dh_data.data,
752 &dh_data.length)) != 0) {
753 pkiDebug("failed to verify pkcs7 enveloped data\n");
754 TRACE_PKINIT_CLIENT_REP_RSA_FAIL(context);
757 TRACE_PKINIT_CLIENT_REP_RSA(context);
760 pkiDebug("unknown as_rep type %d\n", kdc_reply->choice);
764 retval = krb5_build_principal_ext(context, &kdc_princ,
765 request->server->realm.length,
766 request->server->realm.data,
767 strlen(KRB5_TGS_NAME), KRB5_TGS_NAME,
768 request->server->realm.length,
769 request->server->realm.data,
773 retval = verify_kdc_san(context, plgctx, reqctx, kdc_princ,
774 &valid_san, &need_eku_checking);
778 pkiDebug("%s: did not find an acceptable SAN in KDC certificate\n",
780 retval = KRB5KDC_ERR_KDC_NAME_MISMATCH;
784 if (need_eku_checking) {
785 retval = verify_kdc_eku(context, plgctx, reqctx,
790 pkiDebug("%s: did not find an acceptable EKU in KDC certificate\n",
792 retval = KRB5KDC_ERR_INCONSISTENT_KEY_PURPOSE;
796 pkiDebug("%s: skipping EKU check\n", __FUNCTION__);
798 OCTETDATA_TO_KRB5DATA(&dh_data, &k5data);
800 switch(kdc_reply->choice) {
801 case choice_pa_pk_as_rep_dhInfo:
803 print_buffer_bin(dh_data.data, dh_data.length,
804 "/tmp/client_dh_key");
806 if ((retval = k5int_decode_krb5_kdc_dh_key_info(&k5data,
808 pkiDebug("failed to decode kdc_dh_key_info\n");
812 /* client after KDC reply */
813 if ((retval = client_process_dh(context, plgctx->cryptoctx,
814 reqctx->cryptoctx, reqctx->idctx,
816 kdc_dh->subjectPublicKey.data,
817 kdc_dh->subjectPublicKey.length,
818 &client_key, &client_key_len)) != 0) {
819 pkiDebug("failed to process dh params\n");
823 /* If we have a KDF algorithm ID, call the algorithm agility KDF... */
824 if (kdc_reply->u.dh_Info.kdfID) {
825 secret.length = client_key_len;
826 secret.data = (char *)client_key;
828 retval = pkinit_alg_agility_kdf(context, &secret,
829 kdc_reply->u.dh_Info.kdfID,
830 request->client, request->server,
831 etype, encoded_request,
832 (krb5_data *)as_rep, key_block);
835 pkiDebug("failed to create key pkinit_alg_agility_kdf %s\n",
836 error_message(retval));
839 TRACE_PKINIT_CLIENT_KDF_ALG(context, kdc_reply->u.dh_Info.kdfID,
842 /* ...otherwise, use the older octetstring2key function. */
845 retval = pkinit_octetstring2key(context, etype, client_key,
846 client_key_len, key_block);
848 pkiDebug("failed to create key pkinit_octetstring2key %s\n",
849 error_message(retval));
852 TRACE_PKINIT_CLIENT_KDF_OS2K(context, key_block);
856 case choice_pa_pk_as_rep_encKeyPack:
858 print_buffer_bin(dh_data.data, dh_data.length,
859 "/tmp/client_key_pack");
861 if ((retval = k5int_decode_krb5_reply_key_pack(&k5data,
863 pkiDebug("failed to decode reply_key_pack\n");
864 #ifdef LONGHORN_BETA_COMPAT
866 * LH Beta 3 requires the extra pa-data, even for RFC requests,
867 * in order to get the Checksum rather than a Nonce in the reply.
868 * This can be removed when LH SP1 is released.
870 if (pa_type == KRB5_PADATA_PK_AS_REP && longhorn == 0)
872 if (pa_type == KRB5_PADATA_PK_AS_REP)
877 k5int_decode_krb5_reply_key_pack_draft9(&k5data,
879 pkiDebug("failed to decode reply_key_pack_draft9\n");
882 pkiDebug("decode reply_key_pack_draft9\n");
883 if (key_pack9->nonce != request->nonce) {
884 pkiDebug("nonce in AS_REP=%d doesn't match AS_REQ=%d\n", key_pack9->nonce, request->nonce);
888 krb5_copy_keyblock_contents(context, &key_pack9->replyKey,
894 * This is hack but Windows sends back SHA1 checksum
895 * with checksum type of 14. There is currently no
896 * checksum type of 14 defined.
898 if (key_pack->asChecksum.checksum_type == 14)
899 key_pack->asChecksum.checksum_type = CKSUMTYPE_NIST_SHA;
900 retval = krb5_c_make_checksum(context,
901 key_pack->asChecksum.checksum_type,
903 KRB5_KEYUSAGE_TGS_REQ_AUTH_CKSUM,
904 encoded_request, &cksum);
906 pkiDebug("failed to make a checksum\n");
910 if ((cksum.length != key_pack->asChecksum.length) ||
911 memcmp(cksum.contents, key_pack->asChecksum.contents,
913 TRACE_PKINIT_CLIENT_REP_CHECKSUM_FAIL(context, &cksum,
914 &key_pack->asChecksum);
915 pkiDebug("failed to match the checksums\n");
917 pkiDebug("calculating checksum on buf size (%d)\n",
918 encoded_request->length);
919 print_buffer(encoded_request->data, encoded_request->length);
920 pkiDebug("encrypting key (%d)\n", key_pack->replyKey.length);
921 print_buffer(key_pack->replyKey.contents,
922 key_pack->replyKey.length);
923 pkiDebug("received checksum type=%d size=%d ",
924 key_pack->asChecksum.checksum_type,
925 key_pack->asChecksum.length);
926 print_buffer(key_pack->asChecksum.contents,
927 key_pack->asChecksum.length);
928 pkiDebug("expected checksum type=%d size=%d ",
929 cksum.checksum_type, cksum.length);
930 print_buffer(cksum.contents, cksum.length);
934 pkiDebug("checksums match\n");
936 krb5_copy_keyblock_contents(context, &key_pack->replyKey,
938 TRACE_PKINIT_CLIENT_REP_RSA_KEY(context, key_block, &cksum);
942 pkiDebug("unknow as_rep type %d\n", kdc_reply->choice);
950 krb5_free_principal(context, kdc_princ);
952 free_krb5_kdc_dh_key_info(&kdc_dh);
953 free_krb5_pa_pk_as_rep(&kdc_reply);
955 if (key_pack != NULL) {
956 free_krb5_reply_key_pack(&key_pack);
957 free(cksum.contents);
959 if (key_pack9 != NULL)
960 free_krb5_reply_key_pack_draft9(&key_pack9);
964 pkiDebug("pkinit_as_rep_parse returning %d (%s)\n",
965 retval, error_message(retval));
970 pkinit_client_profile(krb5_context context,
971 pkinit_context plgctx,
972 pkinit_req_context reqctx,
973 const krb5_data *realm)
975 char *eku_string = NULL;
977 pkiDebug("pkinit_client_profile %p %p %p %p\n",
978 context, plgctx, reqctx, realm);
980 pkinit_libdefault_boolean(context, realm,
981 KRB5_CONF_PKINIT_WIN2K,
982 reqctx->opts->win2k_target,
983 &reqctx->opts->win2k_target);
984 pkinit_libdefault_boolean(context, realm,
985 KRB5_CONF_PKINIT_WIN2K_REQUIRE_BINDING,
986 reqctx->opts->win2k_require_cksum,
987 &reqctx->opts->win2k_require_cksum);
988 pkinit_libdefault_boolean(context, realm,
989 KRB5_CONF_PKINIT_REQUIRE_CRL_CHECKING,
990 reqctx->opts->require_crl_checking,
991 &reqctx->opts->require_crl_checking);
992 pkinit_libdefault_integer(context, realm,
993 KRB5_CONF_PKINIT_DH_MIN_BITS,
994 reqctx->opts->dh_size,
995 &reqctx->opts->dh_size);
996 if (reqctx->opts->dh_size != 1024 && reqctx->opts->dh_size != 2048
997 && reqctx->opts->dh_size != 4096) {
998 pkiDebug("%s: invalid value (%d) for pkinit_dh_min_bits, "
999 "using default value (%d) instead\n", __FUNCTION__,
1000 reqctx->opts->dh_size, PKINIT_DEFAULT_DH_MIN_BITS);
1001 reqctx->opts->dh_size = PKINIT_DEFAULT_DH_MIN_BITS;
1003 pkinit_libdefault_string(context, realm,
1004 KRB5_CONF_PKINIT_EKU_CHECKING,
1006 if (eku_string != NULL) {
1007 if (strcasecmp(eku_string, "kpKDC") == 0) {
1008 reqctx->opts->require_eku = 1;
1009 reqctx->opts->accept_secondary_eku = 0;
1010 } else if (strcasecmp(eku_string, "kpServerAuth") == 0) {
1011 reqctx->opts->require_eku = 1;
1012 reqctx->opts->accept_secondary_eku = 1;
1013 } else if (strcasecmp(eku_string, "none") == 0) {
1014 reqctx->opts->require_eku = 0;
1015 reqctx->opts->accept_secondary_eku = 0;
1017 pkiDebug("%s: Invalid value for pkinit_eku_checking: '%s'\n",
1018 __FUNCTION__, eku_string);
1022 #ifdef LONGHORN_BETA_COMPAT
1023 /* Temporarily just set global flag from config file */
1024 pkinit_libdefault_boolean(context, realm,
1025 KRB5_CONF_PKINIT_LONGHORN,
1030 /* Only process anchors here if they were not specified on command line */
1031 if (reqctx->idopts->anchors == NULL)
1032 pkinit_libdefault_strings(context, realm,
1033 KRB5_CONF_PKINIT_ANCHORS,
1034 &reqctx->idopts->anchors);
1035 pkinit_libdefault_strings(context, realm,
1036 KRB5_CONF_PKINIT_POOL,
1037 &reqctx->idopts->intermediates);
1038 pkinit_libdefault_strings(context, realm,
1039 KRB5_CONF_PKINIT_REVOKE,
1040 &reqctx->idopts->crls);
1041 pkinit_libdefault_strings(context, realm,
1042 KRB5_CONF_PKINIT_IDENTITIES,
1043 &reqctx->idopts->identity_alt);
1046 static krb5_error_code
1047 pkinit_client_process(krb5_context context, krb5_clpreauth_moddata moddata,
1048 krb5_clpreauth_modreq modreq,
1049 krb5_get_init_creds_opt *gic_opt,
1050 krb5_clpreauth_callbacks cb, krb5_clpreauth_rock rock,
1051 krb5_kdc_req *request, krb5_data *encoded_request_body,
1052 krb5_data *encoded_previous_request,
1053 krb5_pa_data *in_padata,
1054 krb5_prompter_fct prompter, void *prompter_data,
1055 krb5_pa_data ***out_padata)
1057 krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED;
1058 krb5_enctype enctype = -1;
1059 int processing_request = 0;
1060 pkinit_context plgctx = (pkinit_context)moddata;
1061 pkinit_req_context reqctx = (pkinit_req_context)modreq;
1062 krb5_keyblock as_key;
1064 pkiDebug("pkinit_client_process %p %p %p %p\n",
1065 context, plgctx, reqctx, request);
1068 if (plgctx == NULL || reqctx == NULL)
1071 switch ((int) in_padata->pa_type) {
1072 case KRB5_PADATA_PKINIT_KX:
1073 reqctx->rfc6112_kdc = 1;
1075 case KRB5_PADATA_PK_AS_REQ:
1076 pkiDebug("processing KRB5_PADATA_PK_AS_REQ\n");
1077 processing_request = 1;
1080 case KRB5_PADATA_PK_AS_REP:
1081 pkiDebug("processing KRB5_PADATA_PK_AS_REP\n");
1083 case KRB5_PADATA_PK_AS_REP_OLD:
1084 case KRB5_PADATA_PK_AS_REQ_OLD:
1085 if (in_padata->length == 0) {
1086 pkiDebug("processing KRB5_PADATA_PK_AS_REQ_OLD\n");
1087 in_padata->pa_type = KRB5_PADATA_PK_AS_REQ_OLD;
1088 processing_request = 1;
1090 pkiDebug("processing KRB5_PADATA_PK_AS_REP_OLD\n");
1091 in_padata->pa_type = KRB5_PADATA_PK_AS_REP_OLD;
1095 pkiDebug("unrecognized patype = %d for PKINIT\n",
1096 in_padata->pa_type);
1100 if (processing_request) {
1101 pkinit_client_profile(context, plgctx, reqctx,
1102 &request->server->realm);
1103 pkinit_identity_set_prompter(reqctx->idctx, prompter, prompter_data);
1104 retval = pkinit_identity_initialize(context, plgctx->cryptoctx,
1105 reqctx->cryptoctx, reqctx->idopts,
1106 reqctx->idctx, 1, request->client);
1108 TRACE_PKINIT_CLIENT_NO_IDENTITY(context);
1109 pkiDebug("pkinit_identity_initialize returned %d (%s)\n",
1110 retval, error_message(retval));
1113 retval = pa_pkinit_gen_req(context, plgctx, reqctx, request,
1114 in_padata->pa_type, out_padata, prompter,
1115 prompter_data, gic_opt);
1118 * Get the enctype of the reply.
1120 enctype = cb->get_etype(context, rock);
1121 retval = pa_pkinit_parse_rep(context, plgctx, reqctx, request,
1122 in_padata, enctype, &as_key,
1123 encoded_previous_request);
1125 retval = cb->set_as_key(context, rock, &as_key);
1128 pkiDebug("pkinit_client_process: returning %d (%s)\n",
1129 retval, error_message(retval));
1133 static krb5_error_code
1134 pkinit_client_tryagain(krb5_context context, krb5_clpreauth_moddata moddata,
1135 krb5_clpreauth_modreq modreq,
1136 krb5_get_init_creds_opt *gic_opt,
1137 krb5_clpreauth_callbacks cb, krb5_clpreauth_rock rock,
1138 krb5_kdc_req *request, krb5_data *encoded_request_body,
1139 krb5_data *encoded_previous_request,
1140 krb5_preauthtype pa_type, krb5_error *err_reply,
1141 krb5_pa_data **err_padata, krb5_prompter_fct prompter,
1142 void *prompter_data, krb5_pa_data ***out_padata)
1144 krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED;
1145 pkinit_context plgctx = (pkinit_context)moddata;
1146 pkinit_req_context reqctx = (pkinit_req_context)modreq;
1149 krb5_external_principal_identifier **certifiers = NULL;
1150 krb5_algorithm_identifier **algId = NULL;
1153 pkiDebug("pkinit_client_tryagain %p %p %p %p\n",
1154 context, plgctx, reqctx, request);
1156 if (reqctx->pa_type != pa_type || err_padata == NULL)
1159 for (; *err_padata != NULL && !do_again; err_padata++) {
1161 PADATA_TO_KRB5DATA(pa, &scratch);
1162 switch (pa->pa_type) {
1163 case TD_TRUSTED_CERTIFIERS:
1164 case TD_INVALID_CERTIFICATES:
1165 retval = k5int_decode_krb5_td_trusted_certifiers(&scratch,
1168 pkiDebug("failed to decode sequence of trusted certifiers\n");
1171 retval = pkinit_process_td_trusted_certifiers(context,
1180 case TD_DH_PARAMETERS:
1181 retval = k5int_decode_krb5_td_dh_parameters(&scratch, &algId);
1183 pkiDebug("failed to decode td_dh_parameters\n");
1186 retval = pkinit_process_td_dh_params(context, plgctx->cryptoctx,
1188 reqctx->idctx, algId,
1189 &reqctx->opts->dh_size);
1199 TRACE_PKINIT_CLIENT_TRYAGAIN(context);
1200 retval = pa_pkinit_gen_req(context, plgctx, reqctx, request, pa_type,
1201 out_padata, prompter, prompter_data,
1209 if (certifiers != NULL)
1210 free_krb5_external_principal_identifier(&certifiers);
1213 free_krb5_algorithm_identifiers(&algId);
1215 pkiDebug("pkinit_client_tryagain: returning %d (%s)\n",
1216 retval, error_message(retval));
1221 pkinit_client_get_flags(krb5_context kcontext, krb5_preauthtype patype)
1223 if (patype == KRB5_PADATA_PKINIT_KX)
1224 return PA_INFO|PA_PSEUDO;
1229 * We want to be notified about KRB5_PADATA_PKINIT_KX in addition to the actual
1230 * pkinit patypes because RFC 6112 requires anonymous KDCs to send it. We use
1231 * that to determine whether to use the broken MIT 1.9 behavior of sending
1232 * ContentInfo rather than SignedData or the RFC 6112 behavior
1234 static krb5_preauthtype supported_client_pa_types[] = {
1235 KRB5_PADATA_PK_AS_REP,
1236 KRB5_PADATA_PK_AS_REQ,
1237 KRB5_PADATA_PK_AS_REP_OLD,
1238 KRB5_PADATA_PK_AS_REQ_OLD,
1239 KRB5_PADATA_PKINIT_KX,
1244 pkinit_client_req_init(krb5_context context,
1245 krb5_clpreauth_moddata moddata,
1246 krb5_clpreauth_modreq *modreq_out)
1248 krb5_error_code retval = ENOMEM;
1249 pkinit_req_context reqctx = NULL;
1250 pkinit_context plgctx = (pkinit_context)moddata;
1254 reqctx = malloc(sizeof(*reqctx));
1257 memset(reqctx, 0, sizeof(*reqctx));
1259 reqctx->magic = PKINIT_REQ_CTX_MAGIC;
1260 reqctx->cryptoctx = NULL;
1261 reqctx->opts = NULL;
1262 reqctx->idctx = NULL;
1263 reqctx->idopts = NULL;
1265 retval = pkinit_init_req_opts(&reqctx->opts);
1269 reqctx->opts->require_eku = plgctx->opts->require_eku;
1270 reqctx->opts->accept_secondary_eku = plgctx->opts->accept_secondary_eku;
1271 reqctx->opts->dh_or_rsa = plgctx->opts->dh_or_rsa;
1272 reqctx->opts->allow_upn = plgctx->opts->allow_upn;
1273 reqctx->opts->require_crl_checking = plgctx->opts->require_crl_checking;
1275 retval = pkinit_init_req_crypto(&reqctx->cryptoctx);
1279 retval = pkinit_init_identity_crypto(&reqctx->idctx);
1283 retval = pkinit_dup_identity_opts(plgctx->idopts, &reqctx->idopts);
1287 *modreq_out = (krb5_clpreauth_modreq)reqctx;
1288 pkiDebug("%s: returning reqctx at %p\n", __FUNCTION__, reqctx);
1292 if (reqctx->idctx != NULL)
1293 pkinit_fini_identity_crypto(reqctx->idctx);
1294 if (reqctx->cryptoctx != NULL)
1295 pkinit_fini_req_crypto(reqctx->cryptoctx);
1296 if (reqctx->opts != NULL)
1297 pkinit_fini_req_opts(reqctx->opts);
1298 if (reqctx->idopts != NULL)
1299 pkinit_fini_identity_opts(reqctx->idopts);
1307 pkinit_client_req_fini(krb5_context context, krb5_clpreauth_moddata moddata,
1308 krb5_clpreauth_modreq modreq)
1310 pkinit_req_context reqctx = (pkinit_req_context)modreq;
1312 pkiDebug("%s: received reqctx at %p\n", __FUNCTION__, reqctx);
1315 if (reqctx->magic != PKINIT_REQ_CTX_MAGIC) {
1316 pkiDebug("%s: Bad magic value (%x) in req ctx\n",
1317 __FUNCTION__, reqctx->magic);
1320 if (reqctx->opts != NULL)
1321 pkinit_fini_req_opts(reqctx->opts);
1323 if (reqctx->cryptoctx != NULL)
1324 pkinit_fini_req_crypto(reqctx->cryptoctx);
1326 if (reqctx->idctx != NULL)
1327 pkinit_fini_identity_crypto(reqctx->idctx);
1329 if (reqctx->idopts != NULL)
1330 pkinit_fini_identity_opts(reqctx->idopts);
1337 pkinit_client_plugin_init(krb5_context context,
1338 krb5_clpreauth_moddata *moddata_out)
1340 krb5_error_code retval = ENOMEM;
1341 pkinit_context ctx = NULL;
1343 ctx = calloc(1, sizeof(*ctx));
1346 memset(ctx, 0, sizeof(*ctx));
1347 ctx->magic = PKINIT_CTX_MAGIC;
1349 ctx->cryptoctx = NULL;
1352 retval = pkinit_accessor_init();
1356 retval = pkinit_init_plg_opts(&ctx->opts);
1360 retval = pkinit_init_plg_crypto(&ctx->cryptoctx);
1364 retval = pkinit_init_identity_opts(&ctx->idopts);
1368 *moddata_out = (krb5_clpreauth_moddata)ctx;
1370 pkiDebug("%s: returning plgctx at %p\n", __FUNCTION__, ctx);
1374 pkinit_client_plugin_fini(context, (krb5_clpreauth_moddata)ctx);
1380 pkinit_client_plugin_fini(krb5_context context, krb5_clpreauth_moddata moddata)
1382 pkinit_context ctx = (pkinit_context)moddata;
1384 if (ctx == NULL || ctx->magic != PKINIT_CTX_MAGIC) {
1385 pkiDebug("pkinit_lib_fini: got bad plgctx (%p)!\n", ctx);
1388 pkiDebug("%s: got plgctx at %p\n", __FUNCTION__, ctx);
1390 pkinit_fini_identity_opts(ctx->idopts);
1391 pkinit_fini_plg_crypto(ctx->cryptoctx);
1392 pkinit_fini_plg_opts(ctx->opts);
1397 static krb5_error_code
1398 add_string_to_array(krb5_context context, char ***array, const char *addition)
1402 if (*array == NULL) {
1403 out = malloc(2 * sizeof(char *));
1407 out[0] = strdup(addition);
1408 if (out[0] == NULL) {
1415 for (i = 0; a[i] != NULL; i++);
1416 out = malloc( (i + 2) * sizeof(char *));
1419 for (i = 0; a[i] != NULL; i++) {
1422 out[i++] = strdup(addition);
1434 static krb5_error_code
1435 handle_gic_opt(krb5_context context,
1436 pkinit_context plgctx,
1440 krb5_error_code retval;
1442 if (strcmp(attr, "X509_user_identity") == 0) {
1443 if (plgctx->idopts->identity != NULL) {
1444 krb5_set_error_message(context, KRB5_PREAUTH_FAILED,
1445 "X509_user_identity can not be given twice\n");
1446 return KRB5_PREAUTH_FAILED;
1448 plgctx->idopts->identity = strdup(value);
1449 if (plgctx->idopts->identity == NULL) {
1450 krb5_set_error_message(context, ENOMEM,
1451 "Could not duplicate X509_user_identity value\n");
1454 } else if (strcmp(attr, "X509_anchors") == 0) {
1455 retval = add_string_to_array(context, &plgctx->idopts->anchors, value);
1458 } else if (strcmp(attr, "flag_RSA_PROTOCOL") == 0) {
1459 if (strcmp(value, "yes") == 0) {
1460 pkiDebug("Setting flag to use RSA_PROTOCOL\n");
1461 plgctx->opts->dh_or_rsa = RSA_PROTOCOL;
1467 static krb5_error_code
1468 pkinit_client_gic_opt(krb5_context context, krb5_clpreauth_moddata moddata,
1469 krb5_get_init_creds_opt *gic_opt,
1473 krb5_error_code retval;
1474 pkinit_context plgctx = (pkinit_context)moddata;
1476 pkiDebug("(pkinit) received '%s' = '%s'\n", attr, value);
1477 retval = handle_gic_opt(context, plgctx, attr, value);
1485 clpreauth_pkinit_initvt(krb5_context context, int maj_ver, int min_ver,
1486 krb5_plugin_vtable vtable);
1489 clpreauth_pkinit_initvt(krb5_context context, int maj_ver, int min_ver,
1490 krb5_plugin_vtable vtable)
1492 krb5_clpreauth_vtable vt;
1495 return KRB5_PLUGIN_VER_NOTSUPP;
1496 vt = (krb5_clpreauth_vtable)vtable;
1497 vt->name = "pkinit";
1498 vt->pa_type_list = supported_client_pa_types;
1499 vt->init = pkinit_client_plugin_init;
1500 vt->fini = pkinit_client_plugin_fini;
1501 vt->flags = pkinit_client_get_flags;
1502 vt->request_init = pkinit_client_req_init;
1503 vt->request_fini = pkinit_client_req_fini;
1504 vt->process = pkinit_client_process;
1505 vt->tryagain = pkinit_client_tryagain;
1506 vt->gic_opts = pkinit_client_gic_opt;