2
* Copyright (C) 2006 Red Hat, Inc.
5
* Redistribution and use in source and binary forms, with or without
6
* modification, are permitted provided that the following conditions are met:
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
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.
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.
31
/* Worst. Preauthentication. Scheme. Ever. */
33
#ident "$Id: wpse_main.c,v 1.3 2007/01/02 22:33:51 kwc Exp $"
46
#include <arpa/inet.h>
49
#include <krb5/krb5.h>
50
#include <krb5/preauth_plugin.h>
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
57
client_get_flags(krb5_context kcontext, krb5_preauthtype pa_type)
62
static krb5_error_code
63
client_init(krb5_context kcontext, void **ctx)
67
pctx = malloc(sizeof(int));
76
client_fini(krb5_context kcontext, void *ctx)
83
fprintf(stderr, "wpse module called total of %d times\n", *pctx);
89
static krb5_error_code
90
client_process(krb5_context kcontext,
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,
102
preauth_get_as_key_proc gak_fct,
104
krb5_data *salt, krb5_data *s2kparams,
105
krb5_keyblock *as_key,
106
krb5_pa_data ***out_pa_data)
108
krb5_pa_data **send_pa;
109
krb5_int32 nnonce, enctype;
111
krb5_error_code status;
115
fprintf(stderr, "%d bytes of preauthentication data (type %d)\n",
116
pa_data->length, pa_data->pa_type);
119
pctx = plugin_context;
124
if (pa_data->length == 0) {
125
/* Create preauth data. */
126
send_pa = malloc(2 * sizeof(krb5_pa_data *));
129
send_pa[1] = NULL; /* Terminate list */
130
send_pa[0] = malloc(sizeof(krb5_pa_data));
131
if (send_pa[0] == NULL) {
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) {
143
/* Store the preauth data. */
144
nnonce = htonl(request->nonce);
145
memcpy(send_pa[0]->contents, &nnonce, 4);
146
*out_pa_data = send_pa;
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);
154
status = krb5_init_keyblock(kcontext, ntohl(enctype),
155
pa_data->length - 4, &kb);
158
memcpy(kb->contents, pa_data->contents + 4, pa_data->length - 4);
160
fprintf(stderr, "Recovered key type=%d, length=%d.\n",
161
kb->enctype, kb->length);
163
status = krb5_copy_keyblock_contents(kcontext, kb, as_key);
164
krb5_free_keyblock(kcontext, kb);
167
return KRB5KRB_ERR_GENERIC;
172
#define WPSE_MAGIC 0x77707365
173
typedef struct _wpse_req_ctx
180
client_req_init(krb5_context kcontext, void *plugin_context, void **req_context_p)
184
*req_context_p = NULL;
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));
191
ctx->magic = WPSE_MAGIC;
192
ctx->value = 0xc0dec0de;
194
*req_context_p = ctx;
198
client_req_cleanup(krb5_context kcontext, void *plugin_context, void *req_context)
200
wpse_req_ctx *ctx = (wpse_req_ctx *)req_context;
204
fprintf(stderr, "client_req_cleanup: req_ctx at %p has magic %x and value %x\n",
205
ctx, ctx->magic, ctx->value);
207
if (ctx->magic != WPSE_MAGIC) {
209
fprintf(stderr, "client_req_cleanup: req_context at %p has bad magic value %x\n",
219
static krb5_error_code
220
client_gic_opt(krb5_context kcontext,
221
void *plugin_context,
222
krb5_get_init_creds_opt *opt,
227
fprintf(stderr, "(wpse) client_gic_opt: received '%s' = '%s'\n",
235
static krb5_error_code
236
server_free_pa_request_context(krb5_context kcontext, void *plugin_context,
237
void **request_context)
239
if (*request_context != NULL) {
240
free(*request_context);
241
*request_context = NULL;
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,
257
/* Return zero bytes of data. */
259
data->contents = NULL;
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,
268
krb5_kdc_req *request,
269
krb5_enc_tkt_part *enc_tkt_reply,
271
preauth_get_entry_data_proc server_get_entry_data,
272
void *pa_module_context,
273
void **pa_request_context,
275
krb5_authdata ***authz_data)
278
krb5_data *test_edata;
279
krb5_authdata **my_authz_data;
282
fprintf(stderr, "wpse: server_verify()!\n");
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);
300
* Return some junk authorization data just to exercise the
301
* code path handling the returned authorization data.
303
* NOTE that this is NOT VALID authorization data!
306
fprintf(stderr, "wpse: doing authorization data!\n");
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};
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};
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) {
327
my_authz_data[0]->contents = malloc(AD_ALLOC_SIZE);
328
if (my_authz_data[0]->contents == NULL) {
329
free(my_authz_data[0]);
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;
342
fprintf(stderr, "Returning %d bytes of authorization data\n",
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) {
354
test_edata->length = 20;
355
memset(test_edata->data, '#', 20); /* fill it with junk */
356
*e_data = test_edata;
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,
368
krb5_kdc_req *request,
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)
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. */
387
/* We'll want a key with the first supported enctype. */
388
for (i = 0; i < request->nktypes; i++) {
390
if (krb5_init_keyblock(kcontext, request->ktype[i], 0, &kb) == 0) {
394
if (i >= request->nktypes) {
395
/* No matching cipher type found. */
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);
405
fprintf(stderr, "Generated random key, type=%d, length=%d.\n",
406
kb->enctype, kb->length);
409
*send_pa = malloc(sizeof(krb5_pa_data));
410
if (*send_pa == NULL) {
411
krb5_free_keyblock(kcontext, kb);
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) {
420
krb5_free_keyblock(kcontext, kb);
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);
433
krb5_free_keyblock(kcontext, kb);
439
server_get_flags(krb5_context kcontext, krb5_preauthtype pa_type)
441
return PA_HARDWARE | PA_REPLACES_KEY | PA_SUFFICIENT;
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};
447
struct krb5plugin_preauth_client_ftable_v1 preauthentication_client_1 = {
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 */
461
struct krb5plugin_preauth_server_ftable_v1 preauthentication_server_1 = {
463
&supported_server_pa_types[0],
470
server_free_pa_request_context,