Merge lorikeet-heimdal -r 787 into Samba4 tree.
[samba.git] / source4 / heimdal / lib / krb5 / pkinit.c
1 /*
2  * Copyright (c) 2003 - 2007 Kungliga Tekniska Högskolan
3  * (Royal Institute of Technology, Stockholm, Sweden). 
4  * All rights reserved. 
5  *
6  * Redistribution and use in source and binary forms, with or without 
7  * modification, are permitted provided that the following conditions 
8  * are met: 
9  *
10  * 1. Redistributions of source code must retain the above copyright 
11  *    notice, this list of conditions and the following disclaimer. 
12  *
13  * 2. Redistributions in binary form must reproduce the above copyright 
14  *    notice, this list of conditions and the following disclaimer in the 
15  *    documentation and/or other materials provided with the distribution. 
16  *
17  * 3. Neither the name of the Institute nor the names of its contributors 
18  *    may be used to endorse or promote products derived from this software 
19  *    without specific prior written permission. 
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 
31  * SUCH DAMAGE. 
32  */
33
34 #include "krb5_locl.h"
35
36 RCSID("$Id: pkinit.c 22673 2008-03-10 15:00:05Z lha $");
37
38 struct krb5_dh_moduli {
39     char *name;
40     unsigned long bits;
41     heim_integer p;
42     heim_integer g;
43     heim_integer q;
44 };
45
46 #ifdef PKINIT
47
48 #include <heim_asn1.h>
49 #include <rfc2459_asn1.h>
50 #include <cms_asn1.h>
51 #include <pkcs8_asn1.h>
52 #include <pkcs9_asn1.h>
53 #include <pkcs12_asn1.h>
54 #include <pkinit_asn1.h>
55 #include <asn1_err.h>
56
57 #include <der.h>
58
59 #include <hx509.h>
60
61 enum {
62     COMPAT_WIN2K = 1,
63     COMPAT_IETF = 2
64 };
65
66 struct krb5_pk_identity {
67     hx509_context hx509ctx;
68     hx509_verify_ctx verify_ctx;
69     hx509_certs certs;
70     hx509_certs anchors;
71     hx509_certs certpool;
72     hx509_revoke_ctx revokectx;
73 };
74
75 struct krb5_pk_cert {
76     hx509_cert cert;
77 };
78
79 struct krb5_pk_init_ctx_data {
80     struct krb5_pk_identity *id;
81     DH *dh;
82     krb5_data *clientDHNonce;
83     struct krb5_dh_moduli **m;
84     hx509_peer_info peer;
85     int type;
86     unsigned int require_binding:1;
87     unsigned int require_eku:1;
88     unsigned int require_krbtgt_otherName:1;
89     unsigned int require_hostname_match:1;
90     unsigned int trustedCertifiers:1;
91 };
92
93 static void
94 _krb5_pk_copy_error(krb5_context context,
95                     hx509_context hx509ctx,
96                     int hxret,
97                     const char *fmt,
98                     ...)
99     __attribute__ ((format (printf, 4, 5)));
100
101 /*
102  *
103  */
104
105 void KRB5_LIB_FUNCTION
106 _krb5_pk_cert_free(struct krb5_pk_cert *cert)
107 {
108     if (cert->cert) {
109         hx509_cert_free(cert->cert);
110     }
111     free(cert);
112 }
113
114 static krb5_error_code
115 BN_to_integer(krb5_context context, BIGNUM *bn, heim_integer *integer)
116 {
117     integer->length = BN_num_bytes(bn);
118     integer->data = malloc(integer->length);
119     if (integer->data == NULL) {
120         krb5_clear_error_string(context);
121         return ENOMEM;
122     }
123     BN_bn2bin(bn, integer->data);
124     integer->negative = BN_is_negative(bn);
125     return 0;
126 }
127
128 static BIGNUM *
129 integer_to_BN(krb5_context context, const char *field, const heim_integer *f)
130 {
131     BIGNUM *bn;
132
133     bn = BN_bin2bn((const unsigned char *)f->data, f->length, NULL);
134     if (bn == NULL) {
135         krb5_set_error_string(context, "PKINIT: parsing BN failed %s", field);
136         return NULL;
137     }
138     BN_set_negative(bn, f->negative);
139     return bn;
140 }
141
142 struct certfind {
143     const char *type;
144     const heim_oid *oid;
145 };
146
147 /*
148  * Try searchin the key by to use by first looking for for PK-INIT
149  * EKU, then the Microsoft smart card EKU and last, no special EKU at all.
150  */
151
152 static krb5_error_code
153 find_cert(krb5_context context, struct krb5_pk_identity *id, 
154           hx509_query *q, hx509_cert *cert)
155 {
156     struct certfind cf[3] = { 
157         { "PKINIT EKU" },
158         { "MS EKU" },
159         { "no" }
160     };
161     int i, ret;
162
163     cf[0].oid = oid_id_pkekuoid();
164     cf[1].oid = oid_id_pkinit_ms_eku();
165     cf[2].oid = NULL;
166
167     for (i = 0; i < sizeof(cf)/sizeof(cf[0]); i++) {
168         ret = hx509_query_match_eku(q, cf[i].oid);
169         if (ret) {
170             _krb5_pk_copy_error(context, id->hx509ctx, ret, 
171                                 "Failed setting %s OID", cf[i].type);
172             return ret;
173         }
174
175         ret = hx509_certs_find(id->hx509ctx, id->certs, q, cert);
176         if (ret == 0)
177             break;
178         _krb5_pk_copy_error(context, id->hx509ctx, ret, 
179                             "Failed cert for finding %s OID", cf[i].type);
180     }
181     return ret;
182 }
183
184
185 static krb5_error_code
186 create_signature(krb5_context context,
187                  const heim_oid *eContentType,
188                  krb5_data *eContent,
189                  struct krb5_pk_identity *id,
190                  hx509_peer_info peer,
191                  krb5_data *sd_data)
192 {
193     hx509_cert cert = NULL;
194     hx509_query *q = NULL;
195     int ret;
196
197     ret = hx509_query_alloc(id->hx509ctx, &q);
198     if (ret) {
199         _krb5_pk_copy_error(context, id->hx509ctx, ret, 
200                             "Allocate query to find signing certificate");
201         return ret;
202     }
203
204     hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY);
205     hx509_query_match_option(q, HX509_QUERY_OPTION_KU_DIGITALSIGNATURE);
206
207     ret = find_cert(context, id, q, &cert);
208     hx509_query_free(id->hx509ctx, q);
209     if (ret)
210         return ret;
211
212     ret = hx509_cms_create_signed_1(id->hx509ctx,
213                                     0,
214                                     eContentType,
215                                     eContent->data,
216                                     eContent->length,
217                                     NULL,
218                                     cert,
219                                     peer,
220                                     NULL,
221                                     id->certs,
222                                     sd_data);
223     hx509_cert_free(cert);
224     if (ret) {
225         _krb5_pk_copy_error(context, id->hx509ctx, ret,
226                             "Create CMS signedData");
227         return ret;
228     }
229
230     return 0;
231 }
232
233 static int
234 cert2epi(hx509_context context, void *ctx, hx509_cert c)
235 {
236     ExternalPrincipalIdentifiers *ids = ctx;
237     ExternalPrincipalIdentifier id;
238     hx509_name subject = NULL;
239     void *p;
240     int ret;
241
242     memset(&id, 0, sizeof(id));
243
244     ret = hx509_cert_get_subject(c, &subject);
245     if (ret)
246         return ret;
247
248     if (hx509_name_is_null_p(subject) != 0) {
249
250         id.subjectName = calloc(1, sizeof(*id.subjectName));
251         if (id.subjectName == NULL) {
252             hx509_name_free(&subject);
253             free_ExternalPrincipalIdentifier(&id);
254             return ENOMEM;
255         }
256     
257         ret = hx509_name_binary(subject, id.subjectName);
258         if (ret) {
259             hx509_name_free(&subject);
260             free_ExternalPrincipalIdentifier(&id);
261             return ret;
262         }
263     }
264     hx509_name_free(&subject);
265
266
267     id.issuerAndSerialNumber = calloc(1, sizeof(*id.issuerAndSerialNumber));
268     if (id.issuerAndSerialNumber == NULL) {
269         free_ExternalPrincipalIdentifier(&id);
270         return ENOMEM;
271     }
272
273     {
274         IssuerAndSerialNumber iasn;
275         hx509_name issuer;
276         size_t size;
277         
278         memset(&iasn, 0, sizeof(iasn));
279
280         ret = hx509_cert_get_issuer(c, &issuer);
281         if (ret) {
282             free_ExternalPrincipalIdentifier(&id);
283             return ret;
284         }
285
286         ret = hx509_name_to_Name(issuer, &iasn.issuer);
287         hx509_name_free(&issuer);
288         if (ret) {
289             free_ExternalPrincipalIdentifier(&id);
290             return ret;
291         }
292         
293         ret = hx509_cert_get_serialnumber(c, &iasn.serialNumber);
294         if (ret) {
295             free_IssuerAndSerialNumber(&iasn);
296             free_ExternalPrincipalIdentifier(&id);
297             return ret;
298         }
299
300         ASN1_MALLOC_ENCODE(IssuerAndSerialNumber,
301                            id.issuerAndSerialNumber->data, 
302                            id.issuerAndSerialNumber->length,
303                            &iasn, &size, ret);
304         free_IssuerAndSerialNumber(&iasn);
305         if (ret)
306             return ret;
307         if (id.issuerAndSerialNumber->length != size)
308             abort();
309     }
310
311     id.subjectKeyIdentifier = NULL;
312
313     p = realloc(ids->val, sizeof(ids->val[0]) * (ids->len + 1)); 
314     if (p == NULL) {
315         free_ExternalPrincipalIdentifier(&id);
316         return ENOMEM;
317     }
318
319     ids->val = p;
320     ids->val[ids->len] = id;
321     ids->len++;
322
323     return 0;
324 }
325
326 static krb5_error_code
327 build_edi(krb5_context context,
328           hx509_context hx509ctx,
329           hx509_certs certs,
330           ExternalPrincipalIdentifiers *ids)
331 {
332     return hx509_certs_iter(hx509ctx, certs, cert2epi, ids);
333 }
334
335 static krb5_error_code
336 build_auth_pack(krb5_context context,
337                 unsigned nonce,
338                 krb5_pk_init_ctx ctx,
339                 DH *dh,
340                 const KDC_REQ_BODY *body,
341                 AuthPack *a)
342 {
343     size_t buf_size, len;
344     krb5_error_code ret;
345     void *buf;
346     krb5_timestamp sec;
347     int32_t usec;
348     Checksum checksum;
349
350     krb5_clear_error_string(context);
351
352     memset(&checksum, 0, sizeof(checksum));
353
354     krb5_us_timeofday(context, &sec, &usec);
355     a->pkAuthenticator.ctime = sec;
356     a->pkAuthenticator.nonce = nonce;
357
358     ASN1_MALLOC_ENCODE(KDC_REQ_BODY, buf, buf_size, body, &len, ret);
359     if (ret)
360         return ret;
361     if (buf_size != len)
362         krb5_abortx(context, "internal error in ASN.1 encoder");
363
364     ret = krb5_create_checksum(context,
365                                NULL,
366                                0,
367                                CKSUMTYPE_SHA1,
368                                buf,
369                                len,
370                                &checksum);
371     free(buf);
372     if (ret) 
373         return ret;
374
375     ALLOC(a->pkAuthenticator.paChecksum, 1);
376     if (a->pkAuthenticator.paChecksum == NULL) {
377         krb5_set_error_string(context, "malloc: out of memory");
378         return ENOMEM;
379     }
380
381     ret = krb5_data_copy(a->pkAuthenticator.paChecksum,
382                          checksum.checksum.data, checksum.checksum.length);
383     free_Checksum(&checksum);
384     if (ret)
385         return ret;
386
387     if (dh) {
388         DomainParameters dp;
389         heim_integer dh_pub_key;
390         krb5_data dhbuf;
391         size_t size;
392
393         if (1 /* support_cached_dh */) {
394             ALLOC(a->clientDHNonce, 1);
395             if (a->clientDHNonce == NULL) {
396                 krb5_clear_error_string(context);
397                 return ENOMEM;
398             }
399             ret = krb5_data_alloc(a->clientDHNonce, 40);
400             if (a->clientDHNonce == NULL) {
401                 krb5_clear_error_string(context);
402                 return ENOMEM;
403             }
404             memset(a->clientDHNonce->data, 0, a->clientDHNonce->length);
405             ret = krb5_copy_data(context, a->clientDHNonce, 
406                                  &ctx->clientDHNonce);
407             if (ret)
408                 return ret;
409         }
410
411         ALLOC(a->clientPublicValue, 1);
412         if (a->clientPublicValue == NULL)
413             return ENOMEM;
414         ret = der_copy_oid(oid_id_dhpublicnumber(),
415                            &a->clientPublicValue->algorithm.algorithm);
416         if (ret)
417             return ret;
418         
419         memset(&dp, 0, sizeof(dp));
420
421         ret = BN_to_integer(context, dh->p, &dp.p);
422         if (ret) {
423             free_DomainParameters(&dp);
424             return ret;
425         }
426         ret = BN_to_integer(context, dh->g, &dp.g);
427         if (ret) {
428             free_DomainParameters(&dp);
429             return ret;
430         }
431         ret = BN_to_integer(context, dh->q, &dp.q);
432         if (ret) {
433             free_DomainParameters(&dp);
434             return ret;
435         }
436         dp.j = NULL;
437         dp.validationParms = NULL;
438
439         a->clientPublicValue->algorithm.parameters = 
440             malloc(sizeof(*a->clientPublicValue->algorithm.parameters));
441         if (a->clientPublicValue->algorithm.parameters == NULL) {
442             free_DomainParameters(&dp);
443             return ret;
444         }
445
446         ASN1_MALLOC_ENCODE(DomainParameters,
447                            a->clientPublicValue->algorithm.parameters->data,
448                            a->clientPublicValue->algorithm.parameters->length,
449                            &dp, &size, ret);
450         free_DomainParameters(&dp);
451         if (ret)
452             return ret;
453         if (size != a->clientPublicValue->algorithm.parameters->length)
454             krb5_abortx(context, "Internal ASN1 encoder error");
455
456         ret = BN_to_integer(context, dh->pub_key, &dh_pub_key);
457         if (ret)
458             return ret;
459
460         ASN1_MALLOC_ENCODE(DHPublicKey, dhbuf.data, dhbuf.length,
461                            &dh_pub_key, &size, ret);
462         der_free_heim_integer(&dh_pub_key);
463         if (ret)
464             return ret;
465         if (size != dhbuf.length)
466             krb5_abortx(context, "asn1 internal error");
467
468         a->clientPublicValue->subjectPublicKey.length = dhbuf.length * 8;
469         a->clientPublicValue->subjectPublicKey.data = dhbuf.data;
470     }
471
472     {
473         a->supportedCMSTypes = calloc(1, sizeof(*a->supportedCMSTypes));
474         if (a->supportedCMSTypes == NULL)
475             return ENOMEM;
476
477         ret = hx509_crypto_available(ctx->id->hx509ctx, HX509_SELECT_ALL, NULL,
478                                      &a->supportedCMSTypes->val,
479                                      &a->supportedCMSTypes->len);
480         if (ret)
481             return ret;
482     }
483
484     return ret;
485 }
486
487 krb5_error_code KRB5_LIB_FUNCTION
488 _krb5_pk_mk_ContentInfo(krb5_context context,
489                         const krb5_data *buf, 
490                         const heim_oid *oid,
491                         struct ContentInfo *content_info)
492 {
493     krb5_error_code ret;
494
495     ret = der_copy_oid(oid, &content_info->contentType);
496     if (ret)
497         return ret;
498     ALLOC(content_info->content, 1);
499     if (content_info->content == NULL)
500         return ENOMEM;
501     content_info->content->data = malloc(buf->length);
502     if (content_info->content->data == NULL)
503         return ENOMEM;
504     memcpy(content_info->content->data, buf->data, buf->length);
505     content_info->content->length = buf->length;
506     return 0;
507 }
508
509 static krb5_error_code
510 pk_mk_padata(krb5_context context,
511              krb5_pk_init_ctx ctx,
512              const KDC_REQ_BODY *req_body,
513              unsigned nonce,
514              METHOD_DATA *md)
515 {
516     struct ContentInfo content_info;
517     krb5_error_code ret;
518     const heim_oid *oid;
519     size_t size;
520     krb5_data buf, sd_buf;
521     int pa_type;
522
523     krb5_data_zero(&buf);
524     krb5_data_zero(&sd_buf);
525     memset(&content_info, 0, sizeof(content_info));
526
527     if (ctx->type == COMPAT_WIN2K) {
528         AuthPack_Win2k ap;
529         krb5_timestamp sec;
530         int32_t usec;
531
532         memset(&ap, 0, sizeof(ap));
533
534         /* fill in PKAuthenticator */
535         ret = copy_PrincipalName(req_body->sname, &ap.pkAuthenticator.kdcName);
536         if (ret) {
537             free_AuthPack_Win2k(&ap);
538             krb5_clear_error_string(context);
539             goto out;
540         }
541         ret = copy_Realm(&req_body->realm, &ap.pkAuthenticator.kdcRealm);
542         if (ret) {
543             free_AuthPack_Win2k(&ap);
544             krb5_clear_error_string(context);
545             goto out;
546         }
547
548         krb5_us_timeofday(context, &sec, &usec);
549         ap.pkAuthenticator.ctime = sec;
550         ap.pkAuthenticator.cusec = usec;
551         ap.pkAuthenticator.nonce = nonce;
552
553         ASN1_MALLOC_ENCODE(AuthPack_Win2k, buf.data, buf.length,
554                            &ap, &size, ret);
555         free_AuthPack_Win2k(&ap);
556         if (ret) {
557             krb5_set_error_string(context, "AuthPack_Win2k: %d", ret);
558             goto out;
559         }
560         if (buf.length != size)
561             krb5_abortx(context, "internal ASN1 encoder error");
562
563         oid = oid_id_pkcs7_data();
564     } else if (ctx->type == COMPAT_IETF) {
565         AuthPack ap;
566         
567         memset(&ap, 0, sizeof(ap));
568
569         ret = build_auth_pack(context, nonce, ctx, ctx->dh, req_body, &ap);
570         if (ret) {
571             free_AuthPack(&ap);
572             goto out;
573         }
574
575         ASN1_MALLOC_ENCODE(AuthPack, buf.data, buf.length, &ap, &size, ret);
576         free_AuthPack(&ap);
577         if (ret) {
578             krb5_set_error_string(context, "AuthPack: %d", ret);
579             goto out;
580         }
581         if (buf.length != size)
582             krb5_abortx(context, "internal ASN1 encoder error");
583
584         oid = oid_id_pkauthdata();
585     } else
586         krb5_abortx(context, "internal pkinit error");
587
588     ret = create_signature(context, oid, &buf, ctx->id,
589                            ctx->peer, &sd_buf);
590     krb5_data_free(&buf);
591     if (ret)
592         goto out;
593
594     ret = hx509_cms_wrap_ContentInfo(oid_id_pkcs7_signedData(), &sd_buf, &buf);
595     krb5_data_free(&sd_buf);
596     if (ret) {
597         krb5_set_error_string(context,
598                               "ContentInfo wrapping of signedData failed");
599         goto out;
600     }
601
602     if (ctx->type == COMPAT_WIN2K) {
603         PA_PK_AS_REQ_Win2k winreq;
604
605         pa_type = KRB5_PADATA_PK_AS_REQ_WIN;
606
607         memset(&winreq, 0, sizeof(winreq));
608
609         winreq.signed_auth_pack = buf;
610
611         ASN1_MALLOC_ENCODE(PA_PK_AS_REQ_Win2k, buf.data, buf.length,
612                            &winreq, &size, ret);
613         free_PA_PK_AS_REQ_Win2k(&winreq);
614
615     } else if (ctx->type == COMPAT_IETF) {
616         PA_PK_AS_REQ req;
617
618         pa_type = KRB5_PADATA_PK_AS_REQ;
619
620         memset(&req, 0, sizeof(req));
621         req.signedAuthPack = buf;       
622
623         if (ctx->trustedCertifiers) {
624
625             req.trustedCertifiers = calloc(1, sizeof(*req.trustedCertifiers));
626             if (req.trustedCertifiers == NULL) {
627                 krb5_set_error_string(context, "malloc: out of memory");
628                 free_PA_PK_AS_REQ(&req);
629                 goto out;
630             }
631             ret = build_edi(context, ctx->id->hx509ctx, 
632                             ctx->id->anchors, req.trustedCertifiers);
633             if (ret) {
634                 krb5_set_error_string(context, "pk-init: failed to build trustedCertifiers");
635                 free_PA_PK_AS_REQ(&req);
636                 goto out;
637             }
638         }
639         req.kdcPkId = NULL;
640
641         ASN1_MALLOC_ENCODE(PA_PK_AS_REQ, buf.data, buf.length,
642                            &req, &size, ret);
643
644         free_PA_PK_AS_REQ(&req);
645
646     } else
647         krb5_abortx(context, "internal pkinit error");
648     if (ret) {
649         krb5_set_error_string(context, "PA-PK-AS-REQ %d", ret);
650         goto out;
651     }
652     if (buf.length != size)
653         krb5_abortx(context, "Internal ASN1 encoder error");
654
655     ret = krb5_padata_add(context, md, pa_type, buf.data, buf.length);
656     if (ret)
657         free(buf.data);
658
659     if (ret == 0 && ctx->type == COMPAT_WIN2K)
660         krb5_padata_add(context, md, KRB5_PADATA_PK_AS_09_BINDING, NULL, 0);
661
662 out:
663     free_ContentInfo(&content_info);
664
665     return ret;
666 }
667
668
669 krb5_error_code KRB5_LIB_FUNCTION 
670 _krb5_pk_mk_padata(krb5_context context,
671                    void *c,
672                    const KDC_REQ_BODY *req_body,
673                    unsigned nonce,
674                    METHOD_DATA *md)
675 {
676     krb5_pk_init_ctx ctx = c;
677     int win2k_compat;
678
679     win2k_compat = krb5_config_get_bool_default(context, NULL,
680                                                 FALSE,
681                                                 "realms",
682                                                 req_body->realm,
683                                                 "pkinit_win2k",
684                                                 NULL);
685
686     if (win2k_compat) {
687         ctx->require_binding = 
688             krb5_config_get_bool_default(context, NULL,
689                                          FALSE,
690                                          "realms",
691                                          req_body->realm,
692                                          "pkinit_win2k_require_binding",
693                                          NULL);
694         ctx->type = COMPAT_WIN2K;
695     } else
696         ctx->type = COMPAT_IETF;
697
698     ctx->require_eku = 
699         krb5_config_get_bool_default(context, NULL,
700                                      TRUE,
701                                      "realms",
702                                      req_body->realm,
703                                      "pkinit_require_eku",
704                                      NULL);
705     ctx->require_krbtgt_otherName = 
706         krb5_config_get_bool_default(context, NULL,
707                                      TRUE,
708                                      "realms",
709                                      req_body->realm,
710                                      "pkinit_require_krbtgt_otherName",
711                                      NULL);
712
713     ctx->require_hostname_match = 
714         krb5_config_get_bool_default(context, NULL,
715                                      FALSE,
716                                      "realms",
717                                      req_body->realm,
718                                      "pkinit_require_hostname_match",
719                                      NULL);
720
721     ctx->trustedCertifiers = 
722         krb5_config_get_bool_default(context, NULL,
723                                      TRUE,
724                                      "realms",
725                                      req_body->realm,
726                                      "pkinit_trustedCertifiers",
727                                      NULL);
728
729     return pk_mk_padata(context, ctx, req_body, nonce, md);
730 }
731
732 krb5_error_code KRB5_LIB_FUNCTION
733 _krb5_pk_verify_sign(krb5_context context,
734                      const void *data,
735                      size_t length,
736                      struct krb5_pk_identity *id,
737                      heim_oid *contentType,
738                      krb5_data *content,
739                      struct krb5_pk_cert **signer)
740 {
741     hx509_certs signer_certs;
742     int ret;
743
744     *signer = NULL;
745
746     ret = hx509_cms_verify_signed(id->hx509ctx,
747                                   id->verify_ctx,
748                                   data,
749                                   length,
750                                   NULL,
751                                   id->certpool,
752                                   contentType,
753                                   content,
754                                   &signer_certs);
755     if (ret) {
756         _krb5_pk_copy_error(context, id->hx509ctx, ret,
757                             "CMS verify signed failed");
758         return ret;
759     }
760
761     *signer = calloc(1, sizeof(**signer));
762     if (*signer == NULL) {
763         krb5_clear_error_string(context);
764         ret = ENOMEM;
765         goto out;
766     }
767         
768     ret = hx509_get_one_cert(id->hx509ctx, signer_certs, &(*signer)->cert);
769     if (ret) {
770         _krb5_pk_copy_error(context, id->hx509ctx, ret,
771                             "Failed to get on of the signer certs");
772         goto out;
773     }
774
775 out:
776     hx509_certs_free(&signer_certs);
777     if (ret) {
778         if (*signer) {
779             hx509_cert_free((*signer)->cert);
780             free(*signer);
781             *signer = NULL;
782         }
783     }
784
785     return ret;
786 }
787
788 static krb5_error_code
789 get_reply_key_win(krb5_context context,
790                   const krb5_data *content,
791                   unsigned nonce,
792                   krb5_keyblock **key)
793 {
794     ReplyKeyPack_Win2k key_pack;
795     krb5_error_code ret;
796     size_t size;
797
798     ret = decode_ReplyKeyPack_Win2k(content->data,
799                                     content->length,
800                                     &key_pack,
801                                     &size);
802     if (ret) {
803         krb5_set_error_string(context, "PKINIT decoding reply key failed");
804         free_ReplyKeyPack_Win2k(&key_pack);
805         return ret;
806     }
807      
808     if (key_pack.nonce != nonce) {
809         krb5_set_error_string(context, "PKINIT enckey nonce is wrong");
810         free_ReplyKeyPack_Win2k(&key_pack);
811         return KRB5KRB_AP_ERR_MODIFIED;
812     }
813
814     *key = malloc (sizeof (**key));
815     if (*key == NULL) {
816         krb5_set_error_string(context, "PKINIT failed allocating reply key");
817         free_ReplyKeyPack_Win2k(&key_pack);
818         krb5_set_error_string(context, "malloc: out of memory");
819         return ENOMEM;
820     }
821
822     ret = copy_EncryptionKey(&key_pack.replyKey, *key);
823     free_ReplyKeyPack_Win2k(&key_pack);
824     if (ret) {
825         krb5_set_error_string(context, "PKINIT failed copying reply key");
826         free(*key);
827         *key = NULL;
828     }
829
830     return ret;
831 }
832
833 static krb5_error_code
834 get_reply_key(krb5_context context,
835               const krb5_data *content,
836               const krb5_data *req_buffer,
837               krb5_keyblock **key)
838 {
839     ReplyKeyPack key_pack;
840     krb5_error_code ret;
841     size_t size;
842
843     ret = decode_ReplyKeyPack(content->data,
844                               content->length,
845                               &key_pack,
846                               &size);
847     if (ret) {
848         krb5_set_error_string(context, "PKINIT decoding reply key failed");
849         free_ReplyKeyPack(&key_pack);
850         return ret;
851     }
852     
853     {
854         krb5_crypto crypto;
855
856         /* 
857          * XXX Verify kp.replyKey is a allowed enctype in the
858          * configuration file
859          */
860
861         ret = krb5_crypto_init(context, &key_pack.replyKey, 0, &crypto);
862         if (ret) {
863             free_ReplyKeyPack(&key_pack);
864             return ret;
865         }
866
867         ret = krb5_verify_checksum(context, crypto, 6,
868                                    req_buffer->data, req_buffer->length,
869                                    &key_pack.asChecksum);
870         krb5_crypto_destroy(context, crypto);
871         if (ret) {
872             free_ReplyKeyPack(&key_pack);
873             return ret;
874         }
875     }
876
877     *key = malloc (sizeof (**key));
878     if (*key == NULL) {
879         krb5_set_error_string(context, "PKINIT failed allocating reply key");
880         free_ReplyKeyPack(&key_pack);
881         krb5_set_error_string(context, "malloc: out of memory");
882         return ENOMEM;
883     }
884
885     ret = copy_EncryptionKey(&key_pack.replyKey, *key);
886     free_ReplyKeyPack(&key_pack);
887     if (ret) {
888         krb5_set_error_string(context, "PKINIT failed copying reply key");
889         free(*key);
890         *key = NULL;
891     }
892
893     return ret;
894 }
895
896
897 static krb5_error_code
898 pk_verify_host(krb5_context context,
899                const char *realm,
900                const krb5_krbhst_info *hi,
901                struct krb5_pk_init_ctx_data *ctx,
902                struct krb5_pk_cert *host)
903 {
904     krb5_error_code ret = 0;
905
906     if (ctx->require_eku) {
907         ret = hx509_cert_check_eku(ctx->id->hx509ctx, host->cert,
908                                    oid_id_pkkdcekuoid(), 0);
909         if (ret) {
910             krb5_set_error_string(context, "No PK-INIT KDC EKU in kdc certificate");
911             return ret;
912         }
913     }
914     if (ctx->require_krbtgt_otherName) {
915         hx509_octet_string_list list;
916         int i;
917
918         ret = hx509_cert_find_subjectAltName_otherName(ctx->id->hx509ctx,
919                                                        host->cert,
920                                                        oid_id_pkinit_san(),
921                                                        &list);
922         if (ret) {
923             krb5_set_error_string(context, "Failed to find the PK-INIT "
924                                   "subjectAltName in the KDC certificate");
925
926             return ret;
927         }
928
929         for (i = 0; i < list.len; i++) {
930             KRB5PrincipalName r;
931
932             ret = decode_KRB5PrincipalName(list.val[i].data,
933                                            list.val[i].length,
934                                            &r,
935                                            NULL);
936             if (ret) {
937                 krb5_set_error_string(context, "Failed to decode the PK-INIT "
938                                       "subjectAltName in the KDC certificate");
939
940                 break;
941             }
942
943             if (r.principalName.name_string.len != 2 ||
944                 strcmp(r.principalName.name_string.val[0], KRB5_TGS_NAME) != 0 ||
945                 strcmp(r.principalName.name_string.val[1], realm) != 0 ||
946                 strcmp(r.realm, realm) != 0)
947             {
948                 krb5_set_error_string(context, "KDC have wrong realm name in "
949                                       "the certificate");
950                 ret = KRB5_KDC_ERR_INVALID_CERTIFICATE;
951             }
952
953             free_KRB5PrincipalName(&r);
954             if (ret)
955                 break;
956         }
957         hx509_free_octet_string_list(&list);
958     }
959     if (ret)
960         return ret;
961     
962     if (hi) {
963         ret = hx509_verify_hostname(ctx->id->hx509ctx, host->cert, 
964                                     ctx->require_hostname_match,
965                                     HX509_HN_HOSTNAME,
966                                     hi->hostname,
967                                     hi->ai->ai_addr, hi->ai->ai_addrlen);
968
969         if (ret)
970             krb5_set_error_string(context, "Address mismatch in "
971                                   "the KDC certificate");
972     }
973     return ret;
974 }
975
976 static krb5_error_code
977 pk_rd_pa_reply_enckey(krb5_context context,
978                       int type,
979                       const heim_octet_string *indata,
980                       const heim_oid *dataType,
981                       const char *realm,
982                       krb5_pk_init_ctx ctx,
983                       krb5_enctype etype,
984                       const krb5_krbhst_info *hi,
985                       unsigned nonce,
986                       const krb5_data *req_buffer,
987                       PA_DATA *pa,
988                       krb5_keyblock **key) 
989 {
990     krb5_error_code ret;
991     struct krb5_pk_cert *host = NULL;
992     krb5_data content;
993     heim_oid contentType = { 0, NULL };
994
995     if (der_heim_oid_cmp(oid_id_pkcs7_envelopedData(), dataType)) {
996         krb5_set_error_string(context, "PKINIT: Invalid content type");
997         return EINVAL;
998     }
999
1000     ret = hx509_cms_unenvelope(ctx->id->hx509ctx,
1001                                ctx->id->certs,
1002                                HX509_CMS_UE_DONT_REQUIRE_KU_ENCIPHERMENT,
1003                                indata->data,
1004                                indata->length,
1005                                NULL,
1006                                &contentType,
1007                                &content);
1008     if (ret) {
1009         _krb5_pk_copy_error(context, ctx->id->hx509ctx, ret,
1010                             "Failed to unenvelope CMS data in PK-INIT reply");
1011         return ret;
1012     }
1013     der_free_oid(&contentType);
1014
1015 #if 0 /* windows LH with interesting CMS packets, leaks memory */
1016     {
1017         size_t ph = 1 + der_length_len (length);
1018         unsigned char *ptr = malloc(length + ph);
1019         size_t l;
1020
1021         memcpy(ptr + ph, p, length);
1022
1023         ret = der_put_length_and_tag (ptr + ph - 1, ph, length,
1024                                       ASN1_C_UNIV, CONS, UT_Sequence, &l);
1025         if (ret)
1026             return ret;
1027         ptr += ph - l;
1028         length += l;
1029         p = ptr;
1030     }
1031 #endif
1032
1033     /* win2k uses ContentInfo */
1034     if (type == COMPAT_WIN2K) {
1035         heim_oid type;
1036         heim_octet_string out;
1037
1038         ret = hx509_cms_unwrap_ContentInfo(&content, &type, &out, NULL);
1039         if (der_heim_oid_cmp(&type, oid_id_pkcs7_signedData())) {
1040             ret = EINVAL; /* XXX */
1041             krb5_set_error_string(context, "PKINIT: Invalid content type");
1042             der_free_oid(&type);
1043             der_free_octet_string(&out);
1044             goto out;
1045         }
1046         der_free_oid(&type);
1047         krb5_data_free(&content);
1048         ret = krb5_data_copy(&content, out.data, out.length);
1049         der_free_octet_string(&out);
1050         if (ret) {
1051             krb5_set_error_string(context, "PKINIT: out of memory");
1052             goto out;
1053         }
1054     }
1055
1056     ret = _krb5_pk_verify_sign(context, 
1057                                content.data,
1058                                content.length,
1059                                ctx->id,
1060                                &contentType,
1061                                &content,
1062                                &host);
1063     if (ret)
1064         goto out;
1065
1066     /* make sure that it is the kdc's certificate */
1067     ret = pk_verify_host(context, realm, hi, ctx, host);
1068     if (ret) {
1069         goto out;
1070     }
1071
1072 #if 0
1073     if (type == COMPAT_WIN2K) {
1074         if (der_heim_oid_cmp(&contentType, oid_id_pkcs7_data()) != 0) {
1075             krb5_set_error_string(context, "PKINIT: reply key, wrong oid");
1076             ret = KRB5KRB_AP_ERR_MSG_TYPE;
1077             goto out;
1078         }
1079     } else {
1080         if (der_heim_oid_cmp(&contentType, oid_id_pkrkeydata()) != 0) {
1081             krb5_set_error_string(context, "PKINIT: reply key, wrong oid");
1082             ret = KRB5KRB_AP_ERR_MSG_TYPE;
1083             goto out;
1084         }
1085     }
1086 #endif
1087
1088     switch(type) {
1089     case COMPAT_WIN2K:
1090         ret = get_reply_key(context, &content, req_buffer, key);
1091         if (ret != 0 && ctx->require_binding == 0)
1092             ret = get_reply_key_win(context, &content, nonce, key);
1093         break;
1094     case COMPAT_IETF:
1095         ret = get_reply_key(context, &content, req_buffer, key);
1096         break;
1097     }
1098     if (ret)
1099         goto out;
1100
1101     /* XXX compare given etype with key->etype */
1102
1103  out:
1104     if (host)
1105         _krb5_pk_cert_free(host);
1106     der_free_oid(&contentType);
1107     krb5_data_free(&content);
1108
1109     return ret;
1110 }
1111
1112 static krb5_error_code
1113 pk_rd_pa_reply_dh(krb5_context context,
1114                   const heim_octet_string *indata,
1115                   const heim_oid *dataType,
1116                   const char *realm,
1117                   krb5_pk_init_ctx ctx,
1118                   krb5_enctype etype,
1119                   const krb5_krbhst_info *hi,
1120                   const DHNonce *c_n,
1121                   const DHNonce *k_n,
1122                   unsigned nonce,
1123                   PA_DATA *pa,
1124                   krb5_keyblock **key)
1125 {
1126     unsigned char *p, *dh_gen_key = NULL;
1127     struct krb5_pk_cert *host = NULL;
1128     BIGNUM *kdc_dh_pubkey = NULL;
1129     KDCDHKeyInfo kdc_dh_info;
1130     heim_oid contentType = { 0, NULL };
1131     krb5_data content;
1132     krb5_error_code ret;
1133     int dh_gen_keylen;
1134     size_t size;
1135
1136     krb5_data_zero(&content);
1137     memset(&kdc_dh_info, 0, sizeof(kdc_dh_info));
1138
1139     if (der_heim_oid_cmp(oid_id_pkcs7_signedData(), dataType)) {
1140         krb5_set_error_string(context, "PKINIT: Invalid content type");
1141         return EINVAL;
1142     }
1143
1144     ret = _krb5_pk_verify_sign(context, 
1145                                indata->data,
1146                                indata->length,
1147                                ctx->id,
1148                                &contentType,
1149                                &content,
1150                                &host);
1151     if (ret)
1152         goto out;
1153
1154     /* make sure that it is the kdc's certificate */
1155     ret = pk_verify_host(context, realm, hi, ctx, host);
1156     if (ret)
1157         goto out;
1158
1159     if (der_heim_oid_cmp(&contentType, oid_id_pkdhkeydata())) {
1160         krb5_set_error_string(context, "pkinit - dh reply contains wrong oid");
1161         ret = KRB5KRB_AP_ERR_MSG_TYPE;
1162         goto out;
1163     }
1164
1165     ret = decode_KDCDHKeyInfo(content.data,
1166                               content.length,
1167                               &kdc_dh_info,
1168                               &size);
1169
1170     if (ret) {
1171         krb5_set_error_string(context, "pkinit - "
1172                               "failed to decode KDC DH Key Info");
1173         goto out;
1174     }
1175
1176     if (kdc_dh_info.nonce != nonce) {
1177         krb5_set_error_string(context, "PKINIT: DH nonce is wrong");
1178         ret = KRB5KRB_AP_ERR_MODIFIED;
1179         goto out;
1180     }
1181
1182     if (kdc_dh_info.dhKeyExpiration) {
1183         if (k_n == NULL) {
1184             krb5_set_error_string(context, "pkinit; got key expiration "
1185                                   "without server nonce");
1186             ret = KRB5KRB_ERR_GENERIC;
1187             goto out;
1188         }
1189         if (c_n == NULL) {
1190             krb5_set_error_string(context, "pkinit; got DH reuse but no "
1191                                   "client nonce");
1192             ret = KRB5KRB_ERR_GENERIC;
1193             goto out;
1194         }
1195     } else {
1196         if (k_n) {
1197             krb5_set_error_string(context, "pkinit: got server nonce "
1198                                   "without key expiration");
1199             ret = KRB5KRB_ERR_GENERIC;
1200             goto out;
1201         }
1202         c_n = NULL;
1203     }
1204
1205
1206     p = kdc_dh_info.subjectPublicKey.data;
1207     size = (kdc_dh_info.subjectPublicKey.length + 7) / 8;
1208
1209     {
1210         DHPublicKey k;
1211         ret = decode_DHPublicKey(p, size, &k, NULL);
1212         if (ret) {
1213             krb5_set_error_string(context, "pkinit: can't decode "
1214                                   "without key expiration");
1215             goto out;
1216         }
1217
1218         kdc_dh_pubkey = integer_to_BN(context, "DHPublicKey", &k);
1219         free_DHPublicKey(&k);
1220         if (kdc_dh_pubkey == NULL) {
1221             ret = KRB5KRB_ERR_GENERIC;
1222             goto out;
1223         }
1224     }
1225     
1226     dh_gen_keylen = DH_size(ctx->dh);
1227     size = BN_num_bytes(ctx->dh->p);
1228     if (size < dh_gen_keylen)
1229         size = dh_gen_keylen;
1230
1231     dh_gen_key = malloc(size);
1232     if (dh_gen_key == NULL) {
1233         krb5_set_error_string(context, "malloc: out of memory");
1234         ret = ENOMEM;
1235         goto out;
1236     }
1237     memset(dh_gen_key, 0, size - dh_gen_keylen);
1238
1239     dh_gen_keylen = DH_compute_key(dh_gen_key + (size - dh_gen_keylen),
1240                                    kdc_dh_pubkey, ctx->dh);
1241     if (dh_gen_keylen == -1) {
1242         krb5_set_error_string(context, 
1243                               "PKINIT: Can't compute Diffie-Hellman key");
1244         ret = KRB5KRB_ERR_GENERIC;
1245         goto out;
1246     }
1247
1248     *key = malloc (sizeof (**key));
1249     if (*key == NULL) {
1250         krb5_set_error_string(context, "malloc: out of memory");
1251         ret = ENOMEM;
1252         goto out;
1253     }
1254
1255     ret = _krb5_pk_octetstring2key(context,
1256                                    etype,
1257                                    dh_gen_key, dh_gen_keylen,
1258                                    c_n, k_n,
1259                                    *key);
1260     if (ret) {
1261         krb5_set_error_string(context,
1262                               "PKINIT: can't create key from DH key");
1263         free(*key);
1264         *key = NULL;
1265         goto out;
1266     }
1267
1268  out:
1269     if (kdc_dh_pubkey)
1270         BN_free(kdc_dh_pubkey);
1271     if (dh_gen_key) {
1272         memset(dh_gen_key, 0, DH_size(ctx->dh));
1273         free(dh_gen_key);
1274     }
1275     if (host)
1276         _krb5_pk_cert_free(host);
1277     if (content.data)
1278         krb5_data_free(&content);
1279     der_free_oid(&contentType);
1280     free_KDCDHKeyInfo(&kdc_dh_info);
1281
1282     return ret;
1283 }
1284
1285 krb5_error_code KRB5_LIB_FUNCTION
1286 _krb5_pk_rd_pa_reply(krb5_context context,
1287                      const char *realm,
1288                      void *c,
1289                      krb5_enctype etype,
1290                      const krb5_krbhst_info *hi,
1291                      unsigned nonce,
1292                      const krb5_data *req_buffer,
1293                      PA_DATA *pa,
1294                      krb5_keyblock **key)
1295 {
1296     krb5_pk_init_ctx ctx = c;
1297     krb5_error_code ret;
1298     size_t size;
1299
1300     /* Check for IETF PK-INIT first */
1301     if (ctx->type == COMPAT_IETF) {
1302         PA_PK_AS_REP rep;
1303         heim_octet_string os, data;
1304         heim_oid oid;
1305         
1306         if (pa->padata_type != KRB5_PADATA_PK_AS_REP) {
1307             krb5_set_error_string(context, "PKINIT: wrong padata recv");
1308             return EINVAL;
1309         }
1310
1311         ret = decode_PA_PK_AS_REP(pa->padata_value.data,
1312                                   pa->padata_value.length,
1313                                   &rep,
1314                                   &size);
1315         if (ret) {
1316             krb5_set_error_string(context, "Failed to decode pkinit AS rep");
1317             return ret;
1318         }
1319
1320         switch (rep.element) {
1321         case choice_PA_PK_AS_REP_dhInfo:
1322             os = rep.u.dhInfo.dhSignedData;
1323             break;
1324         case choice_PA_PK_AS_REP_encKeyPack:
1325             os = rep.u.encKeyPack;
1326             break;
1327         default:
1328             free_PA_PK_AS_REP(&rep);
1329             krb5_set_error_string(context, "PKINIT: -27 reply "
1330                                   "invalid content type");
1331             return EINVAL;
1332         }
1333
1334         ret = hx509_cms_unwrap_ContentInfo(&os, &oid, &data, NULL);
1335         if (ret) {
1336             free_PA_PK_AS_REP(&rep);
1337             krb5_set_error_string(context, "PKINIT: failed to unwrap CI");
1338             return ret;
1339         }
1340
1341         switch (rep.element) {
1342         case choice_PA_PK_AS_REP_dhInfo:
1343             ret = pk_rd_pa_reply_dh(context, &data, &oid, realm, ctx, etype, hi,
1344                                     ctx->clientDHNonce,
1345                                     rep.u.dhInfo.serverDHNonce,
1346                                     nonce, pa, key);
1347             break;
1348         case choice_PA_PK_AS_REP_encKeyPack:
1349             ret = pk_rd_pa_reply_enckey(context, COMPAT_IETF, &data, &oid, realm, 
1350                                         ctx, etype, hi, nonce, req_buffer, pa, key);
1351             break;
1352         default:
1353             krb5_abortx(context, "pk-init as-rep case not possible to happen");
1354         }
1355         der_free_octet_string(&data);
1356         der_free_oid(&oid);
1357         free_PA_PK_AS_REP(&rep);
1358
1359     } else if (ctx->type == COMPAT_WIN2K) {
1360         PA_PK_AS_REP_Win2k w2krep;
1361
1362         /* Check for Windows encoding of the AS-REP pa data */ 
1363
1364 #if 0 /* should this be ? */
1365         if (pa->padata_type != KRB5_PADATA_PK_AS_REP) {
1366             krb5_set_error_string(context, "PKINIT: wrong padata recv");
1367             return EINVAL;
1368         }
1369 #endif
1370
1371         memset(&w2krep, 0, sizeof(w2krep));
1372         
1373         ret = decode_PA_PK_AS_REP_Win2k(pa->padata_value.data,
1374                                         pa->padata_value.length,
1375                                         &w2krep,
1376                                         &size);
1377         if (ret) {
1378             krb5_set_error_string(context, "PKINIT: Failed decoding windows "
1379                                   "pkinit reply %d", ret);
1380             return ret;
1381         }
1382
1383         krb5_clear_error_string(context);
1384         
1385         switch (w2krep.element) {
1386         case choice_PA_PK_AS_REP_Win2k_encKeyPack: {
1387             heim_octet_string data;
1388             heim_oid oid;
1389             
1390             ret = hx509_cms_unwrap_ContentInfo(&w2krep.u.encKeyPack, 
1391                                                &oid, &data, NULL);
1392             free_PA_PK_AS_REP_Win2k(&w2krep);
1393             if (ret) {
1394                 krb5_set_error_string(context, "PKINIT: failed to unwrap CI");
1395                 return ret;
1396             }
1397
1398             ret = pk_rd_pa_reply_enckey(context, COMPAT_WIN2K, &data, &oid, realm,
1399                                         ctx, etype, hi, nonce, req_buffer, pa, key);
1400             der_free_octet_string(&data);
1401             der_free_oid(&oid);
1402
1403             break;
1404         }
1405         default:
1406             free_PA_PK_AS_REP_Win2k(&w2krep);
1407             krb5_set_error_string(context, "PKINIT: win2k reply invalid "
1408                                   "content type");
1409             ret = EINVAL;
1410             break;
1411         }
1412     
1413     } else {
1414         krb5_set_error_string(context, "PKINIT: unknown reply type");
1415         ret = EINVAL;
1416     }
1417
1418     return ret;
1419 }
1420
1421 struct prompter {
1422     krb5_context context;
1423     krb5_prompter_fct prompter;
1424     void *prompter_data;
1425 };
1426
1427 static int 
1428 hx_pass_prompter(void *data, const hx509_prompt *prompter)
1429 {
1430     krb5_error_code ret;
1431     krb5_prompt prompt;
1432     krb5_data password_data;
1433     struct prompter *p = data;
1434    
1435     password_data.data   = prompter->reply.data;
1436     password_data.length = prompter->reply.length;
1437
1438     prompt.prompt = prompter->prompt;
1439     prompt.hidden = hx509_prompt_hidden(prompter->type);
1440     prompt.reply  = &password_data;
1441
1442     switch (prompter->type) {
1443     case HX509_PROMPT_TYPE_INFO:
1444         prompt.type   = KRB5_PROMPT_TYPE_INFO;
1445         break;
1446     case HX509_PROMPT_TYPE_PASSWORD:
1447     case HX509_PROMPT_TYPE_QUESTION:
1448     default:
1449         prompt.type   = KRB5_PROMPT_TYPE_PASSWORD;
1450         break;
1451     }   
1452    
1453     ret = (*p->prompter)(p->context, p->prompter_data, NULL, NULL, 1, &prompt);
1454     if (ret) {
1455         memset (prompter->reply.data, 0, prompter->reply.length);
1456         return 1;
1457     }
1458     return 0;
1459 }
1460
1461
1462 void KRB5_LIB_FUNCTION
1463 _krb5_pk_allow_proxy_certificate(struct krb5_pk_identity *id,
1464                                  int boolean)
1465 {
1466     hx509_verify_set_proxy_certificate(id->verify_ctx, boolean);
1467 }
1468
1469
1470 krb5_error_code KRB5_LIB_FUNCTION
1471 _krb5_pk_load_id(krb5_context context,
1472                  struct krb5_pk_identity **ret_id,
1473                  const char *user_id,
1474                  const char *anchor_id,
1475                  char * const *chain_list,
1476                  char * const *revoke_list,
1477                  krb5_prompter_fct prompter,
1478                  void *prompter_data,
1479                  char *password)
1480 {
1481     struct krb5_pk_identity *id = NULL;
1482     hx509_lock lock = NULL;
1483     struct prompter p;
1484     int ret;
1485
1486     *ret_id = NULL;
1487
1488     if (anchor_id == NULL) {
1489         krb5_set_error_string(context, "PKINIT: No anchor given");
1490         return HEIM_PKINIT_NO_VALID_CA;
1491     }
1492
1493     if (user_id == NULL) {
1494         krb5_set_error_string(context,
1495                               "PKINIT: No user certificate given");
1496         return HEIM_PKINIT_NO_PRIVATE_KEY;
1497     }
1498
1499     /* load cert */
1500
1501     id = calloc(1, sizeof(*id));
1502     if (id == NULL) {
1503         krb5_set_error_string(context, "malloc: out of memory");
1504         return ENOMEM;
1505     }   
1506
1507     ret = hx509_context_init(&id->hx509ctx);
1508     if (ret)
1509         goto out;
1510
1511     ret = hx509_lock_init(id->hx509ctx, &lock);
1512     if (password && password[0])
1513         hx509_lock_add_password(lock, password);
1514
1515     if (prompter) {
1516         p.context = context;
1517         p.prompter = prompter;
1518         p.prompter_data = prompter_data;
1519
1520         ret = hx509_lock_set_prompter(lock, hx_pass_prompter, &p);
1521         if (ret)
1522             goto out;
1523     }
1524
1525     ret = hx509_certs_init(id->hx509ctx, user_id, 0, lock, &id->certs);
1526     if (ret) {
1527         _krb5_pk_copy_error(context, id->hx509ctx, ret,
1528                             "Failed to init cert certs");
1529         goto out;
1530     }
1531
1532     ret = hx509_certs_init(id->hx509ctx, anchor_id, 0, NULL, &id->anchors);
1533     if (ret) {
1534         _krb5_pk_copy_error(context, id->hx509ctx, ret,
1535                             "Failed to init anchors");
1536         goto out;
1537     }
1538
1539     ret = hx509_certs_init(id->hx509ctx, "MEMORY:pkinit-cert-chain", 
1540                            0, NULL, &id->certpool);
1541     if (ret) {
1542         _krb5_pk_copy_error(context, id->hx509ctx, ret,
1543                             "Failed to init chain");
1544         goto out;
1545     }
1546
1547     while (chain_list && *chain_list) {
1548         ret = hx509_certs_append(id->hx509ctx, id->certpool,
1549                                  NULL, *chain_list);
1550         if (ret) {
1551             _krb5_pk_copy_error(context, id->hx509ctx, ret,
1552                                 "Failed to laod chain %s",
1553                                 *chain_list);
1554             goto out;
1555         }
1556         chain_list++;
1557     }
1558
1559     if (revoke_list) {
1560         ret = hx509_revoke_init(id->hx509ctx, &id->revokectx);
1561         if (ret) {
1562             _krb5_pk_copy_error(context, id->hx509ctx, ret,
1563                                 "Failed init revoke list");
1564             goto out;
1565         }
1566
1567         while (*revoke_list) {
1568             ret = hx509_revoke_add_crl(id->hx509ctx, 
1569                                        id->revokectx,
1570                                        *revoke_list);
1571             if (ret) {
1572                 _krb5_pk_copy_error(context, id->hx509ctx, ret, 
1573                                     "Failed load revoke list");
1574                 goto out;
1575             }
1576             revoke_list++;
1577         }
1578     } else
1579         hx509_context_set_missing_revoke(id->hx509ctx, 1);
1580
1581     ret = hx509_verify_init_ctx(id->hx509ctx, &id->verify_ctx);
1582     if (ret) {
1583         _krb5_pk_copy_error(context, id->hx509ctx, ret, 
1584                             "Failed init verify context");
1585         goto out;
1586     }
1587
1588     hx509_verify_attach_anchors(id->verify_ctx, id->anchors);
1589     hx509_verify_attach_revoke(id->verify_ctx, id->revokectx);
1590
1591 out:
1592     if (ret) {
1593         hx509_verify_destroy_ctx(id->verify_ctx);
1594         hx509_certs_free(&id->certs);
1595         hx509_certs_free(&id->anchors);
1596         hx509_certs_free(&id->certpool);
1597         hx509_revoke_free(&id->revokectx);
1598         hx509_context_free(&id->hx509ctx);
1599         free(id);
1600     } else
1601         *ret_id = id;
1602
1603     hx509_lock_free(lock);
1604
1605     return ret;
1606 }
1607
1608 static krb5_error_code
1609 select_dh_group(krb5_context context, DH *dh, unsigned long bits, 
1610                 struct krb5_dh_moduli **moduli)
1611 {
1612     const struct krb5_dh_moduli *m;
1613
1614     if (bits == 0) {
1615         m = moduli[1]; /* XXX */
1616         if (m == NULL)
1617             m = moduli[0]; /* XXX */
1618     } else {
1619         int i;
1620         for (i = 0; moduli[i] != NULL; i++) {
1621             if (bits < moduli[i]->bits)
1622                 break;
1623         }
1624         if (moduli[i] == NULL) {
1625             krb5_set_error_string(context, 
1626                                   "Did not find a DH group parameter "
1627                                   "matching requirement of %lu bits",
1628                                   bits);
1629             return EINVAL;
1630         }
1631         m = moduli[i];
1632     }
1633
1634     dh->p = integer_to_BN(context, "p", &m->p);
1635     if (dh->p == NULL)
1636         return ENOMEM;
1637     dh->g = integer_to_BN(context, "g", &m->g);
1638     if (dh->g == NULL)
1639         return ENOMEM;
1640     dh->q = integer_to_BN(context, "q", &m->q);
1641     if (dh->q == NULL)
1642         return ENOMEM;
1643
1644     return 0;
1645 }
1646
1647 #endif /* PKINIT */
1648
1649 static int
1650 parse_integer(krb5_context context, char **p, const char *file, int lineno, 
1651               const char *name, heim_integer *integer)
1652 {
1653     int ret;
1654     char *p1;
1655     p1 = strsep(p, " \t");
1656     if (p1 == NULL) {
1657         krb5_set_error_string(context, "moduli file %s missing %s on line %d",
1658                               file, name, lineno);
1659         return EINVAL;
1660     }
1661     ret = der_parse_hex_heim_integer(p1, integer);
1662     if (ret) {
1663         krb5_set_error_string(context, "moduli file %s failed parsing %s "
1664                               "on line %d",
1665                               file, name, lineno);
1666         return ret;
1667     }
1668
1669     return 0;
1670 }
1671
1672 krb5_error_code
1673 _krb5_parse_moduli_line(krb5_context context, 
1674                         const char *file,
1675                         int lineno,
1676                         char *p,
1677                         struct krb5_dh_moduli **m)
1678 {
1679     struct krb5_dh_moduli *m1;
1680     char *p1;
1681     int ret;
1682
1683     *m = NULL;
1684
1685     m1 = calloc(1, sizeof(*m1));
1686     if (m1 == NULL) {
1687         krb5_set_error_string(context, "malloc - out of memory");
1688         return ENOMEM;
1689     }
1690
1691     while (isspace((unsigned char)*p))
1692         p++;
1693     if (*p  == '#')
1694         return 0;
1695     ret = EINVAL;
1696
1697     p1 = strsep(&p, " \t");
1698     if (p1 == NULL) {
1699         krb5_set_error_string(context, "moduli file %s missing name "
1700                               "on line %d", file, lineno);
1701         goto out;
1702     }
1703     m1->name = strdup(p1);
1704     if (p1 == NULL) {
1705         krb5_set_error_string(context, "malloc - out of memeory");
1706         ret = ENOMEM;
1707         goto out;
1708     }
1709
1710     p1 = strsep(&p, " \t");
1711     if (p1 == NULL) {
1712         krb5_set_error_string(context, "moduli file %s missing bits on line %d",
1713                               file, lineno);
1714         goto out;
1715     }
1716
1717     m1->bits = atoi(p1);
1718     if (m1->bits == 0) {
1719         krb5_set_error_string(context, "moduli file %s have un-parsable "
1720                               "bits on line %d", file, lineno);
1721         goto out;
1722     }
1723         
1724     ret = parse_integer(context, &p, file, lineno, "p", &m1->p);
1725     if (ret)
1726         goto out;
1727     ret = parse_integer(context, &p, file, lineno, "g", &m1->g);
1728     if (ret)
1729         goto out;
1730     ret = parse_integer(context, &p, file, lineno, "q", &m1->q);
1731     if (ret)
1732         goto out;
1733
1734     *m = m1;
1735
1736     return 0;
1737 out:
1738     free(m1->name);
1739     der_free_heim_integer(&m1->p);
1740     der_free_heim_integer(&m1->g);
1741     der_free_heim_integer(&m1->q);
1742     free(m1);
1743     return ret;
1744 }
1745
1746 void
1747 _krb5_free_moduli(struct krb5_dh_moduli **moduli)
1748 {
1749     int i;
1750     for (i = 0; moduli[i] != NULL; i++) {
1751         free(moduli[i]->name);
1752         der_free_heim_integer(&moduli[i]->p);
1753         der_free_heim_integer(&moduli[i]->g);
1754         der_free_heim_integer(&moduli[i]->q);
1755         free(moduli[i]);
1756     }
1757     free(moduli);
1758 }
1759
1760 static const char *default_moduli_RFC2412_MODP_group2 =
1761     /* name */
1762     "RFC2412-MODP-group2 "
1763     /* bits */
1764     "1024 "
1765     /* p */
1766     "FFFFFFFF" "FFFFFFFF" "C90FDAA2" "2168C234" "C4C6628B" "80DC1CD1"
1767     "29024E08" "8A67CC74" "020BBEA6" "3B139B22" "514A0879" "8E3404DD"
1768     "EF9519B3" "CD3A431B" "302B0A6D" "F25F1437" "4FE1356D" "6D51C245"
1769     "E485B576" "625E7EC6" "F44C42E9" "A637ED6B" "0BFF5CB6" "F406B7ED"
1770     "EE386BFB" "5A899FA5" "AE9F2411" "7C4B1FE6" "49286651" "ECE65381"
1771     "FFFFFFFF" "FFFFFFFF "
1772     /* g */
1773     "02 "
1774     /* q */
1775     "7FFFFFFF" "FFFFFFFF" "E487ED51" "10B4611A" "62633145" "C06E0E68"
1776     "94812704" "4533E63A" "0105DF53" "1D89CD91" "28A5043C" "C71A026E"
1777     "F7CA8CD9" "E69D218D" "98158536" "F92F8A1B" "A7F09AB6" "B6A8E122"
1778     "F242DABB" "312F3F63" "7A262174" "D31BF6B5" "85FFAE5B" "7A035BF6"
1779     "F71C35FD" "AD44CFD2" "D74F9208" "BE258FF3" "24943328" "F67329C0"
1780     "FFFFFFFF" "FFFFFFFF";
1781
1782 static const char *default_moduli_rfc3526_MODP_group14 =
1783     /* name */
1784     "rfc3526-MODP-group14 "
1785     /* bits */
1786     "1760 "
1787     /* p */
1788     "FFFFFFFF" "FFFFFFFF" "C90FDAA2" "2168C234" "C4C6628B" "80DC1CD1"
1789     "29024E08" "8A67CC74" "020BBEA6" "3B139B22" "514A0879" "8E3404DD"
1790     "EF9519B3" "CD3A431B" "302B0A6D" "F25F1437" "4FE1356D" "6D51C245"
1791     "E485B576" "625E7EC6" "F44C42E9" "A637ED6B" "0BFF5CB6" "F406B7ED"
1792     "EE386BFB" "5A899FA5" "AE9F2411" "7C4B1FE6" "49286651" "ECE45B3D"
1793     "C2007CB8" "A163BF05" "98DA4836" "1C55D39A" "69163FA8" "FD24CF5F"
1794     "83655D23" "DCA3AD96" "1C62F356" "208552BB" "9ED52907" "7096966D"
1795     "670C354E" "4ABC9804" "F1746C08" "CA18217C" "32905E46" "2E36CE3B"
1796     "E39E772C" "180E8603" "9B2783A2" "EC07A28F" "B5C55DF0" "6F4C52C9"
1797     "DE2BCBF6" "95581718" "3995497C" "EA956AE5" "15D22618" "98FA0510"
1798     "15728E5A" "8AACAA68" "FFFFFFFF" "FFFFFFFF "
1799     /* g */
1800     "02 "
1801     /* q */
1802     "7FFFFFFF" "FFFFFFFF" "E487ED51" "10B4611A" "62633145" "C06E0E68"
1803     "94812704" "4533E63A" "0105DF53" "1D89CD91" "28A5043C" "C71A026E"
1804     "F7CA8CD9" "E69D218D" "98158536" "F92F8A1B" "A7F09AB6" "B6A8E122"
1805     "F242DABB" "312F3F63" "7A262174" "D31BF6B5" "85FFAE5B" "7A035BF6"
1806     "F71C35FD" "AD44CFD2" "D74F9208" "BE258FF3" "24943328" "F6722D9E"
1807     "E1003E5C" "50B1DF82" "CC6D241B" "0E2AE9CD" "348B1FD4" "7E9267AF"
1808     "C1B2AE91" "EE51D6CB" "0E3179AB" "1042A95D" "CF6A9483" "B84B4B36"
1809     "B3861AA7" "255E4C02" "78BA3604" "650C10BE" "19482F23" "171B671D"
1810     "F1CF3B96" "0C074301" "CD93C1D1" "7603D147" "DAE2AEF8" "37A62964"
1811     "EF15E5FB" "4AAC0B8C" "1CCAA4BE" "754AB572" "8AE9130C" "4C7D0288"
1812     "0AB9472D" "45565534" "7FFFFFFF" "FFFFFFFF";
1813
1814 krb5_error_code
1815 _krb5_parse_moduli(krb5_context context, const char *file,
1816                    struct krb5_dh_moduli ***moduli)
1817 {
1818     /* name bits P G Q */
1819     krb5_error_code ret;
1820     struct krb5_dh_moduli **m = NULL, **m2;
1821     char buf[4096];
1822     FILE *f;
1823     int lineno = 0, n = 0;
1824
1825     *moduli = NULL;
1826
1827     m = calloc(1, sizeof(m[0]) * 3);
1828     if (m == NULL) {
1829         krb5_set_error_string(context, "malloc: out of memory");
1830         return ENOMEM;
1831     }
1832
1833     strlcpy(buf, default_moduli_rfc3526_MODP_group14, sizeof(buf));
1834     ret = _krb5_parse_moduli_line(context, "builtin", 1, buf,  &m[0]);
1835     if (ret) {
1836         _krb5_free_moduli(m);
1837         return ret;
1838     }
1839     n++;
1840
1841     strlcpy(buf, default_moduli_RFC2412_MODP_group2, sizeof(buf));
1842     ret = _krb5_parse_moduli_line(context, "builtin", 1, buf,  &m[1]);
1843     if (ret) {
1844         _krb5_free_moduli(m);
1845         return ret;
1846     }
1847     n++;
1848
1849
1850     if (file == NULL)
1851         file = MODULI_FILE;
1852
1853     f = fopen(file, "r");
1854     if (f == NULL) {
1855         *moduli = m;
1856         return 0;
1857     }
1858
1859     while(fgets(buf, sizeof(buf), f) != NULL) {
1860         struct krb5_dh_moduli *element;
1861
1862         buf[strcspn(buf, "\n")] = '\0';
1863         lineno++;
1864
1865         m2 = realloc(m, (n + 2) * sizeof(m[0]));
1866         if (m2 == NULL) {
1867             krb5_set_error_string(context, "malloc: out of memory");
1868             _krb5_free_moduli(m);
1869             return ENOMEM;
1870         }
1871         m = m2;
1872         
1873         m[n] = NULL;
1874
1875         ret = _krb5_parse_moduli_line(context, file, lineno, buf,  &element);
1876         if (ret) {
1877             _krb5_free_moduli(m);
1878             return ret;
1879         }
1880         if (element == NULL)
1881             continue;
1882
1883         m[n] = element;
1884         m[n + 1] = NULL;
1885         n++;
1886     }
1887     *moduli = m;
1888     return 0;
1889 }
1890
1891 krb5_error_code
1892 _krb5_dh_group_ok(krb5_context context, unsigned long bits,
1893                   heim_integer *p, heim_integer *g, heim_integer *q,
1894                   struct krb5_dh_moduli **moduli,
1895                   char **name)
1896 {
1897     int i;
1898
1899     if (name)
1900         *name = NULL;
1901
1902     for (i = 0; moduli[i] != NULL; i++) {
1903         if (der_heim_integer_cmp(&moduli[i]->g, g) == 0 &&
1904             der_heim_integer_cmp(&moduli[i]->p, p) == 0 &&
1905             (q == NULL || der_heim_integer_cmp(&moduli[i]->q, q) == 0))
1906         {
1907             if (bits && bits > moduli[i]->bits) {
1908                 krb5_set_error_string(context, "PKINIT: DH group parameter %s "
1909                                       "no accepted, not enough bits generated",
1910                                       moduli[i]->name);
1911                 return KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED;
1912             }
1913             if (name)
1914                 *name = strdup(moduli[i]->name);
1915             return 0;
1916         }
1917     }
1918     krb5_set_error_string(context, "PKINIT: DH group parameter no ok");
1919     return KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED;
1920 }
1921
1922 void KRB5_LIB_FUNCTION
1923 _krb5_get_init_creds_opt_free_pkinit(krb5_get_init_creds_opt *opt)
1924 {
1925 #ifdef PKINIT
1926     krb5_pk_init_ctx ctx;
1927
1928     if (opt->opt_private == NULL || opt->opt_private->pk_init_ctx == NULL)
1929         return;
1930     ctx = opt->opt_private->pk_init_ctx;
1931     if (ctx->dh)
1932         DH_free(ctx->dh);
1933         ctx->dh = NULL;
1934     if (ctx->id) {
1935         hx509_verify_destroy_ctx(ctx->id->verify_ctx);
1936         hx509_certs_free(&ctx->id->certs);
1937         hx509_certs_free(&ctx->id->anchors);
1938         hx509_certs_free(&ctx->id->certpool);
1939         hx509_context_free(&ctx->id->hx509ctx);
1940
1941         if (ctx->clientDHNonce) {
1942             krb5_free_data(NULL, ctx->clientDHNonce);
1943             ctx->clientDHNonce = NULL;
1944         }
1945         if (ctx->m)
1946             _krb5_free_moduli(ctx->m);
1947         free(ctx->id);
1948         ctx->id = NULL;
1949     }
1950     free(opt->opt_private->pk_init_ctx);
1951     opt->opt_private->pk_init_ctx = NULL;
1952 #endif
1953 }
1954     
1955 krb5_error_code KRB5_LIB_FUNCTION
1956 krb5_get_init_creds_opt_set_pkinit(krb5_context context,
1957                                    krb5_get_init_creds_opt *opt,
1958                                    krb5_principal principal,
1959                                    const char *user_id,
1960                                    const char *x509_anchors,
1961                                    char * const * pool,
1962                                    char * const * pki_revoke,
1963                                    int flags,
1964                                    krb5_prompter_fct prompter,
1965                                    void *prompter_data,
1966                                    char *password)
1967 {
1968 #ifdef PKINIT
1969     krb5_error_code ret;
1970     char *anchors = NULL;
1971
1972     if (opt->opt_private == NULL) {
1973         krb5_set_error_string(context, "PKINIT: on non extendable opt");
1974         return EINVAL;
1975     }
1976
1977     opt->opt_private->pk_init_ctx = 
1978         calloc(1, sizeof(*opt->opt_private->pk_init_ctx));
1979     if (opt->opt_private->pk_init_ctx == NULL) {
1980         krb5_set_error_string(context, "malloc: out of memory");
1981         return ENOMEM;
1982     }
1983     opt->opt_private->pk_init_ctx->dh = NULL;
1984     opt->opt_private->pk_init_ctx->id = NULL;
1985     opt->opt_private->pk_init_ctx->clientDHNonce = NULL;
1986     opt->opt_private->pk_init_ctx->require_binding = 0;
1987     opt->opt_private->pk_init_ctx->require_eku = 1;
1988     opt->opt_private->pk_init_ctx->require_krbtgt_otherName = 1;
1989     opt->opt_private->pk_init_ctx->peer = NULL;
1990
1991     /* XXX implement krb5_appdefault_strings  */
1992     if (pool == NULL)
1993         pool = krb5_config_get_strings(context, NULL,
1994                                        "appdefaults", 
1995                                        "pkinit_pool", 
1996                                        NULL);
1997
1998     if (pki_revoke == NULL)
1999         pki_revoke = krb5_config_get_strings(context, NULL,
2000                                              "appdefaults", 
2001                                              "pkinit_revoke", 
2002                                              NULL);
2003
2004     if (x509_anchors == NULL) {
2005         krb5_appdefault_string(context, "kinit",
2006                                krb5_principal_get_realm(context, principal), 
2007                                "pkinit_anchors", NULL, &anchors);
2008         x509_anchors = anchors;
2009     }
2010
2011     ret = _krb5_pk_load_id(context,
2012                            &opt->opt_private->pk_init_ctx->id,
2013                            user_id,
2014                            x509_anchors,
2015                            pool,
2016                            pki_revoke,
2017                            prompter,
2018                            prompter_data,
2019                            password);
2020     if (ret) {
2021         free(opt->opt_private->pk_init_ctx);
2022         opt->opt_private->pk_init_ctx = NULL;
2023         return ret;
2024     }
2025
2026     if ((flags & 2) == 0) {
2027         const char *moduli_file;
2028         unsigned long dh_min_bits;
2029
2030         moduli_file = krb5_config_get_string(context, NULL,
2031                                              "libdefaults",
2032                                              "moduli",
2033                                              NULL);
2034
2035         dh_min_bits =
2036             krb5_config_get_int_default(context, NULL, 0,
2037                                         "libdefaults",
2038                                         "pkinit_dh_min_bits",
2039                                         NULL);
2040
2041         ret = _krb5_parse_moduli(context, moduli_file, 
2042                                  &opt->opt_private->pk_init_ctx->m);
2043         if (ret) {
2044             _krb5_get_init_creds_opt_free_pkinit(opt);
2045             return ret;
2046         }
2047         
2048         opt->opt_private->pk_init_ctx->dh = DH_new();
2049         if (opt->opt_private->pk_init_ctx->dh == NULL) {
2050             krb5_set_error_string(context, "malloc: out of memory");
2051             _krb5_get_init_creds_opt_free_pkinit(opt);
2052             return ENOMEM;
2053         }
2054
2055         ret = select_dh_group(context, opt->opt_private->pk_init_ctx->dh,
2056                               dh_min_bits, 
2057                               opt->opt_private->pk_init_ctx->m);
2058         if (ret) {
2059             _krb5_get_init_creds_opt_free_pkinit(opt);
2060             return ret;
2061         }
2062
2063         if (DH_generate_key(opt->opt_private->pk_init_ctx->dh) != 1) {
2064             krb5_set_error_string(context, "pkinit: failed to generate DH key");
2065             _krb5_get_init_creds_opt_free_pkinit(opt);
2066             return ENOMEM;
2067         }
2068     }
2069
2070     return 0;
2071 #else
2072     krb5_set_error_string(context, "no support for PKINIT compiled in");
2073     return EINVAL;
2074 #endif
2075 }
2076
2077 /*
2078  *
2079  */
2080
2081 static void
2082 _krb5_pk_copy_error(krb5_context context,
2083                     hx509_context hx509ctx,
2084                     int hxret,
2085                     const char *fmt,
2086                     ...)
2087 {
2088     va_list va;
2089     char *s, *f;
2090
2091     va_start(va, fmt);
2092     vasprintf(&f, fmt, va);
2093     va_end(va);
2094     if (f == NULL) {
2095         krb5_clear_error_string(context);
2096         return;
2097     }
2098
2099     s = hx509_get_error_string(hx509ctx, hxret);
2100     if (s == NULL) {
2101         krb5_clear_error_string(context);
2102         free(f);
2103         return;
2104     }
2105     krb5_set_error_string(context, "%s: %s", f, s);
2106     free(s);
2107     free(f);
2108 }