1 /*        $NetBSD: pkinit.c,v 1.6 2023/06/19 21:41:44 christos Exp $  */
2 
3 /*
4  * Copyright (c) 2003 - 2016 Kungliga Tekniska Högskolan
5  * (Royal Institute of Technology, Stockholm, Sweden).
6  * All rights reserved.
7  *
8  * Portions Copyright (c) 2009 Apple Inc. All rights reserved.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  *
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  *
17  * 2. Redistributions in binary form must reproduce the above copyright
18  *    notice, this list of conditions and the following disclaimer in the
19  *    documentation and/or other materials provided with the distribution.
20  *
21  * 3. Neither the name of the Institute nor the names of its contributors
22  *    may be used to endorse or promote products derived from this software
23  *    without specific prior written permission.
24  *
25  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
26  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
29  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35  * SUCH DAMAGE.
36  */
37 
38 #include "krb5_locl.h"
39 
40 struct krb5_dh_moduli {
41     char *name;
42     unsigned long bits;
43     heim_integer p;
44     heim_integer g;
45     heim_integer q;
46 };
47 
48 #ifdef PKINIT
49 
50 #include <krb5/cms_asn1.h>
51 #include <krb5/pkcs8_asn1.h>
52 #include <krb5/pkcs9_asn1.h>
53 #include <krb5/pkcs12_asn1.h>
54 #include <krb5/pkinit_asn1.h>
55 #include <krb5/asn1_err.h>
56 
57 #include <krb5/der.h>
58 
59 struct krb5_pk_cert {
60     hx509_cert cert;
61 };
62 
63 static void
64 pk_copy_error(krb5_context context,
65                 hx509_context hx509ctx,
66                 int hxret,
67                 const char *fmt,
68                 ...)
69     __attribute__ ((__format__ (__printf__, 4, 5)));
70 
71 /*
72  *
73  */
74 
75 KRB5_LIB_FUNCTION void KRB5_LIB_CALL
_krb5_pk_cert_free(struct krb5_pk_cert * cert)76 _krb5_pk_cert_free(struct krb5_pk_cert *cert)
77 {
78     if (cert->cert) {
79           hx509_cert_free(cert->cert);
80     }
81     free(cert);
82 }
83 
84 static krb5_error_code
BN_to_integer(krb5_context context,const BIGNUM * bn,heim_integer * integer)85 BN_to_integer(krb5_context context, const BIGNUM *bn, heim_integer *integer)
86 {
87     integer->length = BN_num_bytes(bn);
88     integer->data = malloc(integer->length);
89     if (integer->data == NULL) {
90           krb5_clear_error_message(context);
91           return ENOMEM;
92     }
93     BN_bn2bin(bn, integer->data);
94     integer->negative = BN_is_negative(bn);
95     return 0;
96 }
97 
98 static BIGNUM *
integer_to_BN(krb5_context context,const char * field,const heim_integer * f)99 integer_to_BN(krb5_context context, const char *field, const heim_integer *f)
100 {
101     BIGNUM *bn;
102 
103     bn = BN_bin2bn((const unsigned char *)f->data, f->length, NULL);
104     if (bn == NULL) {
105           krb5_set_error_message(context, ENOMEM,
106                                      N_("PKINIT: parsing BN failed %s", ""), field);
107           return NULL;
108     }
109     BN_set_negative(bn, f->negative);
110     return bn;
111 }
112 
113 static krb5_error_code
select_dh_group(krb5_context context,DH * dh,unsigned long bits,struct krb5_dh_moduli ** moduli)114 select_dh_group(krb5_context context, DH *dh, unsigned long bits,
115                     struct krb5_dh_moduli **moduli)
116 {
117     const struct krb5_dh_moduli *m;
118 
119     if (bits == 0) {
120           m = moduli[1]; /* XXX */
121           if (m == NULL)
122               m = moduli[0]; /* XXX */
123     } else {
124           int i;
125           for (i = 0; moduli[i] != NULL; i++) {
126               if (bits < moduli[i]->bits)
127                     break;
128           }
129           if (moduli[i] == NULL) {
130               krb5_set_error_message(context, EINVAL,
131                                            N_("Did not find a DH group parameter "
132                                               "matching requirement of %lu bits", ""),
133                                            bits);
134               return EINVAL;
135           }
136           m = moduli[i];
137     }
138 
139     BIGNUM *p = integer_to_BN(context, "p", &m->p);
140     if (p == NULL)
141           return ENOMEM;
142     BIGNUM *g = integer_to_BN(context, "g", &m->g);
143     if (g == NULL)
144           return ENOMEM;
145     BIGNUM *q = integer_to_BN(context, "q", &m->q);
146     if (q == NULL)
147           return ENOMEM;
148 #if OPENSSL_VERSION_NUMBER < 0x10100000UL
149     dh->p = p;
150     dh->q = q;
151     dh->g = g;
152 #else
153     DH_set0_pqg(dh, p, q, g);
154 #endif
155 
156     return 0;
157 }
158 
159 struct certfind {
160     const char *type;
161     const heim_oid *oid;
162 };
163 
164 /*
165  * Try searchin the key by to use by first looking for for PK-INIT
166  * EKU, then the Microsoft smart card EKU and last, no special EKU at all.
167  */
168 
169 static krb5_error_code
find_cert(krb5_context context,struct krb5_pk_identity * id,hx509_query * q,hx509_cert * cert)170 find_cert(krb5_context context, struct krb5_pk_identity *id,
171             hx509_query *q, hx509_cert *cert)
172 {
173     struct certfind cf[4] = {
174           { "MobileMe EKU", NULL },
175           { "PKINIT EKU", NULL },
176           { "MS EKU", NULL },
177           { "any (or no)", NULL }
178     };
179     int ret = HX509_CERT_NOT_FOUND;
180     size_t i, start = 1;
181     unsigned oids[] = { 1, 2, 840, 113635, 100, 3, 2, 1 };
182     const heim_oid mobileMe = { sizeof(oids)/sizeof(oids[0]), oids };
183 
184 
185     if (id->flags & PKINIT_BTMM)
186           start = 0;
187 
188     cf[0].oid = &mobileMe;
189     cf[1].oid = &asn1_oid_id_pkekuoid;
190     cf[2].oid = &asn1_oid_id_pkinit_ms_eku;
191     cf[3].oid = NULL;
192 
193     for (i = start; i < sizeof(cf)/sizeof(cf[0]); i++) {
194           ret = hx509_query_match_eku(q, cf[i].oid);
195           if (ret) {
196               pk_copy_error(context, context->hx509ctx, ret,
197                                 "Failed setting %s OID", cf[i].type);
198               return ret;
199           }
200 
201           ret = hx509_certs_find(context->hx509ctx, id->certs, q, cert);
202           if (ret == 0)
203               break;
204           pk_copy_error(context, context->hx509ctx, ret,
205                           "Failed finding certificate with %s OID", cf[i].type);
206     }
207     return ret;
208 }
209 
210 
211 static krb5_error_code
create_signature(krb5_context context,const heim_oid * eContentType,krb5_data * eContent,struct krb5_pk_identity * id,hx509_peer_info peer,krb5_data * sd_data)212 create_signature(krb5_context context,
213                      const heim_oid *eContentType,
214                      krb5_data *eContent,
215                      struct krb5_pk_identity *id,
216                      hx509_peer_info peer,
217                      krb5_data *sd_data)
218 {
219     int ret, flags = 0;
220 
221     if (id->cert == NULL)
222           flags |= HX509_CMS_SIGNATURE_NO_SIGNER;
223 
224     ret = hx509_cms_create_signed_1(context->hx509ctx,
225                                             flags,
226                                             eContentType,
227                                             eContent->data,
228                                             eContent->length,
229                                             NULL,
230                                             id->cert,
231                                             peer,
232                                             NULL,
233                                             id->certs,
234                                             sd_data);
235     if (ret) {
236           pk_copy_error(context, context->hx509ctx, ret,
237                           "Create CMS signedData");
238           return ret;
239     }
240 
241     return 0;
242 }
243 
244 static int
cert2epi(hx509_context context,void * ctx,hx509_cert c)245 cert2epi(hx509_context context, void *ctx, hx509_cert c)
246 {
247     ExternalPrincipalIdentifiers *ids = ctx;
248     ExternalPrincipalIdentifier id;
249     hx509_name subject = NULL;
250     void *p;
251     int ret;
252 
253     if (ids->len > 10)
254           return 0;
255 
256     memset(&id, 0, sizeof(id));
257 
258     ret = hx509_cert_get_subject(c, &subject);
259     if (ret)
260           return ret;
261 
262     if (hx509_name_is_null_p(subject) != 0) {
263 
264           id.subjectName = calloc(1, sizeof(*id.subjectName));
265           if (id.subjectName == NULL) {
266               hx509_name_free(&subject);
267               free_ExternalPrincipalIdentifier(&id);
268               return ENOMEM;
269           }
270 
271           ret = hx509_name_binary(subject, id.subjectName);
272           if (ret) {
273               hx509_name_free(&subject);
274               free_ExternalPrincipalIdentifier(&id);
275               return ret;
276           }
277     }
278     hx509_name_free(&subject);
279 
280 
281     id.issuerAndSerialNumber = calloc(1, sizeof(*id.issuerAndSerialNumber));
282     if (id.issuerAndSerialNumber == NULL) {
283           free_ExternalPrincipalIdentifier(&id);
284           return ENOMEM;
285     }
286 
287     {
288           IssuerAndSerialNumber iasn;
289           hx509_name issuer;
290           size_t size = 0;
291 
292           memset(&iasn, 0, sizeof(iasn));
293 
294           ret = hx509_cert_get_issuer(c, &issuer);
295           if (ret) {
296               free_ExternalPrincipalIdentifier(&id);
297               return ret;
298           }
299 
300           ret = hx509_name_to_Name(issuer, &iasn.issuer);
301           hx509_name_free(&issuer);
302           if (ret) {
303               free_ExternalPrincipalIdentifier(&id);
304               return ret;
305           }
306 
307           ret = hx509_cert_get_serialnumber(c, &iasn.serialNumber);
308           if (ret) {
309               free_IssuerAndSerialNumber(&iasn);
310               free_ExternalPrincipalIdentifier(&id);
311               return ret;
312           }
313 
314           ASN1_MALLOC_ENCODE(IssuerAndSerialNumber,
315                                  id.issuerAndSerialNumber->data,
316                                  id.issuerAndSerialNumber->length,
317                                  &iasn, &size, ret);
318           free_IssuerAndSerialNumber(&iasn);
319           if (ret) {
320             free_ExternalPrincipalIdentifier(&id);
321               return ret;
322         }
323           if (id.issuerAndSerialNumber->length != size)
324               abort();
325     }
326 
327     id.subjectKeyIdentifier = NULL;
328 
329     p = realloc(ids->val, sizeof(ids->val[0]) * (ids->len + 1));
330     if (p == NULL) {
331           free_ExternalPrincipalIdentifier(&id);
332           return ENOMEM;
333     }
334 
335     ids->val = p;
336     ids->val[ids->len] = id;
337     ids->len++;
338 
339     return 0;
340 }
341 
342 static krb5_error_code
build_edi(krb5_context context,hx509_context hx509ctx,hx509_certs certs,ExternalPrincipalIdentifiers * ids)343 build_edi(krb5_context context,
344             hx509_context hx509ctx,
345             hx509_certs certs,
346             ExternalPrincipalIdentifiers *ids)
347 {
348     return hx509_certs_iter_f(hx509ctx, certs, cert2epi, ids);
349 }
350 
351 static krb5_error_code
build_auth_pack(krb5_context context,unsigned nonce,krb5_pk_init_ctx ctx,const KDC_REQ_BODY * body,AuthPack * a)352 build_auth_pack(krb5_context context,
353                     unsigned nonce,
354                     krb5_pk_init_ctx ctx,
355                     const KDC_REQ_BODY *body,
356                     AuthPack *a)
357 {
358     size_t buf_size, len = 0;
359     krb5_error_code ret;
360     void *buf;
361     krb5_timestamp sec;
362     int32_t usec;
363     Checksum checksum;
364 
365     krb5_clear_error_message(context);
366 
367     memset(&checksum, 0, sizeof(checksum));
368 
369     krb5_us_timeofday(context, &sec, &usec);
370     a->pkAuthenticator.ctime = sec;
371     a->pkAuthenticator.nonce = nonce;
372 
373     ASN1_MALLOC_ENCODE(KDC_REQ_BODY, buf, buf_size, body, &len, ret);
374     if (ret)
375           return ret;
376     if (buf_size != len)
377           krb5_abortx(context, "internal error in ASN.1 encoder");
378 
379     ret = krb5_create_checksum(context,
380                                      NULL,
381                                      0,
382                                      CKSUMTYPE_SHA1,
383                                      buf,
384                                      len,
385                                      &checksum);
386     free(buf);
387     if (ret)
388           return ret;
389 
390     ALLOC(a->pkAuthenticator.paChecksum, 1);
391     if (a->pkAuthenticator.paChecksum == NULL) {
392           return krb5_enomem(context);
393     }
394 
395     ret = krb5_data_copy(a->pkAuthenticator.paChecksum,
396                                checksum.checksum.data, checksum.checksum.length);
397     free_Checksum(&checksum);
398     if (ret)
399           return ret;
400 
401     if (ctx->keyex == USE_DH || ctx->keyex == USE_ECDH) {
402           const char *moduli_file;
403           unsigned long dh_min_bits;
404           krb5_data dhbuf;
405           size_t size = 0;
406 
407           krb5_data_zero(&dhbuf);
408 
409 
410 
411           moduli_file = krb5_config_get_string(context, NULL,
412                                                        "libdefaults",
413                                                        "moduli",
414                                                        NULL);
415 
416           dh_min_bits =
417               krb5_config_get_int_default(context, NULL, 0,
418                                                   "libdefaults",
419                                                   "pkinit_dh_min_bits",
420                                                   NULL);
421 
422           ret = _krb5_parse_moduli(context, moduli_file, &ctx->m);
423           if (ret)
424               return ret;
425 
426           ctx->u.dh = DH_new();
427           if (ctx->u.dh == NULL)
428               return krb5_enomem(context);
429 
430           ret = select_dh_group(context, ctx->u.dh, dh_min_bits, ctx->m);
431           if (ret)
432               return ret;
433 
434           if (DH_generate_key(ctx->u.dh) != 1) {
435               krb5_set_error_message(context, ENOMEM,
436                                            N_("pkinit: failed to generate DH key", ""));
437               return ENOMEM;
438           }
439 
440 
441           if (1 /* support_cached_dh */) {
442               ALLOC(a->clientDHNonce, 1);
443               if (a->clientDHNonce == NULL) {
444                     krb5_clear_error_message(context);
445                     return ENOMEM;
446               }
447               ret = krb5_data_alloc(a->clientDHNonce, 40);
448               if (a->clientDHNonce == NULL) {
449                     krb5_clear_error_message(context);
450                     return ret;
451               }
452               RAND_bytes(a->clientDHNonce->data, a->clientDHNonce->length);
453               ret = krb5_copy_data(context, a->clientDHNonce,
454                                          &ctx->clientDHNonce);
455               if (ret)
456                     return ret;
457           }
458 
459           ALLOC(a->clientPublicValue, 1);
460           if (a->clientPublicValue == NULL)
461               return ENOMEM;
462 
463           if (ctx->keyex == USE_DH) {
464               DH *dh = ctx->u.dh;
465               const BIGNUM *p, *q, *g;
466               DomainParameters dp;
467               heim_integer dh_pub_key;
468 
469               ret = der_copy_oid(&asn1_oid_id_dhpublicnumber,
470                                      &a->clientPublicValue->algorithm.algorithm);
471               if (ret)
472                     return ret;
473 
474               memset(&dp, 0, sizeof(dp));
475 
476 #if OPENSSL_VERSION_NUMBER < 0x10100000UL
477               p = dh->p;
478               q = dh->q;
479               g = dh->g;
480 #else
481               DH_get0_pqg(dh, &p, &q, &g);
482 #endif
483 
484               ret = BN_to_integer(context, p, &dp.p);
485               if (ret) {
486                     free_DomainParameters(&dp);
487                     return ret;
488               }
489               ret = BN_to_integer(context, g, &dp.g);
490               if (ret) {
491                     free_DomainParameters(&dp);
492                     return ret;
493               }
494               dp.q = calloc(1, sizeof(*dp.q));
495               if (dp.q == NULL) {
496                     free_DomainParameters(&dp);
497                     return ENOMEM;
498               }
499               ret = BN_to_integer(context, q, dp.q);
500               if (ret) {
501                     free_DomainParameters(&dp);
502                     return ret;
503               }
504               dp.j = NULL;
505               dp.validationParms = NULL;
506 
507               a->clientPublicValue->algorithm.parameters =
508                     malloc(sizeof(*a->clientPublicValue->algorithm.parameters));
509               if (a->clientPublicValue->algorithm.parameters == NULL) {
510                     free_DomainParameters(&dp);
511                     return ret;
512               }
513 
514               ASN1_MALLOC_ENCODE(DomainParameters,
515                                      a->clientPublicValue->algorithm.parameters->data,
516                                      a->clientPublicValue->algorithm.parameters->length,
517                                      &dp, &size, ret);
518               free_DomainParameters(&dp);
519               if (ret)
520                     return ret;
521               if (size != a->clientPublicValue->algorithm.parameters->length)
522                     krb5_abortx(context, "Internal ASN1 encoder error");
523 
524               const BIGNUM *pub_key;
525 #if OPENSSL_VERSION_NUMBER < 0x10100000UL
526               pub_key = dh->pub_key;
527 #else
528               DH_get0_key(dh, &pub_key, NULL);
529 #endif
530               ret = BN_to_integer(context, pub_key, &dh_pub_key);
531               if (ret)
532                     return ret;
533 
534               ASN1_MALLOC_ENCODE(DHPublicKey, dhbuf.data, dhbuf.length,
535                                      &dh_pub_key, &size, ret);
536               der_free_heim_integer(&dh_pub_key);
537               if (ret)
538                     return ret;
539               if (size != dhbuf.length)
540                     krb5_abortx(context, "asn1 internal error");
541             a->clientPublicValue->subjectPublicKey.length = dhbuf.length * 8;
542             a->clientPublicValue->subjectPublicKey.data = dhbuf.data;
543           } else if (ctx->keyex == USE_ECDH) {
544             ret = _krb5_build_authpack_subjectPK_EC(context, ctx, a);
545             if (ret)
546                 return ret;
547           } else
548               krb5_abortx(context, "internal error");
549     }
550 
551     {
552           a->supportedCMSTypes = calloc(1, sizeof(*a->supportedCMSTypes));
553           if (a->supportedCMSTypes == NULL)
554               return ENOMEM;
555 
556           ret = hx509_crypto_available(context->hx509ctx, HX509_SELECT_ALL,
557                                              ctx->id->cert,
558                                              &a->supportedCMSTypes->val,
559                                              &a->supportedCMSTypes->len);
560           if (ret)
561               return ret;
562     }
563 
564     return ret;
565 }
566 
567 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
_krb5_pk_mk_ContentInfo(krb5_context context,const krb5_data * buf,const heim_oid * oid,struct ContentInfo * content_info)568 _krb5_pk_mk_ContentInfo(krb5_context context,
569                               const krb5_data *buf,
570                               const heim_oid *oid,
571                               struct ContentInfo *content_info)
572 {
573     krb5_error_code ret;
574 
575     ret = der_copy_oid(oid, &content_info->contentType);
576     if (ret)
577           return ret;
578     ALLOC(content_info->content, 1);
579     if (content_info->content == NULL)
580           return ENOMEM;
581     content_info->content->data = malloc(buf->length);
582     if (content_info->content->data == NULL)
583           return ENOMEM;
584     memcpy(content_info->content->data, buf->data, buf->length);
585     content_info->content->length = buf->length;
586     return 0;
587 }
588 
589 static krb5_error_code
pk_mk_padata(krb5_context context,krb5_pk_init_ctx ctx,const KDC_REQ_BODY * req_body,unsigned nonce,METHOD_DATA * md)590 pk_mk_padata(krb5_context context,
591                krb5_pk_init_ctx ctx,
592                const KDC_REQ_BODY *req_body,
593                unsigned nonce,
594                METHOD_DATA *md)
595 {
596     struct ContentInfo content_info;
597     krb5_error_code ret;
598     const heim_oid *oid = NULL;
599     size_t size = 0;
600     krb5_data buf, sd_buf;
601     int pa_type = -1;
602 
603     krb5_data_zero(&buf);
604     krb5_data_zero(&sd_buf);
605     memset(&content_info, 0, sizeof(content_info));
606 
607     if (ctx->type == PKINIT_WIN2K) {
608           AuthPack_Win2k ap;
609           krb5_timestamp sec;
610           int32_t usec;
611 
612           memset(&ap, 0, sizeof(ap));
613 
614           /* fill in PKAuthenticator */
615           ret = copy_PrincipalName(req_body->sname, &ap.pkAuthenticator.kdcName);
616           if (ret) {
617               free_AuthPack_Win2k(&ap);
618               krb5_clear_error_message(context);
619               goto out;
620           }
621           ret = copy_Realm(&req_body->realm, &ap.pkAuthenticator.kdcRealm);
622           if (ret) {
623               free_AuthPack_Win2k(&ap);
624               krb5_clear_error_message(context);
625               goto out;
626           }
627 
628           krb5_us_timeofday(context, &sec, &usec);
629           ap.pkAuthenticator.ctime = sec;
630           ap.pkAuthenticator.cusec = usec;
631           ap.pkAuthenticator.nonce = nonce;
632 
633           ASN1_MALLOC_ENCODE(AuthPack_Win2k, buf.data, buf.length,
634                                  &ap, &size, ret);
635           free_AuthPack_Win2k(&ap);
636           if (ret) {
637               krb5_set_error_message(context, ret,
638                                            N_("Failed encoding AuthPackWin: %d", ""),
639                                            (int)ret);
640               goto out;
641           }
642           if (buf.length != size)
643               krb5_abortx(context, "internal ASN1 encoder error");
644 
645           oid = &asn1_oid_id_pkcs7_data;
646     } else if (ctx->type == PKINIT_27) {
647           AuthPack ap;
648 
649           memset(&ap, 0, sizeof(ap));
650 
651           ret = build_auth_pack(context, nonce, ctx, req_body, &ap);
652           if (ret) {
653               free_AuthPack(&ap);
654               goto out;
655           }
656 
657           ASN1_MALLOC_ENCODE(AuthPack, buf.data, buf.length, &ap, &size, ret);
658           free_AuthPack(&ap);
659           if (ret) {
660               krb5_set_error_message(context, ret,
661                                            N_("Failed encoding AuthPack: %d", ""),
662                                            (int)ret);
663               goto out;
664           }
665           if (buf.length != size)
666               krb5_abortx(context, "internal ASN1 encoder error");
667 
668           oid = &asn1_oid_id_pkauthdata;
669     } else
670           krb5_abortx(context, "internal pkinit error");
671 
672     ret = create_signature(context, oid, &buf, ctx->id,
673                                  ctx->peer, &sd_buf);
674     krb5_data_free(&buf);
675     if (ret)
676           goto out;
677 
678     ret = hx509_cms_wrap_ContentInfo(&asn1_oid_id_pkcs7_signedData, &sd_buf, &buf);
679     krb5_data_free(&sd_buf);
680     if (ret) {
681           krb5_set_error_message(context, ret,
682                                      N_("ContentInfo wrapping of signedData failed",""));
683           goto out;
684     }
685 
686     if (ctx->type == PKINIT_WIN2K) {
687           PA_PK_AS_REQ_Win2k winreq;
688 
689           pa_type = KRB5_PADATA_PK_AS_REQ_WIN;
690 
691           memset(&winreq, 0, sizeof(winreq));
692 
693           winreq.signed_auth_pack = buf;
694 
695           ASN1_MALLOC_ENCODE(PA_PK_AS_REQ_Win2k, buf.data, buf.length,
696                                  &winreq, &size, ret);
697           free_PA_PK_AS_REQ_Win2k(&winreq);
698 
699     } else if (ctx->type == PKINIT_27) {
700           PA_PK_AS_REQ req;
701 
702           pa_type = KRB5_PADATA_PK_AS_REQ;
703 
704           memset(&req, 0, sizeof(req));
705           req.signedAuthPack = buf;
706 
707           if (ctx->trustedCertifiers) {
708 
709               req.trustedCertifiers = calloc(1, sizeof(*req.trustedCertifiers));
710               if (req.trustedCertifiers == NULL) {
711                     ret = krb5_enomem(context);
712                     free_PA_PK_AS_REQ(&req);
713                     goto out;
714               }
715               ret = build_edi(context, context->hx509ctx,
716                                   ctx->id->anchors, req.trustedCertifiers);
717               if (ret) {
718                     krb5_set_error_message(context, ret,
719                                                N_("pk-init: failed to build "
720                                                     "trustedCertifiers", ""));
721                     free_PA_PK_AS_REQ(&req);
722                     goto out;
723               }
724           }
725           req.kdcPkId = NULL;
726 
727           ASN1_MALLOC_ENCODE(PA_PK_AS_REQ, buf.data, buf.length,
728                                  &req, &size, ret);
729 
730           free_PA_PK_AS_REQ(&req);
731 
732     } else
733           krb5_abortx(context, "internal pkinit error");
734     if (ret) {
735           krb5_set_error_message(context, ret, "PA-PK-AS-REQ %d", (int)ret);
736           goto out;
737     }
738     if (buf.length != size)
739           krb5_abortx(context, "Internal ASN1 encoder error");
740 
741     ret = krb5_padata_add(context, md, pa_type, buf.data, buf.length);
742     if (ret)
743           free(buf.data);
744 
745     if (ret == 0)
746           krb5_padata_add(context, md, KRB5_PADATA_PK_AS_09_BINDING, NULL, 0);
747 
748  out:
749     free_ContentInfo(&content_info);
750 
751     return ret;
752 }
753 
754 
755 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
_krb5_pk_mk_padata(krb5_context context,void * c,int ic_flags,int win2k,const KDC_REQ_BODY * req_body,unsigned nonce,METHOD_DATA * md)756 _krb5_pk_mk_padata(krb5_context context,
757                        void *c,
758                        int ic_flags,
759                        int win2k,
760                        const KDC_REQ_BODY *req_body,
761                        unsigned nonce,
762                        METHOD_DATA *md)
763 {
764     krb5_pk_init_ctx ctx = c;
765     int win2k_compat;
766 
767     if (ctx->id->certs == NULL && ctx->anonymous == 0) {
768           krb5_set_error_message(context, HEIM_PKINIT_NO_PRIVATE_KEY,
769                                      N_("PKINIT: No user certificate given", ""));
770           return HEIM_PKINIT_NO_PRIVATE_KEY;
771     }
772 
773     win2k_compat = krb5_config_get_bool_default(context, NULL,
774                                                             win2k,
775                                                             "realms",
776                                                             req_body->realm,
777                                                             "pkinit_win2k",
778                                                             NULL);
779 
780     if (win2k_compat) {
781           ctx->require_binding =
782               krb5_config_get_bool_default(context, NULL,
783                                                    TRUE,
784                                                    "realms",
785                                                    req_body->realm,
786                                                    "pkinit_win2k_require_binding",
787                                                    NULL);
788           ctx->type = PKINIT_WIN2K;
789     } else
790           ctx->type = PKINIT_27;
791 
792     ctx->require_eku =
793           krb5_config_get_bool_default(context, NULL,
794                                              TRUE,
795                                              "realms",
796                                              req_body->realm,
797                                              "pkinit_require_eku",
798                                              NULL);
799     if (ic_flags & KRB5_INIT_CREDS_NO_C_NO_EKU_CHECK)
800           ctx->require_eku = 0;
801     if (ctx->id->flags & PKINIT_BTMM)
802           ctx->require_eku = 0;
803 
804     ctx->require_krbtgt_otherName =
805           krb5_config_get_bool_default(context, NULL,
806                                              TRUE,
807                                              "realms",
808                                              req_body->realm,
809                                              "pkinit_require_krbtgt_otherName",
810                                              NULL);
811     if (ic_flags & KRB5_INIT_CREDS_PKINIT_NO_KRBTGT_OTHERNAME_CHECK)
812           ctx->require_krbtgt_otherName = FALSE;
813 
814     ctx->require_hostname_match =
815           krb5_config_get_bool_default(context, NULL,
816                                              FALSE,
817                                              "realms",
818                                              req_body->realm,
819                                              "pkinit_require_hostname_match",
820                                              NULL);
821 
822     ctx->trustedCertifiers =
823           krb5_config_get_bool_default(context, NULL,
824                                              TRUE,
825                                              "realms",
826                                              req_body->realm,
827                                              "pkinit_trustedCertifiers",
828                                              NULL);
829 
830     return pk_mk_padata(context, ctx, req_body, nonce, md);
831 }
832 
833 static krb5_error_code
pk_verify_sign(krb5_context context,const void * data,size_t length,struct krb5_pk_identity * id,heim_oid * contentType,krb5_data * content,struct krb5_pk_cert ** signer)834 pk_verify_sign(krb5_context context,
835                  const void *data,
836                  size_t length,
837                  struct krb5_pk_identity *id,
838                  heim_oid *contentType,
839                  krb5_data *content,
840                  struct krb5_pk_cert **signer)
841 {
842     hx509_certs signer_certs;
843     int ret, flags = 0;
844 
845     /* BTMM is broken in Leo and SnowLeo */
846     if (id->flags & PKINIT_BTMM) {
847           flags |= HX509_CMS_VS_ALLOW_DATA_OID_MISMATCH;
848           flags |= HX509_CMS_VS_NO_KU_CHECK;
849           flags |= HX509_CMS_VS_NO_VALIDATE;
850     }
851 
852     *signer = NULL;
853 
854     ret = hx509_cms_verify_signed(context->hx509ctx,
855                                           id->verify_ctx,
856                                           flags,
857                                           data,
858                                           length,
859                                           NULL,
860                                           id->certpool,
861                                           contentType,
862                                           content,
863                                           &signer_certs);
864     if (ret) {
865           pk_copy_error(context, context->hx509ctx, ret,
866                           "CMS verify signed failed");
867           return ret;
868     }
869 
870     *signer = calloc(1, sizeof(**signer));
871     if (*signer == NULL) {
872           krb5_clear_error_message(context);
873           ret = ENOMEM;
874           goto out;
875     }
876 
877     ret = hx509_get_one_cert(context->hx509ctx, signer_certs, &(*signer)->cert);
878     if (ret) {
879           pk_copy_error(context, context->hx509ctx, ret,
880                           "Failed to get on of the signer certs");
881           goto out;
882     }
883 
884  out:
885     hx509_certs_free(&signer_certs);
886     if (ret) {
887           if (*signer) {
888               hx509_cert_free((*signer)->cert);
889               free(*signer);
890               *signer = NULL;
891           }
892     }
893 
894     return ret;
895 }
896 
897 static krb5_error_code
get_reply_key_win(krb5_context context,const krb5_data * content,unsigned nonce,krb5_keyblock ** key)898 get_reply_key_win(krb5_context context,
899                       const krb5_data *content,
900                       unsigned nonce,
901                       krb5_keyblock **key)
902 {
903     ReplyKeyPack_Win2k key_pack;
904     krb5_error_code ret;
905     size_t size;
906 
907     ret = decode_ReplyKeyPack_Win2k(content->data,
908                                             content->length,
909                                             &key_pack,
910                                             &size);
911     if (ret) {
912           krb5_set_error_message(context, ret,
913                                      N_("PKINIT decoding reply key failed", ""));
914           free_ReplyKeyPack_Win2k(&key_pack);
915           return ret;
916     }
917 
918     if ((unsigned)key_pack.nonce != nonce) {
919           krb5_set_error_message(context, ret,
920                                      N_("PKINIT enckey nonce is wrong", ""));
921           free_ReplyKeyPack_Win2k(&key_pack);
922           return KRB5KRB_AP_ERR_MODIFIED;
923     }
924 
925     *key = malloc (sizeof (**key));
926     if (*key == NULL) {
927           free_ReplyKeyPack_Win2k(&key_pack);
928           return krb5_enomem(context);
929     }
930 
931     ret = copy_EncryptionKey(&key_pack.replyKey, *key);
932     free_ReplyKeyPack_Win2k(&key_pack);
933     if (ret) {
934           krb5_set_error_message(context, ret,
935                                      N_("PKINIT failed copying reply key", ""));
936           free(*key);
937           *key = NULL;
938     }
939 
940     return ret;
941 }
942 
943 static krb5_error_code
get_reply_key(krb5_context context,const krb5_data * content,const krb5_data * req_buffer,krb5_keyblock ** key)944 get_reply_key(krb5_context context,
945                 const krb5_data *content,
946                 const krb5_data *req_buffer,
947                 krb5_keyblock **key)
948 {
949     ReplyKeyPack key_pack;
950     krb5_error_code ret;
951     size_t size;
952 
953     ret = decode_ReplyKeyPack(content->data,
954                                     content->length,
955                                     &key_pack,
956                                     &size);
957     if (ret) {
958           krb5_set_error_message(context, ret,
959                                      N_("PKINIT decoding reply key failed", ""));
960           free_ReplyKeyPack(&key_pack);
961           return ret;
962     }
963 
964     {
965           krb5_crypto crypto;
966 
967           /*
968            * XXX Verify kp.replyKey is a allowed enctype in the
969            * configuration file
970            */
971 
972           ret = krb5_crypto_init(context, &key_pack.replyKey, 0, &crypto);
973           if (ret) {
974               free_ReplyKeyPack(&key_pack);
975               return ret;
976           }
977 
978           ret = krb5_verify_checksum(context, crypto, 6,
979                                            req_buffer->data, req_buffer->length,
980                                            &key_pack.asChecksum);
981           krb5_crypto_destroy(context, crypto);
982           if (ret) {
983               free_ReplyKeyPack(&key_pack);
984               return ret;
985           }
986     }
987 
988     *key = malloc (sizeof (**key));
989     if (*key == NULL) {
990           free_ReplyKeyPack(&key_pack);
991           return krb5_enomem(context);
992     }
993 
994     ret = copy_EncryptionKey(&key_pack.replyKey, *key);
995     free_ReplyKeyPack(&key_pack);
996     if (ret) {
997           krb5_set_error_message(context, ret,
998                                      N_("PKINIT failed copying reply key", ""));
999           free(*key);
1000           *key = NULL;
1001     }
1002 
1003     return ret;
1004 }
1005 
1006 
1007 static krb5_error_code
pk_verify_host(krb5_context context,const char * realm,const krb5_krbhst_info * hi,struct krb5_pk_init_ctx_data * ctx,struct krb5_pk_cert * host)1008 pk_verify_host(krb5_context context,
1009                  const char *realm,
1010                  const krb5_krbhst_info *hi,
1011                  struct krb5_pk_init_ctx_data *ctx,
1012                  struct krb5_pk_cert *host)
1013 {
1014     krb5_error_code ret = 0;
1015 
1016     if (ctx->require_eku) {
1017           ret = hx509_cert_check_eku(context->hx509ctx, host->cert,
1018                                            &asn1_oid_id_pkkdcekuoid, 0);
1019           if (ret) {
1020               krb5_set_error_message(context, ret,
1021                                            N_("No PK-INIT KDC EKU in kdc certificate", ""));
1022               return ret;
1023           }
1024     }
1025     if (ctx->require_krbtgt_otherName) {
1026           hx509_octet_string_list list;
1027           size_t i;
1028           int matched = 0;
1029 
1030           ret = hx509_cert_find_subjectAltName_otherName(context->hx509ctx,
1031                                                                    host->cert,
1032                                                                    &asn1_oid_id_pkinit_san,
1033                                                                    &list);
1034           if (ret) {
1035               krb5_set_error_message(context, ret,
1036                                            N_("Failed to find the PK-INIT "
1037                                               "subjectAltName in the KDC "
1038                                               "certificate", ""));
1039 
1040               return ret;
1041           }
1042 
1043           /*
1044            * subjectAltNames are multi-valued, and a single KDC may serve
1045            * multiple realms. The SAN validation here must accept
1046            * the KDC's cert if *any* of the SANs match the expected KDC.
1047            * It is OK for *some* of the SANs to not match, provided at least
1048            * one does.
1049            */
1050           for (i = 0; matched == 0 && i < list.len; i++) {
1051               KRB5PrincipalName r;
1052 
1053               ret = decode_KRB5PrincipalName(list.val[i].data,
1054                                                      list.val[i].length,
1055                                                      &r,
1056                                                      NULL);
1057               if (ret) {
1058                     krb5_set_error_message(context, ret,
1059                                                N_("Failed to decode the PK-INIT "
1060                                                     "subjectAltName in the "
1061                                                     "KDC certificate", ""));
1062 
1063                     break;
1064               }
1065 
1066               if (r.principalName.name_string.len == 2 &&
1067                     strcmp(r.principalName.name_string.val[0], KRB5_TGS_NAME) == 0
1068                     && strcmp(r.principalName.name_string.val[1], realm) == 0
1069                     && strcmp(r.realm, realm) == 0)
1070                     matched = 1;
1071 
1072               free_KRB5PrincipalName(&r);
1073           }
1074           hx509_free_octet_string_list(&list);
1075           if (matched == 0) {
1076               ret = KRB5_KDC_ERR_INVALID_CERTIFICATE;
1077               /* XXX: Lost in translation... */
1078               krb5_set_error_message(context, ret,
1079                                            N_("KDC have wrong realm name in "
1080                                               "the certificate", ""));
1081           }
1082     }
1083     if (ret)
1084           return ret;
1085 
1086     if (hi) {
1087           ret = hx509_verify_hostname(context->hx509ctx, host->cert,
1088                                             ctx->require_hostname_match,
1089                                             HX509_HN_HOSTNAME,
1090                                             hi->hostname,
1091                                             hi->ai->ai_addr, hi->ai->ai_addrlen);
1092 
1093           if (ret)
1094               krb5_set_error_message(context, ret,
1095                                            N_("Address mismatch in "
1096                                               "the KDC certificate", ""));
1097     }
1098     return ret;
1099 }
1100 
1101 static krb5_error_code
pk_rd_pa_reply_enckey(krb5_context context,int type,const heim_octet_string * indata,const heim_oid * dataType,const char * realm,krb5_pk_init_ctx ctx,krb5_enctype etype,const krb5_krbhst_info * hi,unsigned nonce,const krb5_data * req_buffer,PA_DATA * pa,krb5_keyblock ** key)1102 pk_rd_pa_reply_enckey(krb5_context context,
1103                           int type,
1104                           const heim_octet_string *indata,
1105                           const heim_oid *dataType,
1106                           const char *realm,
1107                           krb5_pk_init_ctx ctx,
1108                           krb5_enctype etype,
1109                           const krb5_krbhst_info *hi,
1110                           unsigned nonce,
1111                           const krb5_data *req_buffer,
1112                           PA_DATA *pa,
1113                           krb5_keyblock **key)
1114 {
1115     krb5_error_code ret;
1116     struct krb5_pk_cert *host = NULL;
1117     krb5_data content;
1118     heim_oid contentType = { 0, NULL };
1119     int flags = HX509_CMS_UE_DONT_REQUIRE_KU_ENCIPHERMENT;
1120 
1121     if (der_heim_oid_cmp(&asn1_oid_id_pkcs7_envelopedData, dataType)) {
1122           krb5_set_error_message(context, EINVAL,
1123                                      N_("PKINIT: Invalid content type", ""));
1124           return EINVAL;
1125     }
1126 
1127     if (ctx->type == PKINIT_WIN2K)
1128           flags |= HX509_CMS_UE_ALLOW_WEAK;
1129 
1130     ret = hx509_cms_unenvelope(context->hx509ctx,
1131                                      ctx->id->certs,
1132                                      flags,
1133                                      indata->data,
1134                                      indata->length,
1135                                      NULL,
1136                                      0,
1137                                      &contentType,
1138                                      &content);
1139     if (ret) {
1140           pk_copy_error(context, context->hx509ctx, ret,
1141                           "Failed to unenvelope CMS data in PK-INIT reply");
1142           return ret;
1143     }
1144     der_free_oid(&contentType);
1145 
1146     /* win2k uses ContentInfo */
1147     if (type == PKINIT_WIN2K) {
1148           heim_oid type2;
1149           heim_octet_string out;
1150 
1151           ret = hx509_cms_unwrap_ContentInfo(&content, &type2, &out, NULL);
1152           if (ret) {
1153               /* windows LH with interesting CMS packets */
1154               size_t ph = 1 + der_length_len(content.length);
1155               unsigned char *ptr = malloc(content.length + ph);
1156               size_t l;
1157 
1158               memcpy(ptr + ph, content.data, content.length);
1159 
1160               ret = der_put_length_and_tag (ptr + ph - 1, ph, content.length,
1161                                                     ASN1_C_UNIV, CONS, UT_Sequence, &l);
1162               if (ret) {
1163                 free(ptr);
1164                     return ret;
1165             }
1166               free(content.data);
1167               content.data = ptr;
1168               content.length += ph;
1169 
1170               ret = hx509_cms_unwrap_ContentInfo(&content, &type2, &out, NULL);
1171               if (ret)
1172                     goto out;
1173           }
1174           if (der_heim_oid_cmp(&type2, &asn1_oid_id_pkcs7_signedData)) {
1175               ret = EINVAL; /* XXX */
1176               krb5_set_error_message(context, ret,
1177                                            N_("PKINIT: Invalid content type", ""));
1178               der_free_oid(&type2);
1179               der_free_octet_string(&out);
1180               goto out;
1181           }
1182           der_free_oid(&type2);
1183           krb5_data_free(&content);
1184           ret = krb5_data_copy(&content, out.data, out.length);
1185           der_free_octet_string(&out);
1186           if (ret) {
1187               krb5_set_error_message(context, ret,
1188                                            N_("malloc: out of memory", ""));
1189               goto out;
1190           }
1191     }
1192 
1193     ret = pk_verify_sign(context,
1194                                content.data,
1195                                content.length,
1196                                ctx->id,
1197                                &contentType,
1198                                &content,
1199                                &host);
1200     if (ret)
1201           goto out;
1202 
1203     /* make sure that it is the kdc's certificate */
1204     ret = pk_verify_host(context, realm, hi, ctx, host);
1205     if (ret) {
1206           goto out;
1207     }
1208 
1209 #if 0
1210     if (type == PKINIT_WIN2K) {
1211           if (der_heim_oid_cmp(&contentType, &asn1_oid_id_pkcs7_data) != 0) {
1212               ret = KRB5KRB_AP_ERR_MSG_TYPE;
1213               krb5_set_error_message(context, ret, "PKINIT: reply key, wrong oid");
1214               goto out;
1215           }
1216     } else {
1217           if (der_heim_oid_cmp(&contentType, &asn1_oid_id_pkrkeydata) != 0) {
1218               ret = KRB5KRB_AP_ERR_MSG_TYPE;
1219               krb5_set_error_message(context, ret, "PKINIT: reply key, wrong oid");
1220               goto out;
1221           }
1222     }
1223 #endif
1224 
1225     switch(type) {
1226     case PKINIT_WIN2K:
1227           ret = get_reply_key(context, &content, req_buffer, key);
1228           if (ret != 0 && ctx->require_binding == 0)
1229               ret = get_reply_key_win(context, &content, nonce, key);
1230           break;
1231     case PKINIT_27:
1232           ret = get_reply_key(context, &content, req_buffer, key);
1233           break;
1234     }
1235     if (ret)
1236           goto out;
1237 
1238     /* XXX compare given etype with key->etype */
1239 
1240  out:
1241     if (host)
1242           _krb5_pk_cert_free(host);
1243     der_free_oid(&contentType);
1244     krb5_data_free(&content);
1245 
1246     return ret;
1247 }
1248 
1249 /*
1250  * RFC 8062 section 7:
1251  *
1252  *  The client then decrypts the KDC contribution key and verifies that
1253  *  the ticket session key in the returned ticket is the combined key of
1254  *  the KDC contribution key and the reply key.
1255  */
1256 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
_krb5_pk_kx_confirm(krb5_context context,krb5_pk_init_ctx ctx,krb5_keyblock * reply_key,krb5_keyblock * session_key,PA_DATA * pa_pkinit_kx)1257 _krb5_pk_kx_confirm(krb5_context context,
1258                         krb5_pk_init_ctx ctx,
1259                         krb5_keyblock *reply_key,
1260                         krb5_keyblock *session_key,
1261                         PA_DATA *pa_pkinit_kx)
1262 {
1263     krb5_error_code ret;
1264     EncryptedData ed;
1265     krb5_keyblock ck, sk_verify;
1266     krb5_crypto ck_crypto = NULL;
1267     krb5_crypto rk_crypto = NULL;
1268     size_t len;
1269     krb5_data data;
1270     krb5_data p1 = { sizeof("PKINIT") - 1, "PKINIT" };
1271     krb5_data p2 = { sizeof("KEYEXCHANGE") - 1, "KEYEXCHANGE" };
1272 
1273     heim_assert(ctx != NULL, "PKINIT context is non-NULL");
1274     heim_assert(reply_key != NULL, "reply key is non-NULL");
1275     heim_assert(session_key != NULL, "session key is non-NULL");
1276 
1277     /* PA-PKINIT-KX is optional unless anonymous */
1278     if (pa_pkinit_kx == NULL)
1279           return ctx->anonymous ? KRB5_KDCREP_MODIFIED : 0;
1280 
1281     memset(&ed, 0, sizeof(ed));
1282     krb5_keyblock_zero(&ck);
1283     krb5_keyblock_zero(&sk_verify);
1284     krb5_data_zero(&data);
1285 
1286     ret = decode_EncryptedData(pa_pkinit_kx->padata_value.data,
1287                                      pa_pkinit_kx->padata_value.length,
1288                                      &ed, &len);
1289     if (ret)
1290           goto out;
1291 
1292     if (len != pa_pkinit_kx->padata_value.length) {
1293           ret = KRB5_KDCREP_MODIFIED;
1294           goto out;
1295     }
1296 
1297     ret = krb5_crypto_init(context, reply_key, 0, &rk_crypto);
1298     if (ret)
1299           goto out;
1300 
1301     ret = krb5_decrypt_EncryptedData(context, rk_crypto,
1302                                              KRB5_KU_PA_PKINIT_KX,
1303                                              &ed, &data);
1304     if (ret)
1305           goto out;
1306 
1307     ret = decode_EncryptionKey(data.data, data.length,
1308                                      &ck, &len);
1309     if (ret)
1310           goto out;
1311 
1312     ret = krb5_crypto_init(context, &ck, 0, &ck_crypto);
1313     if (ret)
1314           goto out;
1315 
1316     ret = krb5_crypto_fx_cf2(context, ck_crypto, rk_crypto,
1317                                    &p1, &p2, session_key->keytype,
1318                                    &sk_verify);
1319     if (ret)
1320           goto out;
1321 
1322     if (sk_verify.keytype != session_key->keytype ||
1323           krb5_data_ct_cmp(&sk_verify.keyvalue, &session_key->keyvalue) != 0) {
1324           ret = KRB5_KDCREP_MODIFIED;
1325           goto out;
1326     }
1327 
1328 out:
1329     free_EncryptedData(&ed);
1330     krb5_free_keyblock_contents(context, &ck);
1331     krb5_free_keyblock_contents(context, &sk_verify);
1332     if (ck_crypto)
1333           krb5_crypto_destroy(context, ck_crypto);
1334     if (rk_crypto)
1335           krb5_crypto_destroy(context, rk_crypto);
1336     krb5_data_free(&data);
1337 
1338     return ret;
1339 }
1340 
1341 static krb5_error_code
pk_rd_pa_reply_dh(krb5_context context,const heim_octet_string * indata,const heim_oid * dataType,const char * realm,krb5_pk_init_ctx ctx,krb5_enctype etype,const krb5_krbhst_info * hi,const DHNonce * c_n,const DHNonce * k_n,unsigned nonce,PA_DATA * pa,krb5_keyblock ** key)1342 pk_rd_pa_reply_dh(krb5_context context,
1343                       const heim_octet_string *indata,
1344                       const heim_oid *dataType,
1345                       const char *realm,
1346                       krb5_pk_init_ctx ctx,
1347                       krb5_enctype etype,
1348                       const krb5_krbhst_info *hi,
1349                       const DHNonce *c_n,
1350                       const DHNonce *k_n,
1351                   unsigned nonce,
1352                   PA_DATA *pa,
1353                   krb5_keyblock **key)
1354 {
1355     const unsigned char *p;
1356     unsigned char *dh_gen_key = NULL;
1357     struct krb5_pk_cert *host = NULL;
1358     BIGNUM *kdc_dh_pubkey = NULL;
1359     KDCDHKeyInfo kdc_dh_info;
1360     heim_oid contentType = { 0, NULL };
1361     krb5_data content;
1362     krb5_error_code ret;
1363     int dh_gen_keylen = 0;
1364     size_t size;
1365 
1366     krb5_data_zero(&content);
1367     memset(&kdc_dh_info, 0, sizeof(kdc_dh_info));
1368 
1369     if (der_heim_oid_cmp(&asn1_oid_id_pkcs7_signedData, dataType)) {
1370           krb5_set_error_message(context, EINVAL,
1371                                      N_("PKINIT: Invalid content type", ""));
1372           return EINVAL;
1373     }
1374 
1375     ret = pk_verify_sign(context,
1376                                indata->data,
1377                                indata->length,
1378                                ctx->id,
1379                                &contentType,
1380                                &content,
1381                                &host);
1382     if (ret)
1383           goto out;
1384 
1385     /* make sure that it is the kdc's certificate */
1386     ret = pk_verify_host(context, realm, hi, ctx, host);
1387     if (ret)
1388           goto out;
1389 
1390     if (der_heim_oid_cmp(&contentType, &asn1_oid_id_pkdhkeydata)) {
1391           ret = KRB5KRB_AP_ERR_MSG_TYPE;
1392           krb5_set_error_message(context, ret,
1393                                      N_("pkinit - dh reply contains wrong oid", ""));
1394           goto out;
1395     }
1396 
1397     ret = decode_KDCDHKeyInfo(content.data,
1398                                     content.length,
1399                                     &kdc_dh_info,
1400                                     &size);
1401 
1402     if (ret) {
1403           krb5_set_error_message(context, ret,
1404                                      N_("pkinit - failed to decode "
1405                                           "KDC DH Key Info", ""));
1406           goto out;
1407     }
1408 
1409     if (kdc_dh_info.nonce != nonce) {
1410           ret = KRB5KRB_AP_ERR_MODIFIED;
1411           krb5_set_error_message(context, ret,
1412                                      N_("PKINIT: DH nonce is wrong", ""));
1413           goto out;
1414     }
1415 
1416     if (kdc_dh_info.dhKeyExpiration) {
1417           if (k_n == NULL) {
1418               ret = KRB5KRB_ERR_GENERIC;
1419               krb5_set_error_message(context, ret,
1420                                            N_("pkinit; got key expiration "
1421                                               "without server nonce", ""));
1422               goto out;
1423           }
1424           if (c_n == NULL) {
1425               ret = KRB5KRB_ERR_GENERIC;
1426               krb5_set_error_message(context, ret,
1427                                            N_("pkinit; got DH reuse but no "
1428                                               "client nonce", ""));
1429               goto out;
1430           }
1431     } else {
1432           if (k_n) {
1433               ret = KRB5KRB_ERR_GENERIC;
1434               krb5_set_error_message(context, ret,
1435                                            N_("pkinit: got server nonce "
1436                                               "without key expiration", ""));
1437               goto out;
1438           }
1439           c_n = NULL;
1440     }
1441 
1442 
1443     p = kdc_dh_info.subjectPublicKey.data;
1444     size = (kdc_dh_info.subjectPublicKey.length + 7) / 8;
1445 
1446     if (ctx->keyex == USE_DH) {
1447           DHPublicKey k;
1448           ret = decode_DHPublicKey(p, size, &k, NULL);
1449           if (ret) {
1450               krb5_set_error_message(context, ret,
1451                                            N_("pkinit: can't decode "
1452                                               "without key expiration", ""));
1453               goto out;
1454           }
1455 
1456           kdc_dh_pubkey = integer_to_BN(context, "DHPublicKey", &k);
1457           free_DHPublicKey(&k);
1458           if (kdc_dh_pubkey == NULL) {
1459               ret = ENOMEM;
1460               goto out;
1461           }
1462 
1463 
1464           size = DH_size(ctx->u.dh);
1465 
1466           dh_gen_key = malloc(size);
1467           if (dh_gen_key == NULL) {
1468               ret = krb5_enomem(context);
1469               goto out;
1470           }
1471 
1472           dh_gen_keylen = DH_compute_key(dh_gen_key, kdc_dh_pubkey, ctx->u.dh);
1473           if (dh_gen_keylen == -1) {
1474               ret = KRB5KRB_ERR_GENERIC;
1475               dh_gen_keylen = 0;
1476               krb5_set_error_message(context, ret,
1477                                            N_("PKINIT: Can't compute Diffie-Hellman key", ""));
1478               goto out;
1479           }
1480           if (dh_gen_keylen < (int)size) {
1481               size -= dh_gen_keylen;
1482               memmove(dh_gen_key + size, dh_gen_key, dh_gen_keylen);
1483               memset(dh_gen_key, 0, size);
1484           }
1485 
1486     } else {
1487         ret = _krb5_pk_rd_pa_reply_ecdh_compute_key(context, ctx, p,
1488                                                     size, &dh_gen_key,
1489                                                     &dh_gen_keylen);
1490         if (ret)
1491           goto out;
1492     }
1493 
1494     if (dh_gen_keylen <= 0) {
1495           ret = EINVAL;
1496           krb5_set_error_message(context, ret,
1497                                      N_("PKINIT: resulting DH key <= 0", ""));
1498           dh_gen_keylen = 0;
1499           goto out;
1500     }
1501 
1502     *key = malloc (sizeof (**key));
1503     if (*key == NULL) {
1504           ret = krb5_enomem(context);
1505           goto out;
1506     }
1507 
1508     ret = _krb5_pk_octetstring2key(context,
1509                                            etype,
1510                                            dh_gen_key, dh_gen_keylen,
1511                                            c_n, k_n,
1512                                            *key);
1513     if (ret) {
1514           krb5_set_error_message(context, ret,
1515                                      N_("PKINIT: can't create key from DH key", ""));
1516           free(*key);
1517           *key = NULL;
1518           goto out;
1519     }
1520 
1521  out:
1522     if (kdc_dh_pubkey)
1523           BN_free(kdc_dh_pubkey);
1524     if (dh_gen_key) {
1525           memset(dh_gen_key, 0, dh_gen_keylen);
1526           free(dh_gen_key);
1527     }
1528     if (host)
1529           _krb5_pk_cert_free(host);
1530     if (content.data)
1531           krb5_data_free(&content);
1532     der_free_oid(&contentType);
1533     free_KDCDHKeyInfo(&kdc_dh_info);
1534 
1535     return ret;
1536 }
1537 
1538 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
_krb5_pk_rd_pa_reply(krb5_context context,const char * realm,void * c,krb5_enctype etype,const krb5_krbhst_info * hi,unsigned nonce,const krb5_data * req_buffer,PA_DATA * pa,krb5_keyblock ** key)1539 _krb5_pk_rd_pa_reply(krb5_context context,
1540                          const char *realm,
1541                          void *c,
1542                          krb5_enctype etype,
1543                          const krb5_krbhst_info *hi,
1544                          unsigned nonce,
1545                          const krb5_data *req_buffer,
1546                          PA_DATA *pa,
1547                          krb5_keyblock **key)
1548 {
1549     krb5_pk_init_ctx ctx = c;
1550     krb5_error_code ret;
1551     size_t size;
1552 
1553     /* Check for IETF PK-INIT first */
1554     if (ctx->type == PKINIT_27) {
1555           PA_PK_AS_REP rep;
1556           heim_octet_string os, data;
1557           heim_oid oid;
1558 
1559           if (pa->padata_type != KRB5_PADATA_PK_AS_REP) {
1560               krb5_set_error_message(context, EINVAL,
1561                                            N_("PKINIT: wrong padata recv", ""));
1562               return EINVAL;
1563           }
1564 
1565           ret = decode_PA_PK_AS_REP(pa->padata_value.data,
1566                                           pa->padata_value.length,
1567                                           &rep,
1568                                           &size);
1569           if (ret) {
1570               krb5_set_error_message(context, ret,
1571                                            N_("Failed to decode pkinit AS rep", ""));
1572               return ret;
1573           }
1574 
1575           switch (rep.element) {
1576           case choice_PA_PK_AS_REP_dhInfo:
1577               _krb5_debug(context, 5, "krb5_get_init_creds: using pkinit dh");
1578               os = rep.u.dhInfo.dhSignedData;
1579               break;
1580           case choice_PA_PK_AS_REP_encKeyPack:
1581               _krb5_debug(context, 5, "krb5_get_init_creds: using kinit enc reply key");
1582               os = rep.u.encKeyPack;
1583               break;
1584           default: {
1585               PA_PK_AS_REP_BTMM btmm;
1586               free_PA_PK_AS_REP(&rep);
1587               memset(&rep, 0, sizeof(rep));
1588 
1589               _krb5_debug(context, 5, "krb5_get_init_creds: using BTMM kinit enc reply key");
1590 
1591               ret = decode_PA_PK_AS_REP_BTMM(pa->padata_value.data,
1592                                                      pa->padata_value.length,
1593                                                      &btmm,
1594                                                      &size);
1595               if (ret) {
1596                     krb5_set_error_message(context, EINVAL,
1597                                                N_("PKINIT: -27 reply "
1598                                                     "invalid content type", ""));
1599                     return EINVAL;
1600               }
1601 
1602               if (btmm.dhSignedData || btmm.encKeyPack == NULL) {
1603                     free_PA_PK_AS_REP_BTMM(&btmm);
1604                     ret = EINVAL;
1605                     krb5_set_error_message(context, ret,
1606                                                N_("DH mode not supported for BTMM mode", ""));
1607                     return ret;
1608               }
1609 
1610               /*
1611                * Transform to IETF style PK-INIT reply so that free works below
1612                */
1613 
1614               rep.element = choice_PA_PK_AS_REP_encKeyPack;
1615               rep.u.encKeyPack.data = btmm.encKeyPack->data;
1616               rep.u.encKeyPack.length = btmm.encKeyPack->length;
1617               btmm.encKeyPack->data = NULL;
1618               btmm.encKeyPack->length = 0;
1619               free_PA_PK_AS_REP_BTMM(&btmm);
1620               os = rep.u.encKeyPack;
1621           }
1622           }
1623 
1624           ret = hx509_cms_unwrap_ContentInfo(&os, &oid, &data, NULL);
1625           if (ret) {
1626               free_PA_PK_AS_REP(&rep);
1627               krb5_set_error_message(context, ret,
1628                                            N_("PKINIT: failed to unwrap CI", ""));
1629               return ret;
1630           }
1631 
1632           switch (rep.element) {
1633           case choice_PA_PK_AS_REP_dhInfo:
1634               ret = pk_rd_pa_reply_dh(context, &data, &oid, realm, ctx, etype, hi,
1635                                             ctx->clientDHNonce,
1636                                             rep.u.dhInfo.serverDHNonce,
1637                                             nonce, pa, key);
1638               break;
1639           case choice_PA_PK_AS_REP_encKeyPack:
1640               ret = pk_rd_pa_reply_enckey(context, PKINIT_27, &data, &oid, realm,
1641                                                   ctx, etype, hi, nonce, req_buffer, pa, key);
1642               break;
1643           default:
1644               krb5_abortx(context, "pk-init as-rep case not possible to happen");
1645           }
1646           der_free_octet_string(&data);
1647           der_free_oid(&oid);
1648           free_PA_PK_AS_REP(&rep);
1649 
1650     } else if (ctx->type == PKINIT_WIN2K) {
1651           PA_PK_AS_REP_Win2k w2krep;
1652 
1653           /* Check for Windows encoding of the AS-REP pa data */
1654 
1655 #if 0 /* should this be ? */
1656           if (pa->padata_type != KRB5_PADATA_PK_AS_REP) {
1657               krb5_set_error_message(context, EINVAL,
1658                                            "PKINIT: wrong padata recv");
1659               return EINVAL;
1660           }
1661 #endif
1662 
1663           memset(&w2krep, 0, sizeof(w2krep));
1664 
1665           ret = decode_PA_PK_AS_REP_Win2k(pa->padata_value.data,
1666                                                   pa->padata_value.length,
1667                                                   &w2krep,
1668                                                   &size);
1669           if (ret) {
1670               krb5_set_error_message(context, ret,
1671                                            N_("PKINIT: Failed decoding windows "
1672                                               "pkinit reply %d", ""), (int)ret);
1673               return ret;
1674           }
1675 
1676           krb5_clear_error_message(context);
1677 
1678           switch (w2krep.element) {
1679           case choice_PA_PK_AS_REP_Win2k_encKeyPack: {
1680               heim_octet_string data;
1681               heim_oid oid;
1682 
1683               ret = hx509_cms_unwrap_ContentInfo(&w2krep.u.encKeyPack,
1684                                                          &oid, &data, NULL);
1685               free_PA_PK_AS_REP_Win2k(&w2krep);
1686               if (ret) {
1687                     krb5_set_error_message(context, ret,
1688                                                N_("PKINIT: failed to unwrap CI", ""));
1689                     return ret;
1690               }
1691 
1692               ret = pk_rd_pa_reply_enckey(context, PKINIT_WIN2K, &data, &oid, realm,
1693                                                   ctx, etype, hi, nonce, req_buffer, pa, key);
1694               der_free_octet_string(&data);
1695               der_free_oid(&oid);
1696 
1697               break;
1698           }
1699           default:
1700               free_PA_PK_AS_REP_Win2k(&w2krep);
1701               ret = EINVAL;
1702               krb5_set_error_message(context, ret,
1703                                            N_("PKINIT: win2k reply invalid "
1704                                               "content type", ""));
1705               break;
1706           }
1707 
1708     } else {
1709           ret = EINVAL;
1710           krb5_set_error_message(context, ret,
1711                                      N_("PKINIT: unknown reply type", ""));
1712     }
1713 
1714     return ret;
1715 }
1716 
1717 struct prompter {
1718     krb5_context context;
1719     krb5_prompter_fct prompter;
1720     void *prompter_data;
1721 };
1722 
1723 static int
hx_pass_prompter(void * data,const hx509_prompt * prompter)1724 hx_pass_prompter(void *data, const hx509_prompt *prompter)
1725 {
1726     krb5_error_code ret;
1727     krb5_prompt prompt;
1728     krb5_data password_data;
1729     struct prompter *p = data;
1730 
1731     password_data.data   = prompter->reply.data;
1732     password_data.length = prompter->reply.length;
1733 
1734     prompt.prompt = prompter->prompt;
1735     prompt.hidden = hx509_prompt_hidden(prompter->type);
1736     prompt.reply  = &password_data;
1737 
1738     switch (prompter->type) {
1739     case HX509_PROMPT_TYPE_INFO:
1740           prompt.type   = KRB5_PROMPT_TYPE_INFO;
1741           break;
1742     case HX509_PROMPT_TYPE_PASSWORD:
1743     case HX509_PROMPT_TYPE_QUESTION:
1744     default:
1745           prompt.type   = KRB5_PROMPT_TYPE_PASSWORD;
1746           break;
1747     }
1748 
1749     ret = (*p->prompter)(p->context, p->prompter_data, NULL, NULL, 1, &prompt);
1750     if (ret) {
1751           memset (prompter->reply.data, 0, prompter->reply.length);
1752           return 1;
1753     }
1754     return 0;
1755 }
1756 
1757 static krb5_error_code
_krb5_pk_set_user_id(krb5_context context,krb5_principal principal,krb5_pk_init_ctx ctx,struct hx509_certs_data * certs)1758 _krb5_pk_set_user_id(krb5_context context,
1759                          krb5_principal principal,
1760                          krb5_pk_init_ctx ctx,
1761                          struct hx509_certs_data *certs)
1762 {
1763     hx509_certs c = hx509_certs_ref(certs);
1764     hx509_query *q = NULL;
1765     int ret;
1766 
1767     if (ctx->id->certs)
1768           hx509_certs_free(&ctx->id->certs);
1769     if (ctx->id->cert) {
1770           hx509_cert_free(ctx->id->cert);
1771           ctx->id->cert = NULL;
1772     }
1773 
1774     ctx->id->certs = c;
1775     ctx->anonymous = 0;
1776 
1777     ret = hx509_query_alloc(context->hx509ctx, &q);
1778     if (ret) {
1779           pk_copy_error(context, context->hx509ctx, ret,
1780                           "Allocate query to find signing certificate");
1781           return ret;
1782     }
1783 
1784     hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY);
1785     hx509_query_match_option(q, HX509_QUERY_OPTION_KU_DIGITALSIGNATURE);
1786 
1787     if (principal && strncmp("LKDC:SHA1.", krb5_principal_get_realm(context, principal), 9) == 0) {
1788           ctx->id->flags |= PKINIT_BTMM;
1789     }
1790 
1791     ret = find_cert(context, ctx->id, q, &ctx->id->cert);
1792     hx509_query_free(context->hx509ctx, q);
1793 
1794     if (ret == 0 && _krb5_have_debug(context, 2)) {
1795           hx509_name name;
1796           char *str, *sn;
1797           heim_integer i;
1798 
1799           ret = hx509_cert_get_subject(ctx->id->cert, &name);
1800           if (ret)
1801               goto out;
1802 
1803           ret = hx509_name_to_string(name, &str);
1804           hx509_name_free(&name);
1805           if (ret)
1806               goto out;
1807 
1808           ret = hx509_cert_get_serialnumber(ctx->id->cert, &i);
1809           if (ret) {
1810               free(str);
1811               goto out;
1812           }
1813 
1814           ret = der_print_hex_heim_integer(&i, &sn);
1815           der_free_heim_integer(&i);
1816           if (ret) {
1817               free(name);
1818               goto out;
1819           }
1820 
1821           _krb5_debug(context, 2, "using cert: subject: %s sn: %s", str, sn);
1822           free(str);
1823           free(sn);
1824     }
1825  out:
1826 
1827     return ret;
1828 }
1829 
1830 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
_krb5_pk_load_id(krb5_context context,struct krb5_pk_identity ** ret_id,const char * user_id,const char * anchor_id,char * const * chain_list,char * const * revoke_list,krb5_prompter_fct prompter,void * prompter_data,char * password)1831 _krb5_pk_load_id(krb5_context context,
1832                      struct krb5_pk_identity **ret_id,
1833                      const char *user_id,
1834                      const char *anchor_id,
1835                      char * const *chain_list,
1836                      char * const *revoke_list,
1837                      krb5_prompter_fct prompter,
1838                      void *prompter_data,
1839                      char *password)
1840 {
1841     struct krb5_pk_identity *id = NULL;
1842     struct prompter p;
1843     int ret;
1844 
1845     *ret_id = NULL;
1846 
1847     if (anchor_id == NULL) {
1848           krb5_set_error_message(context, HEIM_PKINIT_NO_VALID_CA,
1849                                      N_("PKINIT: No anchor given", ""));
1850           return HEIM_PKINIT_NO_VALID_CA;
1851     }
1852 
1853     /* load cert */
1854 
1855     id = calloc(1, sizeof(*id));
1856     if (id == NULL)
1857           return krb5_enomem(context);
1858 
1859     if (user_id) {
1860           hx509_lock lock;
1861 
1862           ret = hx509_lock_init(context->hx509ctx, &lock);
1863           if (ret) {
1864               pk_copy_error(context, context->hx509ctx, ret, "Failed init lock");
1865               goto out;
1866           }
1867 
1868           if (password && password[0])
1869               hx509_lock_add_password(lock, password);
1870 
1871           if (prompter) {
1872               p.context = context;
1873               p.prompter = prompter;
1874               p.prompter_data = prompter_data;
1875 
1876               ret = hx509_lock_set_prompter(lock, hx_pass_prompter, &p);
1877               if (ret) {
1878                     hx509_lock_free(lock);
1879                     goto out;
1880               }
1881           }
1882 
1883           ret = hx509_certs_init(context->hx509ctx, user_id, 0, lock, &id->certs);
1884         hx509_lock_free(lock);
1885           if (ret) {
1886               pk_copy_error(context, context->hx509ctx, ret,
1887                                 "Failed to init cert certs");
1888               goto out;
1889           }
1890     } else {
1891           id->certs = NULL;
1892     }
1893 
1894     ret = hx509_certs_init(context->hx509ctx, anchor_id, 0, NULL, &id->anchors);
1895     if (ret) {
1896           pk_copy_error(context, context->hx509ctx, ret,
1897                           "Failed to init anchors");
1898           goto out;
1899     }
1900 
1901     ret = hx509_certs_init(context->hx509ctx, "MEMORY:pkinit-cert-chain",
1902                                  0, NULL, &id->certpool);
1903     if (ret) {
1904           pk_copy_error(context, context->hx509ctx, ret,
1905                           "Failed to init chain");
1906           goto out;
1907     }
1908 
1909     while (chain_list && *chain_list) {
1910           ret = hx509_certs_append(context->hx509ctx, id->certpool,
1911                                          NULL, *chain_list);
1912           if (ret) {
1913               pk_copy_error(context, context->hx509ctx, ret,
1914                                 "Failed to laod chain %s",
1915                                 *chain_list);
1916               goto out;
1917           }
1918           chain_list++;
1919     }
1920 
1921     if (revoke_list) {
1922           ret = hx509_revoke_init(context->hx509ctx, &id->revokectx);
1923           if (ret) {
1924               pk_copy_error(context, context->hx509ctx, ret,
1925                                 "Failed init revoke list");
1926               goto out;
1927           }
1928 
1929           while (*revoke_list) {
1930               ret = hx509_revoke_add_crl(context->hx509ctx,
1931                                                id->revokectx,
1932                                                *revoke_list);
1933               if (ret) {
1934                     pk_copy_error(context, context->hx509ctx, ret,
1935                                     "Failed load revoke list");
1936                     goto out;
1937               }
1938               revoke_list++;
1939           }
1940     } else
1941           hx509_context_set_missing_revoke(context->hx509ctx, 1);
1942 
1943     ret = hx509_verify_init_ctx(context->hx509ctx, &id->verify_ctx);
1944     if (ret) {
1945           pk_copy_error(context, context->hx509ctx, ret,
1946                           "Failed init verify context");
1947           goto out;
1948     }
1949 
1950     hx509_verify_attach_anchors(id->verify_ctx, id->anchors);
1951     hx509_verify_attach_revoke(id->verify_ctx, id->revokectx);
1952 
1953  out:
1954     if (ret) {
1955           hx509_verify_destroy_ctx(id->verify_ctx);
1956           hx509_certs_free(&id->certs);
1957           hx509_certs_free(&id->anchors);
1958           hx509_certs_free(&id->certpool);
1959           hx509_revoke_free(&id->revokectx);
1960           free(id);
1961     } else
1962           *ret_id = id;
1963 
1964     return ret;
1965 }
1966 
1967 /*
1968  *
1969  */
1970 
1971 static void
pk_copy_error(krb5_context context,hx509_context hx509ctx,int hxret,const char * fmt,...)1972 pk_copy_error(krb5_context context,
1973                 hx509_context hx509ctx,
1974                 int hxret,
1975                 const char *fmt,
1976                 ...)
1977 {
1978     va_list va;
1979     char *s, *f;
1980     int ret;
1981 
1982     va_start(va, fmt);
1983     ret = vasprintf(&f, fmt, va);
1984     va_end(va);
1985     if (ret == -1 || f == NULL) {
1986           krb5_clear_error_message(context);
1987           return;
1988     }
1989 
1990     s = hx509_get_error_string(hx509ctx, hxret);
1991     if (s == NULL) {
1992           krb5_clear_error_message(context);
1993           free(f);
1994           return;
1995     }
1996     krb5_set_error_message(context, hxret, "%s: %s", f, s);
1997     free(s);
1998     free(f);
1999 }
2000 
2001 static int
parse_integer(krb5_context context,char ** p,const char * file,int lineno,const char * name,heim_integer * integer)2002 parse_integer(krb5_context context, char **p, const char *file, int lineno,
2003                 const char *name, heim_integer *integer)
2004 {
2005     int ret;
2006     char *p1;
2007     p1 = strsep(p, " \t");
2008     if (p1 == NULL) {
2009           krb5_set_error_message(context, EINVAL,
2010                                      N_("moduli file %s missing %s on line %d", ""),
2011                                      file, name, lineno);
2012           return EINVAL;
2013     }
2014     ret = der_parse_hex_heim_integer(p1, integer);
2015     if (ret) {
2016           krb5_set_error_message(context, ret,
2017                                      N_("moduli file %s failed parsing %s "
2018                                           "on line %d", ""),
2019                                      file, name, lineno);
2020           return ret;
2021     }
2022 
2023     return 0;
2024 }
2025 
2026 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
_krb5_parse_moduli_line(krb5_context context,const char * file,int lineno,char * p,struct krb5_dh_moduli ** m)2027 _krb5_parse_moduli_line(krb5_context context,
2028                               const char *file,
2029                               int lineno,
2030                               char *p,
2031                               struct krb5_dh_moduli **m)
2032 {
2033     struct krb5_dh_moduli *m1;
2034     char *p1;
2035     int ret;
2036 
2037     *m = NULL;
2038 
2039     m1 = calloc(1, sizeof(*m1));
2040     if (m1 == NULL)
2041           return krb5_enomem(context);
2042 
2043     while (isspace((unsigned char)*p))
2044           p++;
2045     if (*p  == '#') {
2046         free(m1);
2047           return 0;
2048     }
2049     ret = EINVAL;
2050 
2051     p1 = strsep(&p, " \t");
2052     if (p1 == NULL) {
2053           krb5_set_error_message(context, ret,
2054                                      N_("moduli file %s missing name on line %d", ""),
2055                                      file, lineno);
2056           goto out;
2057     }
2058     m1->name = strdup(p1);
2059     if (m1->name == NULL) {
2060           ret = krb5_enomem(context);
2061           goto out;
2062     }
2063 
2064     p1 = strsep(&p, " \t");
2065     if (p1 == NULL) {
2066           krb5_set_error_message(context, ret,
2067                                      N_("moduli file %s missing bits on line %d", ""),
2068                                      file, lineno);
2069           goto out;
2070     }
2071 
2072     m1->bits = atoi(p1);
2073     if (m1->bits == 0) {
2074           krb5_set_error_message(context, ret,
2075                                      N_("moduli file %s have un-parsable "
2076                                           "bits on line %d", ""), file, lineno);
2077           goto out;
2078     }
2079 
2080     ret = parse_integer(context, &p, file, lineno, "p", &m1->p);
2081     if (ret)
2082           goto out;
2083     ret = parse_integer(context, &p, file, lineno, "g", &m1->g);
2084     if (ret)
2085           goto out;
2086     ret = parse_integer(context, &p, file, lineno, "q", &m1->q);
2087     if (ret)
2088           goto out;
2089 
2090     *m = m1;
2091 
2092     return 0;
2093  out:
2094     free(m1->name);
2095     der_free_heim_integer(&m1->p);
2096     der_free_heim_integer(&m1->g);
2097     der_free_heim_integer(&m1->q);
2098     free(m1);
2099     return ret;
2100 }
2101 
2102 KRB5_LIB_FUNCTION void KRB5_LIB_CALL
_krb5_free_moduli(struct krb5_dh_moduli ** moduli)2103 _krb5_free_moduli(struct krb5_dh_moduli **moduli)
2104 {
2105     int i;
2106     for (i = 0; moduli[i] != NULL; i++) {
2107           free(moduli[i]->name);
2108           der_free_heim_integer(&moduli[i]->p);
2109           der_free_heim_integer(&moduli[i]->g);
2110           der_free_heim_integer(&moduli[i]->q);
2111           free(moduli[i]);
2112     }
2113     free(moduli);
2114 }
2115 
2116 static const char *default_moduli_RFC2412_MODP_group2 =
2117     /* name */
2118     "RFC2412-MODP-group2 "
2119     /* bits */
2120     "1024 "
2121     /* p */
2122     "FFFFFFFF" "FFFFFFFF" "C90FDAA2" "2168C234" "C4C6628B" "80DC1CD1"
2123     "29024E08" "8A67CC74" "020BBEA6" "3B139B22" "514A0879" "8E3404DD"
2124     "EF9519B3" "CD3A431B" "302B0A6D" "F25F1437" "4FE1356D" "6D51C245"
2125     "E485B576" "625E7EC6" "F44C42E9" "A637ED6B" "0BFF5CB6" "F406B7ED"
2126     "EE386BFB" "5A899FA5" "AE9F2411" "7C4B1FE6" "49286651" "ECE65381"
2127     "FFFFFFFF" "FFFFFFFF "
2128     /* g */
2129     "02 "
2130     /* q */
2131     "7FFFFFFF" "FFFFFFFF" "E487ED51" "10B4611A" "62633145" "C06E0E68"
2132     "94812704" "4533E63A" "0105DF53" "1D89CD91" "28A5043C" "C71A026E"
2133     "F7CA8CD9" "E69D218D" "98158536" "F92F8A1B" "A7F09AB6" "B6A8E122"
2134     "F242DABB" "312F3F63" "7A262174" "D31BF6B5" "85FFAE5B" "7A035BF6"
2135     "F71C35FD" "AD44CFD2" "D74F9208" "BE258FF3" "24943328" "F67329C0"
2136     "FFFFFFFF" "FFFFFFFF";
2137 
2138 static const char *default_moduli_rfc3526_MODP_group14 =
2139     /* name */
2140     "rfc3526-MODP-group14 "
2141     /* bits */
2142     "1760 "
2143     /* p */
2144     "FFFFFFFF" "FFFFFFFF" "C90FDAA2" "2168C234" "C4C6628B" "80DC1CD1"
2145     "29024E08" "8A67CC74" "020BBEA6" "3B139B22" "514A0879" "8E3404DD"
2146     "EF9519B3" "CD3A431B" "302B0A6D" "F25F1437" "4FE1356D" "6D51C245"
2147     "E485B576" "625E7EC6" "F44C42E9" "A637ED6B" "0BFF5CB6" "F406B7ED"
2148     "EE386BFB" "5A899FA5" "AE9F2411" "7C4B1FE6" "49286651" "ECE45B3D"
2149     "C2007CB8" "A163BF05" "98DA4836" "1C55D39A" "69163FA8" "FD24CF5F"
2150     "83655D23" "DCA3AD96" "1C62F356" "208552BB" "9ED52907" "7096966D"
2151     "670C354E" "4ABC9804" "F1746C08" "CA18217C" "32905E46" "2E36CE3B"
2152     "E39E772C" "180E8603" "9B2783A2" "EC07A28F" "B5C55DF0" "6F4C52C9"
2153     "DE2BCBF6" "95581718" "3995497C" "EA956AE5" "15D22618" "98FA0510"
2154     "15728E5A" "8AACAA68" "FFFFFFFF" "FFFFFFFF "
2155     /* g */
2156     "02 "
2157     /* q */
2158     "7FFFFFFF" "FFFFFFFF" "E487ED51" "10B4611A" "62633145" "C06E0E68"
2159     "94812704" "4533E63A" "0105DF53" "1D89CD91" "28A5043C" "C71A026E"
2160     "F7CA8CD9" "E69D218D" "98158536" "F92F8A1B" "A7F09AB6" "B6A8E122"
2161     "F242DABB" "312F3F63" "7A262174" "D31BF6B5" "85FFAE5B" "7A035BF6"
2162     "F71C35FD" "AD44CFD2" "D74F9208" "BE258FF3" "24943328" "F6722D9E"
2163     "E1003E5C" "50B1DF82" "CC6D241B" "0E2AE9CD" "348B1FD4" "7E9267AF"
2164     "C1B2AE91" "EE51D6CB" "0E3179AB" "1042A95D" "CF6A9483" "B84B4B36"
2165     "B3861AA7" "255E4C02" "78BA3604" "650C10BE" "19482F23" "171B671D"
2166     "F1CF3B96" "0C074301" "CD93C1D1" "7603D147" "DAE2AEF8" "37A62964"
2167     "EF15E5FB" "4AAC0B8C" "1CCAA4BE" "754AB572" "8AE9130C" "4C7D0288"
2168     "0AB9472D" "45565534" "7FFFFFFF" "FFFFFFFF";
2169 
2170 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
_krb5_parse_moduli(krb5_context context,const char * file,struct krb5_dh_moduli *** moduli)2171 _krb5_parse_moduli(krb5_context context, const char *file,
2172                        struct krb5_dh_moduli ***moduli)
2173 {
2174     /* name bits P G Q */
2175     krb5_error_code ret;
2176     struct krb5_dh_moduli **m = NULL, **m2;
2177     char buf[4096];
2178     FILE *f;
2179     int lineno = 0, n = 0;
2180 
2181     *moduli = NULL;
2182 
2183     m = calloc(1, sizeof(m[0]) * 3);
2184     if (m == NULL)
2185           return krb5_enomem(context);
2186 
2187     strlcpy(buf, default_moduli_rfc3526_MODP_group14, sizeof(buf));
2188     ret = _krb5_parse_moduli_line(context, "builtin", 1, buf,  &m[0]);
2189     if (ret) {
2190           _krb5_free_moduli(m);
2191           return ret;
2192     }
2193     n++;
2194 
2195     strlcpy(buf, default_moduli_RFC2412_MODP_group2, sizeof(buf));
2196     ret = _krb5_parse_moduli_line(context, "builtin", 1, buf,  &m[1]);
2197     if (ret) {
2198           _krb5_free_moduli(m);
2199           return ret;
2200     }
2201     n++;
2202 
2203 
2204     if (file == NULL)
2205           file = MODULI_FILE;
2206 
2207 #ifdef KRB5_USE_PATH_TOKENS
2208     {
2209         char * exp_file;
2210 
2211           if (_krb5_expand_path_tokens(context, file, 1, &exp_file) == 0) {
2212             f = fopen(exp_file, "r");
2213             krb5_xfree(exp_file);
2214         } else {
2215             f = NULL;
2216         }
2217     }
2218 #else
2219     f = fopen(file, "r");
2220 #endif
2221 
2222     if (f == NULL) {
2223           *moduli = m;
2224           return 0;
2225     }
2226     rk_cloexec_file(f);
2227 
2228     while(fgets(buf, sizeof(buf), f) != NULL) {
2229           struct krb5_dh_moduli *element;
2230 
2231           buf[strcspn(buf, "\n")] = '\0';
2232           lineno++;
2233 
2234           m2 = realloc(m, (n + 2) * sizeof(m[0]));
2235           if (m2 == NULL) {
2236               _krb5_free_moduli(m);
2237               return krb5_enomem(context);
2238           }
2239           m = m2;
2240 
2241           m[n] = NULL;
2242 
2243           ret = _krb5_parse_moduli_line(context, file, lineno, buf,  &element);
2244           if (ret) {
2245               _krb5_free_moduli(m);
2246               return ret;
2247           }
2248           if (element == NULL)
2249               continue;
2250 
2251           m[n] = element;
2252           m[n + 1] = NULL;
2253           n++;
2254     }
2255     *moduli = m;
2256     return 0;
2257 }
2258 
2259 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
_krb5_dh_group_ok(krb5_context context,unsigned long bits,heim_integer * p,heim_integer * g,heim_integer * q,struct krb5_dh_moduli ** moduli,char ** name)2260 _krb5_dh_group_ok(krb5_context context, unsigned long bits,
2261                       heim_integer *p, heim_integer *g, heim_integer *q,
2262                       struct krb5_dh_moduli **moduli,
2263                       char **name)
2264 {
2265     int i;
2266 
2267     if (name)
2268           *name = NULL;
2269 
2270     for (i = 0; moduli[i] != NULL; i++) {
2271           if (der_heim_integer_cmp(&moduli[i]->g, g) == 0 &&
2272               der_heim_integer_cmp(&moduli[i]->p, p) == 0 &&
2273               (q == NULL || der_heim_integer_cmp(&moduli[i]->q, q) == 0))
2274               {
2275                     if (bits && bits > moduli[i]->bits) {
2276                         krb5_set_error_message(context,
2277                                                      KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED,
2278                                                      N_("PKINIT: DH group parameter %s "
2279                                                         "no accepted, not enough bits "
2280                                                         "generated", ""),
2281                                                      moduli[i]->name);
2282                         return KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED;
2283                     }
2284                     if (name)
2285                         *name = strdup(moduli[i]->name);
2286                     return 0;
2287               }
2288     }
2289     krb5_set_error_message(context,
2290                                  KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED,
2291                                  N_("PKINIT: DH group parameter no ok", ""));
2292     return KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED;
2293 }
2294 #endif /* PKINIT */
2295 
2296 KRB5_LIB_FUNCTION void KRB5_LIB_CALL
_krb5_get_init_creds_opt_free_pkinit(krb5_get_init_creds_opt * opt)2297 _krb5_get_init_creds_opt_free_pkinit(krb5_get_init_creds_opt *opt)
2298 {
2299 #ifdef PKINIT
2300     krb5_pk_init_ctx ctx;
2301 
2302     if (opt->opt_private == NULL || opt->opt_private->pk_init_ctx == NULL)
2303           return;
2304     ctx = opt->opt_private->pk_init_ctx;
2305     switch (ctx->keyex) {
2306     case USE_DH:
2307           if (ctx->u.dh)
2308               DH_free(ctx->u.dh);
2309           break;
2310     case USE_RSA:
2311           break;
2312     case USE_ECDH:
2313           if (ctx->u.eckey)
2314             _krb5_pk_eckey_free(ctx->u.eckey);
2315           break;
2316     }
2317     if (ctx->id) {
2318           hx509_verify_destroy_ctx(ctx->id->verify_ctx);
2319           hx509_certs_free(&ctx->id->certs);
2320           hx509_cert_free(ctx->id->cert);
2321           hx509_certs_free(&ctx->id->anchors);
2322           hx509_certs_free(&ctx->id->certpool);
2323 
2324           if (ctx->clientDHNonce) {
2325               krb5_free_data(NULL, ctx->clientDHNonce);
2326               ctx->clientDHNonce = NULL;
2327           }
2328           if (ctx->m)
2329               _krb5_free_moduli(ctx->m);
2330           free(ctx->id);
2331           ctx->id = NULL;
2332     }
2333     free(opt->opt_private->pk_init_ctx);
2334     opt->opt_private->pk_init_ctx = NULL;
2335 #endif
2336 }
2337 
2338 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_get_init_creds_opt_set_pkinit(krb5_context context,krb5_get_init_creds_opt * opt,krb5_principal principal,const char * user_id,const char * x509_anchors,char * const * pool,char * const * pki_revoke,int flags,krb5_prompter_fct prompter,void * prompter_data,char * password)2339 krb5_get_init_creds_opt_set_pkinit(krb5_context context,
2340                                            krb5_get_init_creds_opt *opt,
2341                                            krb5_principal principal,
2342                                            const char *user_id,
2343                                            const char *x509_anchors,
2344                                            char * const * pool,
2345                                            char * const * pki_revoke,
2346                                            int flags,
2347                                            krb5_prompter_fct prompter,
2348                                            void *prompter_data,
2349                                            char *password)
2350 {
2351 #ifdef PKINIT
2352     krb5_error_code ret;
2353     char *anchors = NULL;
2354 
2355     if (opt->opt_private == NULL) {
2356           krb5_set_error_message(context, EINVAL,
2357                                      N_("PKINIT: on non extendable opt", ""));
2358           return EINVAL;
2359     }
2360 
2361     opt->opt_private->pk_init_ctx =
2362           calloc(1, sizeof(*opt->opt_private->pk_init_ctx));
2363     if (opt->opt_private->pk_init_ctx == NULL)
2364           return krb5_enomem(context);
2365     opt->opt_private->pk_init_ctx->require_binding = 0;
2366     opt->opt_private->pk_init_ctx->require_eku = 1;
2367     opt->opt_private->pk_init_ctx->require_krbtgt_otherName = 1;
2368     opt->opt_private->pk_init_ctx->peer = NULL;
2369 
2370     /* XXX implement krb5_appdefault_strings  */
2371     if (pool == NULL)
2372           pool = krb5_config_get_strings(context, NULL,
2373                                                "appdefaults",
2374                                                "pkinit_pool",
2375                                                NULL);
2376 
2377     if (pki_revoke == NULL)
2378           pki_revoke = krb5_config_get_strings(context, NULL,
2379                                                        "appdefaults",
2380                                                        "pkinit_revoke",
2381                                                        NULL);
2382 
2383     if (x509_anchors == NULL) {
2384           krb5_appdefault_string(context, "kinit",
2385                                      krb5_principal_get_realm(context, principal),
2386                                      "pkinit_anchors", NULL, &anchors);
2387           x509_anchors = anchors;
2388     }
2389 
2390     if (flags & KRB5_GIC_OPT_PKINIT_ANONYMOUS)
2391           opt->opt_private->pk_init_ctx->anonymous = 1;
2392 
2393     ret = _krb5_pk_load_id(context,
2394                                  &opt->opt_private->pk_init_ctx->id,
2395                                  user_id,
2396                                  x509_anchors,
2397                                  pool,
2398                                  pki_revoke,
2399                                  prompter,
2400                                  prompter_data,
2401                                  password);
2402     if (ret) {
2403           free(opt->opt_private->pk_init_ctx);
2404           opt->opt_private->pk_init_ctx = NULL;
2405           return ret;
2406     }
2407     if (flags & KRB5_GIC_OPT_PKINIT_BTMM)
2408           opt->opt_private->pk_init_ctx->id->flags |= PKINIT_BTMM;
2409 
2410     if (principal && krb5_principal_is_lkdc(context, principal))
2411           opt->opt_private->pk_init_ctx->id->flags |= PKINIT_BTMM;
2412 
2413     if (opt->opt_private->pk_init_ctx->id->certs) {
2414           _krb5_pk_set_user_id(context,
2415                                    principal,
2416                                    opt->opt_private->pk_init_ctx,
2417                                    opt->opt_private->pk_init_ctx->id->certs);
2418     } else
2419           opt->opt_private->pk_init_ctx->id->cert = NULL;
2420 
2421     if ((flags & KRB5_GIC_OPT_PKINIT_USE_ENCKEY) == 0) {
2422           hx509_context hx509ctx = context->hx509ctx;
2423           hx509_cert cert = opt->opt_private->pk_init_ctx->id->cert;
2424 
2425           opt->opt_private->pk_init_ctx->keyex = USE_DH;
2426 
2427           /*
2428            * If its a ECDSA certs, lets select ECDSA as the keyex algorithm.
2429            */
2430           if (cert) {
2431               AlgorithmIdentifier alg;
2432 
2433               ret = hx509_cert_get_SPKI_AlgorithmIdentifier(hx509ctx, cert, &alg);
2434               if (ret == 0) {
2435                     if (der_heim_oid_cmp(&alg.algorithm, &asn1_oid_id_ecPublicKey) == 0)
2436                         opt->opt_private->pk_init_ctx->keyex = USE_ECDH;
2437                     free_AlgorithmIdentifier(&alg);
2438               }
2439           }
2440 
2441     } else {
2442           opt->opt_private->pk_init_ctx->keyex = USE_RSA;
2443 
2444           if (opt->opt_private->pk_init_ctx->id->certs == NULL) {
2445               krb5_set_error_message(context, EINVAL,
2446                                            N_("No anonymous pkinit support in RSA mode", ""));
2447               return EINVAL;
2448           }
2449     }
2450 
2451     return 0;
2452 #else
2453     krb5_set_error_message(context, EINVAL,
2454                                  N_("no support for PKINIT compiled in", ""));
2455     return EINVAL;
2456 #endif
2457 }
2458 
2459 krb5_error_code KRB5_LIB_FUNCTION
krb5_get_init_creds_opt_set_pkinit_user_certs(krb5_context context,krb5_get_init_creds_opt * opt,struct hx509_certs_data * certs)2460 krb5_get_init_creds_opt_set_pkinit_user_certs(krb5_context context,
2461                                                         krb5_get_init_creds_opt *opt,
2462                                                         struct hx509_certs_data *certs)
2463 {
2464 #ifdef PKINIT
2465     if (opt->opt_private == NULL) {
2466           krb5_set_error_message(context, EINVAL,
2467                                      N_("PKINIT: on non extendable opt", ""));
2468           return EINVAL;
2469     }
2470     if (opt->opt_private->pk_init_ctx == NULL) {
2471           krb5_set_error_message(context, EINVAL,
2472                                      N_("PKINIT: on pkinit context", ""));
2473           return EINVAL;
2474     }
2475 
2476     _krb5_pk_set_user_id(context, NULL, opt->opt_private->pk_init_ctx, certs);
2477 
2478     return 0;
2479 #else
2480     krb5_set_error_message(context, EINVAL,
2481                                  N_("no support for PKINIT compiled in", ""));
2482     return EINVAL;
2483 #endif
2484 }
2485 
2486 #ifdef PKINIT
2487 
2488 static int
get_ms_san(hx509_context context,hx509_cert cert,char ** upn)2489 get_ms_san(hx509_context context, hx509_cert cert, char **upn)
2490 {
2491     hx509_octet_string_list list;
2492     int ret;
2493 
2494     *upn = NULL;
2495 
2496     ret = hx509_cert_find_subjectAltName_otherName(context,
2497                                                                cert,
2498                                                                &asn1_oid_id_pkinit_ms_san,
2499                                                                &list);
2500     if (ret)
2501           return 0;
2502 
2503     if (list.len > 0 && list.val[0].length > 0)
2504           ret = decode_MS_UPN_SAN(list.val[0].data, list.val[0].length,
2505                                         upn, NULL);
2506     else
2507           ret = 1;
2508     hx509_free_octet_string_list(&list);
2509 
2510     return ret;
2511 }
2512 
2513 static int
find_ms_san(hx509_context context,hx509_cert cert,void * ctx)2514 find_ms_san(hx509_context context, hx509_cert cert, void *ctx)
2515 {
2516     char *upn;
2517     int ret;
2518 
2519     ret = get_ms_san(context, cert, &upn);
2520     if (ret == 0)
2521           free(upn);
2522     return ret;
2523 }
2524 
2525 
2526 
2527 #endif
2528 
2529 /*
2530  * Private since it need to be redesigned using krb5_get_init_creds()
2531  */
2532 
2533 KRB5_LIB_FUNCTION krb5_error_code  KRB5_LIB_CALL
krb5_pk_enterprise_cert(krb5_context context,const char * user_id,krb5_const_realm realm,krb5_principal * principal,struct hx509_certs_data ** res)2534 krb5_pk_enterprise_cert(krb5_context context,
2535                               const char *user_id,
2536                               krb5_const_realm realm,
2537                               krb5_principal *principal,
2538                               struct hx509_certs_data **res)
2539 {
2540 #ifdef PKINIT
2541     krb5_error_code ret;
2542     hx509_certs certs, result;
2543     hx509_cert cert = NULL;
2544     hx509_query *q;
2545     char *name;
2546 
2547     *principal = NULL;
2548     if (res)
2549           *res = NULL;
2550 
2551     if (user_id == NULL) {
2552           krb5_set_error_message(context, ENOENT, "no user id");
2553           return ENOENT;
2554     }
2555 
2556     ret = hx509_certs_init(context->hx509ctx, user_id, 0, NULL, &certs);
2557     if (ret) {
2558           pk_copy_error(context, context->hx509ctx, ret,
2559                           "Failed to init cert certs");
2560           goto out;
2561     }
2562 
2563     ret = hx509_query_alloc(context->hx509ctx, &q);
2564     if (ret) {
2565           krb5_set_error_message(context, ret, "out of memory");
2566           hx509_certs_free(&certs);
2567           goto out;
2568     }
2569 
2570     hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY);
2571     hx509_query_match_option(q, HX509_QUERY_OPTION_KU_DIGITALSIGNATURE);
2572     hx509_query_match_eku(q, &asn1_oid_id_pkinit_ms_eku);
2573     hx509_query_match_cmp_func(q, find_ms_san, NULL);
2574 
2575     ret = hx509_certs_filter(context->hx509ctx, certs, q, &result);
2576     hx509_query_free(context->hx509ctx, q);
2577     hx509_certs_free(&certs);
2578     if (ret) {
2579           pk_copy_error(context, context->hx509ctx, ret,
2580                           "Failed to find PKINIT certificate");
2581           return ret;
2582     }
2583 
2584     ret = hx509_get_one_cert(context->hx509ctx, result, &cert);
2585     hx509_certs_free(&result);
2586     if (ret) {
2587           pk_copy_error(context, context->hx509ctx, ret,
2588                           "Failed to get one cert");
2589           goto out;
2590     }
2591 
2592     ret = get_ms_san(context->hx509ctx, cert, &name);
2593     if (ret) {
2594           pk_copy_error(context, context->hx509ctx, ret,
2595                           "Failed to get MS SAN");
2596           goto out;
2597     }
2598 
2599     ret = krb5_make_principal(context, principal, realm, name, NULL);
2600     free(name);
2601     if (ret)
2602           goto out;
2603 
2604     krb5_principal_set_type(context, *principal, KRB5_NT_ENTERPRISE_PRINCIPAL);
2605 
2606     if (res) {
2607           ret = hx509_certs_init(context->hx509ctx, "MEMORY:", 0, NULL, res);
2608           if (ret)
2609               goto out;
2610 
2611           ret = hx509_certs_add(context->hx509ctx, *res, cert);
2612           if (ret) {
2613               hx509_certs_free(res);
2614               goto out;
2615           }
2616     }
2617 
2618  out:
2619     hx509_cert_free(cert);
2620 
2621     return ret;
2622 #else
2623     krb5_set_error_message(context, EINVAL,
2624                                  N_("no support for PKINIT compiled in", ""));
2625     return EINVAL;
2626 #endif
2627 }
2628