1 /*
2 * Copyright (c) 2003 - 2007 Kungliga Tekniska H�gskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 *
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 *
17 * 3. Neither the name of the Institute nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34 #include "krb5_locl.h"
35
36 RCSID("$Id: pkinit.c 22433 2008-01-13 14:11:46Z lha $");
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 <heim_asn1.h>
49 #include <rfc2459_asn1.h>
50 #include <cms_asn1.h>
51 #include <pkcs8_asn1.h>
52 #include <pkcs9_asn1.h>
53 #include <pkcs12_asn1.h>
54 #include <pkinit_asn1.h>
55 #include <asn1_err.h>
56
57 #include <der.h>
58
59 #include <hx509.h>
60
61 enum {
62 COMPAT_WIN2K = 1,
63 COMPAT_IETF = 2
64 };
65
66 struct krb5_pk_identity {
67 hx509_context hx509ctx;
68 hx509_verify_ctx verify_ctx;
69 hx509_certs certs;
70 hx509_certs anchors;
71 hx509_certs certpool;
72 hx509_revoke_ctx revokectx;
73 };
74
75 struct krb5_pk_cert {
76 hx509_cert cert;
77 };
78
79 struct krb5_pk_init_ctx_data {
80 struct krb5_pk_identity *id;
81 DH *dh;
82 krb5_data *clientDHNonce;
83 struct krb5_dh_moduli **m;
84 hx509_peer_info peer;
85 int type;
86 unsigned int require_binding:1;
87 unsigned int require_eku:1;
88 unsigned int require_krbtgt_otherName:1;
89 unsigned int require_hostname_match:1;
90 unsigned int trustedCertifiers:1;
91 };
92
93 static void
94 _krb5_pk_copy_error(krb5_context context,
95 hx509_context hx509ctx,
96 int hxret,
97 const char *fmt,
98 ...)
99 __attribute__ ((format (printf, 4, 5)));
100
101 /*
102 *
103 */
104
105 void KRB5_LIB_FUNCTION
_krb5_pk_cert_free(struct krb5_pk_cert * cert)106 _krb5_pk_cert_free(struct krb5_pk_cert *cert)
107 {
108 if (cert->cert) {
109 hx509_cert_free(cert->cert);
110 }
111 free(cert);
112 }
113
114 static krb5_error_code
BN_to_integer(krb5_context context,BIGNUM * bn,heim_integer * integer)115 BN_to_integer(krb5_context context, BIGNUM *bn, heim_integer *integer)
116 {
117 integer->length = BN_num_bytes(bn);
118 integer->data = malloc(integer->length);
119 if (integer->data == NULL) {
120 krb5_clear_error_string(context);
121 return ENOMEM;
122 }
123 BN_bn2bin(bn, integer->data);
124 integer->negative = BN_is_negative(bn);
125 return 0;
126 }
127
128 static BIGNUM *
integer_to_BN(krb5_context context,const char * field,const heim_integer * f)129 integer_to_BN(krb5_context context, const char *field, const heim_integer *f)
130 {
131 BIGNUM *bn;
132
133 bn = BN_bin2bn((const unsigned char *)f->data, f->length, NULL);
134 if (bn == NULL) {
135 krb5_set_error_string(context, "PKINIT: parsing BN failed %s", field);
136 return NULL;
137 }
138 BN_set_negative(bn, f->negative);
139 return bn;
140 }
141
142
143 static krb5_error_code
_krb5_pk_create_sign(krb5_context context,const heim_oid * eContentType,krb5_data * eContent,struct krb5_pk_identity * id,hx509_peer_info peer,krb5_data * sd_data)144 _krb5_pk_create_sign(krb5_context context,
145 const heim_oid *eContentType,
146 krb5_data *eContent,
147 struct krb5_pk_identity *id,
148 hx509_peer_info peer,
149 krb5_data *sd_data)
150 {
151 hx509_cert cert;
152 hx509_query *q;
153 int ret;
154
155 ret = hx509_query_alloc(id->hx509ctx, &q);
156 if (ret) {
157 _krb5_pk_copy_error(context, id->hx509ctx, ret,
158 "Allocate query to find signing certificate");
159 return ret;
160 }
161
162 hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY);
163 hx509_query_match_option(q, HX509_QUERY_OPTION_KU_DIGITALSIGNATURE);
164
165 ret = hx509_certs_find(id->hx509ctx, id->certs, q, &cert);
166 hx509_query_free(id->hx509ctx, q);
167 if (ret) {
168 _krb5_pk_copy_error(context, id->hx509ctx, ret,
169 "Find certificate to signed CMS data");
170 return ret;
171 }
172
173 ret = hx509_cms_create_signed_1(id->hx509ctx,
174 0,
175 eContentType,
176 eContent->data,
177 eContent->length,
178 NULL,
179 cert,
180 peer,
181 NULL,
182 id->certs,
183 sd_data);
184 if (ret)
185 _krb5_pk_copy_error(context, id->hx509ctx, ret, "create CMS signedData");
186 hx509_cert_free(cert);
187
188 return ret;
189 }
190
191 static int
cert2epi(hx509_context context,void * ctx,hx509_cert c)192 cert2epi(hx509_context context, void *ctx, hx509_cert c)
193 {
194 ExternalPrincipalIdentifiers *ids = ctx;
195 ExternalPrincipalIdentifier id;
196 hx509_name subject = NULL;
197 void *p;
198 int ret;
199
200 memset(&id, 0, sizeof(id));
201
202 ret = hx509_cert_get_subject(c, &subject);
203 if (ret)
204 return ret;
205
206 if (hx509_name_is_null_p(subject) != 0) {
207
208 id.subjectName = calloc(1, sizeof(*id.subjectName));
209 if (id.subjectName == NULL) {
210 hx509_name_free(&subject);
211 free_ExternalPrincipalIdentifier(&id);
212 return ENOMEM;
213 }
214
215 ret = hx509_name_binary(subject, id.subjectName);
216 if (ret) {
217 hx509_name_free(&subject);
218 free_ExternalPrincipalIdentifier(&id);
219 return ret;
220 }
221 }
222 hx509_name_free(&subject);
223
224
225 id.issuerAndSerialNumber = calloc(1, sizeof(*id.issuerAndSerialNumber));
226 if (id.issuerAndSerialNumber == NULL) {
227 free_ExternalPrincipalIdentifier(&id);
228 return ENOMEM;
229 }
230
231 {
232 IssuerAndSerialNumber iasn;
233 hx509_name issuer;
234 size_t size;
235
236 memset(&iasn, 0, sizeof(iasn));
237
238 ret = hx509_cert_get_issuer(c, &issuer);
239 if (ret) {
240 free_ExternalPrincipalIdentifier(&id);
241 return ret;
242 }
243
244 ret = hx509_name_to_Name(issuer, &iasn.issuer);
245 hx509_name_free(&issuer);
246 if (ret) {
247 free_ExternalPrincipalIdentifier(&id);
248 return ret;
249 }
250
251 ret = hx509_cert_get_serialnumber(c, &iasn.serialNumber);
252 if (ret) {
253 free_IssuerAndSerialNumber(&iasn);
254 free_ExternalPrincipalIdentifier(&id);
255 return ret;
256 }
257
258 ASN1_MALLOC_ENCODE(IssuerAndSerialNumber,
259 id.issuerAndSerialNumber->data,
260 id.issuerAndSerialNumber->length,
261 &iasn, &size, ret);
262 free_IssuerAndSerialNumber(&iasn);
263 if (ret)
264 return ret;
265 if (id.issuerAndSerialNumber->length != size)
266 abort();
267 }
268
269 id.subjectKeyIdentifier = NULL;
270
271 p = realloc(ids->val, sizeof(ids->val[0]) * (ids->len + 1));
272 if (p == NULL) {
273 free_ExternalPrincipalIdentifier(&id);
274 return ENOMEM;
275 }
276
277 ids->val = p;
278 ids->val[ids->len] = id;
279 ids->len++;
280
281 return 0;
282 }
283
284 static krb5_error_code
build_edi(krb5_context context,hx509_context hx509ctx,hx509_certs certs,ExternalPrincipalIdentifiers * ids)285 build_edi(krb5_context context,
286 hx509_context hx509ctx,
287 hx509_certs certs,
288 ExternalPrincipalIdentifiers *ids)
289 {
290 return hx509_certs_iter(hx509ctx, certs, cert2epi, ids);
291 }
292
293 static krb5_error_code
build_auth_pack(krb5_context context,unsigned nonce,krb5_pk_init_ctx ctx,DH * dh,const KDC_REQ_BODY * body,AuthPack * a)294 build_auth_pack(krb5_context context,
295 unsigned nonce,
296 krb5_pk_init_ctx ctx,
297 DH *dh,
298 const KDC_REQ_BODY *body,
299 AuthPack *a)
300 {
301 size_t buf_size, len;
302 krb5_error_code ret;
303 void *buf;
304 krb5_timestamp sec;
305 int32_t usec;
306 Checksum checksum;
307
308 krb5_clear_error_string(context);
309
310 memset(&checksum, 0, sizeof(checksum));
311
312 krb5_us_timeofday(context, &sec, &usec);
313 a->pkAuthenticator.ctime = sec;
314 a->pkAuthenticator.nonce = nonce;
315
316 ASN1_MALLOC_ENCODE(KDC_REQ_BODY, buf, buf_size, body, &len, ret);
317 if (ret)
318 return ret;
319 if (buf_size != len)
320 krb5_abortx(context, "internal error in ASN.1 encoder");
321
322 ret = krb5_create_checksum(context,
323 NULL,
324 0,
325 CKSUMTYPE_SHA1,
326 buf,
327 len,
328 &checksum);
329 free(buf);
330 if (ret)
331 return ret;
332
333 ALLOC(a->pkAuthenticator.paChecksum, 1);
334 if (a->pkAuthenticator.paChecksum == NULL) {
335 krb5_set_error_string(context, "malloc: out of memory");
336 return ENOMEM;
337 }
338
339 ret = krb5_data_copy(a->pkAuthenticator.paChecksum,
340 checksum.checksum.data, checksum.checksum.length);
341 free_Checksum(&checksum);
342 if (ret)
343 return ret;
344
345 if (dh) {
346 DomainParameters dp;
347 heim_integer dh_pub_key;
348 krb5_data dhbuf;
349 size_t size;
350
351 if (1 /* support_cached_dh */) {
352 ALLOC(a->clientDHNonce, 1);
353 if (a->clientDHNonce == NULL) {
354 krb5_clear_error_string(context);
355 return ENOMEM;
356 }
357 ret = krb5_data_alloc(a->clientDHNonce, 40);
358 if (a->clientDHNonce == NULL) {
359 krb5_clear_error_string(context);
360 return ENOMEM;
361 }
362 memset(a->clientDHNonce->data, 0, a->clientDHNonce->length);
363 ret = krb5_copy_data(context, a->clientDHNonce,
364 &ctx->clientDHNonce);
365 if (ret)
366 return ret;
367 }
368
369 ALLOC(a->clientPublicValue, 1);
370 if (a->clientPublicValue == NULL)
371 return ENOMEM;
372 ret = der_copy_oid(oid_id_dhpublicnumber(),
373 &a->clientPublicValue->algorithm.algorithm);
374 if (ret)
375 return ret;
376
377 memset(&dp, 0, sizeof(dp));
378
379 ret = BN_to_integer(context, dh->p, &dp.p);
380 if (ret) {
381 free_DomainParameters(&dp);
382 return ret;
383 }
384 ret = BN_to_integer(context, dh->g, &dp.g);
385 if (ret) {
386 free_DomainParameters(&dp);
387 return ret;
388 }
389 ret = BN_to_integer(context, dh->q, &dp.q);
390 if (ret) {
391 free_DomainParameters(&dp);
392 return ret;
393 }
394 dp.j = NULL;
395 dp.validationParms = NULL;
396
397 a->clientPublicValue->algorithm.parameters =
398 malloc(sizeof(*a->clientPublicValue->algorithm.parameters));
399 if (a->clientPublicValue->algorithm.parameters == NULL) {
400 free_DomainParameters(&dp);
401 return ret;
402 }
403
404 ASN1_MALLOC_ENCODE(DomainParameters,
405 a->clientPublicValue->algorithm.parameters->data,
406 a->clientPublicValue->algorithm.parameters->length,
407 &dp, &size, ret);
408 free_DomainParameters(&dp);
409 if (ret)
410 return ret;
411 if (size != a->clientPublicValue->algorithm.parameters->length)
412 krb5_abortx(context, "Internal ASN1 encoder error");
413
414 ret = BN_to_integer(context, dh->pub_key, &dh_pub_key);
415 if (ret)
416 return ret;
417
418 ASN1_MALLOC_ENCODE(DHPublicKey, dhbuf.data, dhbuf.length,
419 &dh_pub_key, &size, ret);
420 der_free_heim_integer(&dh_pub_key);
421 if (ret)
422 return ret;
423 if (size != dhbuf.length)
424 krb5_abortx(context, "asn1 internal error");
425
426 a->clientPublicValue->subjectPublicKey.length = dhbuf.length * 8;
427 a->clientPublicValue->subjectPublicKey.data = dhbuf.data;
428 }
429
430 {
431 a->supportedCMSTypes = calloc(1, sizeof(*a->supportedCMSTypes));
432 if (a->supportedCMSTypes == NULL)
433 return ENOMEM;
434
435 ret = hx509_crypto_available(ctx->id->hx509ctx, HX509_SELECT_ALL, NULL,
436 &a->supportedCMSTypes->val,
437 &a->supportedCMSTypes->len);
438 if (ret)
439 return ret;
440 }
441
442 return ret;
443 }
444
445 krb5_error_code KRB5_LIB_FUNCTION
_krb5_pk_mk_ContentInfo(krb5_context context,const krb5_data * buf,const heim_oid * oid,struct ContentInfo * content_info)446 _krb5_pk_mk_ContentInfo(krb5_context context,
447 const krb5_data *buf,
448 const heim_oid *oid,
449 struct ContentInfo *content_info)
450 {
451 krb5_error_code ret;
452
453 ret = der_copy_oid(oid, &content_info->contentType);
454 if (ret)
455 return ret;
456 ALLOC(content_info->content, 1);
457 if (content_info->content == NULL)
458 return ENOMEM;
459 content_info->content->data = malloc(buf->length);
460 if (content_info->content->data == NULL)
461 return ENOMEM;
462 memcpy(content_info->content->data, buf->data, buf->length);
463 content_info->content->length = buf->length;
464 return 0;
465 }
466
467 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)468 pk_mk_padata(krb5_context context,
469 krb5_pk_init_ctx ctx,
470 const KDC_REQ_BODY *req_body,
471 unsigned nonce,
472 METHOD_DATA *md)
473 {
474 struct ContentInfo content_info;
475 krb5_error_code ret;
476 const heim_oid *oid;
477 size_t size;
478 krb5_data buf, sd_buf;
479 int pa_type;
480
481 krb5_data_zero(&buf);
482 krb5_data_zero(&sd_buf);
483 memset(&content_info, 0, sizeof(content_info));
484
485 if (ctx->type == COMPAT_WIN2K) {
486 AuthPack_Win2k ap;
487 krb5_timestamp sec;
488 int32_t usec;
489
490 memset(&ap, 0, sizeof(ap));
491
492 /* fill in PKAuthenticator */
493 ret = copy_PrincipalName(req_body->sname, &ap.pkAuthenticator.kdcName);
494 if (ret) {
495 free_AuthPack_Win2k(&ap);
496 krb5_clear_error_string(context);
497 goto out;
498 }
499 ret = copy_Realm(&req_body->realm, &ap.pkAuthenticator.kdcRealm);
500 if (ret) {
501 free_AuthPack_Win2k(&ap);
502 krb5_clear_error_string(context);
503 goto out;
504 }
505
506 krb5_us_timeofday(context, &sec, &usec);
507 ap.pkAuthenticator.ctime = sec;
508 ap.pkAuthenticator.cusec = usec;
509 ap.pkAuthenticator.nonce = nonce;
510
511 ASN1_MALLOC_ENCODE(AuthPack_Win2k, buf.data, buf.length,
512 &ap, &size, ret);
513 free_AuthPack_Win2k(&ap);
514 if (ret) {
515 krb5_set_error_string(context, "AuthPack_Win2k: %d", ret);
516 goto out;
517 }
518 if (buf.length != size)
519 krb5_abortx(context, "internal ASN1 encoder error");
520
521 oid = oid_id_pkcs7_data();
522 } else if (ctx->type == COMPAT_IETF) {
523 AuthPack ap;
524
525 memset(&ap, 0, sizeof(ap));
526
527 ret = build_auth_pack(context, nonce, ctx, ctx->dh, req_body, &ap);
528 if (ret) {
529 free_AuthPack(&ap);
530 goto out;
531 }
532
533 ASN1_MALLOC_ENCODE(AuthPack, buf.data, buf.length, &ap, &size, ret);
534 free_AuthPack(&ap);
535 if (ret) {
536 krb5_set_error_string(context, "AuthPack: %d", ret);
537 goto out;
538 }
539 if (buf.length != size)
540 krb5_abortx(context, "internal ASN1 encoder error");
541
542 oid = oid_id_pkauthdata();
543 } else
544 krb5_abortx(context, "internal pkinit error");
545
546 ret = _krb5_pk_create_sign(context,
547 oid,
548 &buf,
549 ctx->id,
550 ctx->peer,
551 &sd_buf);
552 krb5_data_free(&buf);
553 if (ret)
554 goto out;
555
556 ret = hx509_cms_wrap_ContentInfo(oid_id_pkcs7_signedData(), &sd_buf, &buf);
557 krb5_data_free(&sd_buf);
558 if (ret) {
559 krb5_set_error_string(context,
560 "ContentInfo wrapping of signedData failed");
561 goto out;
562 }
563
564 if (ctx->type == COMPAT_WIN2K) {
565 PA_PK_AS_REQ_Win2k winreq;
566
567 pa_type = KRB5_PADATA_PK_AS_REQ_WIN;
568
569 memset(&winreq, 0, sizeof(winreq));
570
571 winreq.signed_auth_pack = buf;
572
573 ASN1_MALLOC_ENCODE(PA_PK_AS_REQ_Win2k, buf.data, buf.length,
574 &winreq, &size, ret);
575 free_PA_PK_AS_REQ_Win2k(&winreq);
576
577 } else if (ctx->type == COMPAT_IETF) {
578 PA_PK_AS_REQ req;
579
580 pa_type = KRB5_PADATA_PK_AS_REQ;
581
582 memset(&req, 0, sizeof(req));
583 req.signedAuthPack = buf;
584
585 if (ctx->trustedCertifiers) {
586
587 req.trustedCertifiers = calloc(1, sizeof(*req.trustedCertifiers));
588 if (req.trustedCertifiers == NULL) {
589 krb5_set_error_string(context, "malloc: out of memory");
590 free_PA_PK_AS_REQ(&req);
591 goto out;
592 }
593 ret = build_edi(context, ctx->id->hx509ctx,
594 ctx->id->anchors, req.trustedCertifiers);
595 if (ret) {
596 krb5_set_error_string(context, "pk-init: failed to build trustedCertifiers");
597 free_PA_PK_AS_REQ(&req);
598 goto out;
599 }
600 }
601 req.kdcPkId = NULL;
602
603 ASN1_MALLOC_ENCODE(PA_PK_AS_REQ, buf.data, buf.length,
604 &req, &size, ret);
605
606 free_PA_PK_AS_REQ(&req);
607
608 } else
609 krb5_abortx(context, "internal pkinit error");
610 if (ret) {
611 krb5_set_error_string(context, "PA-PK-AS-REQ %d", ret);
612 goto out;
613 }
614 if (buf.length != size)
615 krb5_abortx(context, "Internal ASN1 encoder error");
616
617 ret = krb5_padata_add(context, md, pa_type, buf.data, buf.length);
618 if (ret)
619 free(buf.data);
620
621 if (ret == 0 && ctx->type == COMPAT_WIN2K)
622 krb5_padata_add(context, md, KRB5_PADATA_PK_AS_09_BINDING, NULL, 0);
623
624 out:
625 free_ContentInfo(&content_info);
626
627 return ret;
628 }
629
630
631 krb5_error_code KRB5_LIB_FUNCTION
_krb5_pk_mk_padata(krb5_context context,void * c,const KDC_REQ_BODY * req_body,unsigned nonce,METHOD_DATA * md)632 _krb5_pk_mk_padata(krb5_context context,
633 void *c,
634 const KDC_REQ_BODY *req_body,
635 unsigned nonce,
636 METHOD_DATA *md)
637 {
638 krb5_pk_init_ctx ctx = c;
639 int win2k_compat;
640
641 win2k_compat = krb5_config_get_bool_default(context, NULL,
642 FALSE,
643 "realms",
644 req_body->realm,
645 "pkinit_win2k",
646 NULL);
647
648 if (win2k_compat) {
649 ctx->require_binding =
650 krb5_config_get_bool_default(context, NULL,
651 FALSE,
652 "realms",
653 req_body->realm,
654 "pkinit_win2k_require_binding",
655 NULL);
656 ctx->type = COMPAT_WIN2K;
657 } else
658 ctx->type = COMPAT_IETF;
659
660 ctx->require_eku =
661 krb5_config_get_bool_default(context, NULL,
662 TRUE,
663 "realms",
664 req_body->realm,
665 "pkinit_require_eku",
666 NULL);
667 ctx->require_krbtgt_otherName =
668 krb5_config_get_bool_default(context, NULL,
669 TRUE,
670 "realms",
671 req_body->realm,
672 "pkinit_require_krbtgt_otherName",
673 NULL);
674
675 ctx->require_hostname_match =
676 krb5_config_get_bool_default(context, NULL,
677 FALSE,
678 "realms",
679 req_body->realm,
680 "pkinit_require_hostname_match",
681 NULL);
682
683 ctx->trustedCertifiers =
684 krb5_config_get_bool_default(context, NULL,
685 TRUE,
686 "realms",
687 req_body->realm,
688 "pkinit_trustedCertifiers",
689 NULL);
690
691 return pk_mk_padata(context, ctx, req_body, nonce, md);
692 }
693
694 krb5_error_code KRB5_LIB_FUNCTION
_krb5_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)695 _krb5_pk_verify_sign(krb5_context context,
696 const void *data,
697 size_t length,
698 struct krb5_pk_identity *id,
699 heim_oid *contentType,
700 krb5_data *content,
701 struct krb5_pk_cert **signer)
702 {
703 hx509_certs signer_certs;
704 int ret;
705
706 *signer = NULL;
707
708 ret = hx509_cms_verify_signed(id->hx509ctx,
709 id->verify_ctx,
710 data,
711 length,
712 NULL,
713 id->certpool,
714 contentType,
715 content,
716 &signer_certs);
717 if (ret) {
718 _krb5_pk_copy_error(context, id->hx509ctx, ret,
719 "CMS verify signed failed");
720 return ret;
721 }
722
723 *signer = calloc(1, sizeof(**signer));
724 if (*signer == NULL) {
725 krb5_clear_error_string(context);
726 ret = ENOMEM;
727 goto out;
728 }
729
730 ret = hx509_get_one_cert(id->hx509ctx, signer_certs, &(*signer)->cert);
731 if (ret) {
732 _krb5_pk_copy_error(context, id->hx509ctx, ret,
733 "Failed to get on of the signer certs");
734 goto out;
735 }
736
737 out:
738 hx509_certs_free(&signer_certs);
739 if (ret) {
740 if (*signer) {
741 hx509_cert_free((*signer)->cert);
742 free(*signer);
743 *signer = NULL;
744 }
745 }
746
747 return ret;
748 }
749
750 static krb5_error_code
get_reply_key_win(krb5_context context,const krb5_data * content,unsigned nonce,krb5_keyblock ** key)751 get_reply_key_win(krb5_context context,
752 const krb5_data *content,
753 unsigned nonce,
754 krb5_keyblock **key)
755 {
756 ReplyKeyPack_Win2k key_pack;
757 krb5_error_code ret;
758 size_t size;
759
760 ret = decode_ReplyKeyPack_Win2k(content->data,
761 content->length,
762 &key_pack,
763 &size);
764 if (ret) {
765 krb5_set_error_string(context, "PKINIT decoding reply key failed");
766 free_ReplyKeyPack_Win2k(&key_pack);
767 return ret;
768 }
769
770 if (key_pack.nonce != nonce) {
771 krb5_set_error_string(context, "PKINIT enckey nonce is wrong");
772 free_ReplyKeyPack_Win2k(&key_pack);
773 return KRB5KRB_AP_ERR_MODIFIED;
774 }
775
776 *key = malloc (sizeof (**key));
777 if (*key == NULL) {
778 krb5_set_error_string(context, "PKINIT failed allocating reply key");
779 free_ReplyKeyPack_Win2k(&key_pack);
780 krb5_set_error_string(context, "malloc: out of memory");
781 return ENOMEM;
782 }
783
784 ret = copy_EncryptionKey(&key_pack.replyKey, *key);
785 free_ReplyKeyPack_Win2k(&key_pack);
786 if (ret) {
787 krb5_set_error_string(context, "PKINIT failed copying reply key");
788 free(*key);
789 *key = NULL;
790 }
791
792 return ret;
793 }
794
795 static krb5_error_code
get_reply_key(krb5_context context,const krb5_data * content,const krb5_data * req_buffer,krb5_keyblock ** key)796 get_reply_key(krb5_context context,
797 const krb5_data *content,
798 const krb5_data *req_buffer,
799 krb5_keyblock **key)
800 {
801 ReplyKeyPack key_pack;
802 krb5_error_code ret;
803 size_t size;
804
805 ret = decode_ReplyKeyPack(content->data,
806 content->length,
807 &key_pack,
808 &size);
809 if (ret) {
810 krb5_set_error_string(context, "PKINIT decoding reply key failed");
811 free_ReplyKeyPack(&key_pack);
812 return ret;
813 }
814
815 {
816 krb5_crypto crypto;
817
818 /*
819 * XXX Verify kp.replyKey is a allowed enctype in the
820 * configuration file
821 */
822
823 ret = krb5_crypto_init(context, &key_pack.replyKey, 0, &crypto);
824 if (ret) {
825 free_ReplyKeyPack(&key_pack);
826 return ret;
827 }
828
829 ret = krb5_verify_checksum(context, crypto, 6,
830 req_buffer->data, req_buffer->length,
831 &key_pack.asChecksum);
832 krb5_crypto_destroy(context, crypto);
833 if (ret) {
834 free_ReplyKeyPack(&key_pack);
835 return ret;
836 }
837 }
838
839 *key = malloc (sizeof (**key));
840 if (*key == NULL) {
841 krb5_set_error_string(context, "PKINIT failed allocating reply key");
842 free_ReplyKeyPack(&key_pack);
843 krb5_set_error_string(context, "malloc: out of memory");
844 return ENOMEM;
845 }
846
847 ret = copy_EncryptionKey(&key_pack.replyKey, *key);
848 free_ReplyKeyPack(&key_pack);
849 if (ret) {
850 krb5_set_error_string(context, "PKINIT failed copying reply key");
851 free(*key);
852 *key = NULL;
853 }
854
855 return ret;
856 }
857
858
859 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)860 pk_verify_host(krb5_context context,
861 const char *realm,
862 const krb5_krbhst_info *hi,
863 struct krb5_pk_init_ctx_data *ctx,
864 struct krb5_pk_cert *host)
865 {
866 krb5_error_code ret = 0;
867
868 if (ctx->require_eku) {
869 ret = hx509_cert_check_eku(ctx->id->hx509ctx, host->cert,
870 oid_id_pkkdcekuoid(), 0);
871 if (ret) {
872 krb5_set_error_string(context, "No PK-INIT KDC EKU in kdc certificate");
873 return ret;
874 }
875 }
876 if (ctx->require_krbtgt_otherName) {
877 hx509_octet_string_list list;
878 int i;
879
880 ret = hx509_cert_find_subjectAltName_otherName(ctx->id->hx509ctx,
881 host->cert,
882 oid_id_pkinit_san(),
883 &list);
884 if (ret) {
885 krb5_set_error_string(context, "Failed to find the PK-INIT "
886 "subjectAltName in the KDC certificate");
887
888 return ret;
889 }
890
891 for (i = 0; i < list.len; i++) {
892 KRB5PrincipalName r;
893
894 ret = decode_KRB5PrincipalName(list.val[i].data,
895 list.val[i].length,
896 &r,
897 NULL);
898 if (ret) {
899 krb5_set_error_string(context, "Failed to decode the PK-INIT "
900 "subjectAltName in the KDC certificate");
901
902 break;
903 }
904
905 if (r.principalName.name_string.len != 2 ||
906 strcmp(r.principalName.name_string.val[0], KRB5_TGS_NAME) != 0 ||
907 strcmp(r.principalName.name_string.val[1], realm) != 0 ||
908 strcmp(r.realm, realm) != 0)
909 {
910 krb5_set_error_string(context, "KDC have wrong realm name in "
911 "the certificate");
912 ret = KRB5_KDC_ERR_INVALID_CERTIFICATE;
913 }
914
915 free_KRB5PrincipalName(&r);
916 if (ret)
917 break;
918 }
919 hx509_free_octet_string_list(&list);
920 }
921 if (ret)
922 return ret;
923
924 if (hi) {
925 ret = hx509_verify_hostname(ctx->id->hx509ctx, host->cert,
926 ctx->require_hostname_match,
927 HX509_HN_HOSTNAME,
928 hi->hostname,
929 hi->ai->ai_addr, hi->ai->ai_addrlen);
930
931 if (ret)
932 krb5_set_error_string(context, "Address mismatch in "
933 "the KDC certificate");
934 }
935 return ret;
936 }
937
938 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)939 pk_rd_pa_reply_enckey(krb5_context context,
940 int type,
941 const heim_octet_string *indata,
942 const heim_oid *dataType,
943 const char *realm,
944 krb5_pk_init_ctx ctx,
945 krb5_enctype etype,
946 const krb5_krbhst_info *hi,
947 unsigned nonce,
948 const krb5_data *req_buffer,
949 PA_DATA *pa,
950 krb5_keyblock **key)
951 {
952 krb5_error_code ret;
953 struct krb5_pk_cert *host = NULL;
954 krb5_data content;
955 heim_oid contentType = { 0, NULL };
956
957 if (der_heim_oid_cmp(oid_id_pkcs7_envelopedData(), dataType)) {
958 krb5_set_error_string(context, "PKINIT: Invalid content type");
959 return EINVAL;
960 }
961
962 ret = hx509_cms_unenvelope(ctx->id->hx509ctx,
963 ctx->id->certs,
964 HX509_CMS_UE_DONT_REQUIRE_KU_ENCIPHERMENT,
965 indata->data,
966 indata->length,
967 NULL,
968 &contentType,
969 &content);
970 if (ret) {
971 _krb5_pk_copy_error(context, ctx->id->hx509ctx, ret,
972 "Failed to unenvelope CMS data in PK-INIT reply");
973 return ret;
974 }
975 der_free_oid(&contentType);
976
977 #if 0 /* windows LH with interesting CMS packets, leaks memory */
978 {
979 size_t ph = 1 + der_length_len (length);
980 unsigned char *ptr = malloc(length + ph);
981 size_t l;
982
983 memcpy(ptr + ph, p, length);
984
985 ret = der_put_length_and_tag (ptr + ph - 1, ph, length,
986 ASN1_C_UNIV, CONS, UT_Sequence, &l);
987 if (ret)
988 return ret;
989 ptr += ph - l;
990 length += l;
991 p = ptr;
992 }
993 #endif
994
995 /* win2k uses ContentInfo */
996 if (type == COMPAT_WIN2K) {
997 heim_oid type;
998 heim_octet_string out;
999
1000 ret = hx509_cms_unwrap_ContentInfo(&content, &type, &out, NULL);
1001 if (der_heim_oid_cmp(&type, oid_id_pkcs7_signedData())) {
1002 ret = EINVAL; /* XXX */
1003 krb5_set_error_string(context, "PKINIT: Invalid content type");
1004 der_free_oid(&type);
1005 der_free_octet_string(&out);
1006 goto out;
1007 }
1008 der_free_oid(&type);
1009 krb5_data_free(&content);
1010 ret = krb5_data_copy(&content, out.data, out.length);
1011 der_free_octet_string(&out);
1012 if (ret) {
1013 krb5_set_error_string(context, "PKINIT: out of memory");
1014 goto out;
1015 }
1016 }
1017
1018 ret = _krb5_pk_verify_sign(context,
1019 content.data,
1020 content.length,
1021 ctx->id,
1022 &contentType,
1023 &content,
1024 &host);
1025 if (ret)
1026 goto out;
1027
1028 /* make sure that it is the kdc's certificate */
1029 ret = pk_verify_host(context, realm, hi, ctx, host);
1030 if (ret) {
1031 goto out;
1032 }
1033
1034 #if 0
1035 if (type == COMPAT_WIN2K) {
1036 if (der_heim_oid_cmp(&contentType, oid_id_pkcs7_data()) != 0) {
1037 krb5_set_error_string(context, "PKINIT: reply key, wrong oid");
1038 ret = KRB5KRB_AP_ERR_MSG_TYPE;
1039 goto out;
1040 }
1041 } else {
1042 if (der_heim_oid_cmp(&contentType, oid_id_pkrkeydata()) != 0) {
1043 krb5_set_error_string(context, "PKINIT: reply key, wrong oid");
1044 ret = KRB5KRB_AP_ERR_MSG_TYPE;
1045 goto out;
1046 }
1047 }
1048 #endif
1049
1050 switch(type) {
1051 case COMPAT_WIN2K:
1052 ret = get_reply_key(context, &content, req_buffer, key);
1053 if (ret != 0 && ctx->require_binding == 0)
1054 ret = get_reply_key_win(context, &content, nonce, key);
1055 break;
1056 case COMPAT_IETF:
1057 ret = get_reply_key(context, &content, req_buffer, key);
1058 break;
1059 }
1060 if (ret)
1061 goto out;
1062
1063 /* XXX compare given etype with key->etype */
1064
1065 out:
1066 if (host)
1067 _krb5_pk_cert_free(host);
1068 der_free_oid(&contentType);
1069 krb5_data_free(&content);
1070
1071 return ret;
1072 }
1073
1074 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)1075 pk_rd_pa_reply_dh(krb5_context context,
1076 const heim_octet_string *indata,
1077 const heim_oid *dataType,
1078 const char *realm,
1079 krb5_pk_init_ctx ctx,
1080 krb5_enctype etype,
1081 const krb5_krbhst_info *hi,
1082 const DHNonce *c_n,
1083 const DHNonce *k_n,
1084 unsigned nonce,
1085 PA_DATA *pa,
1086 krb5_keyblock **key)
1087 {
1088 unsigned char *p, *dh_gen_key = NULL;
1089 struct krb5_pk_cert *host = NULL;
1090 BIGNUM *kdc_dh_pubkey = NULL;
1091 KDCDHKeyInfo kdc_dh_info;
1092 heim_oid contentType = { 0, NULL };
1093 krb5_data content;
1094 krb5_error_code ret;
1095 int dh_gen_keylen;
1096 size_t size;
1097
1098 krb5_data_zero(&content);
1099 memset(&kdc_dh_info, 0, sizeof(kdc_dh_info));
1100
1101 if (der_heim_oid_cmp(oid_id_pkcs7_signedData(), dataType)) {
1102 krb5_set_error_string(context, "PKINIT: Invalid content type");
1103 return EINVAL;
1104 }
1105
1106 ret = _krb5_pk_verify_sign(context,
1107 indata->data,
1108 indata->length,
1109 ctx->id,
1110 &contentType,
1111 &content,
1112 &host);
1113 if (ret)
1114 goto out;
1115
1116 /* make sure that it is the kdc's certificate */
1117 ret = pk_verify_host(context, realm, hi, ctx, host);
1118 if (ret)
1119 goto out;
1120
1121 if (der_heim_oid_cmp(&contentType, oid_id_pkdhkeydata())) {
1122 krb5_set_error_string(context, "pkinit - dh reply contains wrong oid");
1123 ret = KRB5KRB_AP_ERR_MSG_TYPE;
1124 goto out;
1125 }
1126
1127 ret = decode_KDCDHKeyInfo(content.data,
1128 content.length,
1129 &kdc_dh_info,
1130 &size);
1131
1132 if (ret) {
1133 krb5_set_error_string(context, "pkinit - "
1134 "failed to decode KDC DH Key Info");
1135 goto out;
1136 }
1137
1138 if (kdc_dh_info.nonce != nonce) {
1139 krb5_set_error_string(context, "PKINIT: DH nonce is wrong");
1140 ret = KRB5KRB_AP_ERR_MODIFIED;
1141 goto out;
1142 }
1143
1144 if (kdc_dh_info.dhKeyExpiration) {
1145 if (k_n == NULL) {
1146 krb5_set_error_string(context, "pkinit; got key expiration "
1147 "without server nonce");
1148 ret = KRB5KRB_ERR_GENERIC;
1149 goto out;
1150 }
1151 if (c_n == NULL) {
1152 krb5_set_error_string(context, "pkinit; got DH reuse but no "
1153 "client nonce");
1154 ret = KRB5KRB_ERR_GENERIC;
1155 goto out;
1156 }
1157 } else {
1158 if (k_n) {
1159 krb5_set_error_string(context, "pkinit: got server nonce "
1160 "without key expiration");
1161 ret = KRB5KRB_ERR_GENERIC;
1162 goto out;
1163 }
1164 c_n = NULL;
1165 }
1166
1167
1168 p = kdc_dh_info.subjectPublicKey.data;
1169 size = (kdc_dh_info.subjectPublicKey.length + 7) / 8;
1170
1171 {
1172 DHPublicKey k;
1173 ret = decode_DHPublicKey(p, size, &k, NULL);
1174 if (ret) {
1175 krb5_set_error_string(context, "pkinit: can't decode "
1176 "without key expiration");
1177 goto out;
1178 }
1179
1180 kdc_dh_pubkey = integer_to_BN(context, "DHPublicKey", &k);
1181 free_DHPublicKey(&k);
1182 if (kdc_dh_pubkey == NULL) {
1183 ret = KRB5KRB_ERR_GENERIC;
1184 goto out;
1185 }
1186 }
1187
1188 dh_gen_keylen = DH_size(ctx->dh);
1189 size = BN_num_bytes(ctx->dh->p);
1190 if (size < dh_gen_keylen)
1191 size = dh_gen_keylen;
1192
1193 dh_gen_key = malloc(size);
1194 if (dh_gen_key == NULL) {
1195 krb5_set_error_string(context, "malloc: out of memory");
1196 ret = ENOMEM;
1197 goto out;
1198 }
1199 memset(dh_gen_key, 0, size - dh_gen_keylen);
1200
1201 dh_gen_keylen = DH_compute_key(dh_gen_key + (size - dh_gen_keylen),
1202 kdc_dh_pubkey, ctx->dh);
1203 if (dh_gen_keylen == -1) {
1204 krb5_set_error_string(context,
1205 "PKINIT: Can't compute Diffie-Hellman key");
1206 ret = KRB5KRB_ERR_GENERIC;
1207 goto out;
1208 }
1209
1210 *key = malloc (sizeof (**key));
1211 if (*key == NULL) {
1212 krb5_set_error_string(context, "malloc: out of memory");
1213 ret = ENOMEM;
1214 goto out;
1215 }
1216
1217 ret = _krb5_pk_octetstring2key(context,
1218 etype,
1219 dh_gen_key, dh_gen_keylen,
1220 c_n, k_n,
1221 *key);
1222 if (ret) {
1223 krb5_set_error_string(context,
1224 "PKINIT: can't create key from DH key");
1225 free(*key);
1226 *key = NULL;
1227 goto out;
1228 }
1229
1230 out:
1231 if (kdc_dh_pubkey)
1232 BN_free(kdc_dh_pubkey);
1233 if (dh_gen_key) {
1234 memset(dh_gen_key, 0, DH_size(ctx->dh));
1235 free(dh_gen_key);
1236 }
1237 if (host)
1238 _krb5_pk_cert_free(host);
1239 if (content.data)
1240 krb5_data_free(&content);
1241 der_free_oid(&contentType);
1242 free_KDCDHKeyInfo(&kdc_dh_info);
1243
1244 return ret;
1245 }
1246
1247 krb5_error_code KRB5_LIB_FUNCTION
_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)1248 _krb5_pk_rd_pa_reply(krb5_context context,
1249 const char *realm,
1250 void *c,
1251 krb5_enctype etype,
1252 const krb5_krbhst_info *hi,
1253 unsigned nonce,
1254 const krb5_data *req_buffer,
1255 PA_DATA *pa,
1256 krb5_keyblock **key)
1257 {
1258 krb5_pk_init_ctx ctx = c;
1259 krb5_error_code ret;
1260 size_t size;
1261
1262 /* Check for IETF PK-INIT first */
1263 if (ctx->type == COMPAT_IETF) {
1264 PA_PK_AS_REP rep;
1265 heim_octet_string os, data;
1266 heim_oid oid;
1267
1268 if (pa->padata_type != KRB5_PADATA_PK_AS_REP) {
1269 krb5_set_error_string(context, "PKINIT: wrong padata recv");
1270 return EINVAL;
1271 }
1272
1273 ret = decode_PA_PK_AS_REP(pa->padata_value.data,
1274 pa->padata_value.length,
1275 &rep,
1276 &size);
1277 if (ret) {
1278 krb5_set_error_string(context, "Failed to decode pkinit AS rep");
1279 return ret;
1280 }
1281
1282 switch (rep.element) {
1283 case choice_PA_PK_AS_REP_dhInfo:
1284 os = rep.u.dhInfo.dhSignedData;
1285 break;
1286 case choice_PA_PK_AS_REP_encKeyPack:
1287 os = rep.u.encKeyPack;
1288 break;
1289 default:
1290 free_PA_PK_AS_REP(&rep);
1291 krb5_set_error_string(context, "PKINIT: -27 reply "
1292 "invalid content type");
1293 return EINVAL;
1294 }
1295
1296 ret = hx509_cms_unwrap_ContentInfo(&os, &oid, &data, NULL);
1297 if (ret) {
1298 free_PA_PK_AS_REP(&rep);
1299 krb5_set_error_string(context, "PKINIT: failed to unwrap CI");
1300 return ret;
1301 }
1302
1303 switch (rep.element) {
1304 case choice_PA_PK_AS_REP_dhInfo:
1305 ret = pk_rd_pa_reply_dh(context, &data, &oid, realm, ctx, etype, hi,
1306 ctx->clientDHNonce,
1307 rep.u.dhInfo.serverDHNonce,
1308 nonce, pa, key);
1309 break;
1310 case choice_PA_PK_AS_REP_encKeyPack:
1311 ret = pk_rd_pa_reply_enckey(context, COMPAT_IETF, &data, &oid, realm,
1312 ctx, etype, hi, nonce, req_buffer, pa, key);
1313 break;
1314 default:
1315 krb5_abortx(context, "pk-init as-rep case not possible to happen");
1316 }
1317 der_free_octet_string(&data);
1318 der_free_oid(&oid);
1319 free_PA_PK_AS_REP(&rep);
1320
1321 } else if (ctx->type == COMPAT_WIN2K) {
1322 PA_PK_AS_REP_Win2k w2krep;
1323
1324 /* Check for Windows encoding of the AS-REP pa data */
1325
1326 #if 0 /* should this be ? */
1327 if (pa->padata_type != KRB5_PADATA_PK_AS_REP) {
1328 krb5_set_error_string(context, "PKINIT: wrong padata recv");
1329 return EINVAL;
1330 }
1331 #endif
1332
1333 memset(&w2krep, 0, sizeof(w2krep));
1334
1335 ret = decode_PA_PK_AS_REP_Win2k(pa->padata_value.data,
1336 pa->padata_value.length,
1337 &w2krep,
1338 &size);
1339 if (ret) {
1340 krb5_set_error_string(context, "PKINIT: Failed decoding windows "
1341 "pkinit reply %d", ret);
1342 return ret;
1343 }
1344
1345 krb5_clear_error_string(context);
1346
1347 switch (w2krep.element) {
1348 case choice_PA_PK_AS_REP_Win2k_encKeyPack: {
1349 heim_octet_string data;
1350 heim_oid oid;
1351
1352 ret = hx509_cms_unwrap_ContentInfo(&w2krep.u.encKeyPack,
1353 &oid, &data, NULL);
1354 free_PA_PK_AS_REP_Win2k(&w2krep);
1355 if (ret) {
1356 krb5_set_error_string(context, "PKINIT: failed to unwrap CI");
1357 return ret;
1358 }
1359
1360 ret = pk_rd_pa_reply_enckey(context, COMPAT_WIN2K, &data, &oid, realm,
1361 ctx, etype, hi, nonce, req_buffer, pa, key);
1362 der_free_octet_string(&data);
1363 der_free_oid(&oid);
1364
1365 break;
1366 }
1367 default:
1368 free_PA_PK_AS_REP_Win2k(&w2krep);
1369 krb5_set_error_string(context, "PKINIT: win2k reply invalid "
1370 "content type");
1371 ret = EINVAL;
1372 break;
1373 }
1374
1375 } else {
1376 krb5_set_error_string(context, "PKINIT: unknown reply type");
1377 ret = EINVAL;
1378 }
1379
1380 return ret;
1381 }
1382
1383 struct prompter {
1384 krb5_context context;
1385 krb5_prompter_fct prompter;
1386 void *prompter_data;
1387 };
1388
1389 static int
hx_pass_prompter(void * data,const hx509_prompt * prompter)1390 hx_pass_prompter(void *data, const hx509_prompt *prompter)
1391 {
1392 krb5_error_code ret;
1393 krb5_prompt prompt;
1394 krb5_data password_data;
1395 struct prompter *p = data;
1396
1397 password_data.data = prompter->reply.data;
1398 password_data.length = prompter->reply.length;
1399
1400 prompt.prompt = prompter->prompt;
1401 prompt.hidden = hx509_prompt_hidden(prompter->type);
1402 prompt.reply = &password_data;
1403
1404 switch (prompter->type) {
1405 case HX509_PROMPT_TYPE_INFO:
1406 prompt.type = KRB5_PROMPT_TYPE_INFO;
1407 break;
1408 case HX509_PROMPT_TYPE_PASSWORD:
1409 case HX509_PROMPT_TYPE_QUESTION:
1410 default:
1411 prompt.type = KRB5_PROMPT_TYPE_PASSWORD;
1412 break;
1413 }
1414
1415 ret = (*p->prompter)(p->context, p->prompter_data, NULL, NULL, 1, &prompt);
1416 if (ret) {
1417 memset (prompter->reply.data, 0, prompter->reply.length);
1418 return 1;
1419 }
1420 return 0;
1421 }
1422
1423
1424 void KRB5_LIB_FUNCTION
_krb5_pk_allow_proxy_certificate(struct krb5_pk_identity * id,int boolean)1425 _krb5_pk_allow_proxy_certificate(struct krb5_pk_identity *id,
1426 int boolean)
1427 {
1428 hx509_verify_set_proxy_certificate(id->verify_ctx, boolean);
1429 }
1430
1431
1432 krb5_error_code KRB5_LIB_FUNCTION
_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)1433 _krb5_pk_load_id(krb5_context context,
1434 struct krb5_pk_identity **ret_id,
1435 const char *user_id,
1436 const char *anchor_id,
1437 char * const *chain_list,
1438 char * const *revoke_list,
1439 krb5_prompter_fct prompter,
1440 void *prompter_data,
1441 char *password)
1442 {
1443 struct krb5_pk_identity *id = NULL;
1444 hx509_lock lock = NULL;
1445 struct prompter p;
1446 int ret;
1447
1448 *ret_id = NULL;
1449
1450 if (anchor_id == NULL) {
1451 krb5_set_error_string(context, "PKINIT: No anchor given");
1452 return HEIM_PKINIT_NO_VALID_CA;
1453 }
1454
1455 if (user_id == NULL) {
1456 krb5_set_error_string(context,
1457 "PKINIT: No user certificate given");
1458 return HEIM_PKINIT_NO_PRIVATE_KEY;
1459 }
1460
1461 /* load cert */
1462
1463 id = calloc(1, sizeof(*id));
1464 if (id == NULL) {
1465 krb5_set_error_string(context, "malloc: out of memory");
1466 return ENOMEM;
1467 }
1468
1469 ret = hx509_context_init(&id->hx509ctx);
1470 if (ret)
1471 goto out;
1472
1473 ret = hx509_lock_init(id->hx509ctx, &lock);
1474 if (password && password[0])
1475 hx509_lock_add_password(lock, password);
1476
1477 if (prompter) {
1478 p.context = context;
1479 p.prompter = prompter;
1480 p.prompter_data = prompter_data;
1481
1482 ret = hx509_lock_set_prompter(lock, hx_pass_prompter, &p);
1483 if (ret)
1484 goto out;
1485 }
1486
1487 ret = hx509_certs_init(id->hx509ctx, user_id, 0, lock, &id->certs);
1488 if (ret) {
1489 _krb5_pk_copy_error(context, id->hx509ctx, ret,
1490 "Failed to init cert certs");
1491 goto out;
1492 }
1493
1494 ret = hx509_certs_init(id->hx509ctx, anchor_id, 0, NULL, &id->anchors);
1495 if (ret) {
1496 _krb5_pk_copy_error(context, id->hx509ctx, ret,
1497 "Failed to init anchors");
1498 goto out;
1499 }
1500
1501 ret = hx509_certs_init(id->hx509ctx, "MEMORY:pkinit-cert-chain",
1502 0, NULL, &id->certpool);
1503 if (ret) {
1504 _krb5_pk_copy_error(context, id->hx509ctx, ret,
1505 "Failed to init chain");
1506 goto out;
1507 }
1508
1509 while (chain_list && *chain_list) {
1510 ret = hx509_certs_append(id->hx509ctx, id->certpool,
1511 NULL, *chain_list);
1512 if (ret) {
1513 _krb5_pk_copy_error(context, id->hx509ctx, ret,
1514 "Failed to laod chain %s",
1515 *chain_list);
1516 goto out;
1517 }
1518 chain_list++;
1519 }
1520
1521 if (revoke_list) {
1522 ret = hx509_revoke_init(id->hx509ctx, &id->revokectx);
1523 if (ret) {
1524 _krb5_pk_copy_error(context, id->hx509ctx, ret,
1525 "Failed init revoke list");
1526 goto out;
1527 }
1528
1529 while (*revoke_list) {
1530 ret = hx509_revoke_add_crl(id->hx509ctx,
1531 id->revokectx,
1532 *revoke_list);
1533 if (ret) {
1534 _krb5_pk_copy_error(context, id->hx509ctx, ret,
1535 "Failed load revoke list");
1536 goto out;
1537 }
1538 revoke_list++;
1539 }
1540 } else
1541 hx509_context_set_missing_revoke(id->hx509ctx, 1);
1542
1543 ret = hx509_verify_init_ctx(id->hx509ctx, &id->verify_ctx);
1544 if (ret) {
1545 _krb5_pk_copy_error(context, id->hx509ctx, ret,
1546 "Failed init verify context");
1547 goto out;
1548 }
1549
1550 hx509_verify_attach_anchors(id->verify_ctx, id->anchors);
1551 hx509_verify_attach_revoke(id->verify_ctx, id->revokectx);
1552
1553 out:
1554 if (ret) {
1555 hx509_verify_destroy_ctx(id->verify_ctx);
1556 hx509_certs_free(&id->certs);
1557 hx509_certs_free(&id->anchors);
1558 hx509_certs_free(&id->certpool);
1559 hx509_revoke_free(&id->revokectx);
1560 hx509_context_free(&id->hx509ctx);
1561 free(id);
1562 } else
1563 *ret_id = id;
1564
1565 hx509_lock_free(lock);
1566
1567 return ret;
1568 }
1569
1570 static krb5_error_code
select_dh_group(krb5_context context,DH * dh,unsigned long bits,struct krb5_dh_moduli ** moduli)1571 select_dh_group(krb5_context context, DH *dh, unsigned long bits,
1572 struct krb5_dh_moduli **moduli)
1573 {
1574 const struct krb5_dh_moduli *m;
1575
1576 if (bits == 0) {
1577 m = moduli[1]; /* XXX */
1578 if (m == NULL)
1579 m = moduli[0]; /* XXX */
1580 } else {
1581 int i;
1582 for (i = 0; moduli[i] != NULL; i++) {
1583 if (bits < moduli[i]->bits)
1584 break;
1585 }
1586 if (moduli[i] == NULL) {
1587 krb5_set_error_string(context,
1588 "Did not find a DH group parameter "
1589 "matching requirement of %lu bits",
1590 bits);
1591 return EINVAL;
1592 }
1593 m = moduli[i];
1594 }
1595
1596 dh->p = integer_to_BN(context, "p", &m->p);
1597 if (dh->p == NULL)
1598 return ENOMEM;
1599 dh->g = integer_to_BN(context, "g", &m->g);
1600 if (dh->g == NULL)
1601 return ENOMEM;
1602 dh->q = integer_to_BN(context, "q", &m->q);
1603 if (dh->q == NULL)
1604 return ENOMEM;
1605
1606 return 0;
1607 }
1608
1609 #endif /* PKINIT */
1610
1611 static int
parse_integer(krb5_context context,char ** p,const char * file,int lineno,const char * name,heim_integer * integer)1612 parse_integer(krb5_context context, char **p, const char *file, int lineno,
1613 const char *name, heim_integer *integer)
1614 {
1615 int ret;
1616 char *p1;
1617 p1 = strsep(p, " \t");
1618 if (p1 == NULL) {
1619 krb5_set_error_string(context, "moduli file %s missing %s on line %d",
1620 file, name, lineno);
1621 return EINVAL;
1622 }
1623 ret = der_parse_hex_heim_integer(p1, integer);
1624 if (ret) {
1625 krb5_set_error_string(context, "moduli file %s failed parsing %s "
1626 "on line %d",
1627 file, name, lineno);
1628 return ret;
1629 }
1630
1631 return 0;
1632 }
1633
1634 krb5_error_code
_krb5_parse_moduli_line(krb5_context context,const char * file,int lineno,char * p,struct krb5_dh_moduli ** m)1635 _krb5_parse_moduli_line(krb5_context context,
1636 const char *file,
1637 int lineno,
1638 char *p,
1639 struct krb5_dh_moduli **m)
1640 {
1641 struct krb5_dh_moduli *m1;
1642 char *p1;
1643 int ret;
1644
1645 *m = NULL;
1646
1647 m1 = calloc(1, sizeof(*m1));
1648 if (m1 == NULL) {
1649 krb5_set_error_string(context, "malloc - out of memory");
1650 return ENOMEM;
1651 }
1652
1653 while (isspace((unsigned char)*p))
1654 p++;
1655 if (*p == '#')
1656 return 0;
1657 ret = EINVAL;
1658
1659 p1 = strsep(&p, " \t");
1660 if (p1 == NULL) {
1661 krb5_set_error_string(context, "moduli file %s missing name "
1662 "on line %d", file, lineno);
1663 goto out;
1664 }
1665 m1->name = strdup(p1);
1666 if (p1 == NULL) {
1667 krb5_set_error_string(context, "malloc - out of memeory");
1668 ret = ENOMEM;
1669 goto out;
1670 }
1671
1672 p1 = strsep(&p, " \t");
1673 if (p1 == NULL) {
1674 krb5_set_error_string(context, "moduli file %s missing bits on line %d",
1675 file, lineno);
1676 goto out;
1677 }
1678
1679 m1->bits = atoi(p1);
1680 if (m1->bits == 0) {
1681 krb5_set_error_string(context, "moduli file %s have un-parsable "
1682 "bits on line %d", file, lineno);
1683 goto out;
1684 }
1685
1686 ret = parse_integer(context, &p, file, lineno, "p", &m1->p);
1687 if (ret)
1688 goto out;
1689 ret = parse_integer(context, &p, file, lineno, "g", &m1->g);
1690 if (ret)
1691 goto out;
1692 ret = parse_integer(context, &p, file, lineno, "q", &m1->q);
1693 if (ret)
1694 goto out;
1695
1696 *m = m1;
1697
1698 return 0;
1699 out:
1700 free(m1->name);
1701 der_free_heim_integer(&m1->p);
1702 der_free_heim_integer(&m1->g);
1703 der_free_heim_integer(&m1->q);
1704 free(m1);
1705 return ret;
1706 }
1707
1708 void
_krb5_free_moduli(struct krb5_dh_moduli ** moduli)1709 _krb5_free_moduli(struct krb5_dh_moduli **moduli)
1710 {
1711 int i;
1712 for (i = 0; moduli[i] != NULL; i++) {
1713 free(moduli[i]->name);
1714 der_free_heim_integer(&moduli[i]->p);
1715 der_free_heim_integer(&moduli[i]->g);
1716 der_free_heim_integer(&moduli[i]->q);
1717 free(moduli[i]);
1718 }
1719 free(moduli);
1720 }
1721
1722 static const char *default_moduli_RFC2412_MODP_group2 =
1723 /* name */
1724 "RFC2412-MODP-group2 "
1725 /* bits */
1726 "1024 "
1727 /* p */
1728 "FFFFFFFF" "FFFFFFFF" "C90FDAA2" "2168C234" "C4C6628B" "80DC1CD1"
1729 "29024E08" "8A67CC74" "020BBEA6" "3B139B22" "514A0879" "8E3404DD"
1730 "EF9519B3" "CD3A431B" "302B0A6D" "F25F1437" "4FE1356D" "6D51C245"
1731 "E485B576" "625E7EC6" "F44C42E9" "A637ED6B" "0BFF5CB6" "F406B7ED"
1732 "EE386BFB" "5A899FA5" "AE9F2411" "7C4B1FE6" "49286651" "ECE65381"
1733 "FFFFFFFF" "FFFFFFFF "
1734 /* g */
1735 "02 "
1736 /* q */
1737 "7FFFFFFF" "FFFFFFFF" "E487ED51" "10B4611A" "62633145" "C06E0E68"
1738 "94812704" "4533E63A" "0105DF53" "1D89CD91" "28A5043C" "C71A026E"
1739 "F7CA8CD9" "E69D218D" "98158536" "F92F8A1B" "A7F09AB6" "B6A8E122"
1740 "F242DABB" "312F3F63" "7A262174" "D31BF6B5" "85FFAE5B" "7A035BF6"
1741 "F71C35FD" "AD44CFD2" "D74F9208" "BE258FF3" "24943328" "F67329C0"
1742 "FFFFFFFF" "FFFFFFFF";
1743
1744 static const char *default_moduli_rfc3526_MODP_group14 =
1745 /* name */
1746 "rfc3526-MODP-group14 "
1747 /* bits */
1748 "1760 "
1749 /* p */
1750 "FFFFFFFF" "FFFFFFFF" "C90FDAA2" "2168C234" "C4C6628B" "80DC1CD1"
1751 "29024E08" "8A67CC74" "020BBEA6" "3B139B22" "514A0879" "8E3404DD"
1752 "EF9519B3" "CD3A431B" "302B0A6D" "F25F1437" "4FE1356D" "6D51C245"
1753 "E485B576" "625E7EC6" "F44C42E9" "A637ED6B" "0BFF5CB6" "F406B7ED"
1754 "EE386BFB" "5A899FA5" "AE9F2411" "7C4B1FE6" "49286651" "ECE45B3D"
1755 "C2007CB8" "A163BF05" "98DA4836" "1C55D39A" "69163FA8" "FD24CF5F"
1756 "83655D23" "DCA3AD96" "1C62F356" "208552BB" "9ED52907" "7096966D"
1757 "670C354E" "4ABC9804" "F1746C08" "CA18217C" "32905E46" "2E36CE3B"
1758 "E39E772C" "180E8603" "9B2783A2" "EC07A28F" "B5C55DF0" "6F4C52C9"
1759 "DE2BCBF6" "95581718" "3995497C" "EA956AE5" "15D22618" "98FA0510"
1760 "15728E5A" "8AACAA68" "FFFFFFFF" "FFFFFFFF "
1761 /* g */
1762 "02 "
1763 /* q */
1764 "7FFFFFFF" "FFFFFFFF" "E487ED51" "10B4611A" "62633145" "C06E0E68"
1765 "94812704" "4533E63A" "0105DF53" "1D89CD91" "28A5043C" "C71A026E"
1766 "F7CA8CD9" "E69D218D" "98158536" "F92F8A1B" "A7F09AB6" "B6A8E122"
1767 "F242DABB" "312F3F63" "7A262174" "D31BF6B5" "85FFAE5B" "7A035BF6"
1768 "F71C35FD" "AD44CFD2" "D74F9208" "BE258FF3" "24943328" "F6722D9E"
1769 "E1003E5C" "50B1DF82" "CC6D241B" "0E2AE9CD" "348B1FD4" "7E9267AF"
1770 "C1B2AE91" "EE51D6CB" "0E3179AB" "1042A95D" "CF6A9483" "B84B4B36"
1771 "B3861AA7" "255E4C02" "78BA3604" "650C10BE" "19482F23" "171B671D"
1772 "F1CF3B96" "0C074301" "CD93C1D1" "7603D147" "DAE2AEF8" "37A62964"
1773 "EF15E5FB" "4AAC0B8C" "1CCAA4BE" "754AB572" "8AE9130C" "4C7D0288"
1774 "0AB9472D" "45565534" "7FFFFFFF" "FFFFFFFF";
1775
1776 krb5_error_code
_krb5_parse_moduli(krb5_context context,const char * file,struct krb5_dh_moduli *** moduli)1777 _krb5_parse_moduli(krb5_context context, const char *file,
1778 struct krb5_dh_moduli ***moduli)
1779 {
1780 /* name bits P G Q */
1781 krb5_error_code ret;
1782 struct krb5_dh_moduli **m = NULL, **m2;
1783 char buf[4096];
1784 FILE *f;
1785 int lineno = 0, n = 0;
1786
1787 *moduli = NULL;
1788
1789 m = calloc(1, sizeof(m[0]) * 3);
1790 if (m == NULL) {
1791 krb5_set_error_string(context, "malloc: out of memory");
1792 return ENOMEM;
1793 }
1794
1795 strlcpy(buf, default_moduli_rfc3526_MODP_group14, sizeof(buf));
1796 ret = _krb5_parse_moduli_line(context, "builtin", 1, buf, &m[0]);
1797 if (ret) {
1798 _krb5_free_moduli(m);
1799 return ret;
1800 }
1801 n++;
1802
1803 strlcpy(buf, default_moduli_RFC2412_MODP_group2, sizeof(buf));
1804 ret = _krb5_parse_moduli_line(context, "builtin", 1, buf, &m[1]);
1805 if (ret) {
1806 _krb5_free_moduli(m);
1807 return ret;
1808 }
1809 n++;
1810
1811
1812 if (file == NULL)
1813 file = MODULI_FILE;
1814
1815 f = fopen(file, "r");
1816 if (f == NULL) {
1817 *moduli = m;
1818 return 0;
1819 }
1820
1821 while(fgets(buf, sizeof(buf), f) != NULL) {
1822 struct krb5_dh_moduli *element;
1823
1824 buf[strcspn(buf, "\n")] = '\0';
1825 lineno++;
1826
1827 m2 = realloc(m, (n + 2) * sizeof(m[0]));
1828 if (m2 == NULL) {
1829 krb5_set_error_string(context, "malloc: out of memory");
1830 _krb5_free_moduli(m);
1831 return ENOMEM;
1832 }
1833 m = m2;
1834
1835 m[n] = NULL;
1836
1837 ret = _krb5_parse_moduli_line(context, file, lineno, buf, &element);
1838 if (ret) {
1839 _krb5_free_moduli(m);
1840 return ret;
1841 }
1842 if (element == NULL)
1843 continue;
1844
1845 m[n] = element;
1846 m[n + 1] = NULL;
1847 n++;
1848 }
1849 *moduli = m;
1850 return 0;
1851 }
1852
1853 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)1854 _krb5_dh_group_ok(krb5_context context, unsigned long bits,
1855 heim_integer *p, heim_integer *g, heim_integer *q,
1856 struct krb5_dh_moduli **moduli,
1857 char **name)
1858 {
1859 int i;
1860
1861 if (name)
1862 *name = NULL;
1863
1864 for (i = 0; moduli[i] != NULL; i++) {
1865 if (der_heim_integer_cmp(&moduli[i]->g, g) == 0 &&
1866 der_heim_integer_cmp(&moduli[i]->p, p) == 0 &&
1867 (q == NULL || der_heim_integer_cmp(&moduli[i]->q, q) == 0))
1868 {
1869 if (bits && bits > moduli[i]->bits) {
1870 krb5_set_error_string(context, "PKINIT: DH group parameter %s "
1871 "no accepted, not enough bits generated",
1872 moduli[i]->name);
1873 return KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED;
1874 }
1875 if (name)
1876 *name = strdup(moduli[i]->name);
1877 return 0;
1878 }
1879 }
1880 krb5_set_error_string(context, "PKINIT: DH group parameter no ok");
1881 return KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED;
1882 }
1883
1884 void KRB5_LIB_FUNCTION
_krb5_get_init_creds_opt_free_pkinit(krb5_get_init_creds_opt * opt)1885 _krb5_get_init_creds_opt_free_pkinit(krb5_get_init_creds_opt *opt)
1886 {
1887 #ifdef PKINIT
1888 krb5_pk_init_ctx ctx;
1889
1890 if (opt->opt_private == NULL || opt->opt_private->pk_init_ctx == NULL)
1891 return;
1892 ctx = opt->opt_private->pk_init_ctx;
1893 if (ctx->dh)
1894 DH_free(ctx->dh);
1895 ctx->dh = NULL;
1896 if (ctx->id) {
1897 hx509_verify_destroy_ctx(ctx->id->verify_ctx);
1898 hx509_certs_free(&ctx->id->certs);
1899 hx509_certs_free(&ctx->id->anchors);
1900 hx509_certs_free(&ctx->id->certpool);
1901 hx509_context_free(&ctx->id->hx509ctx);
1902
1903 if (ctx->clientDHNonce) {
1904 krb5_free_data(NULL, ctx->clientDHNonce);
1905 ctx->clientDHNonce = NULL;
1906 }
1907 if (ctx->m)
1908 _krb5_free_moduli(ctx->m);
1909 free(ctx->id);
1910 ctx->id = NULL;
1911 }
1912 free(opt->opt_private->pk_init_ctx);
1913 opt->opt_private->pk_init_ctx = NULL;
1914 #endif
1915 }
1916
1917 krb5_error_code KRB5_LIB_FUNCTION
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)1918 krb5_get_init_creds_opt_set_pkinit(krb5_context context,
1919 krb5_get_init_creds_opt *opt,
1920 krb5_principal principal,
1921 const char *user_id,
1922 const char *x509_anchors,
1923 char * const * pool,
1924 char * const * pki_revoke,
1925 int flags,
1926 krb5_prompter_fct prompter,
1927 void *prompter_data,
1928 char *password)
1929 {
1930 #ifdef PKINIT
1931 krb5_error_code ret;
1932 char *anchors = NULL;
1933
1934 if (opt->opt_private == NULL) {
1935 krb5_set_error_string(context, "PKINIT: on non extendable opt");
1936 return EINVAL;
1937 }
1938
1939 opt->opt_private->pk_init_ctx =
1940 calloc(1, sizeof(*opt->opt_private->pk_init_ctx));
1941 if (opt->opt_private->pk_init_ctx == NULL) {
1942 krb5_set_error_string(context, "malloc: out of memory");
1943 return ENOMEM;
1944 }
1945 opt->opt_private->pk_init_ctx->dh = NULL;
1946 opt->opt_private->pk_init_ctx->id = NULL;
1947 opt->opt_private->pk_init_ctx->clientDHNonce = NULL;
1948 opt->opt_private->pk_init_ctx->require_binding = 0;
1949 opt->opt_private->pk_init_ctx->require_eku = 1;
1950 opt->opt_private->pk_init_ctx->require_krbtgt_otherName = 1;
1951 opt->opt_private->pk_init_ctx->peer = NULL;
1952
1953 /* XXX implement krb5_appdefault_strings */
1954 if (pool == NULL)
1955 pool = krb5_config_get_strings(context, NULL,
1956 "appdefaults",
1957 "pkinit_pool",
1958 NULL);
1959
1960 if (pki_revoke == NULL)
1961 pki_revoke = krb5_config_get_strings(context, NULL,
1962 "appdefaults",
1963 "pkinit_revoke",
1964 NULL);
1965
1966 if (x509_anchors == NULL) {
1967 krb5_appdefault_string(context, "kinit",
1968 krb5_principal_get_realm(context, principal),
1969 "pkinit_anchors", NULL, &anchors);
1970 x509_anchors = anchors;
1971 }
1972
1973 ret = _krb5_pk_load_id(context,
1974 &opt->opt_private->pk_init_ctx->id,
1975 user_id,
1976 x509_anchors,
1977 pool,
1978 pki_revoke,
1979 prompter,
1980 prompter_data,
1981 password);
1982 if (ret) {
1983 free(opt->opt_private->pk_init_ctx);
1984 opt->opt_private->pk_init_ctx = NULL;
1985 return ret;
1986 }
1987
1988 if ((flags & 2) == 0) {
1989 const char *moduli_file;
1990 unsigned long dh_min_bits;
1991
1992 moduli_file = krb5_config_get_string(context, NULL,
1993 "libdefaults",
1994 "moduli",
1995 NULL);
1996
1997 dh_min_bits =
1998 krb5_config_get_int_default(context, NULL, 0,
1999 "libdefaults",
2000 "pkinit_dh_min_bits",
2001 NULL);
2002
2003 ret = _krb5_parse_moduli(context, moduli_file,
2004 &opt->opt_private->pk_init_ctx->m);
2005 if (ret) {
2006 _krb5_get_init_creds_opt_free_pkinit(opt);
2007 return ret;
2008 }
2009
2010 opt->opt_private->pk_init_ctx->dh = DH_new();
2011 if (opt->opt_private->pk_init_ctx->dh == NULL) {
2012 krb5_set_error_string(context, "malloc: out of memory");
2013 _krb5_get_init_creds_opt_free_pkinit(opt);
2014 return ENOMEM;
2015 }
2016
2017 ret = select_dh_group(context, opt->opt_private->pk_init_ctx->dh,
2018 dh_min_bits,
2019 opt->opt_private->pk_init_ctx->m);
2020 if (ret) {
2021 _krb5_get_init_creds_opt_free_pkinit(opt);
2022 return ret;
2023 }
2024
2025 if (DH_generate_key(opt->opt_private->pk_init_ctx->dh) != 1) {
2026 krb5_set_error_string(context, "pkinit: failed to generate DH key");
2027 _krb5_get_init_creds_opt_free_pkinit(opt);
2028 return ENOMEM;
2029 }
2030 }
2031
2032 return 0;
2033 #else
2034 krb5_set_error_string(context, "no support for PKINIT compiled in");
2035 return EINVAL;
2036 #endif
2037 }
2038
2039 /*
2040 *
2041 */
2042
2043 static void
_krb5_pk_copy_error(krb5_context context,hx509_context hx509ctx,int hxret,const char * fmt,...)2044 _krb5_pk_copy_error(krb5_context context,
2045 hx509_context hx509ctx,
2046 int hxret,
2047 const char *fmt,
2048 ...)
2049 {
2050 va_list va;
2051 char *s, *f;
2052
2053 va_start(va, fmt);
2054 vasprintf(&f, fmt, va);
2055 va_end(va);
2056 if (f == NULL) {
2057 krb5_clear_error_string(context);
2058 return;
2059 }
2060
2061 s = hx509_get_error_string(hx509ctx, hxret);
2062 if (s == NULL) {
2063 krb5_clear_error_string(context);
2064 free(f);
2065 return;
2066 }
2067 krb5_set_error_string(context, "%s: %s", f, s);
2068 free(s);
2069 free(f);
2070 }
2071