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