~zulcss/samba/server-dailies-3.4

« back to all changes in this revision

Viewing changes to source4/heimdal/kdc/kx509.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) 2006 - 2007 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
#include <hex.h>
 
36
#include <rfc2459_asn1.h>
 
37
#include <hx509.h>
 
38
 
 
39
RCSID("$Id$");
 
40
 
 
41
/*
 
42
 *
 
43
 */
 
44
 
 
45
krb5_error_code
 
46
_kdc_try_kx509_request(void *ptr, size_t len, Kx509Request *req, size_t *size)
 
47
{
 
48
    if (len < 4)
 
49
        return -1;
 
50
    if (memcmp("\x00\x00\x02\x00", ptr, 4) != 0)
 
51
        return -1;
 
52
    return decode_Kx509Request(((unsigned char *)ptr) + 4, len - 4, req, size);
 
53
}
 
54
 
 
55
/*
 
56
 *
 
57
 */
 
58
 
 
59
static const unsigned char version_2_0[4] = {0 , 0, 2, 0};
 
60
 
 
61
static krb5_error_code
 
62
verify_req_hash(krb5_context context,
 
63
                const Kx509Request *req,
 
64
                krb5_keyblock *key)
 
65
{
 
66
    unsigned char digest[SHA_DIGEST_LENGTH];
 
67
    HMAC_CTX ctx;
 
68
 
 
69
    if (req->pk_hash.length != sizeof(digest)) {
 
70
        krb5_set_error_message(context, KRB5KDC_ERR_PREAUTH_FAILED,
 
71
                               "pk-hash have wrong length: %lu",
 
72
                               (unsigned long)req->pk_hash.length);
 
73
        return KRB5KDC_ERR_PREAUTH_FAILED;
 
74
    }
 
75
 
 
76
    HMAC_CTX_init(&ctx);
 
77
    HMAC_Init_ex(&ctx,
 
78
                 key->keyvalue.data, key->keyvalue.length,
 
79
                 EVP_sha1(), NULL);
 
80
    if (sizeof(digest) != HMAC_size(&ctx))
 
81
        krb5_abortx(context, "runtime error, hmac buffer wrong size in kx509");
 
82
    HMAC_Update(&ctx, version_2_0, sizeof(version_2_0));
 
83
    HMAC_Update(&ctx, req->pk_key.data, req->pk_key.length);
 
84
    HMAC_Final(&ctx, digest, 0);
 
85
    HMAC_CTX_cleanup(&ctx);
 
86
 
 
87
    if (memcmp(req->pk_hash.data, digest, sizeof(digest)) != 0) {
 
88
        krb5_set_error_message(context, KRB5KDC_ERR_PREAUTH_FAILED,
 
89
                               "pk-hash is not correct");
 
90
        return KRB5KDC_ERR_PREAUTH_FAILED;
 
91
    }
 
92
    return 0;
 
93
}
 
94
 
 
95
static krb5_error_code
 
96
calculate_reply_hash(krb5_context context,
 
97
                     krb5_keyblock *key,
 
98
                     Kx509Response *rep)
 
99
{
 
100
    HMAC_CTX ctx;
 
101
 
 
102
    HMAC_CTX_init(&ctx);
 
103
 
 
104
    HMAC_Init_ex(&ctx,
 
105
                 key->keyvalue.data, key->keyvalue.length,
 
106
                 EVP_sha1(), NULL);
 
107
    rep->hash->length = HMAC_size(&ctx);
 
108
    rep->hash->data = malloc(rep->hash->length);
 
109
    if (rep->hash->data == NULL) {
 
110
        HMAC_CTX_cleanup(&ctx);
 
111
        krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
 
112
        return ENOMEM;
 
113
    }
 
114
 
 
115
    HMAC_Update(&ctx, version_2_0, sizeof(version_2_0));
 
116
    if (rep->error_code) {
 
117
        int32_t t = *rep->error_code;
 
118
        do {
 
119
            unsigned char p = (t & 0xff);
 
120
            HMAC_Update(&ctx, &p, 1);
 
121
            t >>= 8;
 
122
        } while (t);
 
123
    }
 
124
    if (rep->certificate)
 
125
        HMAC_Update(&ctx, rep->certificate->data, rep->certificate->length);
 
126
    if (rep->e_text)
 
127
        HMAC_Update(&ctx, (unsigned char *)*rep->e_text, strlen(*rep->e_text));
 
128
 
 
129
    HMAC_Final(&ctx, rep->hash->data, 0);
 
130
    HMAC_CTX_cleanup(&ctx);
 
131
 
 
132
    return 0;
 
133
}
 
134
 
 
135
/*
 
136
 * Build a certifate for `principal´ that will expire at `endtime´.
 
137
 */
 
138
 
 
139
static krb5_error_code
 
140
build_certificate(krb5_context context,
 
141
                  krb5_kdc_configuration *config,
 
142
                  const krb5_data *key,
 
143
                  time_t endtime,
 
144
                  krb5_principal principal,
 
145
                  krb5_data *certificate)
 
146
{
 
147
    hx509_context hxctx = NULL;
 
148
    hx509_ca_tbs tbs = NULL;
 
149
    hx509_env env = NULL;
 
150
    hx509_cert cert = NULL;
 
151
    hx509_cert signer = NULL;
 
152
    int ret;
 
153
 
 
154
    if (krb5_principal_get_comp_string(context, principal, 1) != NULL) {
 
155
        kdc_log(context, config, 0, "Principal is not a user");
 
156
        return EINVAL;
 
157
    }
 
158
 
 
159
    ret = hx509_context_init(&hxctx);
 
160
    if (ret)
 
161
        goto out;
 
162
 
 
163
    ret = hx509_env_add(hxctx, &env, "principal-name",
 
164
                        krb5_principal_get_comp_string(context, principal, 0));
 
165
    if (ret)
 
166
        goto out;
 
167
 
 
168
    {
 
169
        hx509_certs certs;
 
170
        hx509_query *q;
 
171
 
 
172
        ret = hx509_certs_init(hxctx, config->kx509_ca, 0,
 
173
                               NULL, &certs);
 
174
        if (ret) {
 
175
            kdc_log(context, config, 0, "Failed to load CA %s",
 
176
                    config->kx509_ca);
 
177
            goto out;
 
178
        }
 
179
        ret = hx509_query_alloc(hxctx, &q);
 
180
        if (ret) {
 
181
            hx509_certs_free(&certs);
 
182
            goto out;
 
183
        }
 
184
 
 
185
        hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY);
 
186
        hx509_query_match_option(q, HX509_QUERY_OPTION_KU_KEYCERTSIGN);
 
187
 
 
188
        ret = hx509_certs_find(hxctx, certs, q, &signer);
 
189
        hx509_query_free(hxctx, q);
 
190
        hx509_certs_free(&certs);
 
191
        if (ret) {
 
192
            kdc_log(context, config, 0, "Failed to find a CA in %s",
 
193
                    config->kx509_ca);
 
194
            goto out;
 
195
        }
 
196
    }
 
197
 
 
198
    ret = hx509_ca_tbs_init(hxctx, &tbs);
 
199
    if (ret)
 
200
        goto out;
 
201
 
 
202
    {
 
203
        SubjectPublicKeyInfo spki;
 
204
        heim_any any;
 
205
 
 
206
        memset(&spki, 0, sizeof(spki));
 
207
 
 
208
        spki.subjectPublicKey.data = key->data;
 
209
        spki.subjectPublicKey.length = key->length * 8;
 
210
 
 
211
        ret = der_copy_oid(oid_id_pkcs1_rsaEncryption(),
 
212
                           &spki.algorithm.algorithm);
 
213
 
 
214
        any.data = "\x05\x00";
 
215
        any.length = 2;
 
216
        spki.algorithm.parameters = &any;
 
217
 
 
218
        ret = hx509_ca_tbs_set_spki(hxctx, tbs, &spki);
 
219
        der_free_oid(&spki.algorithm.algorithm);
 
220
        if (ret)
 
221
            goto out;
 
222
    }
 
223
 
 
224
    {
 
225
        hx509_certs certs;
 
226
        hx509_cert template;
 
227
 
 
228
        ret = hx509_certs_init(hxctx, config->kx509_template, 0,
 
229
                               NULL, &certs);
 
230
        if (ret) {
 
231
            kdc_log(context, config, 0, "Failed to load template %s",
 
232
                    config->kx509_template);
 
233
            goto out;
 
234
        }
 
235
        ret = hx509_get_one_cert(hxctx, certs, &template);
 
236
        hx509_certs_free(&certs);
 
237
        if (ret) {
 
238
            kdc_log(context, config, 0, "Failed to find template in %s",
 
239
                    config->kx509_template);
 
240
            goto out;
 
241
        }
 
242
        ret = hx509_ca_tbs_set_template(hxctx, tbs,
 
243
                                        HX509_CA_TEMPLATE_SUBJECT|
 
244
                                        HX509_CA_TEMPLATE_KU|
 
245
                                        HX509_CA_TEMPLATE_EKU,
 
246
                                        template);
 
247
        hx509_cert_free(template);
 
248
        if (ret)
 
249
            goto out;
 
250
    }
 
251
 
 
252
    hx509_ca_tbs_set_notAfter(hxctx, tbs, endtime);
 
253
 
 
254
    hx509_ca_tbs_subject_expand(hxctx, tbs, env);
 
255
    hx509_env_free(&env);
 
256
 
 
257
    ret = hx509_ca_sign(hxctx, tbs, signer, &cert);
 
258
    hx509_cert_free(signer);
 
259
    if (ret)
 
260
        goto out;
 
261
 
 
262
    hx509_ca_tbs_free(&tbs);
 
263
 
 
264
    ret = hx509_cert_binary(hxctx, cert, certificate);
 
265
    hx509_cert_free(cert);
 
266
    if (ret)
 
267
        goto out;
 
268
                
 
269
    hx509_context_free(&hxctx);
 
270
 
 
271
    return 0;
 
272
out:
 
273
    if (env)
 
274
        hx509_env_free(&env);
 
275
    if (tbs)
 
276
        hx509_ca_tbs_free(&tbs);
 
277
    if (signer)
 
278
        hx509_cert_free(signer);
 
279
    if (hxctx)
 
280
        hx509_context_free(&hxctx);
 
281
    krb5_set_error_message(context, ret, "cert creation failed");
 
282
    return ret;
 
283
}
 
284
 
 
285
/*
 
286
 *
 
287
 */
 
288
 
 
289
krb5_error_code
 
290
_kdc_do_kx509(krb5_context context,
 
291
              krb5_kdc_configuration *config,
 
292
              const Kx509Request *req, krb5_data *reply,
 
293
              const char *from, struct sockaddr *addr)
 
294
{
 
295
    krb5_error_code ret;
 
296
    krb5_ticket *ticket = NULL;
 
297
    krb5_flags ap_req_options;
 
298
    krb5_auth_context ac = NULL;
 
299
    krb5_keytab id = NULL;
 
300
    krb5_principal sprincipal = NULL, cprincipal = NULL;
 
301
    char *cname = NULL;
 
302
    Kx509Response rep;
 
303
    size_t size;
 
304
    krb5_keyblock *key = NULL;
 
305
 
 
306
    krb5_data_zero(reply);
 
307
    memset(&rep, 0, sizeof(rep));
 
308
 
 
309
    if(!config->enable_kx509) {
 
310
        kdc_log(context, config, 0,
 
311
                "Rejected kx509 request (disabled) from %s", from);
 
312
        return KRB5KDC_ERR_POLICY;
 
313
    }
 
314
 
 
315
    kdc_log(context, config, 0, "Kx509 request from %s", from);
 
316
 
 
317
    ret = krb5_kt_resolve(context, "HDB:", &id);
 
318
    if (ret) {
 
319
        kdc_log(context, config, 0, "Can't open database for digest");
 
320
        goto out;
 
321
    }
 
322
 
 
323
    ret = krb5_rd_req(context,
 
324
                      &ac,
 
325
                      &req->authenticator,
 
326
                      NULL,
 
327
                      id,
 
328
                      &ap_req_options,
 
329
                      &ticket);
 
330
    if (ret)
 
331
        goto out;
 
332
 
 
333
    ret = krb5_ticket_get_client(context, ticket, &cprincipal);
 
334
    if (ret)
 
335
        goto out;
 
336
 
 
337
    ret = krb5_unparse_name(context, cprincipal, &cname);
 
338
    if (ret)
 
339
        goto out;
 
340
 
 
341
    /* verify server principal */
 
342
 
 
343
    ret = krb5_sname_to_principal(context, NULL, "kca_service",
 
344
                                  KRB5_NT_UNKNOWN, &sprincipal);
 
345
    if (ret)
 
346
        goto out;
 
347
 
 
348
    {
 
349
        krb5_principal principal = NULL;
 
350
 
 
351
        ret = krb5_ticket_get_server(context, ticket, &principal);
 
352
        if (ret)
 
353
            goto out;
 
354
 
 
355
        ret = krb5_principal_compare(context, sprincipal, principal);
 
356
        krb5_free_principal(context, principal);
 
357
        if (ret != TRUE) {
 
358
            ret = KRB5KDC_ERR_SERVER_NOMATCH;
 
359
            krb5_set_error_message(context, ret,
 
360
                                   "User %s used wrong Kx509 service principal",
 
361
                                   cname);
 
362
            goto out;
 
363
        }
 
364
    }
 
365
 
 
366
    ret = krb5_auth_con_getkey(context, ac, &key);
 
367
    if (ret == 0 && key == NULL)
 
368
        ret = KRB5KDC_ERR_NULL_KEY;
 
369
    if (ret) {
 
370
        krb5_set_error_message(context, ret, "Kx509 can't get session key");
 
371
        goto out;
 
372
    }
 
373
 
 
374
    ret = verify_req_hash(context, req, key);
 
375
    if (ret)
 
376
        goto out;
 
377
 
 
378
    /* Verify that the key is encoded RSA key */
 
379
    {
 
380
        RSAPublicKey key;
 
381
        size_t size;
 
382
 
 
383
        ret = decode_RSAPublicKey(req->pk_key.data, req->pk_key.length,
 
384
                                  &key, &size);
 
385
        if (ret)
 
386
            goto out;
 
387
        free_RSAPublicKey(&key);
 
388
        if (size != req->pk_key.length)
 
389
            ;
 
390
    }
 
391
 
 
392
    ALLOC(rep.certificate);
 
393
    if (rep.certificate == NULL)
 
394
        goto out;
 
395
    krb5_data_zero(rep.certificate);
 
396
    ALLOC(rep.hash);
 
397
    if (rep.hash == NULL)
 
398
        goto out;
 
399
    krb5_data_zero(rep.hash);
 
400
 
 
401
    ret = build_certificate(context, config, &req->pk_key,
 
402
                            krb5_ticket_get_endtime(context, ticket),
 
403
                            cprincipal, rep.certificate);
 
404
    if (ret)
 
405
        goto out;
 
406
 
 
407
    ret = calculate_reply_hash(context, key, &rep);
 
408
    if (ret)
 
409
        goto out;
 
410
 
 
411
    /*
 
412
     * Encode reply, [ version | Kx509Response ]
 
413
     */
 
414
 
 
415
    {
 
416
        krb5_data data;
 
417
 
 
418
        ASN1_MALLOC_ENCODE(Kx509Response, data.data, data.length, &rep,
 
419
                           &size, ret);
 
420
        if (ret) {
 
421
            krb5_set_error_message(context, ret, "Failed to encode kx509 reply");
 
422
            goto out;
 
423
        }
 
424
        if (size != data.length)
 
425
            krb5_abortx(context, "ASN1 internal error");
 
426
 
 
427
        ret = krb5_data_alloc(reply, data.length + sizeof(version_2_0));
 
428
        if (ret) {
 
429
            free(data.data);
 
430
            goto out;
 
431
        }
 
432
        memcpy(reply->data, version_2_0, sizeof(version_2_0));
 
433
        memcpy(((unsigned char *)reply->data) + sizeof(version_2_0),
 
434
               data.data, data.length);
 
435
        free(data.data);
 
436
    }
 
437
 
 
438
    kdc_log(context, config, 0, "Successful Kx509 request for %s", cname);
 
439
 
 
440
out:
 
441
    if (ac)
 
442
        krb5_auth_con_free(context, ac);
 
443
    if (ret)
 
444
        krb5_warn(context, ret, "Kx509 request from %s failed", from);
 
445
    if (ticket)
 
446
        krb5_free_ticket(context, ticket);
 
447
    if (id)
 
448
        krb5_kt_close(context, id);
 
449
    if (sprincipal)
 
450
        krb5_free_principal(context, sprincipal);
 
451
    if (cprincipal)
 
452
        krb5_free_principal(context, cprincipal);
 
453
    if (key)
 
454
        krb5_free_keyblock (context, key);
 
455
    if (cname)
 
456
        free(cname);
 
457
    free_Kx509Response(&rep);
 
458
 
 
459
    return 0;
 
460
}