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