#include <krb5/preauth_plugin.h>
+typedef krb5_error_code
+(*krb5_gic_get_as_key_fct)(krb5_context, krb5_principal, krb5_enctype,
+ krb5_prompter_fct, void *prompter_data,
+ krb5_data *salt, krb5_data *s2kparams,
+ krb5_keyblock *as_key, void *gak_data);
+
#define CLIENT_ROCK_MAGIC 0x4352434b
-/* This structure is passed into the client preauth functions and passed
- * back to the "get_data_proc" function so that it can locate the
- * requested information. It is opaque to the plugin code and can be
- * expanded in the future as new types of requests are defined which
- * may require other things to be passed through. */
+/*
+ * This structure is passed into the clpreauth methods and passed back to
+ * clpreauth callbacks so that they can locate the requested information. It
+ * is opaque to the plugin code and can be expanded in the future as new types
+ * of requests are defined which may require other things to be passed through.
+ * All pointer fields are aliases and should not be freed.
+ */
struct krb5int_fast_request_state;
struct krb5_clpreauth_rock_st {
krb5_magic magic;
krb5_enctype *etype;
struct krb5int_fast_request_state *fast_state;
+
+ /*
+ * These fields allow gak_fct to be called via the rock. The
+ * gak_fct and gak_data fields have an extra level of indirection
+ * since they can change in the init_creds context.
+ */
+ krb5_keyblock *as_key;
+ krb5_gic_get_as_key_fct *gak_fct;
+ void **gak_data;
+ krb5_data *salt;
+ krb5_data *s2kparams;
+ krb5_principal client;
+ krb5_prompter_fct prompter;
+ void *prompter_data;
};
typedef struct _krb5_pa_enc_ts {
krb5_error_code
krb5int_copy_creds_contents(krb5_context, const krb5_creds *, krb5_creds *);
-typedef krb5_error_code
-(*krb5_gic_get_as_key_fct)(krb5_context, krb5_principal, krb5_enctype,
- krb5_prompter_fct, void *prompter_data,
- krb5_data *salt, krb5_data *s2kparams,
- krb5_keyblock *as_key, void *gak_data);
-
krb5_error_code KRB5_CALLCONV
krb5int_get_init_creds(krb5_context context, krb5_creds *creds,
krb5_principal client, krb5_prompter_fct prompter,
krb5_do_preauth(krb5_context context, krb5_kdc_req *request,
krb5_data *encoded_request_body,
krb5_data *encoded_previous_request, krb5_pa_data **in_padata,
- krb5_pa_data ***out_padata, krb5_data *salt,
- krb5_data *s2kparams, krb5_enctype *etype,
- krb5_keyblock *as_key, krb5_prompter_fct prompter,
- void *prompter_data, krb5_gic_get_as_key_fct gak_fct,
- void *gak_data, krb5_clpreauth_rock preauth_rock,
+ krb5_pa_data ***out_padata, krb5_prompter_fct prompter,
+ void *prompter_data, krb5_clpreauth_rock preauth_rock,
krb5_gic_opt_ext *opte);
krb5_error_code KRB5_CALLCONV
krb5_data *encoded_request_body,
krb5_data *encoded_previous_request,
krb5_pa_data **in_padata, krb5_pa_data ***out_padata,
- krb5_error *err_reply,
- krb5_data *salt, krb5_data *s2kparams,
- krb5_enctype *etype, krb5_keyblock *as_key,
- krb5_prompter_fct prompter, void *prompter_data,
- krb5_gic_get_as_key_fct gak_fct, void *gak_data,
- krb5_clpreauth_rock preauth_rock,
+ krb5_error *err_reply, krb5_prompter_fct prompter,
+ void *prompter_data, krb5_clpreauth_rock preauth_rock,
krb5_gic_opt_ext *opte);
void KRB5_CALLCONV krb5_init_preauth_context(krb5_context);
typedef struct krb5_clpreauth_moddata_st *krb5_clpreauth_moddata;
typedef struct krb5_clpreauth_modreq_st *krb5_clpreauth_modreq;
-/*
- * Provided by krb5: a callback which will obtain the user's long-term AS key
- * by prompting the user for the password and converting it to a key using the
- * provided salt and s2kparams. The resulting key will be placed in
- * as_key_out, which should be initialized to empty prior to the call.
- */
-typedef krb5_error_code
-(*krb5_clpreauth_get_as_key_fn)(krb5_context context,
- krb5_principal princ,
- krb5_enctype enctype,
- krb5_prompter_fct prompter,
- void *prompter_data,
- krb5_data *salt,
- krb5_data *s2kparams,
- krb5_keyblock *as_key_out,
- void *gak_data);
-
/* Before using a callback after version 1, modules must check the vers
* field of the callback structure. */
typedef struct krb5_clpreauth_callbacks_st {
krb5_keyblock *(*fast_armor)(krb5_context context,
krb5_clpreauth_rock rock);
+ /*
+ * Get a pointer to the client-supplied reply key, possibly invoking the
+ * prompter to ask for a password if this has not already been done. The
+ * returned pointer is an alias and should not be freed.
+ */
+ krb5_error_code (*get_as_key)(krb5_context context,
+ krb5_clpreauth_rock rock,
+ krb5_keyblock **keyblock);
+
+ /* Replace the reply key to be used to decrypt the AS response. */
+ krb5_error_code (*set_as_key)(krb5_context context,
+ krb5_clpreauth_rock rock,
+ const krb5_keyblock *keyblock);
+
/* End of version 1 clpreauth callbacks. */
} *krb5_clpreauth_callbacks;
krb5_data *encoded_previous_request,
krb5_pa_data *pa_data,
krb5_prompter_fct prompter, void *prompter_data,
- krb5_clpreauth_get_as_key_fn gak_fct,
- void *gak_data,
- krb5_data *salt, krb5_data *s2kparams,
- krb5_keyblock *as_key,
krb5_pa_data ***pa_data_out);
/*
krb5_pa_data *pa_data_in,
krb5_error *error,
krb5_prompter_fct prompter, void *prompter_data,
- krb5_clpreauth_get_as_key_fn gak_fct,
- void *gak_data,
- krb5_data *salt, krb5_data *s2kparams,
- krb5_keyblock *as_key,
krb5_pa_data ***pa_data_out);
/*
ctx->preauth_rock.magic = CLIENT_ROCK_MAGIC;
ctx->preauth_rock.etype = &ctx->etype;
+ ctx->preauth_rock.as_key = &ctx->as_key;
+ ctx->preauth_rock.gak_fct = &ctx->gak_fct;
+ ctx->preauth_rock.gak_data = &ctx->gak_data;
+ ctx->preauth_rock.salt = &ctx->salt;
+ ctx->preauth_rock.s2kparams = &ctx->s2kparams;
+ ctx->preauth_rock.client = client;
+ ctx->preauth_rock.prompter = prompter;
+ ctx->preauth_rock.prompter_data = data;
/* Initialise request parameters as per krb5_get_init_creds() */
ctx->request->kdc_options = context->kdc_default_options;
ctx->encoded_previous_request,
ctx->preauth_to_use,
&ctx->request->padata,
- &ctx->salt,
- &ctx->s2kparams,
- &ctx->etype,
- &ctx->as_key,
ctx->prompter,
ctx->prompter_data,
- ctx->gak_fct,
- ctx->gak_data,
&ctx->preauth_rock,
ctx->opte);
if (code != 0)
ctx->preauth_to_use,
&ctx->request->padata,
ctx->err_reply,
- &ctx->salt,
- &ctx->s2kparams,
- &ctx->etype,
- &ctx->as_key,
ctx->prompter,
ctx->prompter_data,
- ctx->gak_fct,
- ctx->gak_data,
&ctx->preauth_rock,
ctx->opte);
} else {
ctx->encoded_previous_request,
ctx->reply->padata,
&kdc_padata,
- &ctx->salt,
- &ctx->s2kparams,
- &ctx->etype,
- &ctx->as_key,
ctx->prompter,
ctx->prompter_data,
- ctx->gak_fct,
- ctx->gak_data,
&ctx->preauth_rock,
ctx->opte);
if (code != 0)
return rock->fast_state->armor_key;
}
+static krb5_error_code
+get_as_key(krb5_context context, krb5_clpreauth_rock rock,
+ krb5_keyblock **keyblock)
+{
+ krb5_error_code ret;
+
+ if (rock->as_key->length == 0) {
+ ret = (*rock->gak_fct)(context, rock->client, *rock->etype,
+ rock->prompter, rock->prompter_data, rock->salt,
+ rock->s2kparams, rock->as_key, *rock->gak_data);
+ if (ret)
+ return ret;
+ }
+ *keyblock = rock->as_key;
+ return 0;
+}
+
+static krb5_error_code
+set_as_key(krb5_context context, krb5_clpreauth_rock rock,
+ const krb5_keyblock *keyblock)
+{
+ krb5_free_keyblock_contents(context, rock->as_key);
+ return krb5_copy_keyblock_contents(context, keyblock, rock->as_key);
+}
+
static struct krb5_clpreauth_callbacks_st callbacks = {
1,
get_etype,
- fast_armor
+ fast_armor,
+ get_as_key,
+ set_as_key
};
/* Tweak the request body, for now adding any enctypes which the module claims
krb5_pa_data *in_padata,
krb5_prompter_fct prompter,
void *prompter_data,
- krb5_clpreauth_get_as_key_fn gak_fct,
- krb5_data *salt,
- krb5_data *s2kparams,
- void *gak_data,
krb5_clpreauth_rock preauth_rock,
- krb5_keyblock *as_key,
krb5_pa_data ***out_pa_list,
int *out_pa_list_size,
int *module_ret,
&callbacks, preauth_rock,
request, encoded_request_body,
encoded_previous_request, in_padata,
- prompter, prompter_data, gak_fct,
- gak_data, salt, s2kparams, as_key,
- &out_pa_data);
+ prompter, prompter_data, &out_pa_data);
TRACE_PREAUTH_PROCESS(kcontext, module->name, module->pa_type,
module->flags, ret);
/* Make note of the module's flags and status. */
krb5_pa_data **padata,
krb5_pa_data ***return_padata,
krb5_error *err_reply,
- krb5_data *salt, krb5_data *s2kparams,
- krb5_enctype *etype,
- krb5_keyblock *as_key,
krb5_prompter_fct prompter, void *prompter_data,
- krb5_gic_get_as_key_fct gak_fct, void *gak_data,
krb5_clpreauth_rock preauth_rock,
krb5_gic_opt_ext *opte)
{
padata[i],
err_reply,
prompter, prompter_data,
- gak_fct, gak_data, salt, s2kparams,
- as_key,
&out_padata) == 0) {
if (out_padata != NULL) {
int k;
}
krb5_error_code KRB5_CALLCONV
-krb5_do_preauth(krb5_context context,
- krb5_kdc_req *request,
+krb5_do_preauth(krb5_context context, krb5_kdc_req *request,
krb5_data *encoded_request_body,
krb5_data *encoded_previous_request,
krb5_pa_data **in_padata, krb5_pa_data ***out_padata,
- krb5_data *salt, krb5_data *s2kparams,
- krb5_enctype *etype,
- krb5_keyblock *as_key,
krb5_prompter_fct prompter, void *prompter_data,
- krb5_gic_get_as_key_fct gak_fct, void *gak_data,
- krb5_clpreauth_rock preauth_rock, krb5_gic_opt_ext *opte)
+ krb5_clpreauth_rock rock, krb5_gic_opt_ext *opte)
{
unsigned int h;
int i, j, out_pa_list_size;
}
scratch.data = (char *) etype_info[l]->salt;
scratch.length = etype_info[l]->length;
- krb5_free_data_contents(context, salt);
+ krb5_free_data_contents(context, rock->salt);
if (scratch.length == KRB5_ETYPE_NO_SALT)
- salt->data = NULL;
- else
- if ((ret = krb5int_copy_data_contents( context, &scratch, salt)) != 0)
+ rock->salt->data = NULL;
+ else {
+ ret = krb5int_copy_data_contents(context, &scratch,
+ rock->salt);
+ if (ret)
goto cleanup;
- *etype = etype_info[l]->etype;
- krb5_free_data_contents(context, s2kparams);
- if ((ret = krb5int_copy_data_contents(context,
- &etype_info[l]->s2kparams,
- s2kparams)) != 0)
+ }
+ *rock->etype = etype_info[l]->etype;
+ krb5_free_data_contents(context, rock->s2kparams);
+ ret = krb5int_copy_data_contents(context,
+ &etype_info[l]->s2kparams,
+ rock->s2kparams);
+ if (ret)
goto cleanup;
- TRACE_PREAUTH_ETYPE_INFO(context, *etype, salt, s2kparams);
+ TRACE_PREAUTH_ETYPE_INFO(context, *rock->etype, rock->salt,
+ rock->s2kparams);
break;
}
case KRB5_PADATA_PW_SALT:
#endif
out_pa = NULL;
- if ((ret = ((*pa_types[j].fct)(context, request,
- in_padata[i], &out_pa,
- salt, s2kparams, etype, as_key,
- prompter, prompter_data,
- gak_fct, gak_data)))) {
+ ret = pa_types[j].fct(context, request, in_padata[i],
+ &out_pa, rock->salt,
+ rock->s2kparams, rock->etype,
+ rock->as_key, prompter,
+ prompter_data, *rock->gak_fct,
+ *rock->gak_data);
+ if (ret) {
if (paorder[h] == PA_INFO) {
TRACE_PREAUTH_INFO_FAIL(context,
in_padata[i]->pa_type,
in_padata[i],
prompter,
prompter_data,
- gak_fct,
- salt, s2kparams,
- gak_data,
- preauth_rock,
- as_key,
+ rock,
&out_pa_list,
&out_pa_list_size,
&module_ret,
krb5_data *encoded_request_body,
krb5_data *encoded_previous_request, krb5_pa_data *padata,
krb5_prompter_fct prompter, void *prompter_data,
- krb5_clpreauth_get_as_key_fn gak_fct, void *gak_data,
- krb5_data *salt, krb5_data *s2kparams, krb5_keyblock *as_key,
krb5_pa_data ***out_padata)
{
krb5_error_code retval = 0;
- krb5_enctype enctype;
- krb5_keyblock *challenge_key = NULL, *armor_key;
+ krb5_keyblock *challenge_key = NULL, *armor_key, *as_key;
armor_key = cb->fast_armor(context, rock);
- enctype = cb->get_etype(context, rock);
- if (as_key->length == 0 ||as_key->enctype != enctype) {
- retval = gak_fct(context, request->client,
- enctype, prompter, prompter_data,
- salt, s2kparams,
- as_key, gak_data);
- }
+ retval = cb->get_as_key(context, rock, &as_key);
if (retval == 0 && padata->length) {
krb5_enc_data *enc = NULL;
krb5_data scratch;
krb5_kdc_req *request, krb5_data *encoded_request_body,
krb5_data *encoded_previous_request, krb5_pa_data *padata,
krb5_prompter_fct prompter, void *prompter_data,
- krb5_clpreauth_get_as_key_fn gak_fct, void *gak_data,
- krb5_data *salt, krb5_data *s2kparams, krb5_keyblock *as_key,
krb5_pa_data ***out_padata)
{
krb5_error_code ret;
krb5_data *ts = NULL, *enc_ts = NULL;
krb5_enc_data enc_data;
krb5_pa_data **pa = NULL;
- krb5_enctype etype = cb->get_etype(context, rock);
+ krb5_keyblock *as_key;
enc_data.ciphertext = empty_data();
- if (as_key->length == 0) {
-#ifdef DEBUG
- fprintf (stderr, "%s:%d: salt len=%d", __FILE__, __LINE__,
- salt->length);
- if ((int) salt->length > 0)
- fprintf (stderr, " '%.*s'", salt->length, salt->data);
- fprintf (stderr, "; *etype=%d request->ktype[0]=%d\n",
- etype, request->ktype[0]);
-#endif
- ret = (*gak_fct)(context, request->client, etype, prompter,
- prompter_data, salt, s2kparams, as_key, gak_data);
- if (ret)
- goto cleanup;
- TRACE_PREAUTH_ENC_TS_KEY_GAK(context, as_key);
- }
+ ret = cb->get_as_key(context, rock, &as_key);
+ if (ret)
+ goto cleanup;
+ TRACE_PREAUTH_ENC_TS_KEY_GAK(context, as_key);
/* now get the time of day, and encrypt it accordingly */
ret = krb5_us_timeofday(context, &pa_enc.patimestamp, &pa_enc.pausec);
krb5_pa_data *pa_data,
krb5_prompter_fct prompter,
void *prompter_data,
- krb5_clpreauth_get_as_key_fn gak_fct,
- void *gak_data,
- krb5_data *salt, krb5_data *s2kparams,
- krb5_keyblock *as_key,
krb5_pa_data ***out_pa_data)
{
krb5_pa_data **send_pa;
krb5_checksum checksum;
- krb5_enctype enctype;
krb5_cksumtype *cksumtypes;
krb5_error_code status = 0;
- krb5_int32 cksumtype, *enctypes;
- unsigned int i, n_enctypes, cksumtype_count;
+ krb5_int32 cksumtype;
+ unsigned int i, cksumtype_count;
int num_gic_info = 0;
krb5_gic_opt_pa_data *gic_info;
+ krb5_keyblock *as_key;
status = krb5_get_init_creds_opt_get_pa(kcontext, opt,
&num_gic_info, &gic_info);
memset(&checksum, 0, sizeof(checksum));
- /* Get the user's long-term key if we haven't asked for it yet. Try
- * all of the encryption types which the server supports. */
- if (as_key->length == 0) {
- if ((pa_data != NULL) && (pa_data->length >= 4)) {
-#ifdef DEBUG
- fprintf(stderr, "%d bytes of preauth data.\n", pa_data->length);
-#endif
- n_enctypes = pa_data->length / 4;
- enctypes = (krb5_int32*) pa_data->contents;
- } else {
- n_enctypes = request->nktypes;
- }
- for (i = 0; i < n_enctypes; i++) {
- if ((pa_data != NULL) && (pa_data->length >= 4)) {
- memcpy(&enctype, pa_data->contents + 4 * i, 4);
- enctype = ntohl(enctype);
- } else {
- enctype = request->ktype[i];
- }
-#ifdef DEBUG
- fprintf(stderr, "Asking for AS key (type = %d).\n", enctype);
-#endif
- status = (*gak_fct)(kcontext, request->client, enctype,
- prompter, prompter_data,
- salt, s2kparams, as_key, gak_data);
- if (status == 0)
- break;
- }
- if (status != 0)
- return status;
- }
+ status = cb->get_as_key(kcontext, rock, &as_key);
+ if (status != 0)
+ return status;
#ifdef DEBUG
fprintf(stderr, "Got AS key (type = %d).\n", as_key->enctype);
#endif
krb5_data *encoded_previous_request,
krb5_pa_data *in_padata,
krb5_prompter_fct prompter, void *prompter_data,
- krb5_clpreauth_get_as_key_fn gak_fct, void *gak_data,
- krb5_data *salt, krb5_data *s2kparams,
- krb5_keyblock *as_key, krb5_pa_data ***out_padata)
+ krb5_pa_data ***out_padata)
{
krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED;
krb5_enctype enctype = -1;
int processing_request = 0;
pkinit_context plgctx = (pkinit_context)moddata;
pkinit_req_context reqctx = (pkinit_req_context)modreq;
- krb5_keyblock *armor_key = cb->fast_armor(context, rock);
+ krb5_keyblock *armor_key = cb->fast_armor(context, rock), as_key;
pkiDebug("pkinit_client_process %p %p %p %p\n",
context, plgctx, reqctx, request);
*/
enctype = cb->get_etype(context, rock);
retval = pa_pkinit_parse_rep(context, plgctx, reqctx, request,
- in_padata, enctype, as_key,
+ in_padata, enctype, &as_key,
encoded_previous_request);
+ if (retval == 0)
+ retval = cb->set_as_key(context, rock, &as_key);
}
pkiDebug("pkinit_client_process: returning %d (%s)\n",
krb5_data *encoded_previous_request,
krb5_pa_data *in_padata, krb5_error *err_reply,
krb5_prompter_fct prompter, void *prompter_data,
- krb5_clpreauth_get_as_key_fn gak_fct, void *gak_data,
- krb5_data *salt, krb5_data *s2kparams,
- krb5_keyblock *as_key, krb5_pa_data ***out_padata)
+ krb5_pa_data ***out_padata)
{
krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED;
pkinit_context plgctx = (pkinit_context)moddata;
krb5_pa_data *pa_data,
krb5_prompter_fct prompter,
void *prompter_data,
- krb5_clpreauth_get_as_key_fn gak_fct,
- void *gak_data,
- krb5_data *salt, krb5_data *s2kparams,
- krb5_keyblock *as_key,
krb5_pa_data ***out_pa_data)
{
krb5_pa_data **send_pa;
fprintf(stderr, "Recovered key type=%d, length=%d.\n",
kb->enctype, kb->length);
#endif
- status = krb5_copy_keyblock_contents(kcontext, kb, as_key);
+ status = cb->set_as_key(kcontext, rock, kb);
krb5_free_keyblock(kcontext, kb);
return status;
}