xref: /netbsd-src/crypto/external/bsd/heimdal/dist/lib/krb5/pkinit.c (revision afab4e300d3a9fb07dd8c80daf53d0feb3345706)
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