diff options
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | src/include/k5-int.h | 8 | ||||
-rw-r--r-- | src/include/krb5/krb5.hin | 71 | ||||
-rw-r--r-- | src/include/krb5/preauth_plugin.h | 37 | ||||
-rw-r--r-- | src/lib/krb5/krb/Makefile.in | 12 | ||||
-rw-r--r-- | src/lib/krb5/krb/get_in_tkt.c | 6 | ||||
-rw-r--r-- | src/lib/krb5/krb/gic_opt.c | 17 | ||||
-rw-r--r-- | src/lib/krb5/krb/init_creds_ctx.h | 1 | ||||
-rw-r--r-- | src/lib/krb5/krb/int-proto.h | 31 | ||||
-rw-r--r-- | src/lib/krb5/krb/preauth2.c | 103 | ||||
-rw-r--r-- | src/lib/krb5/krb/response_items.c | 212 | ||||
-rw-r--r-- | src/lib/krb5/krb/t_response_items.c | 94 | ||||
-rw-r--r-- | src/lib/krb5/libkrb5.exports | 4 | ||||
-rw-r--r-- | src/lib/krb5_32.def | 4 |
14 files changed, 595 insertions, 6 deletions
@@ -194,6 +194,7 @@ testlog /src/lib/krb5/krb/t_ser /src/lib/krb5/krb/t_vfy_increds /src/lib/krb5/krb/t_walk_rtree +/src/lib/krb5/krb/t_response_items /src/lib/krb5/os/t_an_to_ln /src/lib/krb5/os/t_kuserok diff --git a/src/include/k5-int.h b/src/include/k5-int.h index 670915d..bf36a17 100644 --- a/src/include/k5-int.h +++ b/src/include/k5-int.h @@ -792,6 +792,11 @@ error(MIT_DES_KEYSIZE does not equal KRB5_MIT_DES_KEYSIZE) #include <krb5/preauth_plugin.h> +typedef struct k5_response_items_st k5_response_items; +struct krb5_responder_context_st { + k5_response_items *items; +}; + typedef krb5_error_code (*krb5_gic_get_as_key_fct)(krb5_context, krb5_principal, krb5_enctype, krb5_prompter_fct, void *prompter_data, @@ -831,6 +836,7 @@ struct krb5_clpreauth_rock_st { krb5_timestamp pa_offset; krb5_int32 pa_offset_usec; enum { NO_OFFSET = 0, UNAUTH_OFFSET, AUTH_OFFSET } pa_offset_state; + struct krb5_responder_context_st rctx; }; typedef struct _krb5_pa_enc_ts { @@ -1025,6 +1031,8 @@ typedef struct _krb5_gic_opt_private { krb5_flags fast_flags; krb5_expire_callback_func expire_cb; void *expire_data; + krb5_responder_fn responder; + void *responder_data; } krb5_gic_opt_private; /* diff --git a/src/include/krb5/krb5.hin b/src/include/krb5/krb5.hin index 2f3974a..7c519f0 100644 --- a/src/include/krb5/krb5.hin +++ b/src/include/krb5/krb5.hin @@ -6353,6 +6353,64 @@ krb5_prompter_posix(krb5_context context, void *data, const char *name, const char *banner, int num_prompts, krb5_prompt prompts[]); +typedef struct krb5_responder_context_st *krb5_responder_context; + +/** + * List the question names contained in the responder context. + * + * @param [in] ctx Library context + * @param [in] rctx Responder context + */ +const char * const * KRB5_CALLCONV +krb5_responder_list_questions(krb5_context ctx, krb5_responder_context rctx); + +/** + * Retrieve the challenge data for a given question in the responder context. + * + * @param [in] ctx Library context + * @param [in] rctx Responder context + * @param [in] question Question name + */ +const char * KRB5_CALLCONV +krb5_responder_get_challenge(krb5_context ctx, krb5_responder_context rctx, + const char *question); + +/** + * Answer a named question in the responder context. + * + * @param [in] ctx Library context + * @param [in] rctx Responder context + * @param [in] question Question name + * @param [in] answer The string to set (MUST be printable UTF-8) + */ +krb5_error_code KRB5_CALLCONV +krb5_responder_set_answer(krb5_context ctx, krb5_responder_context rctx, + const char *question, const char *answer); + +/** + * Responder function for an initial credential exchange. + * + * @param [in] ctx Library context + * @param [in] rctx Responder context + * @param [in] data Callback data + * + * A responder function is like a prompter function, but is used for handling + * questions and answers as potentially complex data types. Client + * preauthentication modules will insert a set of named "questions" into + * the responder context. Each question may optionally contain a challenge. + * This challenge is printable UTF-8, but may be an encoded value. The + * precise encoding and contents of the challenge are specific to the question + * asked. When the responder is called, it should answer all the questions it + * understands. Like the challenge, the answer MUST be printable UTF-8, but + * may contain structured/encoded data formatted to the expected answer format + * of the question. + * + * If a required question is unanswered, the prompter may be called. + */ +typedef krb5_error_code +(*krb5_responder_fn)(krb5_context ctx, krb5_responder_context rctx, + void *data); + /** Store options for @c _krb5_get_init_creds */ typedef struct _krb5_get_init_creds_opt { krb5_flags flags; @@ -6712,6 +6770,19 @@ krb5_get_init_creds_opt_set_expire_callback(krb5_context context, void *data); /** + * Set the responder function in initial credential options. + * + * @param [in] context Library context + * @param [in] opt Options structure + * @param [in] responder Responder function + * @param [in] data Responder data argument + */ +krb5_error_code KRB5_CALLCONV +krb5_get_init_creds_opt_set_responder(krb5_context context, + krb5_get_init_creds_opt *opt, + krb5_responder_fn responder, void *data); + +/** * Get initial credentials using a password. * * @param [in] context Library context diff --git a/src/include/krb5/preauth_plugin.h b/src/include/krb5/preauth_plugin.h index 72fd92d..a9a2ab9 100644 --- a/src/include/krb5/preauth_plugin.h +++ b/src/include/krb5/preauth_plugin.h @@ -38,7 +38,7 @@ * * * The clpreauth interface has a single supported major version, which is - * 1. Major version 1 has a current minor version of 1. clpreauth modules + * 1. Major version 1 has a current minor version of 2. clpreauth modules * should define a function named clpreauth_<modulename>_initvt, matching * the signature: * @@ -193,6 +193,19 @@ typedef struct krb5_clpreauth_callbacks_st { krb5_timestamp *time_out, krb5_int32 *usec_out); + /* Set a question to be answered by the responder and optionally provide + * a challenge. */ + krb5_error_code (*ask_responder_question)(krb5_context context, + krb5_clpreauth_rock rock, + const char *question, + const char *challenge); + + /* Get an answer from the responder, or NULL if the question was + * unanswered. */ + const char *(*get_responder_answer)(krb5_context context, + krb5_clpreauth_rock rock, + const char *question); + /* End of version 2 clpreauth callbacks (added in 1.11). */ } *krb5_clpreauth_callbacks; @@ -235,6 +248,25 @@ typedef void krb5_clpreauth_modreq modreq); /* + * Optional: process server-supplied data in pa_data and set responder + * questions. + * + * encoded_previous_request may be NULL if there has been no previous request + * in the AS exchange. + */ +typedef krb5_error_code +(*krb5_clpreauth_prep_questions_fn)(krb5_context context, + krb5_clpreauth_moddata moddata, + krb5_clpreauth_modreq modreq, + krb5_get_init_creds_opt *opt, + krb5_clpreauth_callbacks cb, + krb5_clpreauth_rock rock, + krb5_kdc_req *request, + krb5_data *encoded_request_body, + krb5_data *encoded_previous_request, + krb5_pa_data *pa_data); + +/* * Mandatory: process server-supplied data in pa_data and return created data * in pa_data_out. Also called after the AS-REP is received if the AS-REP * includes preauthentication data of the associated type. @@ -317,6 +349,9 @@ typedef struct krb5_clpreauth_vtable_st { krb5_clpreauth_tryagain_fn tryagain; krb5_clpreauth_supply_gic_opts_fn gic_opts; /* Minor version 1 ends here. */ + + krb5_clpreauth_prep_questions_fn prep_questions; + /* Minor version 2 ends here. */ } *krb5_clpreauth_vtable; /* diff --git a/src/lib/krb5/krb/Makefile.in b/src/lib/krb5/krb/Makefile.in index 4d08478..31160a7 100644 --- a/src/lib/krb5/krb/Makefile.in +++ b/src/lib/krb5/krb/Makefile.in @@ -92,6 +92,7 @@ STLIBOBJS= \ rd_req_dec.o \ rd_safe.o \ recvauth.o \ + response_items.o \ s4u_authdata.o \ s4u_creds.o \ sendauth.o \ @@ -199,6 +200,7 @@ OBJS= $(OUTPRE)addr_comp.$(OBJEXT) \ $(OUTPRE)rd_req_dec.$(OBJEXT) \ $(OUTPRE)rd_safe.$(OBJEXT) \ $(OUTPRE)recvauth.$(OBJEXT) \ + $(OUTPRE)response_items.$(OBJEXT) \ $(OUTPRE)s4u_authdata.$(OBJEXT) \ $(OUTPRE)s4u_creds.$(OBJEXT) \ $(OUTPRE)sendauth.$(OBJEXT) \ @@ -306,6 +308,7 @@ SRCS= $(srcdir)/addr_comp.c \ $(srcdir)/rd_req_dec.c \ $(srcdir)/rd_safe.c \ $(srcdir)/recvauth.c \ + $(srcdir)/response_items.c \ $(srcdir)/s4u_authdata.c\ $(srcdir)/s4u_creds.c \ $(srcdir)/sendauth.c \ @@ -412,8 +415,11 @@ t_expire_warn: t_expire_warn.o $(KRB5_BASE_DEPLIBS) t_vfy_increds: t_vfy_increds.o $(KRB5_BASE_DEPLIBS) $(CC_LINK) -o $@ t_vfy_increds.o $(KRB5_BASE_LIBS) +t_response_items: t_response_items.o response_items.o $(KRB5_BASE_DEPLIBS) + $(CC_LINK) -o $@ t_response_items.o response_items.o $(KRB5_BASE_LIBS) + TEST_PROGS= t_walk_rtree t_kerb t_ser t_deltat t_expand t_authdata t_pac \ - t_princ t_etypes t_vfy_increds + t_princ t_etypes t_vfy_increds t_response_items check-unix:: $(TEST_PROGS) KRB5_CONFIG=$(srcdir)/t_krb5.conf ; export KRB5_CONFIG ;\ @@ -451,6 +457,7 @@ check-unix:: $(TEST_PROGS) $(RUN_SETUP) $(VALGRIND) ./t_pac $(RUN_SETUP) $(VALGRIND) ./t_princ $(RUN_SETUP) $(VALGRIND) ./t_etypes + $(RUN_SETUP) $(VALGRIND) ./t_response_items check-pytests:: t_expire_warn t_vfy_increds $(RUNPYTEST) $(srcdir)/t_expire_warn.py $(PYTESTFLAGS) @@ -467,7 +474,8 @@ clean:: $(OUTPRE)t_pac$(EXEEXT) $(OUTPRE)t_pac.$(OBJEXT) \ $(OUTPRE)t_princ$(EXEEXT) $(OUTPRE)t_princ.$(OBJEXT) \ $(OUTPRE)t_authdata$(EXEEXT) $(OUTPRE)t_authdata.$(OBJEXT) \ - $(OUTPRE)t_vfy_increds$(EXEEXT) $(OUTPRE)t_vfy_increds.$(OBJEXT) + $(OUTPRE)t_vfy_increds$(EXEEXT) $(OUTPRE)t_vfy_increds.$(OBJEXT) \ + $(OUTPRE)t_response_items$(EXEEXT) $(OUTPRE)t_response_items.$(OBJEXT) @libobj_frag@ diff --git a/src/lib/krb5/krb/get_in_tkt.c b/src/lib/krb5/krb/get_in_tkt.c index 3f67df0..d52147a 100644 --- a/src/lib/krb5/krb/get_in_tkt.c +++ b/src/lib/krb5/krb/get_in_tkt.c @@ -495,6 +495,7 @@ krb5_init_creds_free(krb5_context context, krb5_get_init_creds_opt_free(context, (krb5_get_init_creds_opt *)ctx->opte); } + k5_response_items_free(ctx->rctx.items); free(ctx->in_tkt_service); zap(ctx->password.data, ctx->password.length); krb5_free_data_contents(context, &ctx->password); @@ -811,6 +812,10 @@ krb5_init_creds_init(krb5_context context, if (code != 0) goto cleanup; + code = k5_response_items_new(&ctx->rctx.items); + if (code != 0) + goto cleanup; + opte = ctx->opte; ctx->preauth_rock.magic = CLIENT_ROCK_MAGIC; @@ -821,6 +826,7 @@ krb5_init_creds_init(krb5_context context, ctx->preauth_rock.default_salt = &ctx->default_salt; ctx->preauth_rock.salt = &ctx->salt; ctx->preauth_rock.s2kparams = &ctx->s2kparams; + ctx->preauth_rock.rctx = ctx->rctx; ctx->preauth_rock.client = client; ctx->preauth_rock.prompter = prompter; ctx->preauth_rock.prompter_data = data; diff --git a/src/lib/krb5/krb/gic_opt.c b/src/lib/krb5/krb/gic_opt.c index a98a47e..2580abd 100644 --- a/src/lib/krb5/krb/gic_opt.c +++ b/src/lib/krb5/krb/gic_opt.c @@ -523,3 +523,20 @@ krb5_get_init_creds_opt_set_expire_callback(krb5_context context, opte->opt_private->expire_data = data; return retval; } + +krb5_error_code KRB5_CALLCONV +krb5_get_init_creds_opt_set_responder(krb5_context context, + krb5_get_init_creds_opt *opt, + krb5_responder_fn responder, void *data) +{ + krb5_error_code ret; + krb5_gic_opt_ext *opte; + + ret = krb5int_gic_opt_to_opte(context, opt, &opte, 0, + "krb5_get_init_creds_opt_set_responder"); + if (ret) + return ret; + opte->opt_private->responder = responder; + opte->opt_private->responder_data = data; + return 0; +} diff --git a/src/lib/krb5/krb/init_creds_ctx.h b/src/lib/krb5/krb/init_creds_ctx.h index 2653ee1..ae69ed0 100644 --- a/src/lib/krb5/krb/init_creds_ctx.h +++ b/src/lib/krb5/krb/init_creds_ctx.h @@ -46,6 +46,7 @@ struct _krb5_init_creds_context { krb5_boolean have_restarted; krb5_boolean sent_nontrivial_preauth; krb5_boolean preauth_required; + struct krb5_responder_context_st rctx; }; krb5_error_code diff --git a/src/lib/krb5/krb/int-proto.h b/src/lib/krb5/krb/int-proto.h index f794f14..6f3de8f 100644 --- a/src/lib/krb5/krb/int-proto.h +++ b/src/lib/krb5/krb/int-proto.h @@ -203,4 +203,35 @@ krb5_error_code k5_init_creds_get(krb5_context context, krb5_init_creds_context ctx, int *use_master); +krb5_error_code +k5_response_items_new(k5_response_items **ri_out); + +void +k5_response_items_free(k5_response_items *ri); + +void +k5_response_items_reset(k5_response_items *ri); + +krb5_boolean +k5_response_items_empty(const k5_response_items *ri); + +const char * const * +k5_response_items_list_questions(const k5_response_items *ri); + +krb5_error_code +k5_response_items_ask_question(k5_response_items *ri, const char *question, + const char *challenge); + +const char * +k5_response_items_get_challenge(const k5_response_items *ri, + const char *question); + +krb5_error_code +k5_response_items_set_answer(k5_response_items *ri, const char *question, + const char *answer); + +const char * +k5_response_items_get_answer(const k5_response_items *ri, + const char *question); + #endif /* KRB5_INT_FUNC_PROTO__ */ diff --git a/src/lib/krb5/krb/preauth2.c b/src/lib/krb5/krb/preauth2.c index 2ed53ab..1153175 100644 --- a/src/lib/krb5/krb/preauth2.c +++ b/src/lib/krb5/krb/preauth2.c @@ -60,6 +60,7 @@ struct krb5_preauth_context_st { * convenience when we populated the list. */ const char *name; int flags, use_count; + krb5_clpreauth_prep_questions_fn client_prep_questions; krb5_clpreauth_process_fn client_process; krb5_clpreauth_tryagain_fn client_tryagain; krb5_clpreauth_supply_gic_opts_fn client_supply_gic_opts; @@ -138,7 +139,7 @@ krb5_init_preauth_context(krb5_context kcontext) if (vtables == NULL) goto cleanup; for (pl = plugins, n_tables = 0; *pl != NULL; pl++) { - if ((*pl)(kcontext, 1, 1, (krb5_plugin_vtable)&vtables[n_tables]) == 0) + if ((*pl)(kcontext, 1, 2, (krb5_plugin_vtable)&vtables[n_tables]) == 0) n_tables++; } @@ -188,6 +189,7 @@ krb5_init_preauth_context(krb5_context kcontext) mod->name = vt->name; mod->flags = (*vt->flags)(kcontext, pa_type); mod->use_count = 0; + mod->client_prep_questions = vt->prep_questions; mod->client_process = vt->process; mod->client_tryagain = vt->tryagain; mod->client_supply_gic_opts = first ? vt->gic_opts : NULL; @@ -404,13 +406,30 @@ get_preauth_time(krb5_context context, krb5_clpreauth_rock rock, } } +static krb5_error_code +responder_ask_question(krb5_context context, krb5_clpreauth_rock rock, + const char *question, const char *challenge) +{ + return k5_response_items_ask_question(rock->rctx.items, question, + challenge); +} + +static const char * +responder_get_answer(krb5_context context, krb5_clpreauth_rock rock, + const char *question) +{ + return k5_response_items_get_answer(rock->rctx.items, question); +} + static struct krb5_clpreauth_callbacks_st callbacks = { 2, get_etype, fast_armor, get_as_key, set_as_key, - get_preauth_time + get_preauth_time, + responder_ask_question, + responder_get_answer }; /* Tweak the request body, for now adding any enctypes which the module claims @@ -440,6 +459,32 @@ krb5_preauth_prepare_request(krb5_context kcontext, } } +const char * const * KRB5_CALLCONV +krb5_responder_list_questions(krb5_context ctx, krb5_responder_context rctx) +{ + return k5_response_items_list_questions(rctx->items); +} + +const char * KRB5_CALLCONV +krb5_responder_get_challenge(krb5_context ctx, krb5_responder_context rctx, + const char *question) +{ + if (rctx == NULL) + return NULL; + + return k5_response_items_get_challenge(rctx->items, question); +} + +krb5_error_code KRB5_CALLCONV +krb5_responder_set_answer(krb5_context ctx, krb5_responder_context rctx, + const char *question, const char *answer) +{ + if (rctx == NULL) + return EINVAL; + + return k5_response_items_set_answer(rctx->items, question, answer); +} + /* Find the first module which provides for the named preauth type which also * hasn't had a chance to run yet (INFO modules don't count, because as a rule * they don't generate preauth data), and run it. */ @@ -789,6 +834,42 @@ krb5_do_preauth_tryagain(krb5_context kcontext, return ret; } +/* Compile the set of response items for in_padata by invoke each module's + * prep_questions method. */ +static krb5_error_code +fill_response_items(krb5_context context, krb5_kdc_req *request, + krb5_data *encoded_request_body, + krb5_data *encoded_previous_request, + krb5_pa_data **in_padata, krb5_clpreauth_rock rock, + krb5_gic_opt_ext *opte) +{ + krb5_error_code ret; + krb5_pa_data *pa; + struct krb5_preauth_context_module_st *module; + krb5_clpreauth_prep_questions_fn prep_questions; + int i, j; + + k5_response_items_reset(rock->rctx.items); + for (i = 0; in_padata[i] != NULL; i++) { + pa = in_padata[i]; + for (j = 0; j < context->preauth_context->n_modules; j++) { + module = &context->preauth_context->modules[j]; + prep_questions = module->client_prep_questions; + if (module->pa_type != pa->pa_type || prep_questions == NULL) + continue; + ret = (*prep_questions)(context, module->moddata, + *module->modreq_p, + (krb5_get_init_creds_opt *)opte, + &callbacks, rock, request, + encoded_request_body, + encoded_previous_request, pa); + if (ret) + return ret; + } + } + return 0; +} + krb5_error_code KRB5_CALLCONV krb5_do_preauth(krb5_context context, krb5_kdc_req *request, krb5_data *encoded_request_body, @@ -802,6 +883,7 @@ krb5_do_preauth(krb5_context context, krb5_kdc_req *request, int out_pa_list_size = 0; krb5_pa_data **out_pa_list = NULL; krb5_error_code ret, module_ret; + krb5_responder_fn responder = opte->opt_private->responder; static const int paorder[] = { PA_INFO, PA_REAL }; *out_padata = NULL; @@ -839,7 +921,22 @@ krb5_do_preauth(krb5_context context, krb5_kdc_req *request, goto error; } - /* First do all the informational preauths, then the first real one. */ + /* Get a list of response items for in_padata from the preauth modules. */ + ret = fill_response_items(context, request, encoded_request_body, + encoded_previous_request, in_padata, rock, opte); + if (ret) + goto error; + + /* Call the responder to answer response items. */ + if (responder != NULL && !k5_response_items_empty(rock->rctx.items)) { + ret = (*responder)(context, opte->opt_private->responder_data, + &rock->rctx); + if (ret) + goto error; + } + + /* Produce output padata, first from all the informational preauths, then + * the from first real one. */ for (h = 0; h < sizeof(paorder) / sizeof(paorder[0]); h++) { for (i = 0; in_padata[i] != NULL; i++) { #ifdef DEBUG diff --git a/src/lib/krb5/krb/response_items.c b/src/lib/krb5/krb/response_items.c new file mode 100644 index 0000000..243df48 --- /dev/null +++ b/src/lib/krb5/krb/response_items.c @@ -0,0 +1,212 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* lib/krb5/krb/response_items.c - Response items */ +/* + * Copyright 2012 Red Hat, Inc. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of Red Hat not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original Red Hat software. + * Red Hat makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + */ + +#include "k5-int.h" +#include "int-proto.h" + +struct k5_response_items_st { + size_t count; + char **questions; + char **challenges; + char **answers; +}; + +krb5_error_code +k5_response_items_new(k5_response_items **ri_out) +{ + *ri_out = calloc(1, sizeof(**ri_out)); + return (*ri_out == NULL) ? ENOMEM : 0; +} + +void +k5_response_items_free(k5_response_items *ri) +{ + k5_response_items_reset(ri); + free(ri); +} + +void +k5_response_items_reset(k5_response_items *ri) +{ + size_t i; + + for (i = 0; i < ri->count; i++) + free(ri->questions[i]); + free(ri->questions); + ri->questions = NULL; + + for (i = 0; i < ri->count; i++) + zapfreestr(ri->challenges[i]); + free(ri->challenges); + ri->challenges = NULL; + + for (i = 0; i < ri->count; i++) + zapfreestr(ri->answers[i]); + free(ri->answers); + ri->answers = NULL; + + ri->count = 0; +} + +krb5_boolean +k5_response_items_empty(const k5_response_items *ri) +{ + return ri->count == 0; +} + +const char * const * +k5_response_items_list_questions(const k5_response_items *ri) +{ + return (const char * const *)ri->questions; +} + +static ssize_t +find_question(const k5_response_items *ri, const char *question) +{ + size_t i; + + for (i = 0; i < ri->count; i++) { + if (strcmp(ri->questions[i], question) == 0) + return i; + } + + return -1; +} + +static krb5_error_code +push_question(k5_response_items *ri, const char *question, + const char *challenge) +{ + char **tmp; + const size_t size = sizeof(char*) * (ri->count + 2); + + tmp = realloc(ri->questions, size); + if (tmp == NULL) + return ENOMEM; + ri->questions = tmp; + ri->questions[ri->count] = NULL; + ri->questions[ri->count + 1] = NULL; + + tmp = realloc(ri->challenges, size); + if (tmp == NULL) + return ENOMEM; + ri->challenges = tmp; + ri->challenges[ri->count] = NULL; + ri->challenges[ri->count + 1] = NULL; + + tmp = realloc(ri->answers, size); + if (tmp == NULL) + return ENOMEM; + ri->answers = tmp; + ri->answers[ri->count] = NULL; + ri->answers[ri->count + 1] = NULL; + + ri->questions[ri->count] = strdup(question); + if (ri->questions[ri->count] == NULL) + return ENOMEM; + + if (challenge != NULL) { + ri->challenges[ri->count] = strdup(challenge); + if (ri->challenges[ri->count] == NULL) { + free(ri->questions[ri->count]); + ri->questions[ri->count] = NULL; + return ENOMEM; + } + } + + ri->count++; + return 0; +} + +krb5_error_code +k5_response_items_ask_question(k5_response_items *ri, const char *question, + const char *challenge) +{ + ssize_t i; + char *tmp = NULL; + + i = find_question(ri, question); + if (i < 0) + return push_question(ri, question, challenge); + + if (challenge != NULL) { + tmp = strdup(challenge); + if (tmp == NULL) + return ENOMEM; + } + + zapfreestr(ri->challenges[i]); + ri->challenges[i] = tmp; + return 0; +} + +const char * +k5_response_items_get_challenge(const k5_response_items *ri, + const char *question) +{ + ssize_t i; + + i = find_question(ri, question); + if (i < 0) + return NULL; + + return ri->challenges[i]; +} + +krb5_error_code +k5_response_items_set_answer(k5_response_items *ri, const char *question, + const char *answer) +{ + char *tmp = NULL; + ssize_t i; + + i = find_question(ri, question); + if (i < 0) + return EINVAL; + + if (answer != NULL) { + tmp = strdup(answer); + if (tmp == NULL) + return ENOMEM; + } + + zapfreestr(ri->answers[i]); + ri->answers[i] = tmp; + return 0; +} + +const char * +k5_response_items_get_answer(const k5_response_items *ri, + const char *question) +{ + ssize_t i; + + i = find_question(ri, question); + if (i < 0) + return NULL; + + return ri->answers[i]; +} diff --git a/src/lib/krb5/krb/t_response_items.c b/src/lib/krb5/krb/t_response_items.c new file mode 100644 index 0000000..0deb929 --- /dev/null +++ b/src/lib/krb5/krb/t_response_items.c @@ -0,0 +1,94 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* lib/krb5/krb/t_response_set.c - Test krb5_response_set */ +/* + * Copyright 2012 Red Hat, Inc. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of Red Hat not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original Red Hat software. + * Red Hat makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + */ + +#include <k5-int.h> + +#include "int-proto.h" + +#define TEST_STR1 "test1" +#define TEST_STR2 "test2" + +static void +check_pred(int predicate) +{ + if (!predicate) + abort(); +} + +static void +check(krb5_error_code code) +{ + if (code != 0) { + com_err("t_response_items", code, NULL); + abort(); + } +} + +static int +nstrcmp(const char *a, const char *b) +{ + if (a == NULL && b == NULL) + return 0; + else if (a == NULL) + return -1; + else if (b == NULL) + return 1; + + return strcmp(a, b); +} + +int +main() +{ + k5_response_items *ri; + + check(k5_response_items_new(&ri)); + check_pred(k5_response_items_empty(ri)); + + check(k5_response_items_ask_question(ri, TEST_STR1, TEST_STR1)); + check(k5_response_items_ask_question(ri, TEST_STR2, NULL)); + check_pred(nstrcmp(k5_response_items_get_challenge(ri, TEST_STR1), + TEST_STR1) == 0); + check_pred(nstrcmp(k5_response_items_get_challenge(ri, TEST_STR2), + NULL) == 0); + check_pred(!k5_response_items_empty(ri)); + + k5_response_items_reset(ri); + check_pred(k5_response_items_empty(ri)); + check_pred(k5_response_items_get_challenge(ri, TEST_STR1) == NULL); + check_pred(k5_response_items_get_challenge(ri, TEST_STR2) == NULL); + + check(k5_response_items_ask_question(ri, TEST_STR1, TEST_STR1)); + check_pred(nstrcmp(k5_response_items_get_challenge(ri, TEST_STR1), + TEST_STR1) == 0); + check(k5_response_items_set_answer(ri, TEST_STR1, TEST_STR1)); + check_pred(nstrcmp(k5_response_items_get_answer(ri, TEST_STR1), + TEST_STR1) == 0); + + k5_response_items_free(ri); + + return 0; +} diff --git a/src/lib/krb5/libkrb5.exports b/src/lib/krb5/libkrb5.exports index 2709598..701aa39 100644 --- a/src/lib/krb5/libkrb5.exports +++ b/src/lib/krb5/libkrb5.exports @@ -373,6 +373,7 @@ krb5_get_init_creds_opt_set_pa krb5_get_init_creds_opt_set_preauth_list krb5_get_init_creds_opt_set_proxiable krb5_get_init_creds_opt_set_renew_life +krb5_get_init_creds_opt_set_responder krb5_get_init_creds_opt_set_salt krb5_get_init_creds_opt_set_tkt_life krb5_get_init_creds_password @@ -524,6 +525,9 @@ krb5_realm_compare krb5_recvauth krb5_recvauth_version krb5_register_serializer +krb5_responder_get_challenge +krb5_responder_list_questions +krb5_responder_set_answer krb5_salttype_to_string krb5_sendauth krb5_sendto_kdc diff --git a/src/lib/krb5_32.def b/src/lib/krb5_32.def index 09adc92..a363801 100644 --- a/src/lib/krb5_32.def +++ b/src/lib/krb5_32.def @@ -431,3 +431,7 @@ EXPORTS krb5_cccol_have_content @402 krb5_kt_client_default @403 krb5int_cc_user_set_default_name @404 ; PRIVATE LEASH + krb5_get_init_creds_opt_set_responder @405 + krb5_responder_get_challenge @406 + krb5_responder_list_questions @407 + krb5_responder_set_answer @408 |