1 /* $NetBSD: pkinit.c,v 1.1.1.2 2014/04/24 12:45:27 pettai Exp $ */
2
3 /*
4 * Copyright (c) 2003 - 2008 Kungliga Tekniska Högskolan
5 * (Royal Institute of Technology, Stockholm, Sweden).
6 * All rights reserved.
7 *
8 * Portions Copyright (c) 2009 Apple Inc. All rights reserved.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 *
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 *
17 * 2. Redistributions in binary form must reproduce the above copyright
18 * notice, this list of conditions and the following disclaimer in the
19 * documentation and/or other materials provided with the distribution.
20 *
21 * 3. Neither the name of the Institute nor the names of its contributors
22 * may be used to endorse or promote products derived from this software
23 * without specific prior written permission.
24 *
25 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
29 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35 * SUCH DAMAGE.
36 */
37
38 #include "kdc_locl.h"
39
40 #ifdef PKINIT
41
42 #include <krb5/heim_asn1.h>
43 #include <krb5/rfc2459_asn1.h>
44 #include <krb5/cms_asn1.h>
45 #include <krb5/pkinit_asn1.h>
46
47 #include <krb5/hx509.h>
48 #include "crypto-headers.h"
49
50 struct pk_client_params {
51 enum krb5_pk_type type;
52 enum { USE_RSA, USE_DH, USE_ECDH } keyex;
53 union {
54 struct {
55 BIGNUM *public_key;
56 DH *key;
57 } dh;
58 #ifdef HAVE_OPENSSL
59 struct {
60 EC_KEY *public_key;
61 EC_KEY *key;
62 } ecdh;
63 #endif
64 } u;
65 hx509_cert cert;
66 unsigned nonce;
67 EncryptionKey reply_key;
68 char *dh_group_name;
69 hx509_peer_info peer;
70 hx509_certs client_anchors;
71 hx509_verify_ctx verify_ctx;
72 };
73
74 struct pk_principal_mapping {
75 unsigned int len;
76 struct pk_allowed_princ {
77 krb5_principal principal;
78 char *subject;
79 } *val;
80 };
81
82 static struct krb5_pk_identity *kdc_identity;
83 static struct pk_principal_mapping principal_mappings;
84 static struct krb5_dh_moduli **moduli;
85
86 static struct {
87 krb5_data data;
88 time_t expire;
89 time_t next_update;
90 } ocsp;
91
92 /*
93 *
94 */
95
96 static krb5_error_code
pk_check_pkauthenticator_win2k(krb5_context context,PKAuthenticator_Win2k * a,const KDC_REQ * req)97 pk_check_pkauthenticator_win2k(krb5_context context,
98 PKAuthenticator_Win2k *a,
99 const KDC_REQ *req)
100 {
101 krb5_timestamp now;
102
103 krb5_timeofday (context, &now);
104
105 /* XXX cusec */
106 if (a->ctime == 0 || abs(a->ctime - now) > context->max_skew) {
107 krb5_clear_error_message(context);
108 return KRB5KRB_AP_ERR_SKEW;
109 }
110 return 0;
111 }
112
113 static krb5_error_code
pk_check_pkauthenticator(krb5_context context,PKAuthenticator * a,const KDC_REQ * req)114 pk_check_pkauthenticator(krb5_context context,
115 PKAuthenticator *a,
116 const KDC_REQ *req)
117 {
118 u_char *buf = NULL;
119 size_t buf_size;
120 krb5_error_code ret;
121 size_t len = 0;
122 krb5_timestamp now;
123 Checksum checksum;
124
125 krb5_timeofday (context, &now);
126
127 /* XXX cusec */
128 if (a->ctime == 0 || abs(a->ctime - now) > context->max_skew) {
129 krb5_clear_error_message(context);
130 return KRB5KRB_AP_ERR_SKEW;
131 }
132
133 ASN1_MALLOC_ENCODE(KDC_REQ_BODY, buf, buf_size, &req->req_body, &len, ret);
134 if (ret) {
135 krb5_clear_error_message(context);
136 return ret;
137 }
138 if (buf_size != len)
139 krb5_abortx(context, "Internal error in ASN.1 encoder");
140
141 ret = krb5_create_checksum(context,
142 NULL,
143 0,
144 CKSUMTYPE_SHA1,
145 buf,
146 len,
147 &checksum);
148 free(buf);
149 if (ret) {
150 krb5_clear_error_message(context);
151 return ret;
152 }
153
154 if (a->paChecksum == NULL) {
155 krb5_clear_error_message(context);
156 ret = KRB5_KDC_ERR_PA_CHECKSUM_MUST_BE_INCLUDED;
157 goto out;
158 }
159
160 if (der_heim_octet_string_cmp(a->paChecksum, &checksum.checksum) != 0) {
161 krb5_clear_error_message(context);
162 ret = KRB5KRB_ERR_GENERIC;
163 }
164
165 out:
166 free_Checksum(&checksum);
167
168 return ret;
169 }
170
171 void
_kdc_pk_free_client_param(krb5_context context,pk_client_params * cp)172 _kdc_pk_free_client_param(krb5_context context, pk_client_params *cp)
173 {
174 if (cp == NULL)
175 return;
176 if (cp->cert)
177 hx509_cert_free(cp->cert);
178 if (cp->verify_ctx)
179 hx509_verify_destroy_ctx(cp->verify_ctx);
180 if (cp->keyex == USE_DH) {
181 if (cp->u.dh.key)
182 DH_free(cp->u.dh.key);
183 if (cp->u.dh.public_key)
184 BN_free(cp->u.dh.public_key);
185 }
186 #ifdef HAVE_OPENSSL
187 if (cp->keyex == USE_ECDH) {
188 if (cp->u.ecdh.key)
189 EC_KEY_free(cp->u.ecdh.key);
190 if (cp->u.ecdh.public_key)
191 EC_KEY_free(cp->u.ecdh.public_key);
192 }
193 #endif
194 krb5_free_keyblock_contents(context, &cp->reply_key);
195 if (cp->dh_group_name)
196 free(cp->dh_group_name);
197 if (cp->peer)
198 hx509_peer_info_free(cp->peer);
199 if (cp->client_anchors)
200 hx509_certs_free(&cp->client_anchors);
201 memset(cp, 0, sizeof(*cp));
202 free(cp);
203 }
204
205 static krb5_error_code
generate_dh_keyblock(krb5_context context,pk_client_params * client_params,krb5_enctype enctype)206 generate_dh_keyblock(krb5_context context,
207 pk_client_params *client_params,
208 krb5_enctype enctype)
209 {
210 unsigned char *dh_gen_key = NULL;
211 krb5_keyblock key;
212 krb5_error_code ret;
213 size_t dh_gen_keylen, size;
214
215 memset(&key, 0, sizeof(key));
216
217 if (client_params->keyex == USE_DH) {
218
219 if (client_params->u.dh.public_key == NULL) {
220 ret = KRB5KRB_ERR_GENERIC;
221 krb5_set_error_message(context, ret, "public_key");
222 goto out;
223 }
224
225 if (!DH_generate_key(client_params->u.dh.key)) {
226 ret = KRB5KRB_ERR_GENERIC;
227 krb5_set_error_message(context, ret,
228 "Can't generate Diffie-Hellman keys");
229 goto out;
230 }
231
232 size = DH_size(client_params->u.dh.key);
233
234 dh_gen_key = malloc(size);
235 if (dh_gen_key == NULL) {
236 ret = ENOMEM;
237 krb5_set_error_message(context, ret, "malloc: out of memory");
238 goto out;
239 }
240
241 dh_gen_keylen = DH_compute_key(dh_gen_key,client_params->u.dh.public_key, client_params->u.dh.key);
242 if (dh_gen_keylen == (size_t)-1) {
243 ret = KRB5KRB_ERR_GENERIC;
244 krb5_set_error_message(context, ret,
245 "Can't compute Diffie-Hellman key");
246 goto out;
247 }
248 if (dh_gen_keylen < size) {
249 size -= dh_gen_keylen;
250 memmove(dh_gen_key + size, dh_gen_key, dh_gen_keylen);
251 memset(dh_gen_key, 0, size);
252 }
253
254 ret = 0;
255 #ifdef HAVE_OPENSSL
256 } else if (client_params->keyex == USE_ECDH) {
257
258 if (client_params->u.ecdh.public_key == NULL) {
259 ret = KRB5KRB_ERR_GENERIC;
260 krb5_set_error_message(context, ret, "public_key");
261 goto out;
262 }
263
264 client_params->u.ecdh.key = EC_KEY_new();
265 if (client_params->u.ecdh.key == NULL) {
266 ret = ENOMEM;
267 goto out;
268 }
269 EC_KEY_set_group(client_params->u.ecdh.key,
270 EC_KEY_get0_group(client_params->u.ecdh.public_key));
271
272 if (EC_KEY_generate_key(client_params->u.ecdh.key) != 1) {
273 ret = ENOMEM;
274 goto out;
275 }
276
277 size = (EC_GROUP_get_degree(EC_KEY_get0_group(client_params->u.ecdh.key)) + 7) / 8;
278 dh_gen_key = malloc(size);
279 if (dh_gen_key == NULL) {
280 ret = ENOMEM;
281 krb5_set_error_message(context, ret,
282 N_("malloc: out of memory", ""));
283 goto out;
284 }
285
286 dh_gen_keylen = ECDH_compute_key(dh_gen_key, size,
287 EC_KEY_get0_public_key(client_params->u.ecdh.public_key),
288 client_params->u.ecdh.key, NULL);
289
290 #endif /* HAVE_OPENSSL */
291 } else {
292 ret = KRB5KRB_ERR_GENERIC;
293 krb5_set_error_message(context, ret,
294 "Diffie-Hellman not selected keys");
295 goto out;
296 }
297
298 ret = _krb5_pk_octetstring2key(context,
299 enctype,
300 dh_gen_key, dh_gen_keylen,
301 NULL, NULL,
302 &client_params->reply_key);
303
304 out:
305 if (dh_gen_key)
306 free(dh_gen_key);
307 if (key.keyvalue.data)
308 krb5_free_keyblock_contents(context, &key);
309
310 return ret;
311 }
312
313 static BIGNUM *
integer_to_BN(krb5_context context,const char * field,heim_integer * f)314 integer_to_BN(krb5_context context, const char *field, heim_integer *f)
315 {
316 BIGNUM *bn;
317
318 bn = BN_bin2bn((const unsigned char *)f->data, f->length, NULL);
319 if (bn == NULL) {
320 krb5_set_error_message(context, KRB5_BADMSGTYPE,
321 "PKINIT: parsing BN failed %s", field);
322 return NULL;
323 }
324 BN_set_negative(bn, f->negative);
325 return bn;
326 }
327
328 static krb5_error_code
get_dh_param(krb5_context context,krb5_kdc_configuration * config,SubjectPublicKeyInfo * dh_key_info,pk_client_params * client_params)329 get_dh_param(krb5_context context,
330 krb5_kdc_configuration *config,
331 SubjectPublicKeyInfo *dh_key_info,
332 pk_client_params *client_params)
333 {
334 DomainParameters dhparam;
335 DH *dh = NULL;
336 krb5_error_code ret;
337
338 memset(&dhparam, 0, sizeof(dhparam));
339
340 if ((dh_key_info->subjectPublicKey.length % 8) != 0) {
341 ret = KRB5_BADMSGTYPE;
342 krb5_set_error_message(context, ret,
343 "PKINIT: subjectPublicKey not aligned "
344 "to 8 bit boundary");
345 goto out;
346 }
347
348 if (dh_key_info->algorithm.parameters == NULL) {
349 krb5_set_error_message(context, KRB5_BADMSGTYPE,
350 "PKINIT missing algorithm parameter "
351 "in clientPublicValue");
352 return KRB5_BADMSGTYPE;
353 }
354
355 ret = decode_DomainParameters(dh_key_info->algorithm.parameters->data,
356 dh_key_info->algorithm.parameters->length,
357 &dhparam,
358 NULL);
359 if (ret) {
360 krb5_set_error_message(context, ret, "Can't decode algorithm "
361 "parameters in clientPublicValue");
362 goto out;
363 }
364
365 ret = _krb5_dh_group_ok(context, config->pkinit_dh_min_bits,
366 &dhparam.p, &dhparam.g, dhparam.q, moduli,
367 &client_params->dh_group_name);
368 if (ret) {
369 /* XXX send back proposal of better group */
370 goto out;
371 }
372
373 dh = DH_new();
374 if (dh == NULL) {
375 ret = ENOMEM;
376 krb5_set_error_message(context, ret, "Cannot create DH structure");
377 goto out;
378 }
379 ret = KRB5_BADMSGTYPE;
380 dh->p = integer_to_BN(context, "DH prime", &dhparam.p);
381 if (dh->p == NULL)
382 goto out;
383 dh->g = integer_to_BN(context, "DH base", &dhparam.g);
384 if (dh->g == NULL)
385 goto out;
386
387 if (dhparam.q) {
388 dh->q = integer_to_BN(context, "DH p-1 factor", dhparam.q);
389 if (dh->g == NULL)
390 goto out;
391 }
392
393 {
394 heim_integer glue;
395 size_t size;
396
397 ret = decode_DHPublicKey(dh_key_info->subjectPublicKey.data,
398 dh_key_info->subjectPublicKey.length / 8,
399 &glue,
400 &size);
401 if (ret) {
402 krb5_clear_error_message(context);
403 return ret;
404 }
405
406 client_params->u.dh.public_key = integer_to_BN(context,
407 "subjectPublicKey",
408 &glue);
409 der_free_heim_integer(&glue);
410 if (client_params->u.dh.public_key == NULL) {
411 ret = KRB5_BADMSGTYPE;
412 goto out;
413 }
414 }
415
416 client_params->u.dh.key = dh;
417 dh = NULL;
418 ret = 0;
419
420 out:
421 if (dh)
422 DH_free(dh);
423 free_DomainParameters(&dhparam);
424 return ret;
425 }
426
427 #ifdef HAVE_OPENSSL
428
429 static krb5_error_code
get_ecdh_param(krb5_context context,krb5_kdc_configuration * config,SubjectPublicKeyInfo * dh_key_info,pk_client_params * client_params)430 get_ecdh_param(krb5_context context,
431 krb5_kdc_configuration *config,
432 SubjectPublicKeyInfo *dh_key_info,
433 pk_client_params *client_params)
434 {
435 ECParameters ecp;
436 EC_KEY *public = NULL;
437 krb5_error_code ret;
438 const unsigned char *p;
439 size_t len;
440 int nid;
441
442 if (dh_key_info->algorithm.parameters == NULL) {
443 krb5_set_error_message(context, KRB5_BADMSGTYPE,
444 "PKINIT missing algorithm parameter "
445 "in clientPublicValue");
446 return KRB5_BADMSGTYPE;
447 }
448
449 memset(&ecp, 0, sizeof(ecp));
450
451 ret = decode_ECParameters(dh_key_info->algorithm.parameters->data,
452 dh_key_info->algorithm.parameters->length, &ecp, &len);
453 if (ret)
454 goto out;
455
456 if (ecp.element != choice_ECParameters_namedCurve) {
457 ret = KRB5_BADMSGTYPE;
458 goto out;
459 }
460
461 if (der_heim_oid_cmp(&ecp.u.namedCurve, &asn1_oid_id_ec_group_secp256r1) == 0)
462 nid = NID_X9_62_prime256v1;
463 else {
464 ret = KRB5_BADMSGTYPE;
465 goto out;
466 }
467
468 /* XXX verify group is ok */
469
470 public = EC_KEY_new_by_curve_name(nid);
471
472 p = dh_key_info->subjectPublicKey.data;
473 len = dh_key_info->subjectPublicKey.length / 8;
474 if (o2i_ECPublicKey(&public, &p, len) == NULL) {
475 ret = KRB5_BADMSGTYPE;
476 krb5_set_error_message(context, ret,
477 "PKINIT failed to decode ECDH key");
478 goto out;
479 }
480 client_params->u.ecdh.public_key = public;
481 public = NULL;
482
483 out:
484 if (public)
485 EC_KEY_free(public);
486 free_ECParameters(&ecp);
487 return ret;
488 }
489
490 #endif /* HAVE_OPENSSL */
491
492 krb5_error_code
_kdc_pk_rd_padata(krb5_context context,krb5_kdc_configuration * config,const KDC_REQ * req,const PA_DATA * pa,hdb_entry_ex * client,pk_client_params ** ret_params)493 _kdc_pk_rd_padata(krb5_context context,
494 krb5_kdc_configuration *config,
495 const KDC_REQ *req,
496 const PA_DATA *pa,
497 hdb_entry_ex *client,
498 pk_client_params **ret_params)
499 {
500 pk_client_params *cp;
501 krb5_error_code ret;
502 heim_oid eContentType = { 0, NULL }, contentInfoOid = { 0, NULL };
503 krb5_data eContent = { 0, NULL };
504 krb5_data signed_content = { 0, NULL };
505 const char *type = "unknown type";
506 hx509_certs trust_anchors;
507 int have_data = 0;
508 const HDB_Ext_PKINIT_cert *pc;
509
510 *ret_params = NULL;
511
512 if (!config->enable_pkinit) {
513 kdc_log(context, config, 0, "PK-INIT request but PK-INIT not enabled");
514 krb5_clear_error_message(context);
515 return 0;
516 }
517
518 cp = calloc(1, sizeof(*cp));
519 if (cp == NULL) {
520 krb5_clear_error_message(context);
521 ret = ENOMEM;
522 goto out;
523 }
524
525 ret = hx509_certs_init(context->hx509ctx,
526 "MEMORY:trust-anchors",
527 0, NULL, &trust_anchors);
528 if (ret) {
529 krb5_set_error_message(context, ret, "failed to create trust anchors");
530 goto out;
531 }
532
533 ret = hx509_certs_merge(context->hx509ctx, trust_anchors,
534 kdc_identity->anchors);
535 if (ret) {
536 hx509_certs_free(&trust_anchors);
537 krb5_set_error_message(context, ret, "failed to create verify context");
538 goto out;
539 }
540
541 /* Add any registered certificates for this client as trust anchors */
542 ret = hdb_entry_get_pkinit_cert(&client->entry, &pc);
543 if (ret == 0 && pc != NULL) {
544 hx509_cert cert;
545 unsigned int i;
546
547 for (i = 0; i < pc->len; i++) {
548 ret = hx509_cert_init_data(context->hx509ctx,
549 pc->val[i].cert.data,
550 pc->val[i].cert.length,
551 &cert);
552 if (ret)
553 continue;
554 hx509_certs_add(context->hx509ctx, trust_anchors, cert);
555 hx509_cert_free(cert);
556 }
557 }
558
559 ret = hx509_verify_init_ctx(context->hx509ctx, &cp->verify_ctx);
560 if (ret) {
561 hx509_certs_free(&trust_anchors);
562 krb5_set_error_message(context, ret, "failed to create verify context");
563 goto out;
564 }
565
566 hx509_verify_set_time(cp->verify_ctx, kdc_time);
567 hx509_verify_attach_anchors(cp->verify_ctx, trust_anchors);
568 hx509_certs_free(&trust_anchors);
569
570 if (config->pkinit_allow_proxy_certs)
571 hx509_verify_set_proxy_certificate(cp->verify_ctx, 1);
572
573 if (pa->padata_type == KRB5_PADATA_PK_AS_REQ_WIN) {
574 PA_PK_AS_REQ_Win2k r;
575
576 type = "PK-INIT-Win2k";
577
578 if (req->req_body.kdc_options.request_anonymous) {
579 ret = KRB5_KDC_ERR_PUBLIC_KEY_ENCRYPTION_NOT_SUPPORTED;
580 krb5_set_error_message(context, ret,
581 "Anon not supported in RSA mode");
582 goto out;
583 }
584
585 ret = decode_PA_PK_AS_REQ_Win2k(pa->padata_value.data,
586 pa->padata_value.length,
587 &r,
588 NULL);
589 if (ret) {
590 krb5_set_error_message(context, ret, "Can't decode "
591 "PK-AS-REQ-Win2k: %d", ret);
592 goto out;
593 }
594
595 ret = hx509_cms_unwrap_ContentInfo(&r.signed_auth_pack,
596 &contentInfoOid,
597 &signed_content,
598 &have_data);
599 free_PA_PK_AS_REQ_Win2k(&r);
600 if (ret) {
601 krb5_set_error_message(context, ret,
602 "Can't unwrap ContentInfo(win): %d", ret);
603 goto out;
604 }
605
606 } else if (pa->padata_type == KRB5_PADATA_PK_AS_REQ) {
607 PA_PK_AS_REQ r;
608
609 type = "PK-INIT-IETF";
610
611 ret = decode_PA_PK_AS_REQ(pa->padata_value.data,
612 pa->padata_value.length,
613 &r,
614 NULL);
615 if (ret) {
616 krb5_set_error_message(context, ret,
617 "Can't decode PK-AS-REQ: %d", ret);
618 goto out;
619 }
620
621 /* XXX look at r.kdcPkId */
622 if (r.trustedCertifiers) {
623 ExternalPrincipalIdentifiers *edi = r.trustedCertifiers;
624 unsigned int i, maxedi;
625
626 ret = hx509_certs_init(context->hx509ctx,
627 "MEMORY:client-anchors",
628 0, NULL,
629 &cp->client_anchors);
630 if (ret) {
631 krb5_set_error_message(context, ret,
632 "Can't allocate client anchors: %d",
633 ret);
634 goto out;
635
636 }
637 /*
638 * If the client sent more then 10 EDI, don't bother
639 * looking more then 10 of performance reasons.
640 */
641 maxedi = edi->len;
642 if (maxedi > 10)
643 maxedi = 10;
644 for (i = 0; i < maxedi; i++) {
645 IssuerAndSerialNumber iasn;
646 hx509_query *q;
647 hx509_cert cert;
648 size_t size;
649
650 if (edi->val[i].issuerAndSerialNumber == NULL)
651 continue;
652
653 ret = hx509_query_alloc(context->hx509ctx, &q);
654 if (ret) {
655 krb5_set_error_message(context, ret,
656 "Failed to allocate hx509_query");
657 goto out;
658 }
659
660 ret = decode_IssuerAndSerialNumber(edi->val[i].issuerAndSerialNumber->data,
661 edi->val[i].issuerAndSerialNumber->length,
662 &iasn,
663 &size);
664 if (ret) {
665 hx509_query_free(context->hx509ctx, q);
666 continue;
667 }
668 ret = hx509_query_match_issuer_serial(q, &iasn.issuer, &iasn.serialNumber);
669 free_IssuerAndSerialNumber(&iasn);
670 if (ret) {
671 hx509_query_free(context->hx509ctx, q);
672 continue;
673 }
674
675 ret = hx509_certs_find(context->hx509ctx,
676 kdc_identity->certs,
677 q,
678 &cert);
679 hx509_query_free(context->hx509ctx, q);
680 if (ret)
681 continue;
682 hx509_certs_add(context->hx509ctx,
683 cp->client_anchors, cert);
684 hx509_cert_free(cert);
685 }
686 }
687
688 ret = hx509_cms_unwrap_ContentInfo(&r.signedAuthPack,
689 &contentInfoOid,
690 &signed_content,
691 &have_data);
692 free_PA_PK_AS_REQ(&r);
693 if (ret) {
694 krb5_set_error_message(context, ret,
695 "Can't unwrap ContentInfo: %d", ret);
696 goto out;
697 }
698
699 } else {
700 krb5_clear_error_message(context);
701 ret = KRB5KDC_ERR_PADATA_TYPE_NOSUPP;
702 goto out;
703 }
704
705 ret = der_heim_oid_cmp(&contentInfoOid, &asn1_oid_id_pkcs7_signedData);
706 if (ret != 0) {
707 ret = KRB5KRB_ERR_GENERIC;
708 krb5_set_error_message(context, ret,
709 "PK-AS-REQ-Win2k invalid content type oid");
710 goto out;
711 }
712
713 if (!have_data) {
714 ret = KRB5KRB_ERR_GENERIC;
715 krb5_set_error_message(context, ret,
716 "PK-AS-REQ-Win2k no signed auth pack");
717 goto out;
718 }
719
720 {
721 hx509_certs signer_certs;
722 int flags = HX509_CMS_VS_ALLOW_DATA_OID_MISMATCH; /* BTMM */
723
724 if (req->req_body.kdc_options.request_anonymous)
725 flags |= HX509_CMS_VS_ALLOW_ZERO_SIGNER;
726
727 ret = hx509_cms_verify_signed(context->hx509ctx,
728 cp->verify_ctx,
729 flags,
730 signed_content.data,
731 signed_content.length,
732 NULL,
733 kdc_identity->certpool,
734 &eContentType,
735 &eContent,
736 &signer_certs);
737 if (ret) {
738 char *s = hx509_get_error_string(context->hx509ctx, ret);
739 krb5_warnx(context, "PKINIT: failed to verify signature: %s: %d",
740 s, ret);
741 free(s);
742 goto out;
743 }
744
745 if (signer_certs) {
746 ret = hx509_get_one_cert(context->hx509ctx, signer_certs,
747 &cp->cert);
748 hx509_certs_free(&signer_certs);
749 }
750 if (ret)
751 goto out;
752 }
753
754 /* Signature is correct, now verify the signed message */
755 if (der_heim_oid_cmp(&eContentType, &asn1_oid_id_pkcs7_data) != 0 &&
756 der_heim_oid_cmp(&eContentType, &asn1_oid_id_pkauthdata) != 0)
757 {
758 ret = KRB5_BADMSGTYPE;
759 krb5_set_error_message(context, ret, "got wrong oid for pkauthdata");
760 goto out;
761 }
762
763 if (pa->padata_type == KRB5_PADATA_PK_AS_REQ_WIN) {
764 AuthPack_Win2k ap;
765
766 ret = decode_AuthPack_Win2k(eContent.data,
767 eContent.length,
768 &ap,
769 NULL);
770 if (ret) {
771 krb5_set_error_message(context, ret,
772 "Can't decode AuthPack: %d", ret);
773 goto out;
774 }
775
776 ret = pk_check_pkauthenticator_win2k(context,
777 &ap.pkAuthenticator,
778 req);
779 if (ret) {
780 free_AuthPack_Win2k(&ap);
781 goto out;
782 }
783
784 cp->type = PKINIT_WIN2K;
785 cp->nonce = ap.pkAuthenticator.nonce;
786
787 if (ap.clientPublicValue) {
788 ret = KRB5KRB_ERR_GENERIC;
789 krb5_set_error_message(context, ret,
790 "DH not supported for windows");
791 goto out;
792 }
793 free_AuthPack_Win2k(&ap);
794
795 } else if (pa->padata_type == KRB5_PADATA_PK_AS_REQ) {
796 AuthPack ap;
797
798 ret = decode_AuthPack(eContent.data,
799 eContent.length,
800 &ap,
801 NULL);
802 if (ret) {
803 krb5_set_error_message(context, ret,
804 "Can't decode AuthPack: %d", ret);
805 free_AuthPack(&ap);
806 goto out;
807 }
808
809 if (req->req_body.kdc_options.request_anonymous &&
810 ap.clientPublicValue == NULL) {
811 free_AuthPack(&ap);
812 ret = KRB5_KDC_ERR_PUBLIC_KEY_ENCRYPTION_NOT_SUPPORTED;
813 krb5_set_error_message(context, ret,
814 "Anon not supported in RSA mode");
815 goto out;
816 }
817
818 ret = pk_check_pkauthenticator(context,
819 &ap.pkAuthenticator,
820 req);
821 if (ret) {
822 free_AuthPack(&ap);
823 goto out;
824 }
825
826 cp->type = PKINIT_27;
827 cp->nonce = ap.pkAuthenticator.nonce;
828
829 if (ap.clientPublicValue) {
830 if (der_heim_oid_cmp(&ap.clientPublicValue->algorithm.algorithm, &asn1_oid_id_dhpublicnumber) == 0) {
831 cp->keyex = USE_DH;
832 ret = get_dh_param(context, config,
833 ap.clientPublicValue, cp);
834 #ifdef HAVE_OPENSSL
835 } else if (der_heim_oid_cmp(&ap.clientPublicValue->algorithm.algorithm, &asn1_oid_id_ecPublicKey) == 0) {
836 cp->keyex = USE_ECDH;
837 ret = get_ecdh_param(context, config,
838 ap.clientPublicValue, cp);
839 #endif /* HAVE_OPENSSL */
840 } else {
841 ret = KRB5_BADMSGTYPE;
842 krb5_set_error_message(context, ret, "PKINIT unknown DH mechanism");
843 }
844 if (ret) {
845 free_AuthPack(&ap);
846 goto out;
847 }
848 } else
849 cp->keyex = USE_RSA;
850
851 ret = hx509_peer_info_alloc(context->hx509ctx,
852 &cp->peer);
853 if (ret) {
854 free_AuthPack(&ap);
855 goto out;
856 }
857
858 if (ap.supportedCMSTypes) {
859 ret = hx509_peer_info_set_cms_algs(context->hx509ctx,
860 cp->peer,
861 ap.supportedCMSTypes->val,
862 ap.supportedCMSTypes->len);
863 if (ret) {
864 free_AuthPack(&ap);
865 goto out;
866 }
867 } else {
868 /* assume old client */
869 hx509_peer_info_add_cms_alg(context->hx509ctx, cp->peer,
870 hx509_crypto_des_rsdi_ede3_cbc());
871 hx509_peer_info_add_cms_alg(context->hx509ctx, cp->peer,
872 hx509_signature_rsa_with_sha1());
873 hx509_peer_info_add_cms_alg(context->hx509ctx, cp->peer,
874 hx509_signature_sha1());
875 }
876 free_AuthPack(&ap);
877 } else
878 krb5_abortx(context, "internal pkinit error");
879
880 kdc_log(context, config, 0, "PK-INIT request of type %s", type);
881
882 out:
883 if (ret)
884 krb5_warn(context, ret, "PKINIT");
885
886 if (signed_content.data)
887 free(signed_content.data);
888 krb5_data_free(&eContent);
889 der_free_oid(&eContentType);
890 der_free_oid(&contentInfoOid);
891 if (ret) {
892 _kdc_pk_free_client_param(context, cp);
893 } else
894 *ret_params = cp;
895 return ret;
896 }
897
898 /*
899 *
900 */
901
902 static krb5_error_code
BN_to_integer(krb5_context context,BIGNUM * bn,heim_integer * integer)903 BN_to_integer(krb5_context context, BIGNUM *bn, heim_integer *integer)
904 {
905 integer->length = BN_num_bytes(bn);
906 integer->data = malloc(integer->length);
907 if (integer->data == NULL) {
908 krb5_clear_error_message(context);
909 return ENOMEM;
910 }
911 BN_bn2bin(bn, integer->data);
912 integer->negative = BN_is_negative(bn);
913 return 0;
914 }
915
916 static krb5_error_code
pk_mk_pa_reply_enckey(krb5_context context,krb5_kdc_configuration * config,pk_client_params * cp,const KDC_REQ * req,const krb5_data * req_buffer,krb5_keyblock * reply_key,ContentInfo * content_info,hx509_cert * kdc_cert)917 pk_mk_pa_reply_enckey(krb5_context context,
918 krb5_kdc_configuration *config,
919 pk_client_params *cp,
920 const KDC_REQ *req,
921 const krb5_data *req_buffer,
922 krb5_keyblock *reply_key,
923 ContentInfo *content_info,
924 hx509_cert *kdc_cert)
925 {
926 const heim_oid *envelopedAlg = NULL, *sdAlg = NULL, *evAlg = NULL;
927 krb5_error_code ret;
928 krb5_data buf, signed_data;
929 size_t size = 0;
930 int do_win2k = 0;
931
932 krb5_data_zero(&buf);
933 krb5_data_zero(&signed_data);
934
935 *kdc_cert = NULL;
936
937 /*
938 * If the message client is a win2k-type but it send pa data
939 * 09-binding it expects a IETF (checksum) reply so there can be
940 * no replay attacks.
941 */
942
943 switch (cp->type) {
944 case PKINIT_WIN2K: {
945 int i = 0;
946 if (_kdc_find_padata(req, &i, KRB5_PADATA_PK_AS_09_BINDING) == NULL
947 && config->pkinit_require_binding == 0)
948 {
949 do_win2k = 1;
950 }
951 sdAlg = &asn1_oid_id_pkcs7_data;
952 evAlg = &asn1_oid_id_pkcs7_data;
953 envelopedAlg = &asn1_oid_id_rsadsi_des_ede3_cbc;
954 break;
955 }
956 case PKINIT_27:
957 sdAlg = &asn1_oid_id_pkrkeydata;
958 evAlg = &asn1_oid_id_pkcs7_signedData;
959 break;
960 default:
961 krb5_abortx(context, "internal pkinit error");
962 }
963
964 if (do_win2k) {
965 ReplyKeyPack_Win2k kp;
966 memset(&kp, 0, sizeof(kp));
967
968 ret = copy_EncryptionKey(reply_key, &kp.replyKey);
969 if (ret) {
970 krb5_clear_error_message(context);
971 goto out;
972 }
973 kp.nonce = cp->nonce;
974
975 ASN1_MALLOC_ENCODE(ReplyKeyPack_Win2k,
976 buf.data, buf.length,
977 &kp, &size,ret);
978 free_ReplyKeyPack_Win2k(&kp);
979 } else {
980 krb5_crypto ascrypto;
981 ReplyKeyPack kp;
982 memset(&kp, 0, sizeof(kp));
983
984 ret = copy_EncryptionKey(reply_key, &kp.replyKey);
985 if (ret) {
986 krb5_clear_error_message(context);
987 goto out;
988 }
989
990 ret = krb5_crypto_init(context, reply_key, 0, &ascrypto);
991 if (ret) {
992 krb5_clear_error_message(context);
993 goto out;
994 }
995
996 ret = krb5_create_checksum(context, ascrypto, 6, 0,
997 req_buffer->data, req_buffer->length,
998 &kp.asChecksum);
999 if (ret) {
1000 krb5_clear_error_message(context);
1001 goto out;
1002 }
1003
1004 ret = krb5_crypto_destroy(context, ascrypto);
1005 if (ret) {
1006 krb5_clear_error_message(context);
1007 goto out;
1008 }
1009 ASN1_MALLOC_ENCODE(ReplyKeyPack, buf.data, buf.length, &kp, &size,ret);
1010 free_ReplyKeyPack(&kp);
1011 }
1012 if (ret) {
1013 krb5_set_error_message(context, ret, "ASN.1 encoding of ReplyKeyPack "
1014 "failed (%d)", ret);
1015 goto out;
1016 }
1017 if (buf.length != size)
1018 krb5_abortx(context, "Internal ASN.1 encoder error");
1019
1020 {
1021 hx509_query *q;
1022 hx509_cert cert;
1023
1024 ret = hx509_query_alloc(context->hx509ctx, &q);
1025 if (ret)
1026 goto out;
1027
1028 hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY);
1029 if (config->pkinit_kdc_friendly_name)
1030 hx509_query_match_friendly_name(q, config->pkinit_kdc_friendly_name);
1031
1032 ret = hx509_certs_find(context->hx509ctx,
1033 kdc_identity->certs,
1034 q,
1035 &cert);
1036 hx509_query_free(context->hx509ctx, q);
1037 if (ret)
1038 goto out;
1039
1040 ret = hx509_cms_create_signed_1(context->hx509ctx,
1041 0,
1042 sdAlg,
1043 buf.data,
1044 buf.length,
1045 NULL,
1046 cert,
1047 cp->peer,
1048 cp->client_anchors,
1049 kdc_identity->certpool,
1050 &signed_data);
1051 *kdc_cert = cert;
1052 }
1053
1054 krb5_data_free(&buf);
1055 if (ret)
1056 goto out;
1057
1058 if (cp->type == PKINIT_WIN2K) {
1059 ret = hx509_cms_wrap_ContentInfo(&asn1_oid_id_pkcs7_signedData,
1060 &signed_data,
1061 &buf);
1062 if (ret)
1063 goto out;
1064 krb5_data_free(&signed_data);
1065 signed_data = buf;
1066 }
1067
1068 ret = hx509_cms_envelope_1(context->hx509ctx,
1069 HX509_CMS_EV_NO_KU_CHECK,
1070 cp->cert,
1071 signed_data.data, signed_data.length,
1072 envelopedAlg,
1073 evAlg, &buf);
1074 if (ret)
1075 goto out;
1076
1077 ret = _krb5_pk_mk_ContentInfo(context,
1078 &buf,
1079 &asn1_oid_id_pkcs7_envelopedData,
1080 content_info);
1081 out:
1082 if (ret && *kdc_cert) {
1083 hx509_cert_free(*kdc_cert);
1084 *kdc_cert = NULL;
1085 }
1086
1087 krb5_data_free(&buf);
1088 krb5_data_free(&signed_data);
1089 return ret;
1090 }
1091
1092 /*
1093 *
1094 */
1095
1096 static krb5_error_code
pk_mk_pa_reply_dh(krb5_context context,krb5_kdc_configuration * config,pk_client_params * cp,ContentInfo * content_info,hx509_cert * kdc_cert)1097 pk_mk_pa_reply_dh(krb5_context context,
1098 krb5_kdc_configuration *config,
1099 pk_client_params *cp,
1100 ContentInfo *content_info,
1101 hx509_cert *kdc_cert)
1102 {
1103 KDCDHKeyInfo dh_info;
1104 krb5_data signed_data, buf;
1105 ContentInfo contentinfo;
1106 krb5_error_code ret;
1107 hx509_cert cert;
1108 hx509_query *q;
1109 size_t size = 0;
1110
1111 memset(&contentinfo, 0, sizeof(contentinfo));
1112 memset(&dh_info, 0, sizeof(dh_info));
1113 krb5_data_zero(&signed_data);
1114 krb5_data_zero(&buf);
1115
1116 *kdc_cert = NULL;
1117
1118 if (cp->keyex == USE_DH) {
1119 DH *kdc_dh = cp->u.dh.key;
1120 heim_integer i;
1121
1122 ret = BN_to_integer(context, kdc_dh->pub_key, &i);
1123 if (ret)
1124 return ret;
1125
1126 ASN1_MALLOC_ENCODE(DHPublicKey, buf.data, buf.length, &i, &size, ret);
1127 der_free_heim_integer(&i);
1128 if (ret) {
1129 krb5_set_error_message(context, ret, "ASN.1 encoding of "
1130 "DHPublicKey failed (%d)", ret);
1131 return ret;
1132 }
1133 if (buf.length != size)
1134 krb5_abortx(context, "Internal ASN.1 encoder error");
1135
1136 dh_info.subjectPublicKey.length = buf.length * 8;
1137 dh_info.subjectPublicKey.data = buf.data;
1138 krb5_data_zero(&buf);
1139 #ifdef HAVE_OPENSSL
1140 } else if (cp->keyex == USE_ECDH) {
1141 unsigned char *p;
1142 int len;
1143
1144 len = i2o_ECPublicKey(cp->u.ecdh.key, NULL);
1145 if (len <= 0)
1146 abort();
1147
1148 p = malloc(len);
1149 if (p == NULL)
1150 abort();
1151
1152 dh_info.subjectPublicKey.length = len * 8;
1153 dh_info.subjectPublicKey.data = p;
1154
1155 len = i2o_ECPublicKey(cp->u.ecdh.key, &p);
1156 if (len <= 0)
1157 abort();
1158 #endif
1159 } else
1160 krb5_abortx(context, "no keyex selected ?");
1161
1162
1163 dh_info.nonce = cp->nonce;
1164
1165 ASN1_MALLOC_ENCODE(KDCDHKeyInfo, buf.data, buf.length, &dh_info, &size,
1166 ret);
1167 if (ret) {
1168 krb5_set_error_message(context, ret, "ASN.1 encoding of "
1169 "KdcDHKeyInfo failed (%d)", ret);
1170 goto out;
1171 }
1172 if (buf.length != size)
1173 krb5_abortx(context, "Internal ASN.1 encoder error");
1174
1175 /*
1176 * Create the SignedData structure and sign the KdcDHKeyInfo
1177 * filled in above
1178 */
1179
1180 ret = hx509_query_alloc(context->hx509ctx, &q);
1181 if (ret)
1182 goto out;
1183
1184 hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY);
1185 if (config->pkinit_kdc_friendly_name)
1186 hx509_query_match_friendly_name(q, config->pkinit_kdc_friendly_name);
1187
1188 ret = hx509_certs_find(context->hx509ctx,
1189 kdc_identity->certs,
1190 q,
1191 &cert);
1192 hx509_query_free(context->hx509ctx, q);
1193 if (ret)
1194 goto out;
1195
1196 ret = hx509_cms_create_signed_1(context->hx509ctx,
1197 0,
1198 &asn1_oid_id_pkdhkeydata,
1199 buf.data,
1200 buf.length,
1201 NULL,
1202 cert,
1203 cp->peer,
1204 cp->client_anchors,
1205 kdc_identity->certpool,
1206 &signed_data);
1207 if (ret) {
1208 kdc_log(context, config, 0, "Failed signing the DH* reply: %d", ret);
1209 goto out;
1210 }
1211 *kdc_cert = cert;
1212
1213 ret = _krb5_pk_mk_ContentInfo(context,
1214 &signed_data,
1215 &asn1_oid_id_pkcs7_signedData,
1216 content_info);
1217 if (ret)
1218 goto out;
1219
1220 out:
1221 if (ret && *kdc_cert) {
1222 hx509_cert_free(*kdc_cert);
1223 *kdc_cert = NULL;
1224 }
1225
1226 krb5_data_free(&buf);
1227 krb5_data_free(&signed_data);
1228 free_KDCDHKeyInfo(&dh_info);
1229
1230 return ret;
1231 }
1232
1233 /*
1234 *
1235 */
1236
1237 krb5_error_code
_kdc_pk_mk_pa_reply(krb5_context context,krb5_kdc_configuration * config,pk_client_params * cp,const hdb_entry_ex * client,krb5_enctype sessionetype,const KDC_REQ * req,const krb5_data * req_buffer,krb5_keyblock ** reply_key,krb5_keyblock * sessionkey,METHOD_DATA * md)1238 _kdc_pk_mk_pa_reply(krb5_context context,
1239 krb5_kdc_configuration *config,
1240 pk_client_params *cp,
1241 const hdb_entry_ex *client,
1242 krb5_enctype sessionetype,
1243 const KDC_REQ *req,
1244 const krb5_data *req_buffer,
1245 krb5_keyblock **reply_key,
1246 krb5_keyblock *sessionkey,
1247 METHOD_DATA *md)
1248 {
1249 krb5_error_code ret;
1250 void *buf = NULL;
1251 size_t len = 0, size = 0;
1252 krb5_enctype enctype;
1253 int pa_type;
1254 hx509_cert kdc_cert = NULL;
1255 size_t i;
1256
1257 if (!config->enable_pkinit) {
1258 krb5_clear_error_message(context);
1259 return 0;
1260 }
1261
1262 if (req->req_body.etype.len > 0) {
1263 for (i = 0; i < req->req_body.etype.len; i++)
1264 if (krb5_enctype_valid(context, req->req_body.etype.val[i]) == 0)
1265 break;
1266 if (req->req_body.etype.len <= i) {
1267 ret = KRB5KRB_ERR_GENERIC;
1268 krb5_set_error_message(context, ret,
1269 "No valid enctype available from client");
1270 goto out;
1271 }
1272 enctype = req->req_body.etype.val[i];
1273 } else
1274 enctype = ETYPE_DES3_CBC_SHA1;
1275
1276 if (cp->type == PKINIT_27) {
1277 PA_PK_AS_REP rep;
1278 const char *type, *other = "";
1279
1280 memset(&rep, 0, sizeof(rep));
1281
1282 pa_type = KRB5_PADATA_PK_AS_REP;
1283
1284 if (cp->keyex == USE_RSA) {
1285 ContentInfo info;
1286
1287 type = "enckey";
1288
1289 rep.element = choice_PA_PK_AS_REP_encKeyPack;
1290
1291 ret = krb5_generate_random_keyblock(context, enctype,
1292 &cp->reply_key);
1293 if (ret) {
1294 free_PA_PK_AS_REP(&rep);
1295 goto out;
1296 }
1297 ret = pk_mk_pa_reply_enckey(context,
1298 config,
1299 cp,
1300 req,
1301 req_buffer,
1302 &cp->reply_key,
1303 &info,
1304 &kdc_cert);
1305 if (ret) {
1306 free_PA_PK_AS_REP(&rep);
1307 goto out;
1308 }
1309 ASN1_MALLOC_ENCODE(ContentInfo, rep.u.encKeyPack.data,
1310 rep.u.encKeyPack.length, &info, &size,
1311 ret);
1312 free_ContentInfo(&info);
1313 if (ret) {
1314 krb5_set_error_message(context, ret, "encoding of Key ContentInfo "
1315 "failed %d", ret);
1316 free_PA_PK_AS_REP(&rep);
1317 goto out;
1318 }
1319 if (rep.u.encKeyPack.length != size)
1320 krb5_abortx(context, "Internal ASN.1 encoder error");
1321
1322 ret = krb5_generate_random_keyblock(context, sessionetype,
1323 sessionkey);
1324 if (ret) {
1325 free_PA_PK_AS_REP(&rep);
1326 goto out;
1327 }
1328
1329 } else {
1330 ContentInfo info;
1331
1332 switch (cp->keyex) {
1333 case USE_DH: type = "dh"; break;
1334 #ifdef HAVE_OPENSSL
1335 case USE_ECDH: type = "ecdh"; break;
1336 #endif
1337 default: krb5_abortx(context, "unknown keyex"); break;
1338 }
1339
1340 if (cp->dh_group_name)
1341 other = cp->dh_group_name;
1342
1343 rep.element = choice_PA_PK_AS_REP_dhInfo;
1344
1345 ret = generate_dh_keyblock(context, cp, enctype);
1346 if (ret)
1347 return ret;
1348
1349 ret = pk_mk_pa_reply_dh(context, config,
1350 cp,
1351 &info,
1352 &kdc_cert);
1353 if (ret) {
1354 free_PA_PK_AS_REP(&rep);
1355 krb5_set_error_message(context, ret,
1356 "create pa-reply-dh "
1357 "failed %d", ret);
1358 goto out;
1359 }
1360
1361 ASN1_MALLOC_ENCODE(ContentInfo, rep.u.dhInfo.dhSignedData.data,
1362 rep.u.dhInfo.dhSignedData.length, &info, &size,
1363 ret);
1364 free_ContentInfo(&info);
1365 if (ret) {
1366 krb5_set_error_message(context, ret,
1367 "encoding of Key ContentInfo "
1368 "failed %d", ret);
1369 free_PA_PK_AS_REP(&rep);
1370 goto out;
1371 }
1372 if (rep.u.encKeyPack.length != size)
1373 krb5_abortx(context, "Internal ASN.1 encoder error");
1374
1375 /* XXX KRB-FX-CF2 */
1376 ret = krb5_generate_random_keyblock(context, sessionetype,
1377 sessionkey);
1378 if (ret) {
1379 free_PA_PK_AS_REP(&rep);
1380 goto out;
1381 }
1382
1383 /* XXX Add PA-PKINIT-KX */
1384
1385 }
1386
1387 #define use_btmm_with_enckey 0
1388 if (use_btmm_with_enckey && rep.element == choice_PA_PK_AS_REP_encKeyPack) {
1389 PA_PK_AS_REP_BTMM btmm;
1390 heim_any any;
1391
1392 any.data = rep.u.encKeyPack.data;
1393 any.length = rep.u.encKeyPack.length;
1394
1395 btmm.dhSignedData = NULL;
1396 btmm.encKeyPack = &any;
1397
1398 ASN1_MALLOC_ENCODE(PA_PK_AS_REP_BTMM, buf, len, &btmm, &size, ret);
1399 } else {
1400 ASN1_MALLOC_ENCODE(PA_PK_AS_REP, buf, len, &rep, &size, ret);
1401 }
1402
1403 free_PA_PK_AS_REP(&rep);
1404 if (ret) {
1405 krb5_set_error_message(context, ret,
1406 "encode PA-PK-AS-REP failed %d", ret);
1407 goto out;
1408 }
1409 if (len != size)
1410 krb5_abortx(context, "Internal ASN.1 encoder error");
1411
1412 kdc_log(context, config, 0, "PK-INIT using %s %s", type, other);
1413
1414 } else if (cp->type == PKINIT_WIN2K) {
1415 PA_PK_AS_REP_Win2k rep;
1416 ContentInfo info;
1417
1418 if (cp->keyex != USE_RSA) {
1419 ret = KRB5KRB_ERR_GENERIC;
1420 krb5_set_error_message(context, ret,
1421 "Windows PK-INIT doesn't support DH");
1422 goto out;
1423 }
1424
1425 memset(&rep, 0, sizeof(rep));
1426
1427 pa_type = KRB5_PADATA_PK_AS_REP_19;
1428 rep.element = choice_PA_PK_AS_REP_Win2k_encKeyPack;
1429
1430 ret = krb5_generate_random_keyblock(context, enctype,
1431 &cp->reply_key);
1432 if (ret) {
1433 free_PA_PK_AS_REP_Win2k(&rep);
1434 goto out;
1435 }
1436 ret = pk_mk_pa_reply_enckey(context,
1437 config,
1438 cp,
1439 req,
1440 req_buffer,
1441 &cp->reply_key,
1442 &info,
1443 &kdc_cert);
1444 if (ret) {
1445 free_PA_PK_AS_REP_Win2k(&rep);
1446 goto out;
1447 }
1448 ASN1_MALLOC_ENCODE(ContentInfo, rep.u.encKeyPack.data,
1449 rep.u.encKeyPack.length, &info, &size,
1450 ret);
1451 free_ContentInfo(&info);
1452 if (ret) {
1453 krb5_set_error_message(context, ret, "encoding of Key ContentInfo "
1454 "failed %d", ret);
1455 free_PA_PK_AS_REP_Win2k(&rep);
1456 goto out;
1457 }
1458 if (rep.u.encKeyPack.length != size)
1459 krb5_abortx(context, "Internal ASN.1 encoder error");
1460
1461 ASN1_MALLOC_ENCODE(PA_PK_AS_REP_Win2k, buf, len, &rep, &size, ret);
1462 free_PA_PK_AS_REP_Win2k(&rep);
1463 if (ret) {
1464 krb5_set_error_message(context, ret,
1465 "encode PA-PK-AS-REP-Win2k failed %d", ret);
1466 goto out;
1467 }
1468 if (len != size)
1469 krb5_abortx(context, "Internal ASN.1 encoder error");
1470
1471 ret = krb5_generate_random_keyblock(context, sessionetype,
1472 sessionkey);
1473 if (ret) {
1474 free(buf);
1475 goto out;
1476 }
1477
1478 } else
1479 krb5_abortx(context, "PK-INIT internal error");
1480
1481
1482 ret = krb5_padata_add(context, md, pa_type, buf, len);
1483 if (ret) {
1484 krb5_set_error_message(context, ret,
1485 "Failed adding PA-PK-AS-REP %d", ret);
1486 free(buf);
1487 goto out;
1488 }
1489
1490 if (config->pkinit_kdc_ocsp_file) {
1491
1492 if (ocsp.expire == 0 && ocsp.next_update > kdc_time) {
1493 struct stat sb;
1494 int fd;
1495
1496 krb5_data_free(&ocsp.data);
1497
1498 ocsp.expire = 0;
1499 ocsp.next_update = kdc_time + 60 * 5;
1500
1501 fd = open(config->pkinit_kdc_ocsp_file, O_RDONLY);
1502 if (fd < 0) {
1503 kdc_log(context, config, 0,
1504 "PK-INIT failed to open ocsp data file %d", errno);
1505 goto out_ocsp;
1506 }
1507 ret = fstat(fd, &sb);
1508 if (ret) {
1509 ret = errno;
1510 close(fd);
1511 kdc_log(context, config, 0,
1512 "PK-INIT failed to stat ocsp data %d", ret);
1513 goto out_ocsp;
1514 }
1515
1516 ret = krb5_data_alloc(&ocsp.data, sb.st_size);
1517 if (ret) {
1518 close(fd);
1519 kdc_log(context, config, 0,
1520 "PK-INIT failed to stat ocsp data %d", ret);
1521 goto out_ocsp;
1522 }
1523 ocsp.data.length = sb.st_size;
1524 ret = read(fd, ocsp.data.data, sb.st_size);
1525 close(fd);
1526 if (ret != sb.st_size) {
1527 kdc_log(context, config, 0,
1528 "PK-INIT failed to read ocsp data %d", errno);
1529 goto out_ocsp;
1530 }
1531
1532 ret = hx509_ocsp_verify(context->hx509ctx,
1533 kdc_time,
1534 kdc_cert,
1535 0,
1536 ocsp.data.data, ocsp.data.length,
1537 &ocsp.expire);
1538 if (ret) {
1539 kdc_log(context, config, 0,
1540 "PK-INIT failed to verify ocsp data %d", ret);
1541 krb5_data_free(&ocsp.data);
1542 ocsp.expire = 0;
1543 } else if (ocsp.expire > 180) {
1544 ocsp.expire -= 180; /* refetch the ocsp before it expire */
1545 ocsp.next_update = ocsp.expire;
1546 } else {
1547 ocsp.next_update = kdc_time;
1548 }
1549 out_ocsp:
1550 ret = 0;
1551 }
1552
1553 if (ocsp.expire != 0 && ocsp.expire > kdc_time) {
1554
1555 ret = krb5_padata_add(context, md,
1556 KRB5_PADATA_PA_PK_OCSP_RESPONSE,
1557 ocsp.data.data, ocsp.data.length);
1558 if (ret) {
1559 krb5_set_error_message(context, ret,
1560 "Failed adding OCSP response %d", ret);
1561 goto out;
1562 }
1563 }
1564 }
1565
1566 out:
1567 if (kdc_cert)
1568 hx509_cert_free(kdc_cert);
1569
1570 if (ret == 0)
1571 *reply_key = &cp->reply_key;
1572 return ret;
1573 }
1574
1575 static int
match_rfc_san(krb5_context context,krb5_kdc_configuration * config,hx509_context hx509ctx,hx509_cert client_cert,krb5_const_principal match)1576 match_rfc_san(krb5_context context,
1577 krb5_kdc_configuration *config,
1578 hx509_context hx509ctx,
1579 hx509_cert client_cert,
1580 krb5_const_principal match)
1581 {
1582 hx509_octet_string_list list;
1583 int ret, found = 0;
1584 size_t i;
1585
1586 memset(&list, 0 , sizeof(list));
1587
1588 ret = hx509_cert_find_subjectAltName_otherName(hx509ctx,
1589 client_cert,
1590 &asn1_oid_id_pkinit_san,
1591 &list);
1592 if (ret)
1593 goto out;
1594
1595 for (i = 0; !found && i < list.len; i++) {
1596 krb5_principal_data principal;
1597 KRB5PrincipalName kn;
1598 size_t size;
1599
1600 ret = decode_KRB5PrincipalName(list.val[i].data,
1601 list.val[i].length,
1602 &kn, &size);
1603 if (ret) {
1604 const char *msg = krb5_get_error_message(context, ret);
1605 kdc_log(context, config, 0,
1606 "Decoding kerberos name in certificate failed: %s", msg);
1607 krb5_free_error_message(context, msg);
1608 break;
1609 }
1610 if (size != list.val[i].length) {
1611 kdc_log(context, config, 0,
1612 "Decoding kerberos name have extra bits on the end");
1613 return KRB5_KDC_ERR_CLIENT_NAME_MISMATCH;
1614 }
1615
1616 principal.name = kn.principalName;
1617 principal.realm = kn.realm;
1618
1619 if (krb5_principal_compare(context, &principal, match) == TRUE)
1620 found = 1;
1621 free_KRB5PrincipalName(&kn);
1622 }
1623
1624 out:
1625 hx509_free_octet_string_list(&list);
1626 if (ret)
1627 return ret;
1628
1629 if (!found)
1630 return KRB5_KDC_ERR_CLIENT_NAME_MISMATCH;
1631
1632 return 0;
1633 }
1634
1635 static int
match_ms_upn_san(krb5_context context,krb5_kdc_configuration * config,hx509_context hx509ctx,hx509_cert client_cert,HDB * clientdb,hdb_entry_ex * client)1636 match_ms_upn_san(krb5_context context,
1637 krb5_kdc_configuration *config,
1638 hx509_context hx509ctx,
1639 hx509_cert client_cert,
1640 HDB *clientdb,
1641 hdb_entry_ex *client)
1642 {
1643 hx509_octet_string_list list;
1644 krb5_principal principal = NULL;
1645 int ret;
1646 MS_UPN_SAN upn;
1647 size_t size;
1648
1649 memset(&list, 0 , sizeof(list));
1650
1651 ret = hx509_cert_find_subjectAltName_otherName(hx509ctx,
1652 client_cert,
1653 &asn1_oid_id_pkinit_ms_san,
1654 &list);
1655 if (ret)
1656 goto out;
1657
1658 if (list.len != 1) {
1659 kdc_log(context, config, 0,
1660 "More then one PK-INIT MS UPN SAN");
1661 ret = KRB5_KDC_ERR_CLIENT_NAME_MISMATCH;
1662 goto out;
1663 }
1664
1665 ret = decode_MS_UPN_SAN(list.val[0].data, list.val[0].length, &upn, &size);
1666 if (ret) {
1667 kdc_log(context, config, 0, "Decode of MS-UPN-SAN failed");
1668 goto out;
1669 }
1670 if (size != list.val[0].length) {
1671 free_MS_UPN_SAN(&upn);
1672 kdc_log(context, config, 0, "Trailing data in ");
1673 ret = KRB5_KDC_ERR_CLIENT_NAME_MISMATCH;
1674 goto out;
1675 }
1676
1677 kdc_log(context, config, 0, "found MS UPN SAN: %s", upn);
1678
1679 ret = krb5_parse_name(context, upn, &principal);
1680 free_MS_UPN_SAN(&upn);
1681 if (ret) {
1682 kdc_log(context, config, 0, "Failed to parse principal in MS UPN SAN");
1683 goto out;
1684 }
1685
1686 if (clientdb->hdb_check_pkinit_ms_upn_match) {
1687 ret = clientdb->hdb_check_pkinit_ms_upn_match(context, clientdb, client, principal);
1688 } else {
1689
1690 /*
1691 * This is very wrong, but will do for a fallback
1692 */
1693 strupr(principal->realm);
1694
1695 if (krb5_principal_compare(context, principal, client->entry.principal) == FALSE)
1696 ret = KRB5_KDC_ERR_CLIENT_NAME_MISMATCH;
1697 }
1698
1699 out:
1700 if (principal)
1701 krb5_free_principal(context, principal);
1702 hx509_free_octet_string_list(&list);
1703
1704 return ret;
1705 }
1706
1707 krb5_error_code
_kdc_pk_check_client(krb5_context context,krb5_kdc_configuration * config,HDB * clientdb,hdb_entry_ex * client,pk_client_params * cp,char ** subject_name)1708 _kdc_pk_check_client(krb5_context context,
1709 krb5_kdc_configuration *config,
1710 HDB *clientdb,
1711 hdb_entry_ex *client,
1712 pk_client_params *cp,
1713 char **subject_name)
1714 {
1715 const HDB_Ext_PKINIT_acl *acl;
1716 const HDB_Ext_PKINIT_cert *pc;
1717 krb5_error_code ret;
1718 hx509_name name;
1719 size_t i;
1720
1721 if (cp->cert == NULL) {
1722
1723 *subject_name = strdup("anonymous client client");
1724 if (*subject_name == NULL)
1725 return ENOMEM;
1726 return 0;
1727 }
1728
1729 ret = hx509_cert_get_base_subject(context->hx509ctx,
1730 cp->cert,
1731 &name);
1732 if (ret)
1733 return ret;
1734
1735 ret = hx509_name_to_string(name, subject_name);
1736 hx509_name_free(&name);
1737 if (ret)
1738 return ret;
1739
1740 kdc_log(context, config, 0,
1741 "Trying to authorize PK-INIT subject DN %s",
1742 *subject_name);
1743
1744 ret = hdb_entry_get_pkinit_cert(&client->entry, &pc);
1745 if (ret == 0 && pc) {
1746 hx509_cert cert;
1747 size_t j;
1748
1749 for (j = 0; j < pc->len; j++) {
1750 ret = hx509_cert_init_data(context->hx509ctx,
1751 pc->val[j].cert.data,
1752 pc->val[j].cert.length,
1753 &cert);
1754 if (ret)
1755 continue;
1756 ret = hx509_cert_cmp(cert, cp->cert);
1757 hx509_cert_free(cert);
1758 if (ret == 0) {
1759 kdc_log(context, config, 5,
1760 "Found matching PK-INIT cert in hdb");
1761 return 0;
1762 }
1763 }
1764 }
1765
1766
1767 if (config->pkinit_princ_in_cert) {
1768 ret = match_rfc_san(context, config,
1769 context->hx509ctx,
1770 cp->cert,
1771 client->entry.principal);
1772 if (ret == 0) {
1773 kdc_log(context, config, 5,
1774 "Found matching PK-INIT SAN in certificate");
1775 return 0;
1776 }
1777 ret = match_ms_upn_san(context, config,
1778 context->hx509ctx,
1779 cp->cert,
1780 clientdb,
1781 client);
1782 if (ret == 0) {
1783 kdc_log(context, config, 5,
1784 "Found matching MS UPN SAN in certificate");
1785 return 0;
1786 }
1787 }
1788
1789 ret = hdb_entry_get_pkinit_acl(&client->entry, &acl);
1790 if (ret == 0 && acl != NULL) {
1791 /*
1792 * Cheat here and compare the generated name with the string
1793 * and not the reverse.
1794 */
1795 for (i = 0; i < acl->len; i++) {
1796 if (strcmp(*subject_name, acl->val[0].subject) != 0)
1797 continue;
1798
1799 /* Don't support isser and anchor checking right now */
1800 if (acl->val[0].issuer)
1801 continue;
1802 if (acl->val[0].anchor)
1803 continue;
1804
1805 kdc_log(context, config, 5,
1806 "Found matching PK-INIT database ACL");
1807 return 0;
1808 }
1809 }
1810
1811 for (i = 0; i < principal_mappings.len; i++) {
1812 krb5_boolean b;
1813
1814 b = krb5_principal_compare(context,
1815 client->entry.principal,
1816 principal_mappings.val[i].principal);
1817 if (b == FALSE)
1818 continue;
1819 if (strcmp(principal_mappings.val[i].subject, *subject_name) != 0)
1820 continue;
1821 kdc_log(context, config, 5,
1822 "Found matching PK-INIT FILE ACL");
1823 return 0;
1824 }
1825
1826 ret = KRB5_KDC_ERR_CLIENT_NAME_MISMATCH;
1827 krb5_set_error_message(context, ret,
1828 "PKINIT no matching principals for %s",
1829 *subject_name);
1830
1831 kdc_log(context, config, 5,
1832 "PKINIT no matching principals for %s",
1833 *subject_name);
1834
1835 free(*subject_name);
1836 *subject_name = NULL;
1837
1838 return ret;
1839 }
1840
1841 static krb5_error_code
add_principal_mapping(krb5_context context,const char * principal_name,const char * subject)1842 add_principal_mapping(krb5_context context,
1843 const char *principal_name,
1844 const char * subject)
1845 {
1846 struct pk_allowed_princ *tmp;
1847 krb5_principal principal;
1848 krb5_error_code ret;
1849
1850 tmp = realloc(principal_mappings.val,
1851 (principal_mappings.len + 1) * sizeof(*tmp));
1852 if (tmp == NULL)
1853 return ENOMEM;
1854 principal_mappings.val = tmp;
1855
1856 ret = krb5_parse_name(context, principal_name, &principal);
1857 if (ret)
1858 return ret;
1859
1860 principal_mappings.val[principal_mappings.len].principal = principal;
1861
1862 principal_mappings.val[principal_mappings.len].subject = strdup(subject);
1863 if (principal_mappings.val[principal_mappings.len].subject == NULL) {
1864 krb5_free_principal(context, principal);
1865 return ENOMEM;
1866 }
1867 principal_mappings.len++;
1868
1869 return 0;
1870 }
1871
1872 krb5_error_code
_kdc_add_inital_verified_cas(krb5_context context,krb5_kdc_configuration * config,pk_client_params * cp,EncTicketPart * tkt)1873 _kdc_add_inital_verified_cas(krb5_context context,
1874 krb5_kdc_configuration *config,
1875 pk_client_params *cp,
1876 EncTicketPart *tkt)
1877 {
1878 AD_INITIAL_VERIFIED_CAS cas;
1879 krb5_error_code ret;
1880 krb5_data data;
1881 size_t size = 0;
1882
1883 memset(&cas, 0, sizeof(cas));
1884
1885 /* XXX add CAs to cas here */
1886
1887 ASN1_MALLOC_ENCODE(AD_INITIAL_VERIFIED_CAS, data.data, data.length,
1888 &cas, &size, ret);
1889 if (ret)
1890 return ret;
1891 if (data.length != size)
1892 krb5_abortx(context, "internal asn.1 encoder error");
1893
1894 ret = _kdc_tkt_add_if_relevant_ad(context, tkt,
1895 KRB5_AUTHDATA_INITIAL_VERIFIED_CAS,
1896 &data);
1897 krb5_data_free(&data);
1898 return ret;
1899 }
1900
1901 /*
1902 *
1903 */
1904
1905 static void
load_mappings(krb5_context context,const char * fn)1906 load_mappings(krb5_context context, const char *fn)
1907 {
1908 krb5_error_code ret;
1909 char buf[1024];
1910 unsigned long lineno = 0;
1911 FILE *f;
1912
1913 f = fopen(fn, "r");
1914 if (f == NULL)
1915 return;
1916
1917 while (fgets(buf, sizeof(buf), f) != NULL) {
1918 char *subject_name, *p;
1919
1920 buf[strcspn(buf, "\n")] = '\0';
1921 lineno++;
1922
1923 p = buf + strspn(buf, " \t");
1924
1925 if (*p == '#' || *p == '\0')
1926 continue;
1927
1928 subject_name = strchr(p, ':');
1929 if (subject_name == NULL) {
1930 krb5_warnx(context, "pkinit mapping file line %lu "
1931 "missing \":\" :%s",
1932 lineno, buf);
1933 continue;
1934 }
1935 *subject_name++ = '\0';
1936
1937 ret = add_principal_mapping(context, p, subject_name);
1938 if (ret) {
1939 krb5_warn(context, ret, "failed to add line %lu \":\" :%s\n",
1940 lineno, buf);
1941 continue;
1942 }
1943 }
1944
1945 fclose(f);
1946 }
1947
1948 /*
1949 *
1950 */
1951
1952 krb5_error_code
krb5_kdc_pk_initialize(krb5_context context,krb5_kdc_configuration * config,const char * user_id,const char * anchors,char ** pool,char ** revoke_list)1953 krb5_kdc_pk_initialize(krb5_context context,
1954 krb5_kdc_configuration *config,
1955 const char *user_id,
1956 const char *anchors,
1957 char **pool,
1958 char **revoke_list)
1959 {
1960 const char *file;
1961 char *fn = NULL;
1962 krb5_error_code ret;
1963
1964 file = krb5_config_get_string(context, NULL,
1965 "libdefaults", "moduli", NULL);
1966
1967 ret = _krb5_parse_moduli(context, file, &moduli);
1968 if (ret)
1969 krb5_err(context, 1, ret, "PKINIT: failed to load modidi file");
1970
1971 principal_mappings.len = 0;
1972 principal_mappings.val = NULL;
1973
1974 ret = _krb5_pk_load_id(context,
1975 &kdc_identity,
1976 user_id,
1977 anchors,
1978 pool,
1979 revoke_list,
1980 NULL,
1981 NULL,
1982 NULL);
1983 if (ret) {
1984 krb5_warn(context, ret, "PKINIT: ");
1985 config->enable_pkinit = 0;
1986 return ret;
1987 }
1988
1989 {
1990 hx509_query *q;
1991 hx509_cert cert;
1992
1993 ret = hx509_query_alloc(context->hx509ctx, &q);
1994 if (ret) {
1995 krb5_warnx(context, "PKINIT: out of memory");
1996 return ENOMEM;
1997 }
1998
1999 hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY);
2000 if (config->pkinit_kdc_friendly_name)
2001 hx509_query_match_friendly_name(q, config->pkinit_kdc_friendly_name);
2002
2003 ret = hx509_certs_find(context->hx509ctx,
2004 kdc_identity->certs,
2005 q,
2006 &cert);
2007 hx509_query_free(context->hx509ctx, q);
2008 if (ret == 0) {
2009 if (hx509_cert_check_eku(context->hx509ctx, cert,
2010 &asn1_oid_id_pkkdcekuoid, 0)) {
2011 hx509_name name;
2012 char *str;
2013 ret = hx509_cert_get_subject(cert, &name);
2014 if (ret == 0) {
2015 hx509_name_to_string(name, &str);
2016 krb5_warnx(context, "WARNING Found KDC certificate (%s)"
2017 "is missing the PK-INIT KDC EKU, this is bad for "
2018 "interoperability.", str);
2019 hx509_name_free(&name);
2020 free(str);
2021 }
2022 }
2023 hx509_cert_free(cert);
2024 } else
2025 krb5_warnx(context, "PKINIT: failed to find a signing "
2026 "certifiate with a public key");
2027 }
2028
2029 if (krb5_config_get_bool_default(context,
2030 NULL,
2031 FALSE,
2032 "kdc",
2033 "pkinit_allow_proxy_certificate",
2034 NULL))
2035 config->pkinit_allow_proxy_certs = 1;
2036
2037 file = krb5_config_get_string(context,
2038 NULL,
2039 "kdc",
2040 "pkinit_mappings_file",
2041 NULL);
2042 if (file == NULL) {
2043 asprintf(&fn, "%s/pki-mapping", hdb_db_dir(context));
2044 file = fn;
2045 }
2046
2047 load_mappings(context, file);
2048 if (fn)
2049 free(fn);
2050
2051 return 0;
2052 }
2053
2054 #endif /* PKINIT */
2055