~ubuntu-branches/ubuntu/maverick/krb5/maverick

« back to all changes in this revision

Viewing changes to src/plugins/preauth/wpse/wpse_main.c

  • Committer: Bazaar Package Importer
  • Author(s): Sam Hartman, Russ Allbery, Sam Hartman
  • Date: 2008-08-21 10:41:41 UTC
  • mfrom: (11.1.15 intrepid)
  • Revision ID: james.westby@ubuntu.com-20080821104141-a0f9c4o4cpo8xd0o
Tags: 1.6.dfsg.4~beta1-4
[ Russ Allbery ]
* Translation updates:
  - Swedish, thanks Martin Bagge.  (Closes: #487669, #491774)
  - Italian, thanks Luca Monducci.  (Closes: #493962)

[ Sam Hartman ]
* Translation Updates:
    - Dutch, Thanks Vincent Zweije, Closes: #495733

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright (C) 2006 Red Hat, Inc.
 
3
 * All rights reserved.
 
4
 *
 
5
 * Redistribution and use in source and binary forms, with or without
 
6
 * modification, are permitted provided that the following conditions are met:
 
7
 *
 
8
 *     * Redistributions of source code must retain the above copyright
 
9
 *       notice, this list of conditions and the following disclaimer.
 
10
 *     * Redistributions in binary form must reproduce the above copyright
 
11
 *       notice, this list of conditions and the following disclaimer in
 
12
 *       the documentation and/or other materials provided with the
 
13
 *       distribution.
 
14
 *     * Neither the name of Red Hat, Inc., nor the names of its
 
15
 *       contributors may be used to endorse or promote products derived
 
16
 *       from this software without specific prior written permission.
 
17
 *
 
18
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 
19
 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 
20
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 
21
 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
 
22
 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 
23
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 
24
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 
25
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 
26
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 
27
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 
28
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
29
 */
 
30
 
 
31
/* Worst. Preauthentication. Scheme. Ever. */
 
32
 
 
33
#ident "$Id: wpse_main.c,v 1.3 2007/01/02 22:33:51 kwc Exp $"
 
34
 
 
35
#ifdef HAVE_CONFIG_H
 
36
#include "config.h"
 
37
#endif
 
38
 
 
39
#ifdef HAVE_ERRNO_H
 
40
#include <errno.h>
 
41
#endif
 
42
#ifdef HAVE_STRING_H
 
43
#include <string.h>
 
44
#endif
 
45
 
 
46
#include <arpa/inet.h>
 
47
#include <stdio.h>
 
48
 
 
49
#include <krb5/krb5.h>
 
50
#include <krb5/preauth_plugin.h>
 
51
 
 
52
/* This is not a standardized value.  It's defined here only to make it easier
 
53
 * to change in this module. */
 
54
#define KRB5_PADATA_WPSE_REQ 131
 
55
 
 
56
static int
 
57
client_get_flags(krb5_context kcontext, krb5_preauthtype pa_type)
 
58
{
 
59
    return PA_REAL;
 
60
}
 
61
 
 
62
static krb5_error_code
 
63
client_init(krb5_context kcontext, void **ctx)
 
64
{
 
65
    int *pctx;
 
66
 
 
67
    pctx = malloc(sizeof(int));
 
68
    if (pctx == NULL)
 
69
        return ENOMEM;
 
70
    *pctx = 0;
 
71
    *ctx = pctx;
 
72
    return 0;
 
73
}
 
74
 
 
75
static void
 
76
client_fini(krb5_context kcontext, void *ctx)
 
77
{
 
78
    int *pctx;
 
79
 
 
80
    pctx = ctx;
 
81
    if (pctx) {
 
82
#ifdef DEBUG
 
83
        fprintf(stderr, "wpse module called total of %d times\n", *pctx);
 
84
#endif
 
85
        free(pctx);
 
86
    }
 
87
}
 
88
 
 
89
static krb5_error_code
 
90
client_process(krb5_context kcontext,
 
91
               void *plugin_context,
 
92
               void *request_context,
 
93
               krb5_get_init_creds_opt *opt,
 
94
               preauth_get_client_data_proc client_get_data_proc,
 
95
               struct _krb5_preauth_client_rock *rock,
 
96
               krb5_kdc_req *request,
 
97
               krb5_data *encoded_request_body,
 
98
               krb5_data *encoded_previous_request,
 
99
               krb5_pa_data *pa_data,
 
100
               krb5_prompter_fct prompter,
 
101
               void *prompter_data,
 
102
               preauth_get_as_key_proc gak_fct,
 
103
               void *gak_data,
 
104
               krb5_data *salt, krb5_data *s2kparams,
 
105
               krb5_keyblock *as_key,
 
106
               krb5_pa_data ***out_pa_data)
 
107
{
 
108
    krb5_pa_data **send_pa;
 
109
    krb5_int32 nnonce, enctype;
 
110
    krb5_keyblock *kb;
 
111
    krb5_error_code status;
 
112
    int *pctx;
 
113
 
 
114
#ifdef DEBUG
 
115
    fprintf(stderr, "%d bytes of preauthentication data (type %d)\n",
 
116
            pa_data->length, pa_data->pa_type);
 
117
#endif
 
118
 
 
119
    pctx = plugin_context;
 
120
    if (pctx) {
 
121
        (*pctx)++;
 
122
    }
 
123
 
 
124
    if (pa_data->length == 0) {
 
125
        /* Create preauth data. */
 
126
        send_pa = malloc(2 * sizeof(krb5_pa_data *));
 
127
        if (send_pa == NULL)
 
128
            return ENOMEM;
 
129
        send_pa[1] = NULL;  /* Terminate list */
 
130
        send_pa[0] = malloc(sizeof(krb5_pa_data));
 
131
        if (send_pa[0] == NULL) {
 
132
            free(send_pa);
 
133
            return ENOMEM;
 
134
        }
 
135
        send_pa[0]->pa_type = KRB5_PADATA_WPSE_REQ;
 
136
        send_pa[0]->length = 4;
 
137
        send_pa[0]->contents = malloc(4);
 
138
        if (send_pa[0]->contents == NULL) {
 
139
            free(send_pa[0]);
 
140
            free(send_pa);
 
141
            return ENOMEM;
 
142
        }
 
143
        /* Store the preauth data. */
 
144
        nnonce = htonl(request->nonce);
 
145
        memcpy(send_pa[0]->contents, &nnonce, 4);
 
146
        *out_pa_data = send_pa;
 
147
    } else {
 
148
        /* A reply from the KDC.  Conventionally this would be
 
149
         * indicated by a different preauthentication type, but this
 
150
         * mechanism/implementation doesn't do that. */
 
151
        if (pa_data->length > 4) {
 
152
            memcpy(&enctype, pa_data->contents, 4);
 
153
            kb = NULL;
 
154
            status = krb5_init_keyblock(kcontext, ntohl(enctype),
 
155
                                        pa_data->length - 4, &kb);
 
156
            if (status != 0)
 
157
                return status;
 
158
            memcpy(kb->contents, pa_data->contents + 4, pa_data->length - 4);
 
159
#ifdef DEBUG
 
160
            fprintf(stderr, "Recovered key type=%d, length=%d.\n",
 
161
                    kb->enctype, kb->length);
 
162
#endif
 
163
            status = krb5_copy_keyblock_contents(kcontext, kb, as_key);
 
164
            krb5_free_keyblock(kcontext, kb);
 
165
            return status;
 
166
        }
 
167
        return KRB5KRB_ERR_GENERIC;
 
168
    }
 
169
    return 0;
 
170
}
 
171
 
 
172
#define WPSE_MAGIC 0x77707365
 
173
typedef struct _wpse_req_ctx
 
174
{
 
175
    int magic;
 
176
    int value;
 
177
} wpse_req_ctx;
 
178
 
 
179
static void
 
180
client_req_init(krb5_context kcontext, void *plugin_context, void **req_context_p)
 
181
{
 
182
    wpse_req_ctx *ctx;
 
183
 
 
184
    *req_context_p = NULL;
 
185
 
 
186
    /* Allocate a request context. Useful for verifying that we do in fact
 
187
     * do per-request cleanup. */
 
188
    ctx = (wpse_req_ctx *) malloc(sizeof(*ctx));
 
189
    if (ctx == NULL)
 
190
        return;
 
191
    ctx->magic = WPSE_MAGIC;
 
192
    ctx->value = 0xc0dec0de;
 
193
 
 
194
    *req_context_p = ctx;
 
195
}
 
196
 
 
197
static void
 
198
client_req_cleanup(krb5_context kcontext, void *plugin_context, void *req_context)
 
199
{
 
200
    wpse_req_ctx *ctx = (wpse_req_ctx *)req_context;
 
201
 
 
202
    if (ctx) {
 
203
#ifdef DEBUG
 
204
        fprintf(stderr, "client_req_cleanup: req_ctx at %p has magic %x and value %x\n",
 
205
                ctx, ctx->magic, ctx->value);
 
206
#endif
 
207
        if (ctx->magic != WPSE_MAGIC) {
 
208
#ifdef DEBUG
 
209
            fprintf(stderr, "client_req_cleanup: req_context at %p has bad magic value %x\n",
 
210
                    ctx, ctx->magic);
 
211
#endif
 
212
            return;
 
213
        }
 
214
        free(ctx);
 
215
    }
 
216
    return;
 
217
}
 
218
 
 
219
static krb5_error_code
 
220
client_gic_opt(krb5_context kcontext,
 
221
               void *plugin_context,
 
222
               krb5_get_init_creds_opt *opt,
 
223
               const char *attr,
 
224
               const char *value)
 
225
{
 
226
#ifdef DEBUG
 
227
    fprintf(stderr, "(wpse) client_gic_opt: received '%s' = '%s'\n",
 
228
            attr, value);
 
229
#endif
 
230
    return 0;
 
231
}
 
232
 
 
233
 
 
234
/* Free state. */
 
235
static krb5_error_code
 
236
server_free_pa_request_context(krb5_context kcontext, void *plugin_context,
 
237
                               void **request_context)
 
238
{
 
239
    if (*request_context != NULL) {
 
240
        free(*request_context);
 
241
        *request_context = NULL;
 
242
    }
 
243
    return 0;
 
244
}
 
245
 
 
246
/* Obtain and return any preauthentication data (which is destined for the
 
247
 * client) which matches type data->pa_type. */
 
248
static krb5_error_code
 
249
server_get_edata(krb5_context kcontext,
 
250
                 krb5_kdc_req *request,
 
251
                 struct _krb5_db_entry_new *client,
 
252
                 struct _krb5_db_entry_new *server,
 
253
                 preauth_get_entry_data_proc server_get_entry_data,
 
254
                 void *pa_module_context,
 
255
                 krb5_pa_data *data)
 
256
{
 
257
    /* Return zero bytes of data. */
 
258
    data->length = 0;
 
259
    data->contents = NULL;
 
260
    return 0;
 
261
}
 
262
 
 
263
/* Verify a request from a client. */
 
264
static krb5_error_code
 
265
server_verify(krb5_context kcontext,
 
266
              struct _krb5_db_entry_new *client,
 
267
              krb5_data *req_pkt,
 
268
              krb5_kdc_req *request,
 
269
              krb5_enc_tkt_part *enc_tkt_reply,
 
270
              krb5_pa_data *data,
 
271
              preauth_get_entry_data_proc server_get_entry_data,
 
272
              void *pa_module_context,
 
273
              void **pa_request_context,
 
274
              krb5_data **e_data,
 
275
              krb5_authdata ***authz_data)
 
276
{
 
277
    krb5_int32 nnonce;
 
278
    krb5_data *test_edata;
 
279
    krb5_authdata **my_authz_data;
 
280
 
 
281
#ifdef DEBUG
 
282
    fprintf(stderr, "wpse: server_verify()!\n");
 
283
#endif
 
284
    /* Verify the preauth data. */
 
285
    if (data->length != 4)
 
286
        return KRB5KDC_ERR_PREAUTH_FAILED;
 
287
    memcpy(&nnonce, data->contents, 4);
 
288
    nnonce = ntohl(nnonce);
 
289
    if (memcmp(&nnonce, &request->nonce, 4) != 0)
 
290
        return KRB5KDC_ERR_PREAUTH_FAILED;
 
291
    /* Note that preauthentication succeeded. */
 
292
    enc_tkt_reply->flags |= TKT_FLG_PRE_AUTH;
 
293
    enc_tkt_reply->flags |= TKT_FLG_HW_AUTH;
 
294
    /* Allocate a context. Useful for verifying that we do in fact do
 
295
     * per-request cleanup. */
 
296
    if (*pa_request_context == NULL)
 
297
        *pa_request_context = malloc(4);
 
298
 
 
299
    /*
 
300
     * Return some junk authorization data just to exercise the
 
301
     * code path handling the returned authorization data.
 
302
     *
 
303
     * NOTE that this is NOT VALID authorization data!
 
304
     */
 
305
#ifdef DEBUG
 
306
    fprintf(stderr, "wpse: doing authorization data!\n");
 
307
#endif
 
308
#if 1 /* USE_5000_AD */
 
309
#define AD_ALLOC_SIZE 5000
 
310
    /* ad_header consists of a sequence tag (0x30) and length (0x82 0x1384)
 
311
     * followed by octet string tag (0x04) and length (0x82 0x1380) */
 
312
    krb5_octet ad_header[] = {0x30, 0x82, 0x13, 0x84, 0x04, 0x82, 0x13, 0x80};
 
313
#else
 
314
#define AD_ALLOC_SIZE 100
 
315
    /* ad_header consists of a sequence tag (0x30) and length (0x62)
 
316
     * followed by octet string tag (0x04) and length (0x60) */
 
317
    krb5_octet ad_header[] = {0x30, 0x62, 0x04, 0x60};
 
318
#endif
 
319
    my_authz_data = malloc(2 * sizeof(*my_authz_data));
 
320
    if (my_authz_data != NULL) {
 
321
        my_authz_data[1] = NULL;
 
322
        my_authz_data[0] = malloc(sizeof(krb5_authdata));
 
323
        if (my_authz_data[0] == NULL) {
 
324
            free(my_authz_data);
 
325
            return ENOMEM;
 
326
        }
 
327
        my_authz_data[0]->contents = malloc(AD_ALLOC_SIZE);
 
328
        if (my_authz_data[0]->contents == NULL) {
 
329
            free(my_authz_data[0]);
 
330
            free(my_authz_data);
 
331
            return ENOMEM;
 
332
        }
 
333
        memset(my_authz_data[0]->contents, '\0', AD_ALLOC_SIZE);
 
334
        my_authz_data[0]->magic = KV5M_AUTHDATA;
 
335
        my_authz_data[0]->ad_type = 1;
 
336
        my_authz_data[0]->length = AD_ALLOC_SIZE;
 
337
        memcpy(my_authz_data[0]->contents, ad_header, sizeof(ad_header));
 
338
        sprintf(my_authz_data[0]->contents + sizeof(ad_header),
 
339
               "wpse authorization data: %d bytes worth!\n", AD_ALLOC_SIZE);
 
340
        *authz_data = my_authz_data;
 
341
#ifdef DEBUG
 
342
        fprintf(stderr, "Returning %d bytes of authorization data\n",
 
343
                AD_ALLOC_SIZE);
 
344
#endif
 
345
    }
 
346
 
 
347
    /* Return edata to exercise code that handles edata... */
 
348
    test_edata = malloc(sizeof(*test_edata));
 
349
    if (test_edata != NULL) {
 
350
        test_edata->data = malloc(20);
 
351
        if (test_edata->data == NULL) {
 
352
            free(test_edata);
 
353
        } else {
 
354
            test_edata->length = 20;
 
355
            memset(test_edata->data, '#', 20); /* fill it with junk */
 
356
            *e_data = test_edata;
 
357
        }
 
358
    }
 
359
    return 0;
 
360
}
 
361
 
 
362
/* Create the response for a client. */
 
363
static krb5_error_code
 
364
server_return(krb5_context kcontext,
 
365
              krb5_pa_data *padata,
 
366
              struct _krb5_db_entry_new *client,
 
367
              krb5_data *req_pkt,
 
368
              krb5_kdc_req *request,
 
369
              krb5_kdc_rep *reply,
 
370
              struct _krb5_key_data *client_key,
 
371
              krb5_keyblock *encrypting_key,
 
372
              krb5_pa_data **send_pa,
 
373
              preauth_get_entry_data_proc server_get_entry_data,
 
374
              void *pa_module_context,
 
375
              void **pa_request_context)
 
376
{
 
377
    /* This module does a couple of dumb things.  It tags its reply with
 
378
     * the same type as the initial challenge (expecting the client to sort
 
379
     * out whether there's anything useful in there).  Oh, and it replaces
 
380
     * the AS reply key with one which is sent in the clear. */
 
381
    krb5_keyblock *kb;
 
382
    krb5_int32 enctype;
 
383
    int i;
 
384
 
 
385
    *send_pa = NULL;
 
386
 
 
387
    /* We'll want a key with the first supported enctype. */
 
388
    for (i = 0; i < request->nktypes; i++) {
 
389
        kb = NULL;
 
390
        if (krb5_init_keyblock(kcontext, request->ktype[i], 0, &kb) == 0) {
 
391
            break;
 
392
        }
 
393
    }
 
394
    if (i >= request->nktypes) {
 
395
        /* No matching cipher type found. */
 
396
        return 0;
 
397
    }
 
398
 
 
399
    /* Randomize a key and save it for the client. */
 
400
    if (krb5_c_make_random_key(kcontext, request->ktype[i], kb) != 0) {
 
401
        krb5_free_keyblock(kcontext, kb);
 
402
        return 0;
 
403
    }
 
404
#ifdef DEBUG
 
405
    fprintf(stderr, "Generated random key, type=%d, length=%d.\n",
 
406
            kb->enctype, kb->length);
 
407
#endif
 
408
 
 
409
    *send_pa = malloc(sizeof(krb5_pa_data));
 
410
    if (*send_pa == NULL) {
 
411
        krb5_free_keyblock(kcontext, kb);
 
412
        return ENOMEM;
 
413
    }
 
414
    (*send_pa)->pa_type = KRB5_PADATA_WPSE_REQ;
 
415
    (*send_pa)->length = 4 + kb->length;
 
416
    (*send_pa)->contents = malloc(4 + kb->length);
 
417
    if ((*send_pa)->contents == NULL) {
 
418
        free(*send_pa);
 
419
        *send_pa = NULL;
 
420
        krb5_free_keyblock(kcontext, kb);
 
421
        return ENOMEM;
 
422
    }
 
423
 
 
424
    /* Store the preauth data. */
 
425
    enctype = htonl(kb->enctype);
 
426
    memcpy((*send_pa)->contents, &enctype, 4);
 
427
    memcpy((*send_pa)->contents + 4, kb->contents, kb->length);
 
428
    krb5_free_keyblock_contents(kcontext, encrypting_key);
 
429
    krb5_copy_keyblock_contents(kcontext, kb, encrypting_key);
 
430
 
 
431
 
 
432
    /* Clean up. */
 
433
    krb5_free_keyblock(kcontext, kb);
 
434
 
 
435
    return 0;
 
436
}
 
437
 
 
438
static int
 
439
server_get_flags(krb5_context kcontext, krb5_preauthtype pa_type)
 
440
{
 
441
    return PA_HARDWARE | PA_REPLACES_KEY | PA_SUFFICIENT;
 
442
}
 
443
 
 
444
static krb5_preauthtype supported_client_pa_types[] = {KRB5_PADATA_WPSE_REQ, 0};
 
445
static krb5_preauthtype supported_server_pa_types[] = {KRB5_PADATA_WPSE_REQ, 0};
 
446
 
 
447
struct krb5plugin_preauth_client_ftable_v1 preauthentication_client_1 = {
 
448
    "wpse",                                 /* name */
 
449
    &supported_client_pa_types[0],          /* pa_type_list */
 
450
    NULL,                                   /* enctype_list */
 
451
    client_init,                            /* plugin init function */
 
452
    client_fini,                            /* plugin fini function */
 
453
    client_get_flags,                       /* get flags function */
 
454
    client_req_init,                        /* request init function */
 
455
    client_req_cleanup,                     /* request fini function */
 
456
    client_process,                         /* process function */
 
457
    NULL,                                   /* try_again function */
 
458
    client_gic_opt                          /* get init creds opts function */
 
459
};
 
460
 
 
461
struct krb5plugin_preauth_server_ftable_v1 preauthentication_server_1 = {
 
462
    "wpse",
 
463
    &supported_server_pa_types[0],
 
464
    NULL,
 
465
    NULL,
 
466
    server_get_flags,
 
467
    server_get_edata,
 
468
    server_verify,
 
469
    server_return,
 
470
    server_free_pa_request_context,
 
471
};