~zulcss/samba/server-dailies-3.4

« back to all changes in this revision

Viewing changes to source4/heimdal/kdc/pkinit.c

  • Committer: Chuck Short
  • Date: 2010-09-28 20:38:39 UTC
  • Revision ID: zulcss@ubuntu.com-20100928203839-pgjulytsi9ue63x1
Initial version

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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,
 
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,
 
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,
 
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,
 
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)
 
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,
 
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,
 
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)
 
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,
 
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,
 
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,
 
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,
 
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,
 
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,
 
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,
 
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,
 
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)
 
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,
 
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 */