First pass at PKINIT client trace logs
[krb5.git] / src / plugins / preauth / pkinit / pkinit_clnt.c
1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /*
3  * COPYRIGHT (C) 2006,2007
4  * THE REGENTS OF THE UNIVERSITY OF MICHIGAN
5  * ALL RIGHTS RESERVED
6  *
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
16  * also be included.
17  *
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
29  * SUCH DAMAGES.
30  */
31
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <errno.h>
35 #include <unistd.h>
36 #include <string.h>
37 #include <ctype.h>
38 #include <assert.h>
39 #include <dlfcn.h>
40 #include <sys/stat.h>
41
42 #include "pkinit.h"
43
44 /*
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.
50  *
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.
54  */
55 int longhorn = 0;       /* Talking to a Longhorn server? */
56
57 /**
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.
61  */
62 static int
63 use_content_info(krb5_context context, pkinit_req_context req,
64                  krb5_principal client)
65 {
66     if (req->rfc6112_kdc)
67         return 0;
68     if (krb5_principal_compare_any_realm(context, client,
69                                          krb5_anonymous_principal()))
70         return 1;
71     return 0;
72 }
73
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,
80                      krb5_data **as_req);
81
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 *);
87
88 static void pkinit_client_plugin_fini(krb5_context context,
89                                       krb5_clpreauth_moddata moddata);
90
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,
99                   void *prompter_data,
100                   krb5_get_init_creds_opt *gic_opt)
101 {
102
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;
107     krb5_ui_4 nonce = 0;
108     krb5_checksum cksum;
109     krb5_data *der_req = NULL;
110     krb5_pa_data **return_pa_data = NULL;
111
112     cksum.contents = NULL;
113     reqctx->pa_type = pa_type;
114
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;
121     }
122
123     retval = pkinit_get_kdc_cert(context, plgctx->cryptoctx, reqctx->cryptoctx,
124                                  reqctx->idctx, request->server);
125     if (retval) {
126         pkiDebug("pkinit_get_kdc_cert returned %d\n", retval);
127         goto cleanup;
128     }
129
130     /* checksum of the encoded KDC-REQ-BODY */
131     retval = k5int_encode_krb5_kdc_req_body(request, &der_req);
132     if (retval) {
133         pkiDebug("encode_krb5_kdc_req_body returned %d\n", (int) retval);
134         goto cleanup;
135     }
136
137     retval = krb5_c_make_checksum(context, CKSUMTYPE_NIST_SHA, NULL, 0,
138                                   der_req, &cksum);
139     if (retval)
140         goto cleanup;
141     TRACE_PKINIT_CLIENT_REQ_CHECKSUM(context, &cksum);
142 #ifdef DEBUG_CKSUM
143     pkiDebug("calculating checksum on buf size (%d)\n", der_req->length);
144     print_buffer(der_req->data, der_req->length);
145 #endif
146
147     retval = krb5_us_timeofday(context, &ctsec, &cusec);
148     if (retval)
149         goto cleanup;
150
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.
155      */
156     nonce = request->nonce;
157
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",
162                  (int) retval);
163         goto cleanup;
164     }
165     retval = ENOMEM;
166     /*
167      * The most we'll return is two pa_data, normally just one.
168      * We need to make room for the NULL terminator.
169      */
170     return_pa_data = malloc(3 * sizeof(krb5_pa_data *));
171     if (return_pa_data == NULL)
172         goto cleanup;
173
174     return_pa_data[1] = NULL;   /* in case of an early trip to cleanup */
175     return_pa_data[2] = NULL;   /* Terminate the list */
176
177     return_pa_data[0] = malloc(sizeof(krb5_pa_data));
178     if (return_pa_data[0] == NULL)
179         goto cleanup;
180
181     return_pa_data[1] = malloc(sizeof(krb5_pa_data));
182     if (return_pa_data[1] == NULL)
183         goto cleanup;
184
185     return_pa_data[0]->magic = KV5M_PA_DATA;
186
187     if (pa_type == KRB5_PADATA_PK_AS_REQ_OLD)
188         return_pa_data[0]->pa_type = KRB5_PADATA_PK_AS_REP_OLD;
189     else
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;
193
194     /*
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.
198      */
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;
204     } else {
205         free(return_pa_data[1]);
206         return_pa_data[1] = NULL;   /* Move the list terminator */
207     }
208     *out_padata = return_pa_data;
209     retval = 0;
210
211 cleanup:
212     if (der_req != NULL)
213         krb5_free_data(context, der_req);
214     free(out_data);
215
216     if (retval) {
217         if (return_pa_data) {
218             free(return_pa_data[0]);
219             free(return_pa_data[1]);
220             free(return_pa_data);
221         }
222         if (out_data) {
223             free(out_data->data);
224             free(out_data);
225         }
226     }
227     return retval;
228 }
229
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,
235                      krb5_int32 cusec,
236                      krb5_ui_4 nonce,
237                      const krb5_checksum * cksum,
238                      krb5_principal client,
239                      krb5_principal server,
240                      krb5_data ** as_req)
241 {
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;
250
251     pkiDebug("pkinit_as_req_create pa_type = %d\n", reqctx->pa_type);
252
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)
259             goto cleanup;
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);
265         break;
266     case KRB5_PADATA_PK_AS_REQ:
267         init_krb5_subject_pk_info(&info);
268         if (info == NULL)
269             goto cleanup;
270         init_krb5_auth_pack(&auth_pack);
271         if (auth_pack == NULL)
272             goto cleanup;
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;
280
281         /* add List of CMS algorithms */
282         retval = create_krb5_supportedCMSTypes(context, plgctx->cryptoctx,
283                                                reqctx->cryptoctx, reqctx->idctx,
284                                                &auth_pack->supportedCMSTypes);
285         if (retval)
286             goto cleanup;
287         break;
288     default:
289         pkiDebug("as_req: unrecognized pa_type = %d\n",
290                  (int)reqctx->pa_type);
291         retval = -1;
292         goto cleanup;
293     }
294
295     switch(protocol) {
296     case DH_PROTOCOL:
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);
300         if (retval) {
301             pkiDebug("failed to copy dh_oid\n");
302             goto cleanup;
303         }
304
305         /* create client-side DH keys */
306         if ((retval = client_create_dh(context, plgctx->cryptoctx,
307                                        reqctx->cryptoctx, reqctx->idctx, reqctx->opts->dh_size,
308                                        (unsigned char **)
309                                        &info->algorithm.parameters.data,
310                                        &info->algorithm.parameters.length,
311                                        (unsigned char **)
312                                        &info->subjectPublicKey.data,
313                                        &info->subjectPublicKey.length)) != 0) {
314             pkiDebug("failed to create dh parameters\n");
315             goto cleanup;
316         }
317         break;
318     case RSA_PROTOCOL:
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;
324             break;
325         case KRB5_PADATA_PK_AS_REQ:
326             free_krb5_subject_pk_info(&info);
327             auth_pack->clientPublicValue = NULL;
328             break;
329         }
330         break;
331     default:
332         pkiDebug("as_req: unknown key transport protocol %d\n",
333                  protocol);
334         retval = -1;
335         goto cleanup;
336     }
337
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);
342         break;
343     case KRB5_PADATA_PK_AS_REQ_OLD:
344         retval = k5int_encode_krb5_auth_pack_draft9(auth_pack9,
345                                                     &coded_auth_pack);
346         break;
347     }
348     if (retval) {
349         pkiDebug("failed to encode the AuthPack %d\n", retval);
350         goto cleanup;
351     }
352 #ifdef DEBUG_ASN1
353     print_buffer_bin((unsigned char *)coded_auth_pack->data,
354                      coded_auth_pack->length,
355                      "/tmp/client_auth_pack");
356 #endif
357
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);
362         if (req == NULL) {
363             retval = ENOMEM;
364             goto cleanup;
365         }
366         if (use_content_info(context, reqctx, client)) {
367             retval = cms_contentinfo_create(context, plgctx->cryptoctx,
368                                             reqctx->cryptoctx, reqctx->idctx,
369                                             CMS_SIGN_CLIENT,
370                                             (unsigned char *)
371                                             coded_auth_pack->data,
372                                             coded_auth_pack->length,
373                                             (unsigned char **)
374                                             &req->signedAuthPack.data,
375                                             &req->signedAuthPack.length);
376         } else {
377             retval = cms_signeddata_create(context, plgctx->cryptoctx,
378                                            reqctx->cryptoctx, reqctx->idctx,
379                                            CMS_SIGN_CLIENT, 1,
380                                            (unsigned char *)
381                                            coded_auth_pack->data,
382                                            coded_auth_pack->length,
383                                            (unsigned char **)
384                                            &req->signedAuthPack.data,
385                                            &req->signedAuthPack.length);
386         }
387 #ifdef DEBUG_ASN1
388         print_buffer_bin((unsigned char *)req->signedAuthPack.data,
389                          req->signedAuthPack.length,
390                          "/tmp/client_signed_data");
391 #endif
392         break;
393     case KRB5_PADATA_PK_AS_REQ_OLD:
394         init_krb5_pa_pk_as_req_draft9(&req9);
395         if (req9 == NULL) {
396             retval = ENOMEM;
397             goto cleanup;
398         }
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,
403                                        (unsigned char **)
404                                        &req9->signedAuthPack.data,
405                                        &req9->signedAuthPack.length);
406         break;
407 #ifdef DEBUG_ASN1
408         print_buffer_bin((unsigned char *)req9->signedAuthPack.data,
409                          req9->signedAuthPack.length,
410                          "/tmp/client_signed_data_draft9");
411 #endif
412     }
413     krb5_free_data(context, coded_auth_pack);
414     if (retval) {
415         pkiDebug("failed to create pkcs7 signed data\n");
416         goto cleanup;
417     }
418
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);
424         if (retval)
425             goto cleanup;
426         retval = create_issuerAndSerial(context, plgctx->cryptoctx,
427                                         reqctx->cryptoctx, reqctx->idctx,
428                                         (unsigned char **)&req->kdcPkId.data,
429                                         &req->kdcPkId.length);
430         if (retval)
431             goto cleanup;
432
433         /* Encode the as-req */
434         retval = k5int_encode_krb5_pa_pk_as_req(req, as_req);
435         break;
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);
441         if (retval)
442             goto cleanup;
443         /* Encode the as-req */
444         retval = k5int_encode_krb5_pa_pk_as_req_draft9(req9, as_req);
445         break;
446     }
447 #ifdef DEBUG_ASN1
448     if (!retval)
449         print_buffer_bin((unsigned char *)(*as_req)->data, (*as_req)->length,
450                          "/tmp/client_as_req");
451 #endif
452
453 cleanup:
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);
459         break;
460     case KRB5_PADATA_PK_AS_REQ_OLD:
461         free_krb5_pa_pk_as_req_draft9(&req9);
462         free(auth_pack9);
463         break;
464     }
465
466
467     pkiDebug("pkinit_as_req_create retval=%d\n", (int) retval);
468
469     return retval;
470 }
471
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,
478                     krb5_enctype etype,
479                     krb5_keyblock * as_key,
480                     krb5_data *encoded_request)
481 {
482     krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED;
483     krb5_data asRep = { 0, 0, NULL};
484
485     /*
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
488      * decrypt the key.
489      */
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;
493     }
494
495     asRep.data = (char *) in_padata->contents;
496     asRep.length = in_padata->length;
497
498     retval =
499         pkinit_as_rep_parse(context, plgctx, reqctx, in_padata->pa_type,
500                             request, &asRep, as_key, etype, encoded_request);
501     if (retval) {
502         pkiDebug("pkinit_as_rep_parse returned %d (%s)\n",
503                  retval, error_message(retval));
504         goto cleanup;
505     }
506
507     retval = 0;
508
509 cleanup:
510
511     return retval;
512 }
513
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,
519                int *valid_san,
520                int *need_eku_checking)
521 {
522     krb5_error_code retval;
523     char **certhosts = NULL, **cfghosts = NULL, **hostptr;
524     krb5_principal *princs = NULL, *princptr;
525     unsigned char ***get_dns;
526     int i, j;
527
528     *valid_san = 0;
529     *need_eku_checking = 1;
530
531     retval = pkinit_libdefault_strings(context,
532                                        krb5_princ_realm(context, kdcprinc),
533                                        KRB5_CONF_PKINIT_KDC_HOSTNAME,
534                                        &cfghosts);
535     if (retval || cfghosts == NULL) {
536         pkiDebug("%s: No pkinit_kdc_hostname values found in config file\n",
537                  __FUNCTION__);
538         get_dns = NULL;
539     } else {
540         pkiDebug("%s: pkinit_kdc_hostname values found in config file\n",
541                  __FUNCTION__);
542         for (hostptr = cfghosts; *hostptr != NULL; hostptr++)
543             TRACE_PKINIT_CLIENT_SAN_CONFIG_DNSNAME(context, *hostptr);
544         get_dns = (unsigned char ***)&certhosts;
545     }
546
547     retval = crypto_retrieve_cert_sans(context, plgctx->cryptoctx,
548                                        reqctx->cryptoctx, reqctx->idctx,
549                                        &princs, NULL, get_dns);
550     if (retval) {
551         pkiDebug("%s: error from retrieve_certificate_sans()\n", __FUNCTION__);
552         TRACE_PKINIT_CLIENT_SAN_ERR(context);
553         retval = KRB5KDC_ERR_KDC_NAME_MISMATCH;
554         goto out;
555     }
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);
561     }
562 #if 0
563     retval = call_san_checking_plugins(context, plgctx, reqctx, idctx,
564                                        princs, hosts, &plugin_decision,
565                                        need_eku_checking);
566     pkiDebug("%s: call_san_checking_plugins() returned retval %d\n",
567              __FUNCTION__);
568     if (retval) {
569         retval = KRB5KDC_ERR_KDC_NAME_MISMATCH;
570         goto out;
571     }
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;
577         goto out;
578     }
579 #endif
580
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__);
586             *valid_san = 1;
587             *need_eku_checking = 0;
588             retval = 0;
589             goto out;
590         }
591     }
592     pkiDebug("%s: no pkinit san match found\n", __FUNCTION__);
593
594     if (certhosts == NULL) {
595         pkiDebug("%s: no certhosts (or we wouldn't accept them anyway)\n",
596                  __FUNCTION__);
597         retval = KRB5KDC_ERR_KDC_NAME_MISMATCH;
598         goto out;
599     }
600
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__);
608                 *valid_san = 1;
609                 retval = 0;
610                 goto out;
611             }
612         }
613     }
614     TRACE_PKINIT_CLIENT_SAN_MATCH_NONE(context);
615     pkiDebug("%s: no dnsName san match found\n", __FUNCTION__);
616
617     /* We found no match */
618     retval = 0;
619
620 out:
621     if (princs != NULL) {
622         for (i = 0; princs[i] != NULL; i++)
623             krb5_free_principal(context, princs[i]);
624         free(princs);
625     }
626     if (certhosts != NULL) {
627         for (i = 0; certhosts[i] != NULL; i++)
628             free(certhosts[i]);
629         free(certhosts);
630     }
631     if (cfghosts != NULL)
632         profile_free_list(cfghosts);
633
634     pkiDebug("%s: returning retval %d, valid_san %d, need_eku_checking %d\n",
635              __FUNCTION__, retval, *valid_san, *need_eku_checking);
636     return retval;
637 }
638
639 static krb5_error_code
640 verify_kdc_eku(krb5_context context,
641                pkinit_context plgctx,
642                pkinit_req_context reqctx,
643                int *eku_accepted)
644 {
645     krb5_error_code retval;
646
647     *eku_accepted = 0;
648
649     if (reqctx->opts->require_eku == 0) {
650         TRACE_PKINIT_CLIENT_EKU_SKIP(context);
651         pkiDebug("%s: configuration requests no EKU checking\n", __FUNCTION__);
652         *eku_accepted = 1;
653         retval = 0;
654         goto out;
655     }
656     retval = crypto_check_cert_eku(context, plgctx->cryptoctx,
657                                    reqctx->cryptoctx, reqctx->idctx,
658                                    1, /* kdc cert */
659                                    reqctx->opts->accept_secondary_eku,
660                                    eku_accepted);
661     if (retval) {
662         pkiDebug("%s: Error from crypto_check_cert_eku %d (%s)\n",
663                  __FUNCTION__, retval, error_message(retval));
664         goto out;
665     }
666
667 out:
668     if (eku_accepted)
669         TRACE_PKINIT_CLIENT_EKU_ACCEPT(context);
670     else
671         TRACE_PKINIT_CLIENT_EKU_REJECT(context);
672     pkiDebug("%s: returning retval %d, eku_accepted %d\n",
673              __FUNCTION__, retval, *eku_accepted);
674     return retval;
675 }
676
677 /*
678  * Parse PA-PK-AS-REP message. Optionally evaluates the message's
679  * certificate chain.
680  * Optionally returns various components.
681  */
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,
690                     krb5_enctype etype,
691                     krb5_data *encoded_request)
692 {
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};
703     krb5_data k5data;
704     krb5_data secret;
705     int valid_san = 0;
706     int valid_eku = 0;
707     int need_eku_checking = 1;
708
709     assert((as_rep != NULL) && (key_block != NULL));
710
711 #ifdef DEBUG_ASN1
712     print_buffer_bin((unsigned char *)as_rep->data, as_rep->length,
713                      "/tmp/client_as_rep");
714 #endif
715
716     if ((retval = k5int_decode_krb5_pa_pk_as_rep(as_rep, &kdc_reply))) {
717         pkiDebug("decode_krb5_as_rep failed %d\n", retval);
718         return retval;
719     }
720
721     switch(kdc_reply->choice) {
722     case choice_pa_pk_as_rep_dhInfo:
723         pkiDebug("as_rep: DH key transport algorithm\n");
724 #ifdef DEBUG_ASN1
725         print_buffer_bin(kdc_reply->u.dh_Info.dhSignedData.data,
726                          kdc_reply->u.dh_Info.dhSignedData.length, "/tmp/client_kdc_signeddata");
727 #endif
728         if ((retval = cms_signeddata_verify(context, plgctx->cryptoctx,
729                                             reqctx->cryptoctx, reqctx->idctx, CMS_SIGN_SERVER,
730                                             reqctx->opts->require_crl_checking,
731                                             (unsigned char *)
732                                             kdc_reply->u.dh_Info.dhSignedData.data,
733                                             kdc_reply->u.dh_Info.dhSignedData.length,
734                                             (unsigned char **)&dh_data.data,
735                                             &dh_data.length,
736                                             NULL, NULL, NULL)) != 0) {
737             pkiDebug("failed to verify pkcs7 signed data\n");
738             TRACE_PKINIT_CLIENT_REP_DH_FAIL(context);
739             goto cleanup;
740         }
741         TRACE_PKINIT_CLIENT_REP_DH(context);
742         break;
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,
748                                                (unsigned char *)
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);
755             goto cleanup;
756         }
757         TRACE_PKINIT_CLIENT_REP_RSA(context);
758         break;
759     default:
760         pkiDebug("unknown as_rep type %d\n", kdc_reply->choice);
761         retval = -1;
762         goto cleanup;
763     }
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,
770                                       0);
771     if (retval)
772         goto cleanup;
773     retval = verify_kdc_san(context, plgctx, reqctx, kdc_princ,
774                             &valid_san, &need_eku_checking);
775     if (retval)
776         goto cleanup;
777     if (!valid_san) {
778         pkiDebug("%s: did not find an acceptable SAN in KDC certificate\n",
779                  __FUNCTION__);
780         retval = KRB5KDC_ERR_KDC_NAME_MISMATCH;
781         goto cleanup;
782     }
783
784     if (need_eku_checking) {
785         retval = verify_kdc_eku(context, plgctx, reqctx,
786                                 &valid_eku);
787         if (retval)
788             goto cleanup;
789         if (!valid_eku) {
790             pkiDebug("%s: did not find an acceptable EKU in KDC certificate\n",
791                      __FUNCTION__);
792             retval = KRB5KDC_ERR_INCONSISTENT_KEY_PURPOSE;
793             goto cleanup;
794         }
795     } else
796         pkiDebug("%s: skipping EKU check\n", __FUNCTION__);
797
798     OCTETDATA_TO_KRB5DATA(&dh_data, &k5data);
799
800     switch(kdc_reply->choice) {
801     case choice_pa_pk_as_rep_dhInfo:
802 #ifdef DEBUG_ASN1
803         print_buffer_bin(dh_data.data, dh_data.length,
804                          "/tmp/client_dh_key");
805 #endif
806         if ((retval = k5int_decode_krb5_kdc_dh_key_info(&k5data,
807                                                         &kdc_dh)) != 0) {
808             pkiDebug("failed to decode kdc_dh_key_info\n");
809             goto cleanup;
810         }
811
812         /* client after KDC reply */
813         if ((retval = client_process_dh(context, plgctx->cryptoctx,
814                                         reqctx->cryptoctx, reqctx->idctx,
815                                         (unsigned char *)
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");
820             goto cleanup;
821         }
822
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;
827
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);
833
834             if (retval) {
835                 pkiDebug("failed to create key pkinit_alg_agility_kdf %s\n",
836                          error_message(retval));
837                 goto cleanup;
838             }
839             TRACE_PKINIT_CLIENT_KDF_ALG(context, kdc_reply->u.dh_Info.kdfID,
840                                         key_block);
841
842             /* ...otherwise, use the older octetstring2key function. */
843         } else {
844
845             retval = pkinit_octetstring2key(context, etype, client_key,
846                                             client_key_len, key_block);
847             if (retval) {
848                 pkiDebug("failed to create key pkinit_octetstring2key %s\n",
849                          error_message(retval));
850                 goto cleanup;
851             }
852             TRACE_PKINIT_CLIENT_KDF_OS2K(context, key_block);
853         }
854
855         break;
856     case choice_pa_pk_as_rep_encKeyPack:
857 #ifdef DEBUG_ASN1
858         print_buffer_bin(dh_data.data, dh_data.length,
859                          "/tmp/client_key_pack");
860 #endif
861         if ((retval = k5int_decode_krb5_reply_key_pack(&k5data,
862                                                        &key_pack)) != 0) {
863             pkiDebug("failed to decode reply_key_pack\n");
864 #ifdef LONGHORN_BETA_COMPAT
865             /*
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.
869              */
870             if (pa_type == KRB5_PADATA_PK_AS_REP && longhorn == 0)
871 #else
872                 if (pa_type == KRB5_PADATA_PK_AS_REP)
873 #endif
874                     goto cleanup;
875                 else {
876                     if ((retval =
877                          k5int_decode_krb5_reply_key_pack_draft9(&k5data,
878                                                                  &key_pack9)) != 0) {
879                         pkiDebug("failed to decode reply_key_pack_draft9\n");
880                         goto cleanup;
881                     }
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);
885                         retval = -1;
886                         goto cleanup;
887                     }
888                     krb5_copy_keyblock_contents(context, &key_pack9->replyKey,
889                                                 key_block);
890                     break;
891                 }
892         }
893         /*
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.
897          */
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,
902                                       &key_pack->replyKey,
903                                       KRB5_KEYUSAGE_TGS_REQ_AUTH_CKSUM,
904                                       encoded_request, &cksum);
905         if (retval) {
906             pkiDebug("failed to make a checksum\n");
907             goto cleanup;
908         }
909
910         if ((cksum.length != key_pack->asChecksum.length) ||
911             memcmp(cksum.contents, key_pack->asChecksum.contents,
912                    cksum.length)) {
913             TRACE_PKINIT_CLIENT_REP_CHECKSUM_FAIL(context, &cksum,
914                                                   &key_pack->asChecksum);
915             pkiDebug("failed to match the checksums\n");
916 #ifdef DEBUG_CKSUM
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);
931 #endif
932             goto cleanup;
933         } else
934             pkiDebug("checksums match\n");
935
936         krb5_copy_keyblock_contents(context, &key_pack->replyKey,
937                                     key_block);
938         TRACE_PKINIT_CLIENT_REP_RSA_KEY(context, key_block, &cksum);
939
940         break;
941     default:
942         pkiDebug("unknow as_rep type %d\n", kdc_reply->choice);
943         goto cleanup;
944     }
945
946     retval = 0;
947
948 cleanup:
949     free(dh_data.data);
950     krb5_free_principal(context, kdc_princ);
951     free(client_key);
952     free_krb5_kdc_dh_key_info(&kdc_dh);
953     free_krb5_pa_pk_as_rep(&kdc_reply);
954
955     if (key_pack != NULL) {
956         free_krb5_reply_key_pack(&key_pack);
957         free(cksum.contents);
958     }
959     if (key_pack9 != NULL)
960         free_krb5_reply_key_pack_draft9(&key_pack9);
961
962     free(kdc_hostname);
963
964     pkiDebug("pkinit_as_rep_parse returning %d (%s)\n",
965              retval, error_message(retval));
966     return retval;
967 }
968
969 static void
970 pkinit_client_profile(krb5_context context,
971                       pkinit_context plgctx,
972                       pkinit_req_context reqctx,
973                       const krb5_data *realm)
974 {
975     char *eku_string = NULL;
976
977     pkiDebug("pkinit_client_profile %p %p %p %p\n",
978              context, plgctx, reqctx, realm);
979
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;
1002     }
1003     pkinit_libdefault_string(context, realm,
1004                              KRB5_CONF_PKINIT_EKU_CHECKING,
1005                              &eku_string);
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;
1016         } else {
1017             pkiDebug("%s: Invalid value for pkinit_eku_checking: '%s'\n",
1018                      __FUNCTION__, eku_string);
1019         }
1020         free(eku_string);
1021     }
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,
1026                               0,
1027                               &longhorn);
1028 #endif
1029
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);
1044 }
1045
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)
1056 {
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;
1063
1064     pkiDebug("pkinit_client_process %p %p %p %p\n",
1065              context, plgctx, reqctx, request);
1066
1067
1068     if (plgctx == NULL || reqctx == NULL)
1069         return EINVAL;
1070
1071     switch ((int) in_padata->pa_type) {
1072     case KRB5_PADATA_PKINIT_KX:
1073         reqctx->rfc6112_kdc = 1;
1074         return 0;
1075     case KRB5_PADATA_PK_AS_REQ:
1076         pkiDebug("processing KRB5_PADATA_PK_AS_REQ\n");
1077         processing_request = 1;
1078         break;
1079
1080     case KRB5_PADATA_PK_AS_REP:
1081         pkiDebug("processing KRB5_PADATA_PK_AS_REP\n");
1082         break;
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;
1089         } else {
1090             pkiDebug("processing KRB5_PADATA_PK_AS_REP_OLD\n");
1091             in_padata->pa_type = KRB5_PADATA_PK_AS_REP_OLD;
1092         }
1093         break;
1094     default:
1095         pkiDebug("unrecognized patype = %d for PKINIT\n",
1096                  in_padata->pa_type);
1097         return EINVAL;
1098     }
1099
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);
1107         if (retval) {
1108             TRACE_PKINIT_CLIENT_NO_IDENTITY(context);
1109             pkiDebug("pkinit_identity_initialize returned %d (%s)\n",
1110                      retval, error_message(retval));
1111             return retval;
1112         }
1113         retval = pa_pkinit_gen_req(context, plgctx, reqctx, request,
1114                                    in_padata->pa_type, out_padata, prompter,
1115                                    prompter_data, gic_opt);
1116     } else {
1117         /*
1118          * Get the enctype of the reply.
1119          */
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);
1124         if (retval == 0)
1125             retval = cb->set_as_key(context, rock, &as_key);
1126     }
1127
1128     pkiDebug("pkinit_client_process: returning %d (%s)\n",
1129              retval, error_message(retval));
1130     return retval;
1131 }
1132
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)
1143 {
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;
1147     krb5_pa_data *pa;
1148     krb5_data scratch;
1149     krb5_external_principal_identifier **certifiers = NULL;
1150     krb5_algorithm_identifier **algId = NULL;
1151     int do_again = 0;
1152
1153     pkiDebug("pkinit_client_tryagain %p %p %p %p\n",
1154              context, plgctx, reqctx, request);
1155
1156     if (reqctx->pa_type != pa_type || err_padata == NULL)
1157         return retval;
1158
1159     for (; *err_padata != NULL && !do_again; err_padata++) {
1160         pa = *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,
1166                                                              &certifiers);
1167             if (retval) {
1168                 pkiDebug("failed to decode sequence of trusted certifiers\n");
1169                 goto cleanup;
1170             }
1171             retval = pkinit_process_td_trusted_certifiers(context,
1172                                                           plgctx->cryptoctx,
1173                                                           reqctx->cryptoctx,
1174                                                           reqctx->idctx,
1175                                                           certifiers,
1176                                                           pa->pa_type);
1177             if (!retval)
1178                 do_again = 1;
1179             break;
1180         case TD_DH_PARAMETERS:
1181             retval = k5int_decode_krb5_td_dh_parameters(&scratch, &algId);
1182             if (retval) {
1183                 pkiDebug("failed to decode td_dh_parameters\n");
1184                 goto cleanup;
1185             }
1186             retval = pkinit_process_td_dh_params(context, plgctx->cryptoctx,
1187                                                  reqctx->cryptoctx,
1188                                                  reqctx->idctx, algId,
1189                                                  &reqctx->opts->dh_size);
1190             if (!retval)
1191                 do_again = 1;
1192             break;
1193         default:
1194             break;
1195         }
1196     }
1197
1198     if (do_again) {
1199         TRACE_PKINIT_CLIENT_TRYAGAIN(context);
1200         retval = pa_pkinit_gen_req(context, plgctx, reqctx, request, pa_type,
1201                                    out_padata, prompter, prompter_data,
1202                                    gic_opt);
1203         if (retval)
1204             goto cleanup;
1205     }
1206
1207     retval = 0;
1208 cleanup:
1209     if (certifiers != NULL)
1210         free_krb5_external_principal_identifier(&certifiers);
1211
1212     if (algId != NULL)
1213         free_krb5_algorithm_identifiers(&algId);
1214
1215     pkiDebug("pkinit_client_tryagain: returning %d (%s)\n",
1216              retval, error_message(retval));
1217     return retval;
1218 }
1219
1220 static int
1221 pkinit_client_get_flags(krb5_context kcontext, krb5_preauthtype patype)
1222 {
1223     if (patype == KRB5_PADATA_PKINIT_KX)
1224         return PA_INFO|PA_PSEUDO;
1225     return PA_REAL;
1226 }
1227
1228 /*
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
1233  */
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,
1240     0
1241 };
1242
1243 static void
1244 pkinit_client_req_init(krb5_context context,
1245                        krb5_clpreauth_moddata moddata,
1246                        krb5_clpreauth_modreq *modreq_out)
1247 {
1248     krb5_error_code retval = ENOMEM;
1249     pkinit_req_context reqctx = NULL;
1250     pkinit_context plgctx = (pkinit_context)moddata;
1251
1252     *modreq_out = NULL;
1253
1254     reqctx = malloc(sizeof(*reqctx));
1255     if (reqctx == NULL)
1256         return;
1257     memset(reqctx, 0, sizeof(*reqctx));
1258
1259     reqctx->magic = PKINIT_REQ_CTX_MAGIC;
1260     reqctx->cryptoctx = NULL;
1261     reqctx->opts = NULL;
1262     reqctx->idctx = NULL;
1263     reqctx->idopts = NULL;
1264
1265     retval = pkinit_init_req_opts(&reqctx->opts);
1266     if (retval)
1267         goto cleanup;
1268
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;
1274
1275     retval = pkinit_init_req_crypto(&reqctx->cryptoctx);
1276     if (retval)
1277         goto cleanup;
1278
1279     retval = pkinit_init_identity_crypto(&reqctx->idctx);
1280     if (retval)
1281         goto cleanup;
1282
1283     retval = pkinit_dup_identity_opts(plgctx->idopts, &reqctx->idopts);
1284     if (retval)
1285         goto cleanup;
1286
1287     *modreq_out = (krb5_clpreauth_modreq)reqctx;
1288     pkiDebug("%s: returning reqctx at %p\n", __FUNCTION__, reqctx);
1289
1290 cleanup:
1291     if (retval) {
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);
1300         free(reqctx);
1301     }
1302
1303     return;
1304 }
1305
1306 static void
1307 pkinit_client_req_fini(krb5_context context, krb5_clpreauth_moddata moddata,
1308                        krb5_clpreauth_modreq modreq)
1309 {
1310     pkinit_req_context reqctx = (pkinit_req_context)modreq;
1311
1312     pkiDebug("%s: received reqctx at %p\n", __FUNCTION__, reqctx);
1313     if (reqctx == NULL)
1314         return;
1315     if (reqctx->magic != PKINIT_REQ_CTX_MAGIC) {
1316         pkiDebug("%s: Bad magic value (%x) in req ctx\n",
1317                  __FUNCTION__, reqctx->magic);
1318         return;
1319     }
1320     if (reqctx->opts != NULL)
1321         pkinit_fini_req_opts(reqctx->opts);
1322
1323     if (reqctx->cryptoctx != NULL)
1324         pkinit_fini_req_crypto(reqctx->cryptoctx);
1325
1326     if (reqctx->idctx != NULL)
1327         pkinit_fini_identity_crypto(reqctx->idctx);
1328
1329     if (reqctx->idopts != NULL)
1330         pkinit_fini_identity_opts(reqctx->idopts);
1331
1332     free(reqctx);
1333     return;
1334 }
1335
1336 static int
1337 pkinit_client_plugin_init(krb5_context context,
1338                           krb5_clpreauth_moddata *moddata_out)
1339 {
1340     krb5_error_code retval = ENOMEM;
1341     pkinit_context ctx = NULL;
1342
1343     ctx = calloc(1, sizeof(*ctx));
1344     if (ctx == NULL)
1345         return ENOMEM;
1346     memset(ctx, 0, sizeof(*ctx));
1347     ctx->magic = PKINIT_CTX_MAGIC;
1348     ctx->opts = NULL;
1349     ctx->cryptoctx = NULL;
1350     ctx->idopts = NULL;
1351
1352     retval = pkinit_accessor_init();
1353     if (retval)
1354         goto errout;
1355
1356     retval = pkinit_init_plg_opts(&ctx->opts);
1357     if (retval)
1358         goto errout;
1359
1360     retval = pkinit_init_plg_crypto(&ctx->cryptoctx);
1361     if (retval)
1362         goto errout;
1363
1364     retval = pkinit_init_identity_opts(&ctx->idopts);
1365     if (retval)
1366         goto errout;
1367
1368     *moddata_out = (krb5_clpreauth_moddata)ctx;
1369
1370     pkiDebug("%s: returning plgctx at %p\n", __FUNCTION__, ctx);
1371
1372 errout:
1373     if (retval)
1374         pkinit_client_plugin_fini(context, (krb5_clpreauth_moddata)ctx);
1375
1376     return retval;
1377 }
1378
1379 static void
1380 pkinit_client_plugin_fini(krb5_context context, krb5_clpreauth_moddata moddata)
1381 {
1382     pkinit_context ctx = (pkinit_context)moddata;
1383
1384     if (ctx == NULL || ctx->magic != PKINIT_CTX_MAGIC) {
1385         pkiDebug("pkinit_lib_fini: got bad plgctx (%p)!\n", ctx);
1386         return;
1387     }
1388     pkiDebug("%s: got plgctx at %p\n", __FUNCTION__, ctx);
1389
1390     pkinit_fini_identity_opts(ctx->idopts);
1391     pkinit_fini_plg_crypto(ctx->cryptoctx);
1392     pkinit_fini_plg_opts(ctx->opts);
1393     free(ctx);
1394
1395 }
1396
1397 static krb5_error_code
1398 add_string_to_array(krb5_context context, char ***array, const char *addition)
1399 {
1400     char **out = NULL;
1401
1402     if (*array == NULL) {
1403         out = malloc(2 * sizeof(char *));
1404         if (out == NULL)
1405             return ENOMEM;
1406         out[1] = NULL;
1407         out[0] = strdup(addition);
1408         if (out[0] == NULL) {
1409             free(out);
1410             return ENOMEM;
1411         }
1412     } else {
1413         int i;
1414         char **a = *array;
1415         for (i = 0; a[i] != NULL; i++);
1416         out = malloc( (i + 2) * sizeof(char *));
1417         if (out == NULL)
1418             return ENOMEM;
1419         for (i = 0; a[i] != NULL; i++) {
1420             out[i] = a[i];
1421         }
1422         out[i++] = strdup(addition);
1423         if (out == NULL) {
1424             free(out);
1425             return ENOMEM;
1426         }
1427         out[i] = NULL;
1428         free(*array);
1429     }
1430     *array = out;
1431
1432     return 0;
1433 }
1434 static krb5_error_code
1435 handle_gic_opt(krb5_context context,
1436                pkinit_context plgctx,
1437                const char *attr,
1438                const char *value)
1439 {
1440     krb5_error_code retval;
1441
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;
1447         }
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");
1452             return ENOMEM;
1453         }
1454     } else if (strcmp(attr, "X509_anchors") == 0) {
1455         retval = add_string_to_array(context, &plgctx->idopts->anchors, value);
1456         if (retval)
1457             return retval;
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;
1462         }
1463     }
1464     return 0;
1465 }
1466
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,
1470                       const char *attr,
1471                       const char *value)
1472 {
1473     krb5_error_code retval;
1474     pkinit_context plgctx = (pkinit_context)moddata;
1475
1476     pkiDebug("(pkinit) received '%s' = '%s'\n", attr, value);
1477     retval = handle_gic_opt(context, plgctx, attr, value);
1478     if (retval)
1479         return retval;
1480
1481     return 0;
1482 }
1483
1484 krb5_error_code
1485 clpreauth_pkinit_initvt(krb5_context context, int maj_ver, int min_ver,
1486                         krb5_plugin_vtable vtable);
1487
1488 krb5_error_code
1489 clpreauth_pkinit_initvt(krb5_context context, int maj_ver, int min_ver,
1490                         krb5_plugin_vtable vtable)
1491 {
1492     krb5_clpreauth_vtable vt;
1493
1494     if (maj_ver != 1)
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;
1507     return 0;
1508 }