First pass at PKINIT client trace logs
[krb5.git] / src / plugins / preauth / pkinit / pkinit_clnt.c
index a6232e9979da399d592b852034ad84b0bdf76df6..4a27902f3542f46fe920c79d2a4318a1975ebccd 100644 (file)
@@ -41,9 +41,6 @@
 
 #include "pkinit.h"
 
-/* Remove when FAST PKINIT is settled. */
-#include "fast_factor.h"
-
 /*
  * It is anticipated that all the special checks currently
  * required when talking to a Longhorn server will go away
@@ -96,7 +93,7 @@ pa_pkinit_gen_req(krb5_context context,
                   pkinit_context plgctx,
                   pkinit_req_context reqctx,
                   krb5_kdc_req * request,
-                  krb5_pa_data * in_padata,
+                  krb5_preauthtype pa_type,
                   krb5_pa_data *** out_padata,
                   krb5_prompter_fct prompter,
                   void *prompter_data,
@@ -113,7 +110,7 @@ pa_pkinit_gen_req(krb5_context context,
     krb5_pa_data **return_pa_data = NULL;
 
     cksum.contents = NULL;
-    reqctx->pa_type = in_padata->pa_type;
+    reqctx->pa_type = pa_type;
 
     pkiDebug("kdc_options = 0x%x  till = %d\n",
              request->kdc_options, request->till);
@@ -141,6 +138,7 @@ pa_pkinit_gen_req(krb5_context context,
                                   der_req, &cksum);
     if (retval)
         goto cleanup;
+    TRACE_PKINIT_CLIENT_REQ_CHECKSUM(context, &cksum);
 #ifdef DEBUG_CKSUM
     pkiDebug("calculating checksum on buf size (%d)\n", der_req->length);
     print_buffer(der_req->data, der_req->length);
@@ -186,10 +184,10 @@ pa_pkinit_gen_req(krb5_context context,
 
     return_pa_data[0]->magic = KV5M_PA_DATA;
 
-    if (in_padata->pa_type == KRB5_PADATA_PK_AS_REQ_OLD)
+    if (pa_type == KRB5_PADATA_PK_AS_REQ_OLD)
         return_pa_data[0]->pa_type = KRB5_PADATA_PK_AS_REP_OLD;
     else
-        return_pa_data[0]->pa_type = in_padata->pa_type;
+        return_pa_data[0]->pa_type = pa_type;
     return_pa_data[0]->length = out_data->length;
     return_pa_data[0]->contents = (krb5_octet *) out_data->data;
 
@@ -263,10 +261,6 @@ pkinit_as_req_create(krb5_context context,
         auth_pack9->pkAuthenticator.cusec = cusec;
         auth_pack9->pkAuthenticator.nonce = nonce;
         auth_pack9->pkAuthenticator.kdcName = server;
-        auth_pack9->pkAuthenticator.kdcRealm.magic = 0;
-        auth_pack9->pkAuthenticator.kdcRealm.data =
-            (unsigned char *)server->realm.data;
-        auth_pack9->pkAuthenticator.kdcRealm.length = server->realm.length;
         free(cksum->contents);
         break;
     case KRB5_PADATA_PK_AS_REQ:
@@ -282,7 +276,7 @@ pkinit_as_req_create(krb5_context context,
         auth_pack->pkAuthenticator.paChecksum = *cksum;
         auth_pack->clientDHNonce.length = 0;
         auth_pack->clientPublicValue = info;
-        auth_pack->supportedKDFs = (krb5_octet_data **) supported_kdf_alg_ids;
+        auth_pack->supportedKDFs = (krb5_data **) supported_kdf_alg_ids;
 
         /* add List of CMS algorithms */
         retval = create_krb5_supportedCMSTypes(context, plgctx->cryptoctx,
@@ -300,8 +294,9 @@ pkinit_as_req_create(krb5_context context,
 
     switch(protocol) {
     case DH_PROTOCOL:
+        TRACE_PKINIT_CLIENT_REQ_DH(context);
         pkiDebug("as_req: DH key transport algorithm\n");
-        retval = pkinit_copy_krb5_octet_data(&info->algorithm.algorithm, &dh_oid);
+        retval = pkinit_copy_krb5_data(&info->algorithm.algorithm, &dh_oid);
         if (retval) {
             pkiDebug("failed to copy dh_oid\n");
             goto cleanup;
@@ -310,8 +305,10 @@ pkinit_as_req_create(krb5_context context,
         /* create client-side DH keys */
         if ((retval = client_create_dh(context, plgctx->cryptoctx,
                                        reqctx->cryptoctx, reqctx->idctx, reqctx->opts->dh_size,
+                                       (unsigned char **)
                                        &info->algorithm.parameters.data,
                                        &info->algorithm.parameters.length,
+                                       (unsigned char **)
                                        &info->subjectPublicKey.data,
                                        &info->subjectPublicKey.length)) != 0) {
             pkiDebug("failed to create dh parameters\n");
@@ -319,6 +316,7 @@ pkinit_as_req_create(krb5_context context,
         }
         break;
     case RSA_PROTOCOL:
+        TRACE_PKINIT_CLIENT_REQ_RSA(context);
         pkiDebug("as_req: RSA key transport algorithm\n");
         switch((int)reqctx->pa_type) {
         case KRB5_PADATA_PK_AS_REQ_OLD:
@@ -368,9 +366,11 @@ pkinit_as_req_create(krb5_context context,
         if (use_content_info(context, reqctx, client)) {
             retval = cms_contentinfo_create(context, plgctx->cryptoctx,
                                             reqctx->cryptoctx, reqctx->idctx,
-                                            CMS_SIGN_CLIENT, (unsigned char *)
+                                            CMS_SIGN_CLIENT,
+                                            (unsigned char *)
                                             coded_auth_pack->data,
                                             coded_auth_pack->length,
+                                            (unsigned char **)
                                             &req->signedAuthPack.data,
                                             &req->signedAuthPack.length);
         } else {
@@ -380,6 +380,7 @@ pkinit_as_req_create(krb5_context context,
                                            (unsigned char *)
                                            coded_auth_pack->data,
                                            coded_auth_pack->length,
+                                           (unsigned char **)
                                            &req->signedAuthPack.data,
                                            &req->signedAuthPack.length);
         }
@@ -397,8 +398,11 @@ pkinit_as_req_create(krb5_context context,
         }
         retval = cms_signeddata_create(context, plgctx->cryptoctx,
                                        reqctx->cryptoctx, reqctx->idctx, CMS_SIGN_DRAFT9, 1,
-                                       (unsigned char *)coded_auth_pack->data, coded_auth_pack->length,
-                                       &req9->signedAuthPack.data, &req9->signedAuthPack.length);
+                                       (unsigned char *)coded_auth_pack->data,
+                                       coded_auth_pack->length,
+                                       (unsigned char **)
+                                       &req9->signedAuthPack.data,
+                                       &req9->signedAuthPack.length);
         break;
 #ifdef DEBUG_ASN1
         print_buffer_bin((unsigned char *)req9->signedAuthPack.data,
@@ -420,7 +424,8 @@ pkinit_as_req_create(krb5_context context,
         if (retval)
             goto cleanup;
         retval = create_issuerAndSerial(context, plgctx->cryptoctx,
-                                        reqctx->cryptoctx, reqctx->idctx, &req->kdcPkId.data,
+                                        reqctx->cryptoctx, reqctx->idctx,
+                                        (unsigned char **)&req->kdcPkId.data,
                                         &req->kdcPkId.length);
         if (retval)
             goto cleanup;
@@ -429,16 +434,9 @@ pkinit_as_req_create(krb5_context context,
         retval = k5int_encode_krb5_pa_pk_as_req(req, as_req);
         break;
     case KRB5_PADATA_PK_AS_REQ_OLD:
-#if 0
-        /* W2K3 KDC doesn't like this */
-        retval = create_krb5_trustedCas(context, plgctx->cryptoctx,
-                                        reqctx->cryptoctx, reqctx->idctx, 1, &req9->trustedCertifiers);
-        if (retval)
-            goto cleanup;
-
-#endif
         retval = create_issuerAndSerial(context, plgctx->cryptoctx,
-                                        reqctx->cryptoctx, reqctx->idctx, &req9->kdcCert.data,
+                                        reqctx->cryptoctx, reqctx->idctx,
+                                        (unsigned char **)&req9->kdcCert.data,
                                         &req9->kdcCert.length);
         if (retval)
             goto cleanup;
@@ -522,8 +520,8 @@ verify_kdc_san(krb5_context context,
                int *need_eku_checking)
 {
     krb5_error_code retval;
-    char **certhosts = NULL, **cfghosts = NULL;
-    krb5_principal *princs = NULL;
+    char **certhosts = NULL, **cfghosts = NULL, **hostptr;
+    krb5_principal *princs = NULL, *princptr;
     unsigned char ***get_dns;
     int i, j;
 
@@ -541,6 +539,8 @@ verify_kdc_san(krb5_context context,
     } else {
         pkiDebug("%s: pkinit_kdc_hostname values found in config file\n",
                  __FUNCTION__);
+        for (hostptr = cfghosts; *hostptr != NULL; hostptr++)
+            TRACE_PKINIT_CLIENT_SAN_CONFIG_DNSNAME(context, *hostptr);
         get_dns = (unsigned char ***)&certhosts;
     }
 
@@ -549,9 +549,16 @@ verify_kdc_san(krb5_context context,
                                        &princs, NULL, get_dns);
     if (retval) {
         pkiDebug("%s: error from retrieve_certificate_sans()\n", __FUNCTION__);
+        TRACE_PKINIT_CLIENT_SAN_ERR(context);
         retval = KRB5KDC_ERR_KDC_NAME_MISMATCH;
         goto out;
     }
+    for (princptr = princs; *princptr != NULL; princptr++)
+        TRACE_PKINIT_CLIENT_SAN_KDCCERT_PRINC(context, *princptr);
+    if (certhosts != NULL) {
+        for (hostptr = certhosts; *hostptr != NULL; hostptr++)
+            TRACE_PKINIT_CLIENT_SAN_KDCCERT_DNSNAME(context, *hostptr);
+    }
 #if 0
     retval = call_san_checking_plugins(context, plgctx, reqctx, idctx,
                                        princs, hosts, &plugin_decision,
@@ -574,6 +581,7 @@ verify_kdc_san(krb5_context context,
     pkiDebug("%s: Checking pkinit sans\n", __FUNCTION__);
     for (i = 0; princs != NULL && princs[i] != NULL; i++) {
         if (krb5_principal_compare(context, princs[i], kdcprinc)) {
+            TRACE_PKINIT_CLIENT_SAN_MATCH_PRINC(context, kdcprinc);
             pkiDebug("%s: pkinit san match found\n", __FUNCTION__);
             *valid_san = 1;
             *need_eku_checking = 0;
@@ -595,6 +603,7 @@ verify_kdc_san(krb5_context context,
             pkiDebug("%s: comparing cert name '%s' with config name '%s'\n",
                      __FUNCTION__, certhosts[i], cfghosts[j]);
             if (strcmp(certhosts[i], cfghosts[j]) == 0) {
+                TRACE_PKINIT_CLIENT_SAN_MATCH_DNSNAME(context, certhosts[i]);
                 pkiDebug("%s: we have a dnsName match\n", __FUNCTION__);
                 *valid_san = 1;
                 retval = 0;
@@ -602,6 +611,7 @@ verify_kdc_san(krb5_context context,
             }
         }
     }
+    TRACE_PKINIT_CLIENT_SAN_MATCH_NONE(context);
     pkiDebug("%s: no dnsName san match found\n", __FUNCTION__);
 
     /* We found no match */
@@ -637,6 +647,7 @@ verify_kdc_eku(krb5_context context,
     *eku_accepted = 0;
 
     if (reqctx->opts->require_eku == 0) {
+        TRACE_PKINIT_CLIENT_EKU_SKIP(context);
         pkiDebug("%s: configuration requests no EKU checking\n", __FUNCTION__);
         *eku_accepted = 1;
         retval = 0;
@@ -654,6 +665,10 @@ verify_kdc_eku(krb5_context context,
     }
 
 out:
+    if (eku_accepted)
+        TRACE_PKINIT_CLIENT_EKU_ACCEPT(context);
+    else
+        TRACE_PKINIT_CLIENT_EKU_REJECT(context);
     pkiDebug("%s: returning retval %d, eku_accepted %d\n",
              __FUNCTION__, retval, *eku_accepted);
     return retval;
@@ -681,12 +696,12 @@ pkinit_as_rep_parse(krb5_context context,
     krb5_kdc_dh_key_info *kdc_dh = NULL;
     krb5_reply_key_pack *key_pack = NULL;
     krb5_reply_key_pack_draft9 *key_pack9 = NULL;
-    krb5_octet_data dh_data = { 0, 0, NULL };
+    krb5_data dh_data = { 0, 0, NULL };
     unsigned char *client_key = NULL, *kdc_hostname = NULL;
     unsigned int client_key_len = 0;
     krb5_checksum cksum = {0, 0, 0, NULL};
     krb5_data k5data;
-    krb5_octet_data secret;
+    krb5_data secret;
     int valid_san = 0;
     int valid_eku = 0;
     int need_eku_checking = 1;
@@ -713,26 +728,33 @@ pkinit_as_rep_parse(krb5_context context,
         if ((retval = cms_signeddata_verify(context, plgctx->cryptoctx,
                                             reqctx->cryptoctx, reqctx->idctx, CMS_SIGN_SERVER,
                                             reqctx->opts->require_crl_checking,
+                                            (unsigned char *)
                                             kdc_reply->u.dh_Info.dhSignedData.data,
                                             kdc_reply->u.dh_Info.dhSignedData.length,
-                                            &dh_data.data, &dh_data.length,
+                                            (unsigned char **)&dh_data.data,
+                                            &dh_data.length,
                                             NULL, NULL, NULL)) != 0) {
             pkiDebug("failed to verify pkcs7 signed data\n");
+            TRACE_PKINIT_CLIENT_REP_DH_FAIL(context);
             goto cleanup;
         }
-
+        TRACE_PKINIT_CLIENT_REP_DH(context);
         break;
     case choice_pa_pk_as_rep_encKeyPack:
         pkiDebug("as_rep: RSA key transport algorithm\n");
         if ((retval = cms_envelopeddata_verify(context, plgctx->cryptoctx,
                                                reqctx->cryptoctx, reqctx->idctx, pa_type,
                                                reqctx->opts->require_crl_checking,
+                                               (unsigned char *)
                                                kdc_reply->u.encKeyPack.data,
                                                kdc_reply->u.encKeyPack.length,
-                                               &dh_data.data, &dh_data.length)) != 0) {
+                                               (unsigned char **)&dh_data.data,
+                                               &dh_data.length)) != 0) {
             pkiDebug("failed to verify pkcs7 enveloped data\n");
+            TRACE_PKINIT_CLIENT_REP_RSA_FAIL(context);
             goto cleanup;
         }
+        TRACE_PKINIT_CLIENT_REP_RSA(context);
         break;
     default:
         pkiDebug("unknown as_rep type %d\n", kdc_reply->choice);
@@ -790,6 +812,7 @@ pkinit_as_rep_parse(krb5_context context,
         /* client after KDC reply */
         if ((retval = client_process_dh(context, plgctx->cryptoctx,
                                         reqctx->cryptoctx, reqctx->idctx,
+                                        (unsigned char *)
                                         kdc_dh->subjectPublicKey.data,
                                         kdc_dh->subjectPublicKey.length,
                                         &client_key, &client_key_len)) != 0) {
@@ -800,32 +823,33 @@ pkinit_as_rep_parse(krb5_context context,
         /* If we have a KDF algorithm ID, call the algorithm agility KDF... */
         if (kdc_reply->u.dh_Info.kdfID) {
             secret.length = client_key_len;
-            secret.data = client_key;
+            secret.data = (char *)client_key;
 
             retval = pkinit_alg_agility_kdf(context, &secret,
                                             kdc_reply->u.dh_Info.kdfID,
-                                            request->client,
-                                            request->server, etype,
-                                            (krb5_octet_data *)encoded_request,
-                                            (krb5_octet_data *)as_rep,
-                                            key_block);
+                                            request->client, request->server,
+                                            etype, encoded_request,
+                                            (krb5_data *)as_rep, key_block);
 
             if (retval) {
                 pkiDebug("failed to create key pkinit_alg_agility_kdf %s\n",
                          error_message(retval));
                 goto cleanup;
             }
+            TRACE_PKINIT_CLIENT_KDF_ALG(context, kdc_reply->u.dh_Info.kdfID,
+                                        key_block);
 
-        /* ...otherwise, use the older octetstring2key function. */
+            /* ...otherwise, use the older octetstring2key function. */
         } else {
 
             retval = pkinit_octetstring2key(context, etype, client_key,
-                                        client_key_len, key_block);
+                                            client_key_len, key_block);
             if (retval) {
                 pkiDebug("failed to create key pkinit_octetstring2key %s\n",
                          error_message(retval));
                 goto cleanup;
             }
+            TRACE_PKINIT_CLIENT_KDF_OS2K(context, key_block);
         }
 
         break;
@@ -886,6 +910,8 @@ pkinit_as_rep_parse(krb5_context context,
         if ((cksum.length != key_pack->asChecksum.length) ||
             memcmp(cksum.contents, key_pack->asChecksum.contents,
                    cksum.length)) {
+            TRACE_PKINIT_CLIENT_REP_CHECKSUM_FAIL(context, &cksum,
+                                                  &key_pack->asChecksum);
             pkiDebug("failed to match the checksums\n");
 #ifdef DEBUG_CKSUM
             pkiDebug("calculating checksum on buf size (%d)\n",
@@ -909,6 +935,7 @@ pkinit_as_rep_parse(krb5_context context,
 
         krb5_copy_keyblock_contents(context, &key_pack->replyKey,
                                     key_block);
+        TRACE_PKINIT_CLIENT_REP_RSA_KEY(context, key_block, &cksum);
 
         break;
     default:
@@ -1020,34 +1047,23 @@ static krb5_error_code
 pkinit_client_process(krb5_context context, krb5_clpreauth_moddata moddata,
                       krb5_clpreauth_modreq modreq,
                       krb5_get_init_creds_opt *gic_opt,
-                      krb5_clpreauth_get_data_fn get_data_proc,
-                      krb5_clpreauth_rock rock, krb5_kdc_req *request,
-                      krb5_data *encoded_request_body,
+                      krb5_clpreauth_callbacks cb, krb5_clpreauth_rock rock,
+                      krb5_kdc_req *request, krb5_data *encoded_request_body,
                       krb5_data *encoded_previous_request,
                       krb5_pa_data *in_padata,
                       krb5_prompter_fct prompter, void *prompter_data,
-                      krb5_clpreauth_get_as_key_fn gak_fct, void *gak_data,
-                      krb5_data *salt, krb5_data *s2kparams,
-                      krb5_keyblock *as_key, krb5_pa_data ***out_padata)
+                      krb5_pa_data ***out_padata)
 {
     krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED;
     krb5_enctype enctype = -1;
-    krb5_data *cdata = NULL;
     int processing_request = 0;
     pkinit_context plgctx = (pkinit_context)moddata;
     pkinit_req_context reqctx = (pkinit_req_context)modreq;
-    krb5_keyblock *armor_key = NULL;
+    krb5_keyblock as_key;
 
     pkiDebug("pkinit_client_process %p %p %p %p\n",
              context, plgctx, reqctx, request);
 
-    /* Remove (along with armor_key) when FAST PKINIT is settled. */
-    retval = fast_get_armor_key(context, get_data_proc, rock, &armor_key);
-    if (retval == 0 && armor_key != NULL) {
-        /* Don't use PKINIT if also using FAST. */
-        krb5_free_keyblock(context, armor_key);
-        return EINVAL;
-    }
 
     if (plgctx == NULL || reqctx == NULL)
         return EINVAL;
@@ -1089,29 +1105,24 @@ pkinit_client_process(krb5_context context, krb5_clpreauth_moddata moddata,
                                             reqctx->cryptoctx, reqctx->idopts,
                                             reqctx->idctx, 1, request->client);
         if (retval) {
+            TRACE_PKINIT_CLIENT_NO_IDENTITY(context);
             pkiDebug("pkinit_identity_initialize returned %d (%s)\n",
                      retval, error_message(retval));
             return retval;
         }
         retval = pa_pkinit_gen_req(context, plgctx, reqctx, request,
-                                   in_padata, out_padata, prompter,
+                                   in_padata->pa_type, out_padata, prompter,
                                    prompter_data, gic_opt);
     } else {
         /*
          * Get the enctype of the reply.
          */
-        retval = (*get_data_proc)(context, rock, krb5_clpreauth_get_etype,
-                                  &cdata);
-        if (retval) {
-            pkiDebug("get_data_proc returned %d (%s)\n",
-                     retval, error_message(retval));
-            return retval;
-        }
-        enctype = *((krb5_enctype *)cdata->data);
-        (*get_data_proc)(context, rock, krb5_clpreauth_free_etype, &cdata);
+        enctype = cb->get_etype(context, rock);
         retval = pa_pkinit_parse_rep(context, plgctx, reqctx, request,
-                                     in_padata, enctype, as_key,
+                                     in_padata, enctype, &as_key,
                                      encoded_previous_request);
+        if (retval == 0)
+            retval = cb->set_as_key(context, rock, &as_key);
     }
 
     pkiDebug("pkinit_client_process: returning %d (%s)\n",
@@ -1123,91 +1134,80 @@ static krb5_error_code
 pkinit_client_tryagain(krb5_context context, krb5_clpreauth_moddata moddata,
                        krb5_clpreauth_modreq modreq,
                        krb5_get_init_creds_opt *gic_opt,
-                       krb5_clpreauth_get_data_fn get_data_proc,
-                       krb5_clpreauth_rock rock, krb5_kdc_req *request,
-                       krb5_data *encoded_request_body,
+                       krb5_clpreauth_callbacks cb, krb5_clpreauth_rock rock,
+                       krb5_kdc_req *request, krb5_data *encoded_request_body,
                        krb5_data *encoded_previous_request,
-                       krb5_pa_data *in_padata, krb5_error *err_reply,
-                       krb5_prompter_fct prompter, void *prompter_data,
-                       krb5_clpreauth_get_as_key_fn gak_fct, void *gak_data,
-                       krb5_data *salt, krb5_data *s2kparams,
-                       krb5_keyblock *as_key, krb5_pa_data ***out_padata)
+                       krb5_preauthtype pa_type, krb5_error *err_reply,
+                       krb5_pa_data **err_padata, krb5_prompter_fct prompter,
+                       void *prompter_data, krb5_pa_data ***out_padata)
 {
     krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED;
     pkinit_context plgctx = (pkinit_context)moddata;
     pkinit_req_context reqctx = (pkinit_req_context)modreq;
-    krb5_typed_data **typed_data = NULL;
+    krb5_pa_data *pa;
     krb5_data scratch;
-    krb5_external_principal_identifier **krb5_trusted_certifiers = NULL;
+    krb5_external_principal_identifier **certifiers = NULL;
     krb5_algorithm_identifier **algId = NULL;
     int do_again = 0;
 
     pkiDebug("pkinit_client_tryagain %p %p %p %p\n",
              context, plgctx, reqctx, request);
 
-    if (reqctx->pa_type != in_padata->pa_type)
+    if (reqctx->pa_type != pa_type || err_padata == NULL)
         return retval;
 
-#ifdef DEBUG_ASN1
-    print_buffer_bin((unsigned char *)err_reply->e_data.data,
-                     err_reply->e_data.length, "/tmp/client_edata");
-#endif
-    retval = k5int_decode_krb5_typed_data(&err_reply->e_data, &typed_data);
-    if (retval) {
-        pkiDebug("decode_krb5_typed_data failed\n");
-        goto cleanup;
-    }
-#ifdef DEBUG_ASN1
-    print_buffer_bin(typed_data[0]->data, typed_data[0]->length,
-                     "/tmp/client_typed_data");
-#endif
-    OCTETDATA_TO_KRB5DATA(typed_data[0], &scratch);
-
-    switch(typed_data[0]->type) {
-    case TD_TRUSTED_CERTIFIERS:
-    case TD_INVALID_CERTIFICATES:
-        retval = k5int_decode_krb5_td_trusted_certifiers(&scratch,
-                                                         &krb5_trusted_certifiers);
-        if (retval) {
-            pkiDebug("failed to decode sequence of trusted certifiers\n");
-            goto cleanup;
-        }
-        retval = pkinit_process_td_trusted_certifiers(context,
-                                                      plgctx->cryptoctx, reqctx->cryptoctx, reqctx->idctx,
-                                                      krb5_trusted_certifiers, typed_data[0]->type);
-        if (!retval)
-            do_again = 1;
-        break;
-    case TD_DH_PARAMETERS:
-        retval = k5int_decode_krb5_td_dh_parameters(&scratch, &algId);
-        if (retval) {
-            pkiDebug("failed to decode td_dh_parameters\n");
-            goto cleanup;
+    for (; *err_padata != NULL && !do_again; err_padata++) {
+        pa = *err_padata;
+        PADATA_TO_KRB5DATA(pa, &scratch);
+        switch (pa->pa_type) {
+        case TD_TRUSTED_CERTIFIERS:
+        case TD_INVALID_CERTIFICATES:
+            retval = k5int_decode_krb5_td_trusted_certifiers(&scratch,
+                                                             &certifiers);
+            if (retval) {
+                pkiDebug("failed to decode sequence of trusted certifiers\n");
+                goto cleanup;
+            }
+            retval = pkinit_process_td_trusted_certifiers(context,
+                                                          plgctx->cryptoctx,
+                                                          reqctx->cryptoctx,
+                                                          reqctx->idctx,
+                                                          certifiers,
+                                                          pa->pa_type);
+            if (!retval)
+                do_again = 1;
+            break;
+        case TD_DH_PARAMETERS:
+            retval = k5int_decode_krb5_td_dh_parameters(&scratch, &algId);
+            if (retval) {
+                pkiDebug("failed to decode td_dh_parameters\n");
+                goto cleanup;
+            }
+            retval = pkinit_process_td_dh_params(context, plgctx->cryptoctx,
+                                                 reqctx->cryptoctx,
+                                                 reqctx->idctx, algId,
+                                                 &reqctx->opts->dh_size);
+            if (!retval)
+                do_again = 1;
+            break;
+        default:
+            break;
         }
-        retval = pkinit_process_td_dh_params(context, plgctx->cryptoctx,
-                                             reqctx->cryptoctx, reqctx->idctx, algId,
-                                             &reqctx->opts->dh_size);
-        if (!retval)
-            do_again = 1;
-        break;
-    default:
-        break;
     }
 
     if (do_again) {
-        retval = pa_pkinit_gen_req(context, plgctx, reqctx, request, in_padata,
-                                   out_padata, prompter, prompter_data, gic_opt);
+        TRACE_PKINIT_CLIENT_TRYAGAIN(context);
+        retval = pa_pkinit_gen_req(context, plgctx, reqctx, request, pa_type,
+                                   out_padata, prompter, prompter_data,
+                                   gic_opt);
         if (retval)
             goto cleanup;
     }
 
     retval = 0;
 cleanup:
-    if (krb5_trusted_certifiers != NULL)
-        free_krb5_external_principal_identifier(&krb5_trusted_certifiers);
-
-    if (typed_data != NULL)
-        free_krb5_typed_data(&typed_data);
+    if (certifiers != NULL)
+        free_krb5_external_principal_identifier(&certifiers);
 
     if (algId != NULL)
         free_krb5_algorithm_identifiers(&algId);