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