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