52
#define PA_REAL 0x0001
53
#define PA_INFO 0x0002
65
/* Create the per-krb5_context context. This means loading the modules
66
* if we haven't done that yet (applications which never obtain initial
67
* credentials should never hit this routine), breaking up the module's
68
* list of support pa_types so that we can iterate over the modules more
69
* easily, and copying over the relevant parts of the module's table. */
71
krb5_init_preauth_context(krb5_context kcontext)
73
int n_modules, n_tables, i, j, k;
75
struct krb5plugin_preauth_client_ftable_v1 *table;
76
krb5_preauth_context *context = NULL;
78
krb5_preauthtype pa_type;
81
/* Only do this once for each krb5_context */
82
if (kcontext->preauth_context != NULL)
85
/* load the plugins for the current context */
86
if (PLUGIN_DIR_OPEN(&kcontext->preauth_plugins) == 0) {
87
if (krb5int_open_plugin_dirs(objdirs, NULL,
88
&kcontext->preauth_plugins,
89
&kcontext->err) != 0) {
94
/* pull out the module function tables for all of the modules */
96
if (krb5int_get_plugin_dir_data(&kcontext->preauth_plugins,
97
"preauthentication_client_1",
99
&kcontext->err) != 0) {
102
if (tables == NULL) {
106
/* count how many modules we ended up loading, and how many preauth
107
* types we may claim to support as a result */
110
(tables != NULL) && (tables[n_tables] != NULL);
112
table = tables[n_tables];
113
if ((table->pa_type_list != NULL) && (table->process != NULL)) {
114
for (j = 0; table->pa_type_list[j] > 0; j++) {
120
/* allocate the space we need */
121
context = malloc(sizeof(*context));
122
if (context == NULL) {
123
krb5int_free_plugin_dir_data(tables);
126
context->modules = malloc(sizeof(context->modules[0]) * n_modules);
127
if (context->modules == NULL) {
128
krb5int_free_plugin_dir_data(tables);
132
memset(context->modules, 0, sizeof(context->modules[0]) * n_modules);
133
context->n_modules = n_modules;
135
/* fill in the structure */
137
for (i = 0; i < n_tables; i++) {
139
if ((table->pa_type_list != NULL) && (table->process != NULL)) {
140
plugin_context = NULL;
141
if ((table->init != NULL) &&
142
((*table->init)(kcontext, &plugin_context) != 0)) {
144
fprintf (stderr, "init err, skipping module \"%s\"\n",
151
for (j = 0; table->pa_type_list[j] > 0; j++) {
152
pa_type = table->pa_type_list[j];
153
context->modules[k].pa_type = pa_type;
154
context->modules[k].enctypes = table->enctype_list;
155
context->modules[k].plugin_context = plugin_context;
156
/* Only call client_fini once per plugin */
158
context->modules[k].client_fini = table->fini;
160
context->modules[k].client_fini = NULL;
161
context->modules[k].ftable = table;
162
context->modules[k].name = table->name;
163
context->modules[k].flags = (*table->flags)(kcontext, pa_type);
164
context->modules[k].use_count = 0;
165
context->modules[k].client_process = table->process;
166
context->modules[k].client_tryagain = table->tryagain;
168
context->modules[k].client_supply_gic_opts = table->gic_opts;
170
context->modules[k].client_supply_gic_opts = NULL;
171
context->modules[k].request_context = NULL;
173
* Only call request_init and request_fini once per plugin.
174
* Only the first module within each plugin will ever
175
* have request_context filled in. Every module within
176
* the plugin will have its request_context_pp pointing
177
* to that entry's request_context. That way all the
178
* modules within the plugin share the same request_context
181
context->modules[k].client_req_init = table->request_init;
182
context->modules[k].client_req_fini = table->request_fini;
183
rcpp = &context->modules[k].request_context;
185
context->modules[k].client_req_init = NULL;
186
context->modules[k].client_req_fini = NULL;
188
context->modules[k].request_context_pp = rcpp;
190
fprintf (stderr, "init module \"%s\", pa_type %d, flag %d\n",
191
context->modules[k].name,
192
context->modules[k].pa_type,
193
context->modules[k].flags);
199
krb5int_free_plugin_dir_data(tables);
201
/* return the result */
202
kcontext->preauth_context = context;
205
/* Zero the use counts for the modules herein. Usually used before we
206
* start processing any data from the server, at which point every module
207
* will again be able to take a crack at whatever the server sent. */
209
krb5_clear_preauth_context_use_counts(krb5_context context)
212
if (context->preauth_context != NULL) {
213
for (i = 0; i < context->preauth_context->n_modules; i++) {
214
context->preauth_context->modules[i].use_count = 0;
220
* Give all the preauth plugins a look at the preauth option which
224
krb5_preauth_supply_preauth_data(krb5_context context,
225
krb5_gic_opt_ext *opte,
229
krb5_error_code retval;
232
const char *emsg = NULL;
234
if (context->preauth_context == NULL)
235
krb5_init_preauth_context(context);
236
if (context->preauth_context == NULL) {
238
krb5int_set_error(&context->err, retval,
239
"krb5_preauth_supply_preauth_data: "
240
"Unable to initialize preauth context");
245
* Go down the list of preauth modules, and supply them with the
246
* attribute/value pair.
248
for (i = 0; i < context->preauth_context->n_modules; i++) {
249
if (context->preauth_context->modules[i].client_supply_gic_opts == NULL)
251
pctx = context->preauth_context->modules[i].plugin_context;
252
retval = (*context->preauth_context->modules[i].client_supply_gic_opts)
254
(krb5_get_init_creds_opt *)opte, attr, value);
256
emsg = krb5_get_error_message(context, retval);
257
krb5int_set_error(&context->err, retval, "Preauth plugin %s: %s",
258
context->preauth_context->modules[i].name, emsg);
265
/* Free the per-krb5_context preauth_context. This means clearing any
266
* plugin-specific context which may have been created, and then
267
* freeing the context itself. */
269
krb5_free_preauth_context(krb5_context context)
273
if (context->preauth_context != NULL) {
274
for (i = 0; i < context->preauth_context->n_modules; i++) {
275
pctx = context->preauth_context->modules[i].plugin_context;
276
if (context->preauth_context->modules[i].client_fini != NULL) {
277
(*context->preauth_context->modules[i].client_fini)(context, pctx);
279
memset(&context->preauth_context->modules[i], 0,
280
sizeof(context->preauth_context->modules[i]));
282
if (context->preauth_context->modules != NULL) {
283
free(context->preauth_context->modules);
284
context->preauth_context->modules = NULL;
286
free(context->preauth_context);
287
context->preauth_context = NULL;
291
/* Initialize the per-AS-REQ context. This means calling the client_req_init
292
* function to give the plugin a chance to allocate a per-request context. */
294
krb5_preauth_request_context_init(krb5_context context)
299
/* Limit this to only one attempt per context? */
300
if (context->preauth_context == NULL)
301
krb5_init_preauth_context(context);
302
if (context->preauth_context != NULL) {
303
for (i = 0; i < context->preauth_context->n_modules; i++) {
304
pctx = context->preauth_context->modules[i].plugin_context;
305
if (context->preauth_context->modules[i].client_req_init != NULL) {
306
rctx = context->preauth_context->modules[i].request_context_pp;
307
(*context->preauth_context->modules[i].client_req_init) (context, pctx, rctx);
313
/* Free the per-AS-REQ context. This means clearing any request-specific
314
* context which the plugin may have created. */
316
krb5_preauth_request_context_fini(krb5_context context)
320
if (context->preauth_context != NULL) {
321
for (i = 0; i < context->preauth_context->n_modules; i++) {
322
pctx = context->preauth_context->modules[i].plugin_context;
323
rctx = context->preauth_context->modules[i].request_context;
325
if (context->preauth_context->modules[i].client_req_fini != NULL) {
326
(*context->preauth_context->modules[i].client_req_fini)(context, pctx, rctx);
328
context->preauth_context->modules[i].request_context = NULL;
334
/* Add the named encryption type to the existing list of ktypes. */
336
grow_ktypes(krb5_enctype **out_ktypes, int *out_nktypes, krb5_enctype ktype)
339
krb5_enctype *ktypes;
340
for (i = 0; i < *out_nktypes; i++) {
341
if ((*out_ktypes)[i] == ktype)
344
ktypes = malloc((*out_nktypes + 2) * sizeof(ktype));
346
for (i = 0; i < *out_nktypes; i++)
347
ktypes[i] = (*out_ktypes)[i];
351
*out_ktypes = ktypes;
357
* Add the given list of pa_data items to the existing list of items.
358
* Factored out here to make reading the do_preauth logic easier to read.
361
grow_pa_list(krb5_pa_data ***out_pa_list, int *out_pa_list_size,
362
krb5_pa_data **addition, int num_addition)
364
krb5_pa_data **pa_list;
367
if (out_pa_list == NULL || addition == NULL) {
371
if (*out_pa_list == NULL) {
372
/* Allocate room for the new additions and a NULL terminator. */
373
pa_list = malloc((num_addition + 1) * sizeof(krb5_pa_data *));
376
for (i = 0; i < num_addition; i++)
377
pa_list[i] = addition[i];
379
*out_pa_list = pa_list;
380
*out_pa_list_size = num_addition;
383
* Allocate room for the existing entries plus
384
* the new additions and a NULL terminator.
386
pa_list = malloc((*out_pa_list_size + num_addition + 1)
387
* sizeof(krb5_pa_data *));
390
for (i = 0; i < *out_pa_list_size; i++)
391
pa_list[i] = (*out_pa_list)[i];
392
for (j = 0; j < num_addition;)
393
pa_list[i++] = addition[j++];
396
*out_pa_list = pa_list;
397
*out_pa_list_size = i;
403
* Retrieve a specific piece of information required by the plugin and
404
* return it in a new krb5_data item. There are separate request_types
405
* to obtain the data and free it.
407
* This may require massaging data into a contrived format, but it will
408
* hopefully keep us from having to reveal library-internal functions
409
* or data to the plugin modules.
412
static krb5_error_code
413
client_data_proc(krb5_context kcontext,
414
krb5_preauth_client_rock *rock,
415
krb5_int32 request_type,
421
if (rock->magic != CLIENT_ROCK_MAGIC)
426
switch (request_type) {
427
case krb5plugin_preauth_client_get_etype:
430
if (rock->as_reply == NULL)
432
ret = malloc(sizeof(krb5_data));
435
data = malloc(sizeof(krb5_enctype));
441
ret->length = sizeof(krb5_enctype);
442
eptr = (krb5_enctype *)data;
443
*eptr = rock->as_reply->enc_part.enctype;
448
case krb5plugin_preauth_client_free_etype:
462
/* Tweak the request body, for now adding any enctypes which the module claims
463
* to add support for to the list, but in the future perhaps doing more
464
* involved things. */
466
krb5_preauth_prepare_request(krb5_context kcontext,
467
krb5_gic_opt_ext *opte,
468
krb5_kdc_req *request)
472
if (kcontext->preauth_context == NULL) {
475
/* Add the module-specific enctype list to the request, but only if
476
* it's something we can safely modify. */
477
if (!(opte && (opte->flags & KRB5_GET_INIT_CREDS_OPT_ETYPE_LIST))) {
478
for (i = 0; i < kcontext->preauth_context->n_modules; i++) {
479
if (kcontext->preauth_context->modules[i].enctypes == NULL)
481
for (j = 0; kcontext->preauth_context->modules[i].enctypes[j] != 0; j++) {
482
grow_ktypes(&request->ktype, &request->nktypes,
483
kcontext->preauth_context->modules[i].enctypes[j]);
489
/* Find the first module which provides for the named preauth type which also
490
* hasn't had a chance to run yet (INFO modules don't count, because as a rule
491
* they don't generate preauth data), and run it. */
492
static krb5_error_code
493
krb5_run_preauth_plugins(krb5_context kcontext,
494
int module_required_flags,
495
krb5_kdc_req *request,
496
krb5_data *encoded_request_body,
497
krb5_data *encoded_previous_request,
498
krb5_pa_data *in_padata,
499
krb5_prompter_fct prompter,
501
preauth_get_as_key_proc gak_fct,
503
krb5_data *s2kparams,
505
krb5_preauth_client_rock *get_data_rock,
506
krb5_keyblock *as_key,
507
krb5_pa_data ***out_pa_list,
508
int *out_pa_list_size,
511
krb5_gic_opt_ext *opte)
514
krb5_pa_data **out_pa_data;
516
struct _krb5_preauth_context_module *module;
518
if (kcontext->preauth_context == NULL) {
521
/* iterate over all loaded modules */
522
for (i = 0; i < kcontext->preauth_context->n_modules; i++) {
523
module = &kcontext->preauth_context->modules[i];
524
/* skip over those which don't match the preauth type */
525
if (module->pa_type != in_padata->pa_type)
527
/* skip over those which don't match the flags (INFO vs REAL, mainly) */
528
if ((module->flags & module_required_flags) == 0)
530
/* if it's a REAL module, try to call it only once per library call */
531
if (module_required_flags & PA_REAL) {
532
if (module->use_count > 0) {
534
fprintf(stderr, "skipping already-used module \"%s\"(%d)\n",
535
module->name, module->pa_type);
541
/* run the module's callback function */
544
fprintf(stderr, "using module \"%s\" (%d), flags = %d\n",
545
module->name, module->pa_type, module->flags);
547
ret = module->client_process(kcontext,
548
module->plugin_context,
549
*module->request_context_pp,
550
(krb5_get_init_creds_opt *)opte,
554
encoded_request_body,
555
encoded_previous_request,
557
prompter, prompter_data,
558
gak_fct, gak_data, salt, s2kparams,
561
/* Make note of the module's flags and status. */
562
*module_flags = module->flags;
564
/* Save the new preauth data item. */
565
if (out_pa_data != NULL) {
567
for (j = 0; out_pa_data[j] != NULL; j++);
568
ret = grow_pa_list(out_pa_list, out_pa_list_size, out_pa_data, j);
575
if (i >= kcontext->preauth_context->n_modules) {
56
582
krb5_error_code pa_salt(krb5_context context,
1349
* If one of the modules can adjust its AS_REQ data using the contents of the
1350
* err_reply, return 0. If it's the sort of correction which requires that we
1351
* ask the user another question, we let the calling application deal with it.
1353
krb5_error_code KRB5_CALLCONV
1354
krb5_do_preauth_tryagain(krb5_context kcontext,
1355
krb5_kdc_req *request,
1356
krb5_data *encoded_request_body,
1357
krb5_data *encoded_previous_request,
1358
krb5_pa_data **padata,
1359
krb5_pa_data ***return_padata,
1360
krb5_error *err_reply,
1361
krb5_data *salt, krb5_data *s2kparams,
1362
krb5_enctype *etype,
1363
krb5_keyblock *as_key,
1364
krb5_prompter_fct prompter, void *prompter_data,
1365
krb5_gic_get_as_key_fct gak_fct, void *gak_data,
1366
krb5_preauth_client_rock *get_data_rock,
1367
krb5_gic_opt_ext *opte)
1369
krb5_error_code ret;
1370
krb5_pa_data **out_padata;
1371
krb5_preauth_context *context;
1372
struct _krb5_preauth_context_module *module;
1374
int out_pa_list_size = 0;
1376
ret = KRB5KRB_ERR_GENERIC;
1377
if (kcontext->preauth_context == NULL) {
1378
return KRB5KRB_ERR_GENERIC;
1380
context = kcontext->preauth_context;
1381
if (context == NULL) {
1382
return KRB5KRB_ERR_GENERIC;
1385
for (i = 0; padata[i] != NULL && padata[i]->pa_type != 0; i++) {
1387
for (j = 0; j < context->n_modules; j++) {
1388
module = &context->modules[j];
1389
if (module->pa_type != padata[i]->pa_type) {
1392
if (module->client_tryagain == NULL) {
1395
if ((*module->client_tryagain)(kcontext,
1396
module->plugin_context,
1397
*module->request_context_pp,
1398
(krb5_get_init_creds_opt *)opte,
1402
encoded_request_body,
1403
encoded_previous_request,
1406
prompter, prompter_data,
1407
gak_fct, gak_data, salt, s2kparams,
1409
&out_padata) == 0) {
1410
if (out_padata != NULL) {
1412
for (k = 0; out_padata[k] != NULL; k++);
1413
grow_pa_list(return_padata, &out_pa_list_size,
1424
krb5_error_code KRB5_CALLCONV
823
1425
krb5_do_preauth(krb5_context context,
824
1426
krb5_kdc_req *request,
1427
krb5_data *encoded_request_body,
1428
krb5_data *encoded_previous_request,
825
1429
krb5_pa_data **in_padata, krb5_pa_data ***out_padata,
826
1430
krb5_data *salt, krb5_data *s2kparams,
827
1431
krb5_enctype *etype,
828
1432
krb5_keyblock *as_key,
829
1433
krb5_prompter_fct prompter, void *prompter_data,
830
krb5_gic_get_as_key_fct gak_fct, void *gak_data)
1434
krb5_gic_get_as_key_fct gak_fct, void *gak_data,
1435
krb5_preauth_client_rock *get_data_rock,
1436
krb5_gic_opt_ext *opte)
832
1438
int h, i, j, out_pa_list_size;
833
1439
int seen_etype_info2 = 0;