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