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