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 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)1310 pk_rd_pa_reply_dh(krb5_context context,
1311 const heim_octet_string *indata,
1312 const heim_oid *dataType,
1313 const char *realm,
1314 krb5_pk_init_ctx ctx,
1315 krb5_enctype etype,
1316 const krb5_krbhst_info *hi,
1317 const DHNonce *c_n,
1318 const DHNonce *k_n,
1319 unsigned nonce,
1320 PA_DATA *pa,
1321 krb5_keyblock **key)
1322 {
1323 const unsigned char *p;
1324 unsigned char *dh_gen_key = NULL;
1325 struct krb5_pk_cert *host = NULL;
1326 BIGNUM *kdc_dh_pubkey = NULL;
1327 KDCDHKeyInfo kdc_dh_info;
1328 heim_oid contentType = { 0, NULL };
1329 krb5_data content;
1330 krb5_error_code ret;
1331 int dh_gen_keylen = 0;
1332 size_t size;
1333
1334 krb5_data_zero(&content);
1335 memset(&kdc_dh_info, 0, sizeof(kdc_dh_info));
1336
1337 if (der_heim_oid_cmp(&asn1_oid_id_pkcs7_signedData, dataType)) {
1338 krb5_set_error_message(context, EINVAL,
1339 N_("PKINIT: Invalid content type", ""));
1340 return EINVAL;
1341 }
1342
1343 ret = pk_verify_sign(context,
1344 indata->data,
1345 indata->length,
1346 ctx->id,
1347 &contentType,
1348 &content,
1349 &host);
1350 if (ret)
1351 goto out;
1352
1353 /* make sure that it is the kdc's certificate */
1354 ret = pk_verify_host(context, realm, hi, ctx, host);
1355 if (ret)
1356 goto out;
1357
1358 if (der_heim_oid_cmp(&contentType, &asn1_oid_id_pkdhkeydata)) {
1359 ret = KRB5KRB_AP_ERR_MSG_TYPE;
1360 krb5_set_error_message(context, ret,
1361 N_("pkinit - dh reply contains wrong oid", ""));
1362 goto out;
1363 }
1364
1365 ret = decode_KDCDHKeyInfo(content.data,
1366 content.length,
1367 &kdc_dh_info,
1368 &size);
1369
1370 if (ret) {
1371 krb5_set_error_message(context, ret,
1372 N_("pkinit - failed to decode "
1373 "KDC DH Key Info", ""));
1374 goto out;
1375 }
1376
1377 if (kdc_dh_info.nonce != nonce) {
1378 ret = KRB5KRB_AP_ERR_MODIFIED;
1379 krb5_set_error_message(context, ret,
1380 N_("PKINIT: DH nonce is wrong", ""));
1381 goto out;
1382 }
1383
1384 if (kdc_dh_info.dhKeyExpiration) {
1385 if (k_n == NULL) {
1386 ret = KRB5KRB_ERR_GENERIC;
1387 krb5_set_error_message(context, ret,
1388 N_("pkinit; got key expiration "
1389 "without server nonce", ""));
1390 goto out;
1391 }
1392 if (c_n == NULL) {
1393 ret = KRB5KRB_ERR_GENERIC;
1394 krb5_set_error_message(context, ret,
1395 N_("pkinit; got DH reuse but no "
1396 "client nonce", ""));
1397 goto out;
1398 }
1399 } else {
1400 if (k_n) {
1401 ret = KRB5KRB_ERR_GENERIC;
1402 krb5_set_error_message(context, ret,
1403 N_("pkinit: got server nonce "
1404 "without key expiration", ""));
1405 goto out;
1406 }
1407 c_n = NULL;
1408 }
1409
1410
1411 p = kdc_dh_info.subjectPublicKey.data;
1412 size = (kdc_dh_info.subjectPublicKey.length + 7) / 8;
1413
1414 if (ctx->keyex == USE_DH) {
1415 DHPublicKey k;
1416 ret = decode_DHPublicKey(p, size, &k, NULL);
1417 if (ret) {
1418 krb5_set_error_message(context, ret,
1419 N_("pkinit: can't decode "
1420 "without key expiration", ""));
1421 goto out;
1422 }
1423
1424 kdc_dh_pubkey = integer_to_BN(context, "DHPublicKey", &k);
1425 free_DHPublicKey(&k);
1426 if (kdc_dh_pubkey == NULL) {
1427 ret = ENOMEM;
1428 goto out;
1429 }
1430
1431
1432 size = DH_size(ctx->u.dh);
1433
1434 dh_gen_key = malloc(size);
1435 if (dh_gen_key == NULL) {
1436 ret = ENOMEM;
1437 krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
1438 goto out;
1439 }
1440
1441 dh_gen_keylen = DH_compute_key(dh_gen_key, kdc_dh_pubkey, ctx->u.dh);
1442 if (dh_gen_keylen == -1) {
1443 ret = KRB5KRB_ERR_GENERIC;
1444 dh_gen_keylen = 0;
1445 krb5_set_error_message(context, ret,
1446 N_("PKINIT: Can't compute Diffie-Hellman key", ""));
1447 goto out;
1448 }
1449 if (dh_gen_keylen < (int)size) {
1450 size -= dh_gen_keylen;
1451 memmove(dh_gen_key + size, dh_gen_key, dh_gen_keylen);
1452 memset(dh_gen_key, 0, size);
1453 }
1454
1455 } else {
1456 #ifdef HAVE_OPENSSL
1457 const EC_GROUP *group;
1458 EC_KEY *public = NULL;
1459
1460 group = EC_KEY_get0_group(ctx->u.eckey);
1461
1462 public = EC_KEY_new();
1463 if (public == NULL) {
1464 ret = ENOMEM;
1465 goto out;
1466 }
1467 if (EC_KEY_set_group(public, group) != 1) {
1468 EC_KEY_free(public);
1469 ret = ENOMEM;
1470 goto out;
1471 }
1472
1473 if (o2i_ECPublicKey(&public, &p, size) == NULL) {
1474 EC_KEY_free(public);
1475 ret = KRB5KRB_ERR_GENERIC;
1476 krb5_set_error_message(context, ret,
1477 N_("PKINIT: Can't parse ECDH public key", ""));
1478 goto out;
1479 }
1480
1481 size = (EC_GROUP_get_degree(group) + 7) / 8;
1482 dh_gen_key = malloc(size);
1483 if (dh_gen_key == NULL) {
1484 EC_KEY_free(public);
1485 ret = ENOMEM;
1486 krb5_set_error_message(context, ret,
1487 N_("malloc: out of memory", ""));
1488 goto out;
1489 }
1490 dh_gen_keylen = ECDH_compute_key(dh_gen_key, size,
1491 EC_KEY_get0_public_key(public), ctx->u.eckey, NULL);
1492 EC_KEY_free(public);
1493 if (dh_gen_keylen == -1) {
1494 ret = KRB5KRB_ERR_GENERIC;
1495 dh_gen_keylen = 0;
1496 krb5_set_error_message(context, ret,
1497 N_("PKINIT: Can't compute ECDH public key", ""));
1498 goto out;
1499 }
1500 #else
1501 ret = EINVAL;
1502 #endif
1503 }
1504
1505 if (dh_gen_keylen <= 0) {
1506 ret = EINVAL;
1507 krb5_set_error_message(context, ret,
1508 N_("PKINIT: resulting DH key <= 0", ""));
1509 dh_gen_keylen = 0;
1510 goto out;
1511 }
1512
1513 *key = malloc (sizeof (**key));
1514 if (*key == NULL) {
1515 ret = ENOMEM;
1516 krb5_set_error_message(context, ret,
1517 N_("malloc: out of memory", ""));
1518 goto out;
1519 }
1520
1521 ret = _krb5_pk_octetstring2key(context,
1522 etype,
1523 dh_gen_key, dh_gen_keylen,
1524 c_n, k_n,
1525 *key);
1526 if (ret) {
1527 krb5_set_error_message(context, ret,
1528 N_("PKINIT: can't create key from DH key", ""));
1529 free(*key);
1530 *key = NULL;
1531 goto out;
1532 }
1533
1534 out:
1535 if (kdc_dh_pubkey)
1536 BN_free(kdc_dh_pubkey);
1537 if (dh_gen_key) {
1538 memset(dh_gen_key, 0, dh_gen_keylen);
1539 free(dh_gen_key);
1540 }
1541 if (host)
1542 _krb5_pk_cert_free(host);
1543 if (content.data)
1544 krb5_data_free(&content);
1545 der_free_oid(&contentType);
1546 free_KDCDHKeyInfo(&kdc_dh_info);
1547
1548 return ret;
1549 }
1550
1551 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)1552 _krb5_pk_rd_pa_reply(krb5_context context,
1553 const char *realm,
1554 void *c,
1555 krb5_enctype etype,
1556 const krb5_krbhst_info *hi,
1557 unsigned nonce,
1558 const krb5_data *req_buffer,
1559 PA_DATA *pa,
1560 krb5_keyblock **key)
1561 {
1562 krb5_pk_init_ctx ctx = c;
1563 krb5_error_code ret;
1564 size_t size;
1565
1566 /* Check for IETF PK-INIT first */
1567 if (ctx->type == PKINIT_27) {
1568 PA_PK_AS_REP rep;
1569 heim_octet_string os, data;
1570 heim_oid oid;
1571
1572 if (pa->padata_type != KRB5_PADATA_PK_AS_REP) {
1573 krb5_set_error_message(context, EINVAL,
1574 N_("PKINIT: wrong padata recv", ""));
1575 return EINVAL;
1576 }
1577
1578 ret = decode_PA_PK_AS_REP(pa->padata_value.data,
1579 pa->padata_value.length,
1580 &rep,
1581 &size);
1582 if (ret) {
1583 krb5_set_error_message(context, ret,
1584 N_("Failed to decode pkinit AS rep", ""));
1585 return ret;
1586 }
1587
1588 switch (rep.element) {
1589 case choice_PA_PK_AS_REP_dhInfo:
1590 _krb5_debug(context, 5, "krb5_get_init_creds: using pkinit dh");
1591 os = rep.u.dhInfo.dhSignedData;
1592 break;
1593 case choice_PA_PK_AS_REP_encKeyPack:
1594 _krb5_debug(context, 5, "krb5_get_init_creds: using kinit enc reply key");
1595 os = rep.u.encKeyPack;
1596 break;
1597 default: {
1598 PA_PK_AS_REP_BTMM btmm;
1599 free_PA_PK_AS_REP(&rep);
1600 memset(&rep, 0, sizeof(rep));
1601
1602 _krb5_debug(context, 5, "krb5_get_init_creds: using BTMM kinit enc reply key");
1603
1604 ret = decode_PA_PK_AS_REP_BTMM(pa->padata_value.data,
1605 pa->padata_value.length,
1606 &btmm,
1607 &size);
1608 if (ret) {
1609 krb5_set_error_message(context, EINVAL,
1610 N_("PKINIT: -27 reply "
1611 "invalid content type", ""));
1612 return EINVAL;
1613 }
1614
1615 if (btmm.dhSignedData || btmm.encKeyPack == NULL) {
1616 free_PA_PK_AS_REP_BTMM(&btmm);
1617 ret = EINVAL;
1618 krb5_set_error_message(context, ret,
1619 N_("DH mode not supported for BTMM mode", ""));
1620 return ret;
1621 }
1622
1623 /*
1624 * Transform to IETF style PK-INIT reply so that free works below
1625 */
1626
1627 rep.element = choice_PA_PK_AS_REP_encKeyPack;
1628 rep.u.encKeyPack.data = btmm.encKeyPack->data;
1629 rep.u.encKeyPack.length = btmm.encKeyPack->length;
1630 btmm.encKeyPack->data = NULL;
1631 btmm.encKeyPack->length = 0;
1632 free_PA_PK_AS_REP_BTMM(&btmm);
1633 os = rep.u.encKeyPack;
1634 }
1635 }
1636
1637 ret = hx509_cms_unwrap_ContentInfo(&os, &oid, &data, NULL);
1638 if (ret) {
1639 free_PA_PK_AS_REP(&rep);
1640 krb5_set_error_message(context, ret,
1641 N_("PKINIT: failed to unwrap CI", ""));
1642 return ret;
1643 }
1644
1645 switch (rep.element) {
1646 case choice_PA_PK_AS_REP_dhInfo:
1647 ret = pk_rd_pa_reply_dh(context, &data, &oid, realm, ctx, etype, hi,
1648 ctx->clientDHNonce,
1649 rep.u.dhInfo.serverDHNonce,
1650 nonce, pa, key);
1651 break;
1652 case choice_PA_PK_AS_REP_encKeyPack:
1653 ret = pk_rd_pa_reply_enckey(context, PKINIT_27, &data, &oid, realm,
1654 ctx, etype, hi, nonce, req_buffer, pa, key);
1655 break;
1656 default:
1657 krb5_abortx(context, "pk-init as-rep case not possible to happen");
1658 }
1659 der_free_octet_string(&data);
1660 der_free_oid(&oid);
1661 free_PA_PK_AS_REP(&rep);
1662
1663 } else if (ctx->type == PKINIT_WIN2K) {
1664 PA_PK_AS_REP_Win2k w2krep;
1665
1666 /* Check for Windows encoding of the AS-REP pa data */
1667
1668 #if 0 /* should this be ? */
1669 if (pa->padata_type != KRB5_PADATA_PK_AS_REP) {
1670 krb5_set_error_message(context, EINVAL,
1671 "PKINIT: wrong padata recv");
1672 return EINVAL;
1673 }
1674 #endif
1675
1676 memset(&w2krep, 0, sizeof(w2krep));
1677
1678 ret = decode_PA_PK_AS_REP_Win2k(pa->padata_value.data,
1679 pa->padata_value.length,
1680 &w2krep,
1681 &size);
1682 if (ret) {
1683 krb5_set_error_message(context, ret,
1684 N_("PKINIT: Failed decoding windows "
1685 "pkinit reply %d", ""), (int)ret);
1686 return ret;
1687 }
1688
1689 krb5_clear_error_message(context);
1690
1691 switch (w2krep.element) {
1692 case choice_PA_PK_AS_REP_Win2k_encKeyPack: {
1693 heim_octet_string data;
1694 heim_oid oid;
1695
1696 ret = hx509_cms_unwrap_ContentInfo(&w2krep.u.encKeyPack,
1697 &oid, &data, NULL);
1698 free_PA_PK_AS_REP_Win2k(&w2krep);
1699 if (ret) {
1700 krb5_set_error_message(context, ret,
1701 N_("PKINIT: failed to unwrap CI", ""));
1702 return ret;
1703 }
1704
1705 ret = pk_rd_pa_reply_enckey(context, PKINIT_WIN2K, &data, &oid, realm,
1706 ctx, etype, hi, nonce, req_buffer, pa, key);
1707 der_free_octet_string(&data);
1708 der_free_oid(&oid);
1709
1710 break;
1711 }
1712 default:
1713 free_PA_PK_AS_REP_Win2k(&w2krep);
1714 ret = EINVAL;
1715 krb5_set_error_message(context, ret,
1716 N_("PKINIT: win2k reply invalid "
1717 "content type", ""));
1718 break;
1719 }
1720
1721 } else {
1722 ret = EINVAL;
1723 krb5_set_error_message(context, ret,
1724 N_("PKINIT: unknown reply type", ""));
1725 }
1726
1727 return ret;
1728 }
1729
1730 struct prompter {
1731 krb5_context context;
1732 krb5_prompter_fct prompter;
1733 void *prompter_data;
1734 };
1735
1736 static int
hx_pass_prompter(void * data,const hx509_prompt * prompter)1737 hx_pass_prompter(void *data, const hx509_prompt *prompter)
1738 {
1739 krb5_error_code ret;
1740 krb5_prompt prompt;
1741 krb5_data password_data;
1742 struct prompter *p = data;
1743
1744 password_data.data = prompter->reply.data;
1745 password_data.length = prompter->reply.length;
1746
1747 prompt.prompt = prompter->prompt;
1748 prompt.hidden = hx509_prompt_hidden(prompter->type);
1749 prompt.reply = &password_data;
1750
1751 switch (prompter->type) {
1752 case HX509_PROMPT_TYPE_INFO:
1753 prompt.type = KRB5_PROMPT_TYPE_INFO;
1754 break;
1755 case HX509_PROMPT_TYPE_PASSWORD:
1756 case HX509_PROMPT_TYPE_QUESTION:
1757 default:
1758 prompt.type = KRB5_PROMPT_TYPE_PASSWORD;
1759 break;
1760 }
1761
1762 ret = (*p->prompter)(p->context, p->prompter_data, NULL, NULL, 1, &prompt);
1763 if (ret) {
1764 memset (prompter->reply.data, 0, prompter->reply.length);
1765 return 1;
1766 }
1767 return 0;
1768 }
1769
1770 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)1771 _krb5_pk_set_user_id(krb5_context context,
1772 krb5_principal principal,
1773 krb5_pk_init_ctx ctx,
1774 struct hx509_certs_data *certs)
1775 {
1776 hx509_certs c = hx509_certs_ref(certs);
1777 hx509_query *q = NULL;
1778 int ret;
1779
1780 if (ctx->id->certs)
1781 hx509_certs_free(&ctx->id->certs);
1782 if (ctx->id->cert) {
1783 hx509_cert_free(ctx->id->cert);
1784 ctx->id->cert = NULL;
1785 }
1786
1787 ctx->id->certs = c;
1788 ctx->anonymous = 0;
1789
1790 ret = hx509_query_alloc(context->hx509ctx, &q);
1791 if (ret) {
1792 pk_copy_error(context, context->hx509ctx, ret,
1793 "Allocate query to find signing certificate");
1794 return ret;
1795 }
1796
1797 hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY);
1798 hx509_query_match_option(q, HX509_QUERY_OPTION_KU_DIGITALSIGNATURE);
1799
1800 if (principal && strncmp("LKDC:SHA1.", krb5_principal_get_realm(context, principal), 9) == 0) {
1801 ctx->id->flags |= PKINIT_BTMM;
1802 }
1803
1804 ret = find_cert(context, ctx->id, q, &ctx->id->cert);
1805 hx509_query_free(context->hx509ctx, q);
1806
1807 if (ret == 0 && _krb5_have_debug(context, 2)) {
1808 hx509_name name;
1809 char *str, *sn;
1810 heim_integer i;
1811
1812 ret = hx509_cert_get_subject(ctx->id->cert, &name);
1813 if (ret)
1814 goto out;
1815
1816 ret = hx509_name_to_string(name, &str);
1817 hx509_name_free(&name);
1818 if (ret)
1819 goto out;
1820
1821 ret = hx509_cert_get_serialnumber(ctx->id->cert, &i);
1822 if (ret) {
1823 free(str);
1824 goto out;
1825 }
1826
1827 ret = der_print_hex_heim_integer(&i, &sn);
1828 der_free_heim_integer(&i);
1829 if (ret) {
1830 free(name);
1831 goto out;
1832 }
1833
1834 _krb5_debug(context, 2, "using cert: subject: %s sn: %s", str, sn);
1835 free(str);
1836 free(sn);
1837 }
1838 out:
1839
1840 return ret;
1841 }
1842
1843 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)1844 _krb5_pk_load_id(krb5_context context,
1845 struct krb5_pk_identity **ret_id,
1846 const char *user_id,
1847 const char *anchor_id,
1848 char * const *chain_list,
1849 char * const *revoke_list,
1850 krb5_prompter_fct prompter,
1851 void *prompter_data,
1852 char *password)
1853 {
1854 struct krb5_pk_identity *id = NULL;
1855 struct prompter p;
1856 int ret;
1857
1858 *ret_id = NULL;
1859
1860 if (anchor_id == NULL) {
1861 krb5_set_error_message(context, HEIM_PKINIT_NO_VALID_CA,
1862 N_("PKINIT: No anchor given", ""));
1863 return HEIM_PKINIT_NO_VALID_CA;
1864 }
1865
1866 /* load cert */
1867
1868 id = calloc(1, sizeof(*id));
1869 if (id == NULL) {
1870 krb5_set_error_message(context, ENOMEM,
1871 N_("malloc: out of memory", ""));
1872 return ENOMEM;
1873 }
1874
1875 if (user_id) {
1876 hx509_lock lock;
1877
1878 ret = hx509_lock_init(context->hx509ctx, &lock);
1879 if (ret) {
1880 pk_copy_error(context, context->hx509ctx, ret, "Failed init lock");
1881 goto out;
1882 }
1883
1884 if (password && password[0])
1885 hx509_lock_add_password(lock, password);
1886
1887 if (prompter) {
1888 p.context = context;
1889 p.prompter = prompter;
1890 p.prompter_data = prompter_data;
1891
1892 ret = hx509_lock_set_prompter(lock, hx_pass_prompter, &p);
1893 if (ret) {
1894 hx509_lock_free(lock);
1895 goto out;
1896 }
1897 }
1898
1899 ret = hx509_certs_init(context->hx509ctx, user_id, 0, lock, &id->certs);
1900 hx509_lock_free(lock);
1901 if (ret) {
1902 pk_copy_error(context, context->hx509ctx, ret,
1903 "Failed to init cert certs");
1904 goto out;
1905 }
1906 } else {
1907 id->certs = NULL;
1908 }
1909
1910 ret = hx509_certs_init(context->hx509ctx, anchor_id, 0, NULL, &id->anchors);
1911 if (ret) {
1912 pk_copy_error(context, context->hx509ctx, ret,
1913 "Failed to init anchors");
1914 goto out;
1915 }
1916
1917 ret = hx509_certs_init(context->hx509ctx, "MEMORY:pkinit-cert-chain",
1918 0, NULL, &id->certpool);
1919 if (ret) {
1920 pk_copy_error(context, context->hx509ctx, ret,
1921 "Failed to init chain");
1922 goto out;
1923 }
1924
1925 while (chain_list && *chain_list) {
1926 ret = hx509_certs_append(context->hx509ctx, id->certpool,
1927 NULL, *chain_list);
1928 if (ret) {
1929 pk_copy_error(context, context->hx509ctx, ret,
1930 "Failed to laod chain %s",
1931 *chain_list);
1932 goto out;
1933 }
1934 chain_list++;
1935 }
1936
1937 if (revoke_list) {
1938 ret = hx509_revoke_init(context->hx509ctx, &id->revokectx);
1939 if (ret) {
1940 pk_copy_error(context, context->hx509ctx, ret,
1941 "Failed init revoke list");
1942 goto out;
1943 }
1944
1945 while (*revoke_list) {
1946 ret = hx509_revoke_add_crl(context->hx509ctx,
1947 id->revokectx,
1948 *revoke_list);
1949 if (ret) {
1950 pk_copy_error(context, context->hx509ctx, ret,
1951 "Failed load revoke list");
1952 goto out;
1953 }
1954 revoke_list++;
1955 }
1956 } else
1957 hx509_context_set_missing_revoke(context->hx509ctx, 1);
1958
1959 ret = hx509_verify_init_ctx(context->hx509ctx, &id->verify_ctx);
1960 if (ret) {
1961 pk_copy_error(context, context->hx509ctx, ret,
1962 "Failed init verify context");
1963 goto out;
1964 }
1965
1966 hx509_verify_attach_anchors(id->verify_ctx, id->anchors);
1967 hx509_verify_attach_revoke(id->verify_ctx, id->revokectx);
1968
1969 out:
1970 if (ret) {
1971 hx509_verify_destroy_ctx(id->verify_ctx);
1972 hx509_certs_free(&id->certs);
1973 hx509_certs_free(&id->anchors);
1974 hx509_certs_free(&id->certpool);
1975 hx509_revoke_free(&id->revokectx);
1976 free(id);
1977 } else
1978 *ret_id = id;
1979
1980 return ret;
1981 }
1982
1983 /*
1984 *
1985 */
1986
1987 static void
pk_copy_error(krb5_context context,hx509_context hx509ctx,int hxret,const char * fmt,...)1988 pk_copy_error(krb5_context context,
1989 hx509_context hx509ctx,
1990 int hxret,
1991 const char *fmt,
1992 ...)
1993 {
1994 va_list va;
1995 char *s, *f;
1996 int ret;
1997
1998 va_start(va, fmt);
1999 ret = vasprintf(&f, fmt, va);
2000 va_end(va);
2001 if (ret == -1 || f == NULL) {
2002 krb5_clear_error_message(context);
2003 return;
2004 }
2005
2006 s = hx509_get_error_string(hx509ctx, hxret);
2007 if (s == NULL) {
2008 krb5_clear_error_message(context);
2009 free(f);
2010 return;
2011 }
2012 krb5_set_error_message(context, hxret, "%s: %s", f, s);
2013 free(s);
2014 free(f);
2015 }
2016
2017 static int
parse_integer(krb5_context context,char ** p,const char * file,int lineno,const char * name,heim_integer * integer)2018 parse_integer(krb5_context context, char **p, const char *file, int lineno,
2019 const char *name, heim_integer *integer)
2020 {
2021 int ret;
2022 char *p1;
2023 p1 = strsep(p, " \t");
2024 if (p1 == NULL) {
2025 krb5_set_error_message(context, EINVAL,
2026 N_("moduli file %s missing %s on line %d", ""),
2027 file, name, lineno);
2028 return EINVAL;
2029 }
2030 ret = der_parse_hex_heim_integer(p1, integer);
2031 if (ret) {
2032 krb5_set_error_message(context, ret,
2033 N_("moduli file %s failed parsing %s "
2034 "on line %d", ""),
2035 file, name, lineno);
2036 return ret;
2037 }
2038
2039 return 0;
2040 }
2041
2042 krb5_error_code
_krb5_parse_moduli_line(krb5_context context,const char * file,int lineno,char * p,struct krb5_dh_moduli ** m)2043 _krb5_parse_moduli_line(krb5_context context,
2044 const char *file,
2045 int lineno,
2046 char *p,
2047 struct krb5_dh_moduli **m)
2048 {
2049 struct krb5_dh_moduli *m1;
2050 char *p1;
2051 int ret;
2052
2053 *m = NULL;
2054
2055 m1 = calloc(1, sizeof(*m1));
2056 if (m1 == NULL) {
2057 krb5_set_error_message(context, ENOMEM,
2058 N_("malloc: out of memory", ""));
2059 return ENOMEM;
2060 }
2061
2062 while (isspace((unsigned char)*p))
2063 p++;
2064 if (*p == '#') {
2065 free(m1);
2066 return 0;
2067 }
2068 ret = EINVAL;
2069
2070 p1 = strsep(&p, " \t");
2071 if (p1 == NULL) {
2072 krb5_set_error_message(context, ret,
2073 N_("moduli file %s missing name on line %d", ""),
2074 file, lineno);
2075 goto out;
2076 }
2077 m1->name = strdup(p1);
2078 if (m1->name == NULL) {
2079 ret = ENOMEM;
2080 krb5_set_error_message(context, ret, N_("malloc: out of memeory", ""));
2081 goto out;
2082 }
2083
2084 p1 = strsep(&p, " \t");
2085 if (p1 == NULL) {
2086 krb5_set_error_message(context, ret,
2087 N_("moduli file %s missing bits on line %d", ""),
2088 file, lineno);
2089 goto out;
2090 }
2091
2092 m1->bits = atoi(p1);
2093 if (m1->bits == 0) {
2094 krb5_set_error_message(context, ret,
2095 N_("moduli file %s have un-parsable "
2096 "bits on line %d", ""), file, lineno);
2097 goto out;
2098 }
2099
2100 ret = parse_integer(context, &p, file, lineno, "p", &m1->p);
2101 if (ret)
2102 goto out;
2103 ret = parse_integer(context, &p, file, lineno, "g", &m1->g);
2104 if (ret)
2105 goto out;
2106 ret = parse_integer(context, &p, file, lineno, "q", &m1->q);
2107 if (ret)
2108 goto out;
2109
2110 *m = m1;
2111
2112 return 0;
2113 out:
2114 free(m1->name);
2115 der_free_heim_integer(&m1->p);
2116 der_free_heim_integer(&m1->g);
2117 der_free_heim_integer(&m1->q);
2118 free(m1);
2119 return ret;
2120 }
2121
2122 void
_krb5_free_moduli(struct krb5_dh_moduli ** moduli)2123 _krb5_free_moduli(struct krb5_dh_moduli **moduli)
2124 {
2125 int i;
2126 for (i = 0; moduli[i] != NULL; i++) {
2127 free(moduli[i]->name);
2128 der_free_heim_integer(&moduli[i]->p);
2129 der_free_heim_integer(&moduli[i]->g);
2130 der_free_heim_integer(&moduli[i]->q);
2131 free(moduli[i]);
2132 }
2133 free(moduli);
2134 }
2135
2136 static const char *default_moduli_RFC2412_MODP_group2 =
2137 /* name */
2138 "RFC2412-MODP-group2 "
2139 /* bits */
2140 "1024 "
2141 /* p */
2142 "FFFFFFFF" "FFFFFFFF" "C90FDAA2" "2168C234" "C4C6628B" "80DC1CD1"
2143 "29024E08" "8A67CC74" "020BBEA6" "3B139B22" "514A0879" "8E3404DD"
2144 "EF9519B3" "CD3A431B" "302B0A6D" "F25F1437" "4FE1356D" "6D51C245"
2145 "E485B576" "625E7EC6" "F44C42E9" "A637ED6B" "0BFF5CB6" "F406B7ED"
2146 "EE386BFB" "5A899FA5" "AE9F2411" "7C4B1FE6" "49286651" "ECE65381"
2147 "FFFFFFFF" "FFFFFFFF "
2148 /* g */
2149 "02 "
2150 /* q */
2151 "7FFFFFFF" "FFFFFFFF" "E487ED51" "10B4611A" "62633145" "C06E0E68"
2152 "94812704" "4533E63A" "0105DF53" "1D89CD91" "28A5043C" "C71A026E"
2153 "F7CA8CD9" "E69D218D" "98158536" "F92F8A1B" "A7F09AB6" "B6A8E122"
2154 "F242DABB" "312F3F63" "7A262174" "D31BF6B5" "85FFAE5B" "7A035BF6"
2155 "F71C35FD" "AD44CFD2" "D74F9208" "BE258FF3" "24943328" "F67329C0"
2156 "FFFFFFFF" "FFFFFFFF";
2157
2158 static const char *default_moduli_rfc3526_MODP_group14 =
2159 /* name */
2160 "rfc3526-MODP-group14 "
2161 /* bits */
2162 "1760 "
2163 /* p */
2164 "FFFFFFFF" "FFFFFFFF" "C90FDAA2" "2168C234" "C4C6628B" "80DC1CD1"
2165 "29024E08" "8A67CC74" "020BBEA6" "3B139B22" "514A0879" "8E3404DD"
2166 "EF9519B3" "CD3A431B" "302B0A6D" "F25F1437" "4FE1356D" "6D51C245"
2167 "E485B576" "625E7EC6" "F44C42E9" "A637ED6B" "0BFF5CB6" "F406B7ED"
2168 "EE386BFB" "5A899FA5" "AE9F2411" "7C4B1FE6" "49286651" "ECE45B3D"
2169 "C2007CB8" "A163BF05" "98DA4836" "1C55D39A" "69163FA8" "FD24CF5F"
2170 "83655D23" "DCA3AD96" "1C62F356" "208552BB" "9ED52907" "7096966D"
2171 "670C354E" "4ABC9804" "F1746C08" "CA18217C" "32905E46" "2E36CE3B"
2172 "E39E772C" "180E8603" "9B2783A2" "EC07A28F" "B5C55DF0" "6F4C52C9"
2173 "DE2BCBF6" "95581718" "3995497C" "EA956AE5" "15D22618" "98FA0510"
2174 "15728E5A" "8AACAA68" "FFFFFFFF" "FFFFFFFF "
2175 /* g */
2176 "02 "
2177 /* q */
2178 "7FFFFFFF" "FFFFFFFF" "E487ED51" "10B4611A" "62633145" "C06E0E68"
2179 "94812704" "4533E63A" "0105DF53" "1D89CD91" "28A5043C" "C71A026E"
2180 "F7CA8CD9" "E69D218D" "98158536" "F92F8A1B" "A7F09AB6" "B6A8E122"
2181 "F242DABB" "312F3F63" "7A262174" "D31BF6B5" "85FFAE5B" "7A035BF6"
2182 "F71C35FD" "AD44CFD2" "D74F9208" "BE258FF3" "24943328" "F6722D9E"
2183 "E1003E5C" "50B1DF82" "CC6D241B" "0E2AE9CD" "348B1FD4" "7E9267AF"
2184 "C1B2AE91" "EE51D6CB" "0E3179AB" "1042A95D" "CF6A9483" "B84B4B36"
2185 "B3861AA7" "255E4C02" "78BA3604" "650C10BE" "19482F23" "171B671D"
2186 "F1CF3B96" "0C074301" "CD93C1D1" "7603D147" "DAE2AEF8" "37A62964"
2187 "EF15E5FB" "4AAC0B8C" "1CCAA4BE" "754AB572" "8AE9130C" "4C7D0288"
2188 "0AB9472D" "45565534" "7FFFFFFF" "FFFFFFFF";
2189
2190 krb5_error_code
_krb5_parse_moduli(krb5_context context,const char * file,struct krb5_dh_moduli *** moduli)2191 _krb5_parse_moduli(krb5_context context, const char *file,
2192 struct krb5_dh_moduli ***moduli)
2193 {
2194 /* name bits P G Q */
2195 krb5_error_code ret;
2196 struct krb5_dh_moduli **m = NULL, **m2;
2197 char buf[4096];
2198 FILE *f;
2199 int lineno = 0, n = 0;
2200
2201 *moduli = NULL;
2202
2203 m = calloc(1, sizeof(m[0]) * 3);
2204 if (m == NULL) {
2205 krb5_set_error_message(context, ENOMEM,
2206 N_("malloc: out of memory", ""));
2207 return ENOMEM;
2208 }
2209
2210 strlcpy(buf, default_moduli_rfc3526_MODP_group14, sizeof(buf));
2211 ret = _krb5_parse_moduli_line(context, "builtin", 1, buf, &m[0]);
2212 if (ret) {
2213 _krb5_free_moduli(m);
2214 return ret;
2215 }
2216 n++;
2217
2218 strlcpy(buf, default_moduli_RFC2412_MODP_group2, sizeof(buf));
2219 ret = _krb5_parse_moduli_line(context, "builtin", 1, buf, &m[1]);
2220 if (ret) {
2221 _krb5_free_moduli(m);
2222 return ret;
2223 }
2224 n++;
2225
2226
2227 if (file == NULL)
2228 file = MODULI_FILE;
2229
2230 #ifdef KRB5_USE_PATH_TOKENS
2231 {
2232 char * exp_file;
2233
2234 if (_krb5_expand_path_tokens(context, file, &exp_file) == 0) {
2235 f = fopen(exp_file, "r");
2236 krb5_xfree(exp_file);
2237 } else {
2238 f = NULL;
2239 }
2240 }
2241 #else
2242 f = fopen(file, "r");
2243 #endif
2244
2245 if (f == NULL) {
2246 *moduli = m;
2247 return 0;
2248 }
2249 rk_cloexec_file(f);
2250
2251 while(fgets(buf, sizeof(buf), f) != NULL) {
2252 struct krb5_dh_moduli *element;
2253
2254 buf[strcspn(buf, "\n")] = '\0';
2255 lineno++;
2256
2257 m2 = realloc(m, (n + 2) * sizeof(m[0]));
2258 if (m2 == NULL) {
2259 _krb5_free_moduli(m);
2260 krb5_set_error_message(context, ENOMEM,
2261 N_("malloc: out of memory", ""));
2262 return ENOMEM;
2263 }
2264 m = m2;
2265
2266 m[n] = NULL;
2267
2268 ret = _krb5_parse_moduli_line(context, file, lineno, buf, &element);
2269 if (ret) {
2270 _krb5_free_moduli(m);
2271 return ret;
2272 }
2273 if (element == NULL)
2274 continue;
2275
2276 m[n] = element;
2277 m[n + 1] = NULL;
2278 n++;
2279 }
2280 *moduli = m;
2281 return 0;
2282 }
2283
2284 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)2285 _krb5_dh_group_ok(krb5_context context, unsigned long bits,
2286 heim_integer *p, heim_integer *g, heim_integer *q,
2287 struct krb5_dh_moduli **moduli,
2288 char **name)
2289 {
2290 int i;
2291
2292 if (name)
2293 *name = NULL;
2294
2295 for (i = 0; moduli[i] != NULL; i++) {
2296 if (der_heim_integer_cmp(&moduli[i]->g, g) == 0 &&
2297 der_heim_integer_cmp(&moduli[i]->p, p) == 0 &&
2298 (q == NULL || der_heim_integer_cmp(&moduli[i]->q, q) == 0))
2299 {
2300 if (bits && bits > moduli[i]->bits) {
2301 krb5_set_error_message(context,
2302 KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED,
2303 N_("PKINIT: DH group parameter %s "
2304 "no accepted, not enough bits "
2305 "generated", ""),
2306 moduli[i]->name);
2307 return KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED;
2308 }
2309 if (name)
2310 *name = strdup(moduli[i]->name);
2311 return 0;
2312 }
2313 }
2314 krb5_set_error_message(context,
2315 KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED,
2316 N_("PKINIT: DH group parameter no ok", ""));
2317 return KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED;
2318 }
2319 #endif /* PKINIT */
2320
2321 KRB5_LIB_FUNCTION void KRB5_LIB_CALL
_krb5_get_init_creds_opt_free_pkinit(krb5_get_init_creds_opt * opt)2322 _krb5_get_init_creds_opt_free_pkinit(krb5_get_init_creds_opt *opt)
2323 {
2324 #ifdef PKINIT
2325 krb5_pk_init_ctx ctx;
2326
2327 if (opt->opt_private == NULL || opt->opt_private->pk_init_ctx == NULL)
2328 return;
2329 ctx = opt->opt_private->pk_init_ctx;
2330 switch (ctx->keyex) {
2331 case USE_DH:
2332 if (ctx->u.dh)
2333 DH_free(ctx->u.dh);
2334 break;
2335 case USE_RSA:
2336 break;
2337 case USE_ECDH:
2338 #ifdef HAVE_OPENSSL
2339 if (ctx->u.eckey)
2340 EC_KEY_free(ctx->u.eckey);
2341 #endif
2342 break;
2343 }
2344 if (ctx->id) {
2345 hx509_verify_destroy_ctx(ctx->id->verify_ctx);
2346 hx509_certs_free(&ctx->id->certs);
2347 hx509_cert_free(ctx->id->cert);
2348 hx509_certs_free(&ctx->id->anchors);
2349 hx509_certs_free(&ctx->id->certpool);
2350
2351 if (ctx->clientDHNonce) {
2352 krb5_free_data(NULL, ctx->clientDHNonce);
2353 ctx->clientDHNonce = NULL;
2354 }
2355 if (ctx->m)
2356 _krb5_free_moduli(ctx->m);
2357 free(ctx->id);
2358 ctx->id = NULL;
2359 }
2360 free(opt->opt_private->pk_init_ctx);
2361 opt->opt_private->pk_init_ctx = NULL;
2362 #endif
2363 }
2364
2365 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)2366 krb5_get_init_creds_opt_set_pkinit(krb5_context context,
2367 krb5_get_init_creds_opt *opt,
2368 krb5_principal principal,
2369 const char *user_id,
2370 const char *x509_anchors,
2371 char * const * pool,
2372 char * const * pki_revoke,
2373 int flags,
2374 krb5_prompter_fct prompter,
2375 void *prompter_data,
2376 char *password)
2377 {
2378 #ifdef PKINIT
2379 krb5_error_code ret;
2380 char *anchors = NULL;
2381
2382 if (opt->opt_private == NULL) {
2383 krb5_set_error_message(context, EINVAL,
2384 N_("PKINIT: on non extendable opt", ""));
2385 return EINVAL;
2386 }
2387
2388 opt->opt_private->pk_init_ctx =
2389 calloc(1, sizeof(*opt->opt_private->pk_init_ctx));
2390 if (opt->opt_private->pk_init_ctx == NULL) {
2391 krb5_set_error_message(context, ENOMEM,
2392 N_("malloc: out of memory", ""));
2393 return ENOMEM;
2394 }
2395 opt->opt_private->pk_init_ctx->require_binding = 0;
2396 opt->opt_private->pk_init_ctx->require_eku = 1;
2397 opt->opt_private->pk_init_ctx->require_krbtgt_otherName = 1;
2398 opt->opt_private->pk_init_ctx->peer = NULL;
2399
2400 /* XXX implement krb5_appdefault_strings */
2401 if (pool == NULL)
2402 pool = krb5_config_get_strings(context, NULL,
2403 "appdefaults",
2404 "pkinit_pool",
2405 NULL);
2406
2407 if (pki_revoke == NULL)
2408 pki_revoke = krb5_config_get_strings(context, NULL,
2409 "appdefaults",
2410 "pkinit_revoke",
2411 NULL);
2412
2413 if (x509_anchors == NULL) {
2414 krb5_appdefault_string(context, "kinit",
2415 krb5_principal_get_realm(context, principal),
2416 "pkinit_anchors", NULL, &anchors);
2417 x509_anchors = anchors;
2418 }
2419
2420 if (flags & 4)
2421 opt->opt_private->pk_init_ctx->anonymous = 1;
2422
2423 ret = _krb5_pk_load_id(context,
2424 &opt->opt_private->pk_init_ctx->id,
2425 user_id,
2426 x509_anchors,
2427 pool,
2428 pki_revoke,
2429 prompter,
2430 prompter_data,
2431 password);
2432 if (ret) {
2433 free(opt->opt_private->pk_init_ctx);
2434 opt->opt_private->pk_init_ctx = NULL;
2435 return ret;
2436 }
2437
2438 if (opt->opt_private->pk_init_ctx->id->certs) {
2439 _krb5_pk_set_user_id(context,
2440 principal,
2441 opt->opt_private->pk_init_ctx,
2442 opt->opt_private->pk_init_ctx->id->certs);
2443 } else
2444 opt->opt_private->pk_init_ctx->id->cert = NULL;
2445
2446 if ((flags & 2) == 0) {
2447 hx509_context hx509ctx = context->hx509ctx;
2448 hx509_cert cert = opt->opt_private->pk_init_ctx->id->cert;
2449
2450 opt->opt_private->pk_init_ctx->keyex = USE_DH;
2451
2452 /*
2453 * If its a ECDSA certs, lets select ECDSA as the keyex algorithm.
2454 */
2455 if (cert) {
2456 AlgorithmIdentifier alg;
2457
2458 ret = hx509_cert_get_SPKI_AlgorithmIdentifier(hx509ctx, cert, &alg);
2459 if (ret == 0) {
2460 if (der_heim_oid_cmp(&alg.algorithm, &asn1_oid_id_ecPublicKey) == 0)
2461 opt->opt_private->pk_init_ctx->keyex = USE_ECDH;
2462 free_AlgorithmIdentifier(&alg);
2463 }
2464 }
2465
2466 } else {
2467 opt->opt_private->pk_init_ctx->keyex = USE_RSA;
2468
2469 if (opt->opt_private->pk_init_ctx->id->certs == NULL) {
2470 krb5_set_error_message(context, EINVAL,
2471 N_("No anonymous pkinit support in RSA mode", ""));
2472 return EINVAL;
2473 }
2474 }
2475
2476 return 0;
2477 #else
2478 krb5_set_error_message(context, EINVAL,
2479 N_("no support for PKINIT compiled in", ""));
2480 return EINVAL;
2481 #endif
2482 }
2483
2484 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)2485 krb5_get_init_creds_opt_set_pkinit_user_certs(krb5_context context,
2486 krb5_get_init_creds_opt *opt,
2487 struct hx509_certs_data *certs)
2488 {
2489 #ifdef PKINIT
2490 if (opt->opt_private == NULL) {
2491 krb5_set_error_message(context, EINVAL,
2492 N_("PKINIT: on non extendable opt", ""));
2493 return EINVAL;
2494 }
2495 if (opt->opt_private->pk_init_ctx == NULL) {
2496 krb5_set_error_message(context, EINVAL,
2497 N_("PKINIT: on pkinit context", ""));
2498 return EINVAL;
2499 }
2500
2501 _krb5_pk_set_user_id(context, NULL, opt->opt_private->pk_init_ctx, certs);
2502
2503 return 0;
2504 #else
2505 krb5_set_error_message(context, EINVAL,
2506 N_("no support for PKINIT compiled in", ""));
2507 return EINVAL;
2508 #endif
2509 }
2510
2511 #ifdef PKINIT
2512
2513 static int
get_ms_san(hx509_context context,hx509_cert cert,char ** upn)2514 get_ms_san(hx509_context context, hx509_cert cert, char **upn)
2515 {
2516 hx509_octet_string_list list;
2517 int ret;
2518
2519 *upn = NULL;
2520
2521 ret = hx509_cert_find_subjectAltName_otherName(context,
2522 cert,
2523 &asn1_oid_id_pkinit_ms_san,
2524 &list);
2525 if (ret)
2526 return 0;
2527
2528 if (list.len > 0 && list.val[0].length > 0)
2529 ret = decode_MS_UPN_SAN(list.val[0].data, list.val[0].length,
2530 upn, NULL);
2531 else
2532 ret = 1;
2533 hx509_free_octet_string_list(&list);
2534
2535 return ret;
2536 }
2537
2538 static int
find_ms_san(hx509_context context,hx509_cert cert,void * ctx)2539 find_ms_san(hx509_context context, hx509_cert cert, void *ctx)
2540 {
2541 char *upn;
2542 int ret;
2543
2544 ret = get_ms_san(context, cert, &upn);
2545 if (ret == 0)
2546 free(upn);
2547 return ret;
2548 }
2549
2550
2551
2552 #endif
2553
2554 /*
2555 * Private since it need to be redesigned using krb5_get_init_creds()
2556 */
2557
2558 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)2559 krb5_pk_enterprise_cert(krb5_context context,
2560 const char *user_id,
2561 krb5_const_realm realm,
2562 krb5_principal *principal,
2563 struct hx509_certs_data **res)
2564 {
2565 #ifdef PKINIT
2566 krb5_error_code ret;
2567 hx509_certs certs, result;
2568 hx509_cert cert = NULL;
2569 hx509_query *q;
2570 char *name;
2571
2572 *principal = NULL;
2573 if (res)
2574 *res = NULL;
2575
2576 if (user_id == NULL) {
2577 krb5_set_error_message(context, ENOENT, "no user id");
2578 return ENOENT;
2579 }
2580
2581 ret = hx509_certs_init(context->hx509ctx, user_id, 0, NULL, &certs);
2582 if (ret) {
2583 pk_copy_error(context, context->hx509ctx, ret,
2584 "Failed to init cert certs");
2585 goto out;
2586 }
2587
2588 ret = hx509_query_alloc(context->hx509ctx, &q);
2589 if (ret) {
2590 krb5_set_error_message(context, ret, "out of memory");
2591 hx509_certs_free(&certs);
2592 goto out;
2593 }
2594
2595 hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY);
2596 hx509_query_match_option(q, HX509_QUERY_OPTION_KU_DIGITALSIGNATURE);
2597 hx509_query_match_eku(q, &asn1_oid_id_pkinit_ms_eku);
2598 hx509_query_match_cmp_func(q, find_ms_san, NULL);
2599
2600 ret = hx509_certs_filter(context->hx509ctx, certs, q, &result);
2601 hx509_query_free(context->hx509ctx, q);
2602 hx509_certs_free(&certs);
2603 if (ret) {
2604 pk_copy_error(context, context->hx509ctx, ret,
2605 "Failed to find PKINIT certificate");
2606 return ret;
2607 }
2608
2609 ret = hx509_get_one_cert(context->hx509ctx, result, &cert);
2610 hx509_certs_free(&result);
2611 if (ret) {
2612 pk_copy_error(context, context->hx509ctx, ret,
2613 "Failed to get one cert");
2614 goto out;
2615 }
2616
2617 ret = get_ms_san(context->hx509ctx, cert, &name);
2618 if (ret) {
2619 pk_copy_error(context, context->hx509ctx, ret,
2620 "Failed to get MS SAN");
2621 goto out;
2622 }
2623
2624 ret = krb5_make_principal(context, principal, realm, name, NULL);
2625 free(name);
2626 if (ret)
2627 goto out;
2628
2629 krb5_principal_set_type(context, *principal, KRB5_NT_ENTERPRISE_PRINCIPAL);
2630
2631 if (res) {
2632 ret = hx509_certs_init(context->hx509ctx, "MEMORY:", 0, NULL, res);
2633 if (ret)
2634 goto out;
2635
2636 ret = hx509_certs_add(context->hx509ctx, *res, cert);
2637 if (ret) {
2638 hx509_certs_free(res);
2639 goto out;
2640 }
2641 }
2642
2643 out:
2644 hx509_cert_free(cert);
2645
2646 return ret;
2647 #else
2648 krb5_set_error_message(context, EINVAL,
2649 N_("no support for PKINIT compiled in", ""));
2650 return EINVAL;
2651 #endif
2652 }
2653