summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNalin Dahyabhai <nalin@fedoraproject.org>2007-01-22 21:24:18 +0000
committerNalin Dahyabhai <nalin@fedoraproject.org>2007-01-22 21:24:18 +0000
commit3108b87f7256524698119beb790bea5bfade77ac (patch)
treec2147380d1315e1f4c2b45d965e18b99412178aa
parentf3820b972d502161542e121eee4b18bc64e36037 (diff)
downloadkrb5-3108b87f7256524698119beb790bea5bfade77ac.tar.gz
krb5-3108b87f7256524698119beb790bea5bfade77ac.tar.xz
krb5-3108b87f7256524698119beb790bea5bfade77ac.zip
- backport of preauth plugin support from 1.6
-rw-r--r--krb5-1.5.1-1.6-pal.patch4175
1 files changed, 4175 insertions, 0 deletions
diff --git a/krb5-1.5.1-1.6-pal.patch b/krb5-1.5.1-1.6-pal.patch
new file mode 100644
index 0000000..fbb748d
--- /dev/null
+++ b/krb5-1.5.1-1.6-pal.patch
@@ -0,0 +1,4175 @@
+diff -upNr krb5-1.5.1 krb5-1.6
+--- krb5/src/kdc/dispatch.c
++++ krb5/src/kdc/dispatch.c
+@@ -94,7 +94,7 @@ dispatch(krb5_data *pkt, const krb5_full
+ * pointer.
+ */
+ if (!(retval = setup_server_realm(as_req->server))) {
+- retval = process_as_req(as_req, from, response);
++ retval = process_as_req(as_req, pkt, from, response);
+ }
+ krb5_free_kdc_req(kdc_context, as_req);
+ }
+--- krb5/src/kdc/Makefile.in
++++ krb5/src/kdc/Makefile.in
+@@ -13,7 +13,7 @@ PROG_LIBPATH=-L$(TOPLIBD) $(KRB4_LIBPATH
+ KDB5_LIB_DEPS=$(DL_LIB) $(THREAD_LINKOPTS)
+ PROG_RPATH=$(KRB5_LIBDIR)
+ FAKEKA=@FAKEKA@
+-DEFS=
++DEFS=-DLIBDIR=\"$(KRB5_LIBDIR)\"
+
+ all:: krb5kdc rtest $(FAKEKA)
+
+--- krb5/src/kdc/do_as_req.c
++++ krb5/src/kdc/do_as_req.c
+@@ -50,8 +50,8 @@ static krb5_error_code prepare_error_as
+
+ /*ARGSUSED*/
+ krb5_error_code
+-process_as_req(krb5_kdc_req *request, const krb5_fulladdr *from,
+- krb5_data **response)
++process_as_req(krb5_kdc_req *request, krb5_data *req_pkt,
++ const krb5_fulladdr *from, krb5_data **response)
+ {
+ krb5_db_entry client, server;
+ krb5_kdc_rep reply;
+@@ -78,6 +78,7 @@ process_as_req(krb5_kdc_req *request, co
+ char ktypestr[128];
+ char rep_etypestr[128];
+ char fromstringbuf[70];
++ void *pa_context = NULL;
+
+ ticket_reply.enc_part.ciphertext.data = 0;
+ e_data.data = 0;
+@@ -260,7 +261,8 @@ process_as_req(krb5_kdc_req *request, co
+ * Check the preauthentication if it is there.
+ */
+ if (request->padata) {
+- errcode = check_padata(kdc_context, &client, request, &enc_tkt_reply);
++ errcode = check_padata(kdc_context, &client, req_pkt, request,
++ &enc_tkt_reply, &pa_context, &e_data);
+ if (errcode) {
+ #ifdef KRBCONF_KDC_MODIFIES_KDB
+ /*
+@@ -381,8 +383,8 @@ process_as_req(krb5_kdc_req *request, co
+ reply_encpart.caddrs = enc_tkt_reply.caddrs;
+
+ /* Fetch the padata info to be returned */
+- errcode = return_padata(kdc_context, &client, request, &reply, client_key,
+- &encrypting_key);
++ errcode = return_padata(kdc_context, &client, req_pkt, request,
++ &reply, client_key, &encrypting_key, &pa_context);
+ if (errcode) {
+ status = "KDC_RETURN_PADATA";
+ goto errout;
+@@ -427,8 +429,11 @@ process_as_req(krb5_kdc_req *request, co
+ #endif /* KRBCONF_KDC_MODIFIES_KDB */
+
+ errout:
++ if (pa_context)
++ free_padata_context(kdc_context, &pa_context);
++
+ if (status) {
+- char * emsg = 0;
++ const char * emsg = 0;
+ if (errcode)
+ emsg = krb5_get_error_message (kdc_context, errcode);
+
+--- krb5/src/kdc/kdc_preauth.c
++++ krb5/src/kdc/kdc_preauth.c
+@@ -60,6 +60,13 @@
+ #include <syslog.h>
+
+ #include <assert.h>
++#include "../include/krb5/preauth_plugin.h"
++
++#if TARGET_OS_MAC
++static const char *objdirs[] = { KRB5_PLUGIN_BUNDLE_DIR, LIBDIR "/krb5/plugins/preauth", NULL }; /* should be a list */
++#else
++static const char *objdirs[] = { LIBDIR "/krb5/plugins/preauth", NULL };
++#endif
+
+ /* XXX This is ugly and should be in a header file somewhere */
+ #ifndef KRB5INT_DES_TYPES_DEFINED
+@@ -72,44 +79,76 @@ extern int mit_des_is_weak_key (mit_des_
+
+ typedef krb5_error_code (*verify_proc)
+ (krb5_context, krb5_db_entry *client,
++ krb5_data *req_pkt,
+ krb5_kdc_req *request,
+- krb5_enc_tkt_part * enc_tkt_reply, krb5_pa_data *data);
++ krb5_enc_tkt_part * enc_tkt_reply, krb5_pa_data *data,
++ preauth_get_entry_data_proc get_entry_data,
++ void *pa_module_context,
++ void **pa_request_context,
++ krb5_data **e_data);
+
+ typedef krb5_error_code (*edata_proc)
+ (krb5_context, krb5_kdc_req *request,
+ krb5_db_entry *client, krb5_db_entry *server,
++ preauth_get_entry_data_proc get_entry_data,
++ void *pa_module_context,
+ krb5_pa_data *data);
+
+ typedef krb5_error_code (*return_proc)
+ (krb5_context, krb5_pa_data * padata,
+ krb5_db_entry *client,
++ krb5_data *req_pkt,
+ krb5_kdc_req *request, krb5_kdc_rep *reply,
+ krb5_key_data *client_key,
+ krb5_keyblock *encrypting_key,
+- krb5_pa_data **send_pa);
++ krb5_pa_data **send_pa,
++ preauth_get_entry_data_proc get_entry_data,
++ void *pa_module_context,
++ void **pa_request_context);
++
++typedef krb5_error_code (*freepa_proc)
++ (krb5_context, void *pa_module_context, void **pa_request_context);
++
++typedef krb5_error_code (*init_proc)
++ (krb5_context, void **);
++typedef void (*fini_proc)
++ (krb5_context, void *);
+
+ typedef struct _krb5_preauth_systems {
+- char * name;
++ const char *name;
+ int type;
+ int flags;
++ void *plugin_context;
++ init_proc init;
++ fini_proc fini;
+ edata_proc get_edata;
+ verify_proc verify_padata;
+ return_proc return_padata;
++ freepa_proc free_pa_request_context;
+ } krb5_preauth_systems;
+
+ static krb5_error_code verify_enc_timestamp
+ (krb5_context, krb5_db_entry *client,
++ krb5_data *req_pkt,
+ krb5_kdc_req *request,
+- krb5_enc_tkt_part * enc_tkt_reply, krb5_pa_data *data);
++ krb5_enc_tkt_part * enc_tkt_reply, krb5_pa_data *data,
++ preauth_get_entry_data_proc get_entry_data,
++ void *pa_system_context,
++ void **pa_request_context,
++ krb5_data **e_data);
+
+ static krb5_error_code get_etype_info
+ (krb5_context, krb5_kdc_req *request,
+ krb5_db_entry *client, krb5_db_entry *server,
++ preauth_get_entry_data_proc get_entry_data,
++ void *pa_system_context,
+ krb5_pa_data *data);
+ static krb5_error_code
+ get_etype_info2(krb5_context context, krb5_kdc_req *request,
+- krb5_db_entry *client, krb5_db_entry *server,
+- krb5_pa_data *pa_data);
++ krb5_db_entry *client, krb5_db_entry *server,
++ preauth_get_entry_data_proc get_entry_data,
++ void *pa_system_context,
++ krb5_pa_data *pa_data);
+ static krb5_error_code
+ etype_info_as_rep_helper(krb5_context context, krb5_pa_data * padata,
+ krb5_db_entry *client,
+@@ -122,58 +161,76 @@ etype_info_as_rep_helper(krb5_context co
+ static krb5_error_code
+ return_etype_info(krb5_context, krb5_pa_data * padata,
+ krb5_db_entry *client,
++ krb5_data *req_pkt,
+ krb5_kdc_req *request, krb5_kdc_rep *reply,
+ krb5_key_data *client_key,
+ krb5_keyblock *encrypting_key,
+- krb5_pa_data **send_pa);
++ krb5_pa_data **send_pa,
++ preauth_get_entry_data_proc get_entry_data,
++ void *pa_system_context,
++ void **pa_request_context);
+
+ static krb5_error_code
+ return_etype_info2(krb5_context, krb5_pa_data * padata,
+ krb5_db_entry *client,
++ krb5_data *req_pkt,
+ krb5_kdc_req *request, krb5_kdc_rep *reply,
+ krb5_key_data *client_key,
+ krb5_keyblock *encrypting_key,
+- krb5_pa_data **send_pa);
++ krb5_pa_data **send_pa,
++ preauth_get_entry_data_proc get_entry_data,
++ void *pa_system_context,
++ void **pa_request_context);
+
+ static krb5_error_code return_pw_salt
+ (krb5_context, krb5_pa_data * padata,
+ krb5_db_entry *client,
++ krb5_data *req_pkt,
+ krb5_kdc_req *request, krb5_kdc_rep *reply,
+ krb5_key_data *client_key,
+ krb5_keyblock *encrypting_key,
+- krb5_pa_data **send_pa);
++ krb5_pa_data **send_pa,
++ preauth_get_entry_data_proc get_entry_data,
++ void *pa_system_context,
++ void **pa_request_context);
+
+ /* SAM preauth support */
+ static krb5_error_code verify_sam_response
+ (krb5_context, krb5_db_entry *client,
++ krb5_data *req_pkt,
+ krb5_kdc_req *request,
+- krb5_enc_tkt_part * enc_tkt_reply, krb5_pa_data *data);
++ krb5_enc_tkt_part * enc_tkt_reply, krb5_pa_data *data,
++ preauth_get_entry_data_proc get_entry_data,
++ void *pa_module_context,
++ void **pa_request_context,
++ krb5_data **e_data);
+
+ static krb5_error_code get_sam_edata
+ (krb5_context, krb5_kdc_req *request,
+ krb5_db_entry *client, krb5_db_entry *server,
++ preauth_get_entry_data_proc get_entry_data,
++ void *pa_module_context,
+ krb5_pa_data *data);
+ static krb5_error_code return_sam_data
+ (krb5_context, krb5_pa_data * padata,
+ krb5_db_entry *client,
++ krb5_data *req_pkt,
+ krb5_kdc_req *request, krb5_kdc_rep *reply,
+ krb5_key_data *client_key,
+ krb5_keyblock *encrypting_key,
+- krb5_pa_data **send_pa);
+-/*
+- * Preauth property flags
+- */
+-#define PA_HARDWARE 0x00000001
+-#define PA_REQUIRED 0x00000002
+-#define PA_SUFFICIENT 0x00000004
+- /* Not really a padata, so don't include it in the etype list*/
+-#define PA_PSEUDO 0x00000008
++ krb5_pa_data **send_pa,
++ preauth_get_entry_data_proc get_entry_data,
++ void *pa_module_context,
++ void **pa_request_context);
+
+-static krb5_preauth_systems preauth_systems[] = {
++static krb5_preauth_systems static_preauth_systems[] = {
+ {
+ "timestamp",
+ KRB5_PADATA_ENC_TIMESTAMP,
+ 0,
++ NULL,
++ NULL,
++ NULL,
+ 0,
+ verify_enc_timestamp,
+ 0
+@@ -182,6 +239,9 @@ static krb5_preauth_systems preauth_syst
+ "etype-info",
+ KRB5_PADATA_ETYPE_INFO,
+ 0,
++ NULL,
++ NULL,
++ NULL,
+ get_etype_info,
+ 0,
+ return_etype_info
+@@ -190,6 +250,9 @@ static krb5_preauth_systems preauth_syst
+ "etype-info2",
+ KRB5_PADATA_ETYPE_INFO2,
+ 0,
++ NULL,
++ NULL,
++ NULL,
+ get_etype_info2,
+ 0,
+ return_etype_info2
+@@ -198,6 +261,9 @@ static krb5_preauth_systems preauth_syst
+ "pw-salt",
+ KRB5_PADATA_PW_SALT,
+ PA_PSEUDO, /* Don't include this in the error list */
++ NULL,
++ NULL,
++ NULL,
+ 0,
+ 0,
+ return_pw_salt
+@@ -206,6 +272,9 @@ static krb5_preauth_systems preauth_syst
+ "sam-response",
+ KRB5_PADATA_SAM_RESPONSE,
+ 0,
++ NULL,
++ NULL,
++ NULL,
+ 0,
+ verify_sam_response,
+ return_sam_data
+@@ -214,6 +283,9 @@ static krb5_preauth_systems preauth_syst
+ "sam-challenge",
+ KRB5_PADATA_SAM_CHALLENGE,
+ PA_HARDWARE, /* causes get_preauth_hint_list to use this */
++ NULL,
++ NULL,
++ NULL,
+ get_sam_edata,
+ 0,
+ 0
+@@ -221,13 +293,378 @@ static krb5_preauth_systems preauth_syst
+ { "[end]", -1,}
+ };
+
+-#define MAX_PREAUTH_SYSTEMS (sizeof(preauth_systems)/sizeof(preauth_systems[0]))
++static krb5_preauth_systems *preauth_systems;
++static int n_preauth_systems;
++static struct plugin_dir_handle preauth_plugins;
++
++krb5_error_code
++load_preauth_plugins(krb5_context context)
++{
++ struct errinfo err;
++ void **preauth_plugins_ftables;
++ struct krb5plugin_preauth_server_ftable_v0 *ftable;
++ int module_count, i, j, k;
++ void *plugin_context;
++ init_proc server_init_proc = NULL;
++
++ memset(&err, 0, sizeof(err));
++
++ /* Attempt to load all of the preauth plugins we can find. */
++ PLUGIN_DIR_INIT(&preauth_plugins);
++ if (PLUGIN_DIR_OPEN(&preauth_plugins) == 0) {
++ if (krb5int_open_plugin_dirs(objdirs, NULL,
++ &preauth_plugins, &err) != 0) {
++ return KRB5_PLUGIN_NO_HANDLE;
++ }
++ }
++
++ /* Get the method tables provided by the loaded plugins. */
++ preauth_plugins_ftables = NULL;
++ if (krb5int_get_plugin_dir_data(&preauth_plugins,
++ "preauthentication_server_0_backport_1_6",
++ &preauth_plugins_ftables, &err) != 0) {
++ return KRB5_PLUGIN_NO_HANDLE;
++ }
++
++ /* Count the valid modules. */
++ module_count = sizeof(static_preauth_systems)
++ / sizeof(static_preauth_systems[0]);
++ if (preauth_plugins_ftables != NULL) {
++ for (i = 0; preauth_plugins_ftables[i] != NULL; i++) {
++ ftable = preauth_plugins_ftables[i];
++ if ((ftable->flags_proc == NULL) &&
++ (ftable->edata_proc == NULL) &&
++ (ftable->verify_proc == NULL) &&
++ (ftable->return_proc == NULL)) {
++ continue;
++ }
++ for (j = 0;
++ ftable->pa_type_list != NULL &&
++ ftable->pa_type_list[j] > 0;
++ j++) {
++ module_count++;
++ }
++ }
++ }
++
++ /* Build the complete list of supported preauthentication options, and
++ * leave room for a terminator entry. */
++ preauth_systems = malloc(sizeof(krb5_preauth_systems) * (module_count + 1));
++ if (preauth_systems == NULL) {
++ krb5int_free_plugin_dir_data(preauth_plugins_ftables);
++ return ENOMEM;
++ }
++
++ /* Add the locally-supplied mechanisms to the dynamic list first. */
++ for (i = 0, k = 0;
++ i < sizeof(static_preauth_systems) / sizeof(static_preauth_systems[0]);
++ i++) {
++ if (static_preauth_systems[i].type == -1)
++ break;
++ preauth_systems[k] = static_preauth_systems[i];
++ /* Try to initialize the preauth system. If it fails, we'll remove it
++ * from the list of systems we'll be using. */
++ plugin_context = NULL;
++ server_init_proc = static_preauth_systems[i].init;
++ if ((server_init_proc != NULL) &&
++ ((*server_init_proc)(context, &plugin_context) != 0)) {
++ memset(&preauth_systems[k], 0, sizeof(preauth_systems[k]));
++ continue;
++ }
++ preauth_systems[k].plugin_context = plugin_context;
++ k++;
++ }
++
++ /* Now add the dynamically-loaded mechanisms to the list. */
++ if (preauth_plugins_ftables != NULL) {
++ for (i = 0; preauth_plugins_ftables[i] != NULL; i++) {
++ ftable = preauth_plugins_ftables[i];
++ if ((ftable->flags_proc == NULL) &&
++ (ftable->edata_proc == NULL) &&
++ (ftable->verify_proc == NULL) &&
++ (ftable->return_proc == NULL)) {
++ continue;
++ }
++ plugin_context = NULL;
++ for (j = 0;
++ ftable->pa_type_list != NULL &&
++ ftable->pa_type_list[j] > 0;
++ j++) {
++ /* Try to initialize the plugin. If it fails, we'll remove it
++ * from the list of modules we'll be using. */
++ if (j == 0) {
++ server_init_proc = ftable->init_proc;
++ if (server_init_proc != NULL) {
++ krb5_error_code initerr;
++ initerr = (*server_init_proc)(context, &plugin_context);
++ if (initerr) {
++ const char *emsg;
++ emsg = krb5_get_error_message(context, initerr);
++ if (emsg) {
++ krb5_klog_syslog(LOG_ERR,
++ "preauth %s failed to initialize: %s",
++ ftable->name, emsg);
++ krb5_free_error_message(context, emsg);
++ }
++ memset(&preauth_systems[k], 0, sizeof(preauth_systems[k]));
++
++ break; /* skip all modules in this plugin */
++ }
++ }
++ }
++ preauth_systems[k].name = ftable->name;
++ preauth_systems[k].type = ftable->pa_type_list[j];
++ if (ftable->flags_proc != NULL)
++ preauth_systems[k].flags = ftable->flags_proc(context, preauth_systems[k].type);
++ else
++ preauth_systems[k].flags = 0;
++ preauth_systems[k].plugin_context = plugin_context;
++ preauth_systems[k].init = server_init_proc;
++ /* Only call fini once for each plugin */
++ if (j == 0)
++ preauth_systems[k].fini = ftable->fini_proc;
++ else
++ preauth_systems[k].fini = NULL;
++ preauth_systems[k].get_edata = ftable->edata_proc;
++ preauth_systems[k].verify_padata = ftable->verify_proc;
++ preauth_systems[k].return_padata = ftable->return_proc;
++ preauth_systems[k].free_pa_request_context =
++ ftable->freepa_reqcontext_proc;
++ k++;
++ }
++ }
++ krb5int_free_plugin_dir_data(preauth_plugins_ftables);
++ }
++ n_preauth_systems = k;
++ /* Add the end-of-list marker. */
++ preauth_systems[k].name = "[end]";
++ preauth_systems[k].type = -1;
++ return 0;
++}
++
++krb5_error_code
++unload_preauth_plugins(krb5_context context)
++{
++ int i;
++ if (preauth_systems != NULL) {
++ for (i = 0; i < n_preauth_systems; i++) {
++ if (preauth_systems[i].fini != NULL) {
++ (*preauth_systems[i].fini)(context,
++ preauth_systems[i].plugin_context);
++ }
++ memset(&preauth_systems[i], 0, sizeof(preauth_systems[i]));
++ }
++ free(preauth_systems);
++ preauth_systems = NULL;
++ n_preauth_systems = 0;
++ krb5int_close_plugin_dirs(&preauth_plugins);
++ }
++ return 0;
++}
++
++/*
++ * The make_padata_context() function creates a space for storing any context
++ * information which will be needed by return_padata() later. Each preauth
++ * type gets a context storage location of its own.
++ */
++struct request_pa_context {
++ int n_contexts;
++ struct {
++ krb5_preauth_systems *pa_system;
++ void *pa_context;
++ } *contexts;
++};
++
++static krb5_error_code
++make_padata_context(krb5_context context, void **padata_context)
++{
++ int i;
++ struct request_pa_context *ret;
++
++ ret = malloc(sizeof(*ret));
++ if (ret == NULL) {
++ return ENOMEM;
++ }
++
++ ret->n_contexts = n_preauth_systems;
++ ret->contexts = malloc(sizeof(ret->contexts[0]) * ret->n_contexts);
++ if (ret->contexts == NULL) {
++ free(ret);
++ return ENOMEM;
++ }
++
++ memset(ret->contexts, 0, sizeof(ret->contexts[0]) * ret->n_contexts);
++
++ for (i = 0; i < ret->n_contexts; i++) {
++ ret->contexts[i].pa_system = &preauth_systems[i];
++ ret->contexts[i].pa_context = NULL;
++ }
++
++ *padata_context = ret;
++
++ return 0;
++}
++
++/*
++ * The free_padata_context function frees any context information pointers
++ * which the check_padata() function created but which weren't already cleaned
++ * up by return_padata().
++ */
++krb5_error_code
++free_padata_context(krb5_context kcontext, void **padata_context)
++{
++ struct request_pa_context *context;
++ krb5_preauth_systems *preauth_system;
++ void **pctx, *mctx;
++ int i;
++
++ if (padata_context == NULL)
++ return 0;
++
++ context = *padata_context;
++
++ for (i = 0; i < context->n_contexts; i++) {
++ if (context->contexts[i].pa_context != NULL) {
++ preauth_system = context->contexts[i].pa_system;
++ mctx = preauth_system->plugin_context;
++ if (preauth_system->free_pa_request_context != NULL) {
++ pctx = &context->contexts[i].pa_context;
++ (*preauth_system->free_pa_request_context)(kcontext, mctx,
++ pctx);
++ }
++ context->contexts[i].pa_context = NULL;
++ }
++ }
++
++ free(context->contexts);
++ free(context);
++
++ return 0;
++}
++
++/* Retrieve a specified tl_data item from the given entry, and return its
++ * contents in a new krb5_data, which must be freed by the caller. */
++static krb5_error_code
++get_entry_tl_data(krb5_context context, krb5_db_entry *entry,
++ krb5_int16 tl_data_type, krb5_data **result)
++{
++ krb5_tl_data *tl;
++ for (tl = entry->tl_data; tl != NULL; tl = tl->tl_data_next) {
++ if (tl->tl_data_type == tl_data_type) {
++ *result = malloc(sizeof(krb5_data));
++ if (*result == NULL) {
++ return ENOMEM;
++ }
++ (*result)->magic = KV5M_DATA;
++ (*result)->data = malloc(tl->tl_data_length);
++ if ((*result)->data == NULL) {
++ free(*result);
++ *result = NULL;
++ return ENOMEM;
++ }
++ memcpy((*result)->data, tl->tl_data_contents, tl->tl_data_length);
++ return 0;
++ }
++ }
++ return ENOENT;
++}
++
++/*
++ * Retrieve a specific piece of information pertaining to the entry or the
++ * request and return it in a new krb5_data item which the caller must free.
++ *
++ * This may require massaging data into a contrived format, but it will
++ * hopefully keep us from having to reveal library-internal functions to
++ * modules.
++ */
++static krb5_error_code
++get_entry_data(krb5_context context,
++ krb5_kdc_req *request, krb5_db_entry *entry,
++ krb5_int32 type,
++ krb5_data **result)
++{
++ int i, k;
++ krb5_data *ret;
++ krb5_deltat *delta;
++ krb5_keyblock *keys;
++ krb5_key_data *entry_key;
++
++ switch (type) {
++ case krb5plugin_preauth_entry_request_certificate:
++ return get_entry_tl_data(context, entry,
++ KRB5_TL_USER_CERTIFICATE, result);
++ break;
++ case krb5plugin_preauth_entry_max_time_skew:
++ ret = malloc(sizeof(krb5_data));
++ if (ret == NULL)
++ return ENOMEM;
++ delta = malloc(sizeof(krb5_deltat));
++ if (delta == NULL) {
++ free(ret);
++ return ENOMEM;
++ }
++ *delta = context->clockskew;
++ ret->data = (char *) delta;
++ ret->length = sizeof(*delta);
++ *result = ret;
++ return 0;
++ break;
++ case krb5plugin_preauth_keys:
++ ret = malloc(sizeof(krb5_data));
++ if (ret == NULL)
++ return ENOMEM;
++ keys = malloc(sizeof(krb5_keyblock) * (request->nktypes + 1));
++ if (keys == NULL) {
++ free(ret);
++ return ENOMEM;
++ }
++ ret->data = (char *) keys;
++ ret->length = sizeof(krb5_keyblock) * (request->nktypes + 1);
++ memset(ret->data, 0, ret->length);
++ k = 0;
++ for (i = 0; i < request->nktypes; i++) {
++ entry_key = NULL;
++ if (krb5_dbe_find_enctype(context, entry, request->ktype[i],
++ -1, 0, &entry_key) != 0)
++ continue;
++ if (krb5_dbekd_decrypt_key_data(context, &master_keyblock,
++ entry_key, &keys[k], NULL) != 0) {
++ if (keys[k].contents != NULL)
++ krb5_free_keyblock_contents(context, &keys[k]);
++ memset(&keys[k], 0, sizeof(keys[k]));
++ continue;
++ }
++ k++;
++ }
++ if (k > 0) {
++ *result = ret;
++ return 0;
++ } else {
++ free(keys);
++ free(ret);
++ }
++ break;
++ case krb5plugin_preauth_request_body:
++ ret = NULL;
++ encode_krb5_kdc_req_body(request, &ret);
++ if (ret != NULL) {
++ *result = ret;
++ return 0;
++ }
++ return ASN1_PARSE_ERROR;
++ break;
++ default:
++ break;
++ }
++ return ENOENT;
++}
+
+ static krb5_error_code
+ find_pa_system(int type, krb5_preauth_systems **preauth)
+ {
+- krb5_preauth_systems *ap = preauth_systems;
+-
++ krb5_preauth_systems *ap;
++
++ ap = preauth_systems ? preauth_systems : static_preauth_systems;
+ while ((ap->type != -1) && (ap->type != type))
+ ap++;
+ if (ap->type == -1)
+@@ -236,6 +673,113 @@ find_pa_system(int type, krb5_preauth_sy
+ return 0;
+ }
+
++static krb5_error_code
++find_pa_context(krb5_preauth_systems *pa_sys,
++ struct request_pa_context *context,
++ void ***pa_context)
++{
++ int i;
++
++ *pa_context = 0;
++
++ if (context == NULL)
++ return KRB5KRB_ERR_GENERIC;
++
++ for (i = 0; i < context->n_contexts; i++) {
++ if (context->contexts[i].pa_system == pa_sys) {
++ *pa_context = &context->contexts[i].pa_context;
++ return 0;
++ }
++ }
++
++ return KRB5KRB_ERR_GENERIC;
++}
++
++/*
++ * Create a list of indices into the preauth_systems array, sorted by order of
++ * preference.
++ */
++static krb5_boolean
++pa_list_includes(krb5_pa_data **pa_data, krb5_preauthtype pa_type)
++{
++ while (*pa_data != NULL) {
++ if ((*pa_data)->pa_type == pa_type)
++ return TRUE;
++ pa_data++;
++ }
++ return FALSE;
++}
++static void
++sort_pa_order(krb5_context context, krb5_kdc_req *request, int *pa_order)
++{
++ int i, j, k, n_repliers, n_key_replacers;
++
++ /* First, set up the default order. */
++ i = 0;
++ for (j = 0; j < n_preauth_systems; j++) {
++ if (preauth_systems[j].return_padata != NULL)
++ pa_order[i++] = j;
++ }
++ n_repliers = i;
++ pa_order[n_repliers] = -1;
++
++ /* Reorder so that PA_REPLACES_KEY modules are listed first. */
++ for (i = 0; i < n_repliers; i++) {
++ /* If this module replaces the key, then it's okay to leave it where it
++ * is in the order. */
++ if (preauth_systems[pa_order[i]].flags & PA_REPLACES_KEY)
++ continue;
++ /* If not, search for a module which does, and swap in the first one we
++ * find. */
++ for (j = i + 1; j < n_repliers; j++) {
++ if (preauth_systems[pa_order[j]].flags & PA_REPLACES_KEY) {
++ k = pa_order[j];
++ pa_order[j] = pa_order[i];
++ pa_order[i] = k;
++ break;
++ }
++ }
++ }
++
++ if (request->padata != NULL) {
++ /* Now reorder the subset of modules which replace the key,
++ * bubbling those which handle pa_data types provided by the
++ * client ahead of the others. */
++ for (i = 0; preauth_systems[pa_order[i]].flags & PA_REPLACES_KEY; i++) {
++ continue;
++ }
++ n_key_replacers = i;
++ for (i = 0; i < n_key_replacers; i++) {
++ if (pa_list_includes(request->padata,
++ preauth_systems[pa_order[i]].type))
++ continue;
++ for (j = i + 1; j < n_key_replacers; j++) {
++ if (pa_list_includes(request->padata,
++ preauth_systems[pa_order[j]].type)) {
++ k = pa_order[j];
++ pa_order[j] = pa_order[i];
++ pa_order[i] = k;
++ break;
++ }
++ }
++ }
++ }
++#ifdef DEBUG
++ krb5_klog_syslog(LOG_DEBUG, "original preauth mechanism list:");
++ for (i = 0; i < n_preauth_systems; i++) {
++ if (preauth_systems[i].return_padata != NULL)
++ krb5_klog_syslog(LOG_DEBUG, "... %s(%d)", preauth_systems[i].name,
++ preauth_systems[i].type);
++ }
++ krb5_klog_syslog(LOG_DEBUG, "sorted preauth mechanism list:");
++ for (i = 0; pa_order[i] != -1; i++) {
++ krb5_klog_syslog(LOG_DEBUG, "... %s(%d)",
++ preauth_systems[pa_order[i]].name,
++ preauth_systems[pa_order[i]].type);
++ }
++#endif
++}
++
+ const char *missing_required_preauth(krb5_db_entry *client,
+ krb5_db_entry *server,
+ krb5_enc_tkt_part *enc_tkt_reply)
+@@ -287,10 +831,10 @@ void get_preauth_hint_list(krb5_kdc_req
+ e_data->data = 0;
+
+ hw_only = isflagset(client->attributes, KRB5_KDB_REQUIRES_HW_AUTH);
+- pa_data = malloc(sizeof(krb5_pa_data *) * (MAX_PREAUTH_SYSTEMS+1));
++ pa_data = malloc(sizeof(krb5_pa_data *) * (n_preauth_systems+1));
+ if (pa_data == 0)
+ return;
+- memset(pa_data, 0, sizeof(krb5_pa_data *) * (MAX_PREAUTH_SYSTEMS+1));
++ memset(pa_data, 0, sizeof(krb5_pa_data *) * (n_preauth_systems+1));
+ pa = pa_data;
+
+ for (ap = preauth_systems; ap->type != -1; ap++) {
+@@ -305,7 +849,8 @@ void get_preauth_hint_list(krb5_kdc_req
+ (*pa)->magic = KV5M_PA_DATA;
+ (*pa)->pa_type = ap->type;
+ if (ap->get_edata) {
+- retval = (ap->get_edata)(kdc_context, request, client, server, *pa);
++ retval = (ap->get_edata)(kdc_context, request, client, server,
++ get_entry_data, ap->plugin_context, *pa);
+ if (retval) {
+ /* just failed on this type, continue */
+ free(*pa);
+@@ -335,23 +880,33 @@ errout:
+ /*
+ * This routine is called to verify the preauthentication information
+ * for a V5 request.
+- *
++ *
+ * Returns 0 if the pre-authentication is valid, non-zero to indicate
+ * an error code of some sort.
+ */
+
+ krb5_error_code
+-check_padata (krb5_context context, krb5_db_entry *client,
+- krb5_kdc_req *request, krb5_enc_tkt_part *enc_tkt_reply)
++check_padata (krb5_context context, krb5_db_entry *client, krb5_data *req_pkt,
++ krb5_kdc_req *request, krb5_enc_tkt_part *enc_tkt_reply,
++ void **padata_context, krb5_data *e_data)
+ {
+ krb5_error_code retval = 0;
+ krb5_pa_data **padata;
+ krb5_preauth_systems *pa_sys;
+- int pa_ok = 0, pa_found = 0;
++ void **pa_context;
++ krb5_data *pa_e_data = NULL, *tmp_e_data = NULL;
++ int pa_ok = 0, pa_found = 0;
++ krb5_error_code saved_retval = 0;
++ int use_saved_retval = 0;
++ const char *emsg;
+
+ if (request->padata == 0)
+ return 0;
+
++ if (make_padata_context(context, padata_context) != 0) {
++ return KRB5KRB_ERR_GENERIC;
++ }
++
+ #ifdef DEBUG
+ krb5_klog_syslog (LOG_DEBUG, "checking padata");
+ #endif
+@@ -361,52 +916,128 @@ check_padata (krb5_context context, krb5
+ #endif
+ if (find_pa_system((*padata)->pa_type, &pa_sys))
+ continue;
++ if (find_pa_context(pa_sys, *padata_context, &pa_context))
++ continue;
+ #ifdef DEBUG
+ krb5_klog_syslog (LOG_DEBUG, ".. pa_type %s", pa_sys->name);
+ #endif
+ if (pa_sys->verify_padata == 0)
+ continue;
+ pa_found++;
+- retval = pa_sys->verify_padata(context, client, request,
+- enc_tkt_reply, *padata);
++ retval = pa_sys->verify_padata(context, client, req_pkt, request,
++ enc_tkt_reply, *padata,
++ get_entry_data, pa_sys->plugin_context,
++ pa_context, &tmp_e_data);
+ if (retval) {
+- char * emsg = krb5_get_error_message (context, retval);
++ emsg = krb5_get_error_message (context, retval);
+ krb5_klog_syslog (LOG_INFO, "preauth (%s) verify failure: %s",
+ pa_sys->name, emsg);
+ krb5_free_error_message (context, emsg);
+ if (pa_sys->flags & PA_REQUIRED) {
++ /* free up any previous edata we might have been saving */
++ if (pa_e_data != NULL)
++ krb5_free_data(context, pa_e_data);
++ pa_e_data = tmp_e_data;
++ tmp_e_data = NULL;
++ use_saved_retval = 0; /* Make sure we use the current retval */
+ pa_ok = 0;
+ break;
+ }
++ /*
++ * We'll return edata from either the first PA_REQUIRED module
++ * that fails, or the first non-PA_REQUIRED module that fails.
++ * Hang on to edata from the first non-PA_REQUIRED module.
++ * If we've already got one saved, simply discard this one.
++ */
++ if (tmp_e_data != NULL) {
++ if (pa_e_data == NULL) {
++ /* save the first error code and e-data */
++ pa_e_data = tmp_e_data;
++ tmp_e_data = NULL;
++ saved_retval = retval;
++ use_saved_retval = 1;
++ } else {
++ /* discard this extra e-data from non-PA_REQUIRED module */
++ krb5_free_data(context, tmp_e_data);
++ tmp_e_data = NULL;
++ }
++ }
+ } else {
+ #ifdef DEBUG
+ krb5_klog_syslog (LOG_DEBUG, ".. .. ok");
+ #endif
++ /* Ignore any edata returned on success */
++ if (tmp_e_data != NULL) {
++ krb5_free_data(context, tmp_e_data);
++ tmp_e_data = NULL;
++ }
+ pa_ok = 1;
+- if (pa_sys->flags & PA_SUFFICIENT)
++ if (pa_sys->flags & PA_SUFFICIENT)
+ break;
+ }
+ }
++
++ /* Don't bother copying and returning e-data on success */
++ if (pa_ok && pa_e_data != NULL) {
++ krb5_free_data(context, pa_e_data);
++ pa_e_data = NULL;
++ }
++ /* Return any e-data from the preauth that caused us to exit the loop */
++ if (pa_e_data != NULL) {
++ e_data->data = malloc(pa_e_data->length);
++ if (e_data->data == NULL) {
++ krb5_free_data(context, pa_e_data);
++ return KRB5KRB_ERR_GENERIC;
++ }
++ memcpy(e_data->data, pa_e_data->data, pa_e_data->length);
++ e_data->length = pa_e_data->length;
++ krb5_free_data(context, pa_e_data);
++ pa_e_data = NULL;
++ if (use_saved_retval != 0)
++ retval = saved_retval;
++ }
++
+ if (pa_ok)
+ return 0;
+
+ /* pa system was not found, but principal doesn't require preauth */
+ if (!pa_found &&
+- !isflagset(client->attributes, KRB5_KDB_REQUIRES_PRE_AUTH) &&
+- !isflagset(client->attributes, KRB5_KDB_REQUIRES_HW_AUTH))
++ !isflagset(client->attributes, KRB5_KDB_REQUIRES_PRE_AUTH) &&
++ !isflagset(client->attributes, KRB5_KDB_REQUIRES_HW_AUTH))
+ return 0;
+
+ if (!pa_found) {
+- char *emsg = krb5_get_error_message(context, retval);
++ emsg = krb5_get_error_message(context, retval);
+ krb5_klog_syslog (LOG_INFO, "no valid preauth type found: %s", emsg);
+ krb5_free_error_message(context, emsg);
+ }
+-/* The following switch statement allows us
+- * to return some preauth system errors back to the client.
+- */
+- switch(retval) {
+- case KRB5KRB_AP_ERR_BAD_INTEGRITY:
++ /* The following switch statement allows us
++ * to return some preauth system errors back to the client.
++ */
++ switch(retval) {
++ case KRB5KRB_AP_ERR_BAD_INTEGRITY:
+ case KRB5KRB_AP_ERR_SKEW:
++ case KRB5KDC_ERR_ETYPE_NOSUPP:
++ /* rfc 4556 */
++ case KRB5KDC_ERR_CLIENT_NOT_TRUSTED:
++ case KRB5KDC_ERR_INVALID_SIG:
++ case KRB5KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED:
++ case KRB5KDC_ERR_CANT_VERIFY_CERTIFICATE:
++ case KRB5KDC_ERR_INVALID_CERTIFICATE:
++ case KRB5KDC_ERR_REVOKED_CERTIFICATE:
++ case KRB5KDC_ERR_REVOCATION_STATUS_UNKNOWN:
++ case KRB5KDC_ERR_CLIENT_NAME_MISMATCH:
++ case KRB5KDC_ERR_INCONSISTENT_KEY_PURPOSE:
++ case KRB5KDC_ERR_DIGEST_IN_CERT_NOT_ACCEPTED:
++ case KRB5KDC_ERR_PA_CHECKSUM_MUST_BE_INCLUDED:
++ case KRB5KDC_ERR_DIGEST_IN_SIGNED_DATA_NOT_ACCEPTED:
++ case KRB5KDC_ERR_PUBLIC_KEY_ENCRYPTION_NOT_SUPPORTED:
++ /* earlier drafts of what became rfc 4556 */
++ case KRB5KDC_ERR_CERTIFICATE_MISMATCH:
++ case KRB5KDC_ERR_KDC_NOT_TRUSTED:
++ case KRB5KDC_ERR_REVOCATION_STATUS_UNAVAILABLE:
++ /* This value is shared with KRB5KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED. */
++ /* case KRB5KDC_ERR_KEY_TOO_WEAK: */
+ return retval;
+ default:
+ return KRB5KDC_ERR_PREAUTH_FAILED;
+@@ -418,9 +1049,10 @@ check_padata (krb5_context context, krb5
+ * structures which should be returned by the KDC to the client
+ */
+ krb5_error_code
+-return_padata(krb5_context context, krb5_db_entry *client,
++return_padata(krb5_context context, krb5_db_entry *client, krb5_data *req_pkt,
+ krb5_kdc_req *request, krb5_kdc_rep *reply,
+- krb5_key_data *client_key, krb5_keyblock *encrypting_key)
++ krb5_key_data *client_key, krb5_keyblock *encrypting_key,
++ void **padata_context)
+ {
+ krb5_error_code retval;
+ krb5_pa_data ** padata;
+@@ -428,7 +1060,15 @@ return_padata(krb5_context context, krb5
+ krb5_pa_data ** send_pa;
+ krb5_pa_data * pa = 0;
+ krb5_preauth_systems * ap;
++ int * pa_order;
++ int * pa_type;
+ int size = 0;
++ void ** pa_context;
++ krb5_boolean key_modified;
++ krb5_keyblock original_key;
++ if ((!*padata_context)&& (make_padata_context(context, padata_context) != 0)) {
++ return KRB5KRB_ERR_GENERIC;
++ }
+
+ for (ap = preauth_systems; ap->type != -1; ap++) {
+ if (ap->return_padata)
+@@ -437,13 +1077,42 @@ return_padata(krb5_context context, krb5
+
+ if ((send_pa_list = malloc((size+1) * sizeof(krb5_pa_data *))) == NULL)
+ return ENOMEM;
++ if ((pa_order = malloc((size+1) * sizeof(int))) == NULL) {
++ free(send_pa_list);
++ return ENOMEM;
++ }
++ sort_pa_order(context, request, pa_order);
++
++ retval = krb5_copy_keyblock_contents(context, encrypting_key,
++ &original_key);
++ if (retval) {
++ free(send_pa_list);
++ free(pa_order);
++ return retval;
++ }
++ key_modified = FALSE;
+
+ send_pa = send_pa_list;
+ *send_pa = 0;
+-
+- for (ap = preauth_systems; ap->type != -1; ap++) {
++
++ for (pa_type = pa_order; *pa_type != -1; pa_type++) {
++ ap = &preauth_systems[*pa_type];
++ if (!key_modified)
++ if (original_key.enctype != encrypting_key->enctype)
++ key_modified = TRUE;
++ if (!key_modified)
++ if (original_key.length != encrypting_key->length)
++ key_modified = TRUE;
++ if (!key_modified)
++ if (memcmp(original_key.contents, encrypting_key->contents,
++ original_key.length) != 0)
++ key_modified = TRUE;
++ if (key_modified && (ap->flags & PA_REPLACES_KEY))
++ continue;
+ if (ap->return_padata == 0)
+ continue;
++ if (find_pa_context(ap, *padata_context, &pa_context))
++ continue;
+ pa = 0;
+ if (request->padata) {
+ for (padata = request->padata; *padata; padata++) {
+@@ -453,9 +1122,12 @@ return_padata(krb5_context context, krb5
+ }
+ }
+ }
+- if ((retval = ap->return_padata(context, pa, client, request, reply,
+- client_key, encrypting_key, send_pa)))
++ if ((retval = ap->return_padata(context, pa, client, req_pkt, request, reply,
++ client_key, encrypting_key, send_pa,
++ get_entry_data, ap->plugin_context,
++ pa_context))) {
+ goto cleanup;
++ }
+
+ if (*send_pa)
+ send_pa++;
+@@ -470,6 +1142,8 @@ return_padata(krb5_context context, krb5
+ }
+
+ cleanup:
++ krb5_free_keyblock_contents(context, &original_key);
++ free(pa_order);
+ if (send_pa_list)
+ krb5_free_pa_data(context, send_pa_list);
+ return (retval);
+@@ -508,8 +1182,13 @@ request_contains_enctype (krb5_context c
+
+ static krb5_error_code
+ verify_enc_timestamp(krb5_context context, krb5_db_entry *client,
++ krb5_data *req_pkt,
+ krb5_kdc_req *request, krb5_enc_tkt_part *enc_tkt_reply,
+- krb5_pa_data *pa)
++ krb5_pa_data *pa,
++ preauth_get_entry_data_proc ets_get_entry_data,
++ void *pa_system_context,
++ void **pa_request_context,
++ krb5_data **e_data)
+ {
+ krb5_pa_enc_ts * pa_enc = 0;
+ krb5_error_code retval;
+@@ -749,6 +1428,8 @@ cleanup:
+ static krb5_error_code
+ get_etype_info(krb5_context context, krb5_kdc_req *request,
+ krb5_db_entry *client, krb5_db_entry *server,
++ preauth_get_entry_data_proc etype_get_entry_data,
++ void *pa_system_context,
+ krb5_pa_data *pa_data)
+ {
+ int i;
+@@ -764,6 +1445,8 @@ get_etype_info(krb5_context context, krb
+ static krb5_error_code
+ get_etype_info2(krb5_context context, krb5_kdc_req *request,
+ krb5_db_entry *client, krb5_db_entry *server,
++ preauth_get_entry_data_proc etype_get_entry_data,
++ void *pa_system_context,
+ krb5_pa_data *pa_data)
+ {
+ return etype_info_helper( context, request, client, server, pa_data, 1);
+@@ -849,10 +1532,14 @@ etype_info_as_rep_helper(krb5_context co
+ static krb5_error_code
+ return_etype_info2(krb5_context context, krb5_pa_data * padata,
+ krb5_db_entry *client,
++ krb5_data *req_pkt,
+ krb5_kdc_req *request, krb5_kdc_rep *reply,
+ krb5_key_data *client_key,
+ krb5_keyblock *encrypting_key,
+- krb5_pa_data **send_pa)
++ krb5_pa_data **send_pa,
++ preauth_get_entry_data_proc etype_get_entry_data,
++ void *pa_system_context,
++ void **pa_request_context)
+ {
+ return etype_info_as_rep_helper(context, padata, client, request, reply,
+ client_key, encrypting_key, send_pa, 1);
+@@ -862,10 +1549,14 @@ return_etype_info2(krb5_context context,
+ static krb5_error_code
+ return_etype_info(krb5_context context, krb5_pa_data * padata,
+ krb5_db_entry *client,
++ krb5_data *req_pkt,
+ krb5_kdc_req *request, krb5_kdc_rep *reply,
+ krb5_key_data *client_key,
+ krb5_keyblock *encrypting_key,
+- krb5_pa_data **send_pa)
++ krb5_pa_data **send_pa,
++ preauth_get_entry_data_proc etypeget_entry_data,
++ void *pa_system_context,
++ void **pa_request_context)
+ {
+ return etype_info_as_rep_helper(context, padata, client, request, reply,
+ client_key, encrypting_key, send_pa, 0);
+@@ -873,9 +1564,12 @@ return_etype_info(krb5_context context,
+
+ static krb5_error_code
+ return_pw_salt(krb5_context context, krb5_pa_data *in_padata,
+- krb5_db_entry *client, krb5_kdc_req *request,
++ krb5_db_entry *client, krb5_data *req_pkt, krb5_kdc_req *request,
+ krb5_kdc_rep *reply, krb5_key_data *client_key,
+- krb5_keyblock *encrypting_key, krb5_pa_data **send_pa)
++ krb5_keyblock *encrypting_key, krb5_pa_data **send_pa,
++ preauth_get_entry_data_proc etype_get_entry_data,
++ void *pa_system_context,
++ void **pa_request_context)
+ {
+ krb5_error_code retval;
+ krb5_pa_data * padata;
+@@ -960,9 +1654,12 @@ cleanup:
+
+ static krb5_error_code
+ return_sam_data(krb5_context context, krb5_pa_data *in_padata,
+- krb5_db_entry *client, krb5_kdc_req *request,
++ krb5_db_entry *client, krb5_data *req_pkt, krb5_kdc_req *request,
+ krb5_kdc_rep *reply, krb5_key_data *client_key,
+- krb5_keyblock *encrypting_key, krb5_pa_data **send_pa)
++ krb5_keyblock *encrypting_key, krb5_pa_data **send_pa,
++ preauth_get_entry_data_proc sam_get_entry_data,
++ void *pa_system_context,
++ void **pa_request_context)
+ {
+ krb5_error_code retval;
+ krb5_data scratch;
+@@ -1101,7 +1798,8 @@ static struct {
+ static krb5_error_code
+ get_sam_edata(krb5_context context, krb5_kdc_req *request,
+ krb5_db_entry *client, krb5_db_entry *server,
+- krb5_pa_data *pa_data)
++ preauth_get_entry_data_proc sam_get_entry_data,
++ void *pa_system_context, krb5_pa_data *pa_data)
+ {
+ krb5_error_code retval;
+ krb5_sam_challenge sc;
+@@ -1472,8 +2170,13 @@ cleanup:
+
+ static krb5_error_code
+ verify_sam_response(krb5_context context, krb5_db_entry *client,
++ krb5_data *req_pkt,
+ krb5_kdc_req *request, krb5_enc_tkt_part *enc_tkt_reply,
+- krb5_pa_data *pa)
++ krb5_pa_data *pa,
++ preauth_get_entry_data_proc sam_get_entry_data,
++ void *pa_system_context,
++ void **pa_request_context,
++ krb5_data **e_data)
+ {
+ krb5_error_code retval;
+ krb5_data scratch;
+--- krb5/src/kdc/main.c
++++ krb5/src/kdc/main.c
+@@ -382,10 +382,13 @@ setup_signal_handlers(void)
+ (void) sigaction(SIGTERM, &s_action, (struct sigaction *) NULL);
+ s_action.sa_handler = request_hup;
+ (void) sigaction(SIGHUP, &s_action, (struct sigaction *) NULL);
++ s_action.sa_handler = SIG_IGN;
++ (void) sigaction(SIGPIPE, &s_action, (struct sigaction *) NULL);
+ #else /* POSIX_SIGNALS */
+ signal(SIGINT, request_exit);
+ signal(SIGTERM, request_exit);
+ signal(SIGHUP, request_hup);
++ signal(SIGPIPE, SIG_IGN);
+ #endif /* POSIX_SIGNALS */
+
+ return;
+@@ -711,6 +714,8 @@ int main(int argc, char **argv)
+
+ setup_signal_handlers();
+
++ load_preauth_plugins(kcontext);
++
+ retval = setup_sam();
+ if (retval) {
+ com_err(argv[0], retval, "while initializing SAM");
+@@ -738,6 +743,7 @@ int main(int argc, char **argv)
+ errout++;
+ }
+ krb5_klog_syslog(LOG_INFO, "shutting down");
++ unload_preauth_plugins(kcontext);
+ krb5_klog_close(kdc_context);
+ finish_realms(argv[0]);
+ if (kdc_realmlist)
+--- krb5/src/kdc/kdc_util.h
++++ krb5/src/kdc/kdc_util.h
+@@ -107,7 +107,7 @@ void
+ rep_etypes2str(char *s, size_t len, krb5_kdc_rep *rep);
+
+ /* do_as_req.c */
+-krb5_error_code process_as_req (krb5_kdc_req *,
++krb5_error_code process_as_req (krb5_kdc_req *, krb5_data *,
+ const krb5_fulladdr *,
+ krb5_data ** );
+
+@@ -146,15 +146,23 @@ void get_preauth_hint_list (krb5_kdc_req
+ krb5_db_entry *client,
+ krb5_db_entry *server,
+ krb5_data *e_data);
++krb5_error_code load_preauth_plugins(krb5_context context);
++krb5_error_code unload_preauth_plugins(krb5_context context);
++
+ krb5_error_code check_padata
+- (krb5_context context, krb5_db_entry *client,
+- krb5_kdc_req *request, krb5_enc_tkt_part *enc_tkt_reply);
++ (krb5_context context, krb5_db_entry *client, krb5_data *req_pkt,
++ krb5_kdc_req *request, krb5_enc_tkt_part *enc_tkt_reply,
++ void **padata_context, krb5_data *e_data);
+
+ krb5_error_code return_padata
+ (krb5_context context, krb5_db_entry *client,
+- krb5_kdc_req *request, krb5_kdc_rep *reply,
+- krb5_key_data *client_key, krb5_keyblock *encrypting_key);
++ krb5_data *req_pkt, krb5_kdc_req *request, krb5_kdc_rep *reply,
++ krb5_key_data *client_key, krb5_keyblock *encrypting_key,
++ void **padata_context);
+
++krb5_error_code free_padata_context
++ (krb5_context context, void **padata_context);
++
+ /* replay.c */
+ krb5_boolean kdc_check_lookaside (krb5_data *, krb5_data **);
+ void kdc_insert_lookaside (krb5_data *, krb5_data *);
+@@ -191,4 +199,8 @@ void enable_v4_crossrealm(char *);
+ ((X) == ADDRTYPE_INET ? AF_INET : -1)
+ #endif
+
++/* RFC 4120: KRB5KDC_ERR_KEY_TOO_WEAK
++ * RFC 4556: KRB5KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED */
++#define KRB5KDC_ERR_KEY_TOO_WEAK KRB5KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED
++
+ #endif /* __KRB5_KDC_UTIL__ */
+--- krb5/src/lib/krb5/os/init_os_ctx.c
++++ krb5/src/lib/krb5/os/init_os_ctx.c
+@@ -391,6 +391,8 @@ krb5_os_init_context(krb5_context ctx, k
+
+ ctx->vtbl = 0;
+ PLUGIN_DIR_INIT(&ctx->libkrb5_plugins);
++ PLUGIN_DIR_INIT(&ctx->preauth_plugins);
++ ctx->preauth_context = NULL;
+
+ retval = os_init_paths(ctx, kdc);
+ /*
+@@ -492,6 +494,11 @@ krb5_os_free_context(krb5_context ctx)
+ ctx->profile = 0;
+ }
+
++ if (ctx->preauth_context) {
++ krb5_free_preauth_context(ctx);
++ ctx->preauth_context = NULL;
++ }
++ krb5int_close_plugin_dirs (&ctx->preauth_plugins);
+ krb5int_close_plugin_dirs (&ctx->libkrb5_plugins);
+
+ #ifdef _WIN32
+--- krb5/src/lib/krb5/krb/init_ctx.c
++++ krb5/src/lib/krb5/krb/init_ctx.c
+@@ -97,7 +97,7 @@ krb5_init_secure_context(krb5_context *c
+ return init_common (context, TRUE, FALSE);
+ }
+
+-krb5_error_code KRB5_CALLCONV
++krb5_error_code
+ krb5int_init_context_kdc(krb5_context *context)
+ {
+ return init_common (context, FALSE, TRUE);
+@@ -272,6 +272,8 @@ krb5_free_context(krb5_context ctx)
+ ctx->ser_ctx = 0;
+ }
+
++ krb5_clear_error_message(ctx);
++
+ ctx->magic = 0;
+ free(ctx);
+ }
+@@ -534,6 +536,9 @@ krb5_copy_context(krb5_context ctx, krb5
+ nctx->prompt_types = NULL;
+ nctx->os_context->default_ccname = NULL;
+
++ memset(&nctx->preauth_plugins, 0, sizeof(nctx->preauth_plugins));
++ nctx->preauth_context = NULL;
++
+ memset(&nctx->libkrb5_plugins, 0, sizeof(nctx->libkrb5_plugins));
+ nctx->vtbl = NULL;
+ nctx->locate_fptrs = NULL;
+--- krb5/src/lib/krb5/krb/preauth2.c 2005-01-17 12:32:26.000000000 -0500
++++ krb5/src/lib/krb5/krb/preauth2.c
+@@ -30,6 +30,18 @@
+ */
+
+ #include "k5-int.h"
++#include "osconf.h"
++#include <krb5/preauth_plugin.h>
++
++#if !defined(_WIN32)
++#include <unistd.h>
++#endif
++
++#if TARGET_OS_MAC
++static const char *objdirs[] = { KRB5_PLUGIN_BUNDLE_DIR, LIBDIR "/krb5/plugins/preauth", NULL }; /* should be a list */
++#else
++static const char *objdirs[] = { LIBDIR "/krb5/plugins/preauth", NULL };
++#endif
+
+ typedef krb5_error_code (*pa_function)(krb5_context,
+ krb5_kdc_req *request,
+@@ -49,8 +61,458 @@ typedef struct _pa_types_t {
+ int flags;
+ } pa_types_t;
+
+-#define PA_REAL 0x0001
+-#define PA_INFO 0x0002
++/* Create the per-krb5_context context. This means loading the modules
++ * if we haven't done that yet (applications which never obtain initial
++ * credentials should never hit this routine), breaking up the module's
++ * list of support pa_types so that we can iterate over the modules more
++ * easily, and copying over the relevant parts of the module's table. */
++void KRB5_CALLCONV
++krb5_init_preauth_context(krb5_context kcontext)
++{
++ int n_modules, n_tables, i, j, k;
++ void **tables;
++ struct krb5plugin_preauth_client_ftable_v0 *table;
++ krb5_preauth_context *context = NULL;
++ void *plugin_context;
++ krb5_preauthtype pa_type;
++ void **rcpp;
++
++ /* Only do this once for each krb5_context */
++ if (kcontext->preauth_context != NULL)
++ return;
++
++ /* load the plugins for the current context */
++ if (PLUGIN_DIR_OPEN(&kcontext->preauth_plugins) == 0) {
++ if (krb5int_open_plugin_dirs(objdirs, NULL,
++ &kcontext->preauth_plugins,
++ &kcontext->err) != 0) {
++ return;
++ }
++ }
++
++ /* pull out the module function tables for all of the modules */
++ tables = NULL;
++ if (krb5int_get_plugin_dir_data(&kcontext->preauth_plugins,
++ "preauthentication_client_0_backport_1_6",
++ &tables,
++ &kcontext->err) != 0) {
++ return;
++ }
++ if (tables == NULL) {
++ return;
++ }
++
++ /* count how many modules we ended up loading, and how many preauth
++ * types we may claim to support as a result */
++ n_modules = 0;
++ for (n_tables = 0;
++ (tables != NULL) && (tables[n_tables] != NULL);
++ n_tables++) {
++ table = tables[n_tables];
++ if ((table->pa_type_list != NULL) && (table->process != NULL)) {
++ for (j = 0; table->pa_type_list[j] > 0; j++) {
++ n_modules++;
++ }
++ }
++ }
++
++ /* allocate the space we need */
++ context = malloc(sizeof(*context));
++ if (context == NULL) {
++ krb5int_free_plugin_dir_data(tables);
++ return;
++ }
++ context->modules = malloc(sizeof(context->modules[0]) * n_modules);
++ if (context->modules == NULL) {
++ krb5int_free_plugin_dir_data(tables);
++ free(context);
++ return;
++ }
++ memset(context->modules, 0, sizeof(context->modules[0]) * n_modules);
++ context->n_modules = n_modules;
++
++ /* fill in the structure */
++ k = 0;
++ for (i = 0; i < n_tables; i++) {
++ table = tables[i];
++ if ((table->pa_type_list != NULL) && (table->process != NULL)) {
++ plugin_context = NULL;
++ if ((table->init != NULL) &&
++ ((*table->init)(kcontext, &plugin_context) != 0)) {
++#ifdef DEBUG
++ fprintf (stderr, "init err, skipping module \"%s\"\n",
++ table->name);
++#endif
++ continue;
++ }
++
++ rcpp = NULL;
++ for (j = 0; table->pa_type_list[j] > 0; j++) {
++ pa_type = table->pa_type_list[j];
++ context->modules[k].pa_type = pa_type;
++ context->modules[k].enctypes = table->enctype_list;
++ context->modules[k].plugin_context = plugin_context;
++ /* Only call client_fini once per plugin */
++ if (j == 0)
++ context->modules[k].client_fini = table->fini;
++ else
++ context->modules[k].client_fini = NULL;
++ context->modules[k].ftable = table;
++ context->modules[k].name = table->name;
++ context->modules[k].flags = (*table->flags)(kcontext, pa_type);
++ context->modules[k].use_count = 0;
++ context->modules[k].client_process = table->process;
++ context->modules[k].client_tryagain = table->tryagain;
++ context->modules[k].request_context = NULL;
++ /*
++ * Only call request_init and request_fini once per plugin.
++ * Only the first module within each plugin will ever
++ * have request_context filled in. Every module within
++ * the plugin will have its request_context_pp pointing
++ * to that entry's request_context. That way all the
++ * modules within the plugin share the same request_context
++ */
++ if (j == 0) {
++ context->modules[k].client_req_init = table->request_init;
++ context->modules[k].client_req_fini = table->request_fini;
++ rcpp = &context->modules[k].request_context;
++ } else {
++ context->modules[k].client_req_init = NULL;
++ context->modules[k].client_req_fini = NULL;
++ }
++ context->modules[k].request_context_pp = rcpp;
++#ifdef DEBUG
++ fprintf (stderr, "init module \"%s\", pa_type %d, flag %d\n",
++ context->modules[k].name,
++ context->modules[k].pa_type,
++ context->modules[k].flags);
++#endif
++ k++;
++ }
++ }
++ }
++ krb5int_free_plugin_dir_data(tables);
++
++ /* return the result */
++ kcontext->preauth_context = context;
++}
++
++/* Zero the use counts for the modules herein. Usually used before we
++ * start processing any data from the server, at which point every module
++ * will again be able to take a crack at whatever the server sent. */
++void KRB5_CALLCONV
++krb5_clear_preauth_context_use_counts(krb5_context context)
++{
++ int i;
++ if (context->preauth_context != NULL) {
++ for (i = 0; i < context->preauth_context->n_modules; i++) {
++ context->preauth_context->modules[i].use_count = 0;
++ }
++ }
++}
++
++/* Free the per-krb5_context preauth_context. This means clearing any
++ * plugin-specific context which may have been created, and then
++ * freeing the context itself. */
++void KRB5_CALLCONV
++krb5_free_preauth_context(krb5_context context)
++{
++ int i;
++ void *pctx;
++ if (context->preauth_context != NULL) {
++ for (i = 0; i < context->preauth_context->n_modules; i++) {
++ pctx = context->preauth_context->modules[i].plugin_context;
++ if (context->preauth_context->modules[i].client_fini != NULL) {
++ (*context->preauth_context->modules[i].client_fini)(context, pctx);
++ }
++ memset(&context->preauth_context->modules[i], 0,
++ sizeof(context->preauth_context->modules[i]));
++ }
++ if (context->preauth_context->modules != NULL) {
++ free(context->preauth_context->modules);
++ context->preauth_context->modules = NULL;
++ }
++ free(context->preauth_context);
++ context->preauth_context = NULL;
++ }
++}
++
++/* Initialize the per-AS-REQ context. This means calling the client_req_init
++ * function to give the plugin a chance to allocate a per-request context. */
++void KRB5_CALLCONV
++krb5_preauth_request_context_init(krb5_context context)
++{
++ int i;
++ void *rctx, *pctx;
++
++ /* Limit this to only one attempt per context? */
++ if (context->preauth_context == NULL)
++ krb5_init_preauth_context(context);
++ if (context->preauth_context != NULL) {
++ for (i = 0; i < context->preauth_context->n_modules; i++) {
++ pctx = context->preauth_context->modules[i].plugin_context;
++ if (context->preauth_context->modules[i].client_req_init != NULL) {
++ rctx = context->preauth_context->modules[i].request_context_pp;
++ (*context->preauth_context->modules[i].client_req_init) (context, pctx, rctx);
++ }
++ }
++ }
++}
++
++/* Free the per-AS-REQ context. This means clearing any request-specific
++ * context which the plugin may have created. */
++void KRB5_CALLCONV
++krb5_preauth_request_context_fini(krb5_context context)
++{
++ int i;
++ void *rctx, *pctx;
++ if (context->preauth_context != NULL) {
++ for (i = 0; i < context->preauth_context->n_modules; i++) {
++ pctx = context->preauth_context->modules[i].plugin_context;
++ rctx = context->preauth_context->modules[i].request_context;
++ if (rctx != NULL) {
++ if (context->preauth_context->modules[i].client_req_fini != NULL) {
++ (*context->preauth_context->modules[i].client_req_fini)(context, pctx, rctx);
++ }
++ context->preauth_context->modules[i].request_context = NULL;
++ }
++ }
++ }
++}
++
++/* Add the named encryption type to the existing list of ktypes. */
++static void
++grow_ktypes(krb5_enctype **out_ktypes, int *out_nktypes, krb5_enctype ktype)
++{
++ int i;
++ krb5_enctype *ktypes;
++ for (i = 0; i < *out_nktypes; i++) {
++ if ((*out_ktypes)[i] == ktype)
++ return;
++ }
++ ktypes = malloc((*out_nktypes + 2) * sizeof(ktype));
++ if (ktypes) {
++ for (i = 0; i < *out_nktypes; i++)
++ ktypes[i] = (*out_ktypes)[i];
++ ktypes[i++] = ktype;
++ ktypes[i] = 0;
++ free(*out_ktypes);
++ *out_ktypes = ktypes;
++ *out_nktypes = i;
++ }
++}
++
++/* Add the given pa_data item to the list of items. Factored out here to make
++ * reading the do_preauth logic easier to read. */
++static int
++grow_pa_list(krb5_pa_data ***out_pa_list, int *out_pa_list_size,
++ krb5_pa_data *addition)
++{
++ krb5_pa_data **pa_list;
++ int i;
++
++ if (out_pa_list == NULL) {
++ return EINVAL;
++ }
++
++ if (*out_pa_list == NULL) {
++ /* Allocate room for one entry and a NULL terminator. */
++ pa_list = malloc(2 * sizeof(krb5_pa_data *));
++ if (pa_list == NULL)
++ return ENOMEM;
++ pa_list[0] = addition;
++ pa_list[1] = NULL;
++ *out_pa_list = pa_list;
++ *out_pa_list_size = 1;
++ } else {
++ /* Allocate room for one more entry and a NULL terminator. */
++ pa_list = malloc((*out_pa_list_size + 2) * sizeof(krb5_pa_data *));
++ if (pa_list == NULL)
++ return ENOMEM;
++ for (i = 0; i < *out_pa_list_size; i++)
++ pa_list[i] = (*out_pa_list)[i];
++ pa_list[i++] = addition;
++ pa_list[i++] = NULL;
++ free(*out_pa_list);
++ *out_pa_list = pa_list;
++ *out_pa_list_size = i;
++ }
++ return 0;
++}
++
++/*
++ * Retrieve a specific piece of information required by the plugin and
++ * return it in a new krb5_data item. There are separate request_types
++ * to obtain the data and free it.
++ *
++ * This may require massaging data into a contrived format, but it will
++ * hopefully keep us from having to reveal library-internal functions
++ * or data to the plugin modules.
++ */
++
++static krb5_error_code
++client_data_proc(krb5_context kcontext,
++ krb5_preauth_client_rock *rock,
++ krb5_int32 request_type,
++ krb5_data **retdata)
++{
++ krb5_data *ret;
++ char *data;
++
++ if (rock->magic != CLIENT_ROCK_MAGIC)
++ return EINVAL;
++ if (retdata == NULL)
++ return EINVAL;
++
++ switch (request_type) {
++ case krb5plugin_preauth_client_get_etype:
++ {
++ krb5_enctype *eptr;
++ if (rock->as_reply == NULL)
++ return ENOENT;
++ ret = malloc(sizeof(krb5_data));
++ if (ret == NULL)
++ return ENOMEM;
++ data = malloc(sizeof(krb5_enctype));
++ if (data == NULL) {
++ free(ret);
++ return ENOMEM;
++ }
++ ret->data = data;
++ ret->length = sizeof(krb5_enctype);
++ eptr = (krb5_enctype *)data;
++ *eptr = rock->as_reply->enc_part.enctype;
++ *retdata = ret;
++ return 0;
++ }
++ break;
++ case krb5plugin_preauth_client_free_etype:
++ ret = *retdata;
++ if (ret == NULL)
++ return 0;
++ if (ret->data)
++ free(ret->data);
++ free(ret);
++ return 0;
++ break;
++ default:
++ return EINVAL;
++ }
++}
++
++/* Tweak the request body, for now adding any enctypes which the module claims
++ * to add support for to the list, but in the future perhaps doing more
++ * involved things. */
++void KRB5_CALLCONV
++krb5_preauth_prepare_request(krb5_context kcontext,
++ krb5_get_init_creds_opt *options,
++ krb5_kdc_req *request)
++{
++ int i, j;
++
++ if (kcontext->preauth_context == NULL) {
++ return;
++ }
++ /* Add the module-specific enctype list to the request, but only if
++ * it's something we can safely modify. */
++ if (!(options && (options->flags & KRB5_GET_INIT_CREDS_OPT_ETYPE_LIST))) {
++ for (i = 0; i < kcontext->preauth_context->n_modules; i++) {
++ if (kcontext->preauth_context->modules[i].enctypes == NULL)
++ continue;
++ for (j = 0; kcontext->preauth_context->modules[i].enctypes[j] != 0; j++) {
++ grow_ktypes(&request->ktype, &request->nktypes,
++ kcontext->preauth_context->modules[i].enctypes[j]);
++ }
++ }
++ }
++}
++
++/* 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. */
++static krb5_error_code
++krb5_run_preauth_plugins(krb5_context kcontext,
++ int module_required_flags,
++ krb5_kdc_req *request,
++ krb5_data *encoded_request_body,
++ krb5_data *encoded_previous_request,
++ krb5_pa_data *in_padata,
++ krb5_prompter_fct prompter,
++ void *prompter_data,
++ preauth_get_as_key_proc gak_fct,
++ krb5_data *salt,
++ krb5_data *s2kparams,
++ void *gak_data,
++ krb5_preauth_client_rock *get_data_rock,
++ krb5_keyblock *as_key,
++ krb5_pa_data ***out_pa_list,
++ int *out_pa_list_size,
++ int *module_ret,
++ int *module_flags)
++{
++ int i;
++ krb5_pa_data *out_pa_data;
++ krb5_error_code ret;
++ struct _krb5_preauth_context_module *module;
++
++ if (kcontext->preauth_context == NULL) {
++ return ENOENT;
++ }
++ /* iterate over all loaded modules */
++ for (i = 0; i < kcontext->preauth_context->n_modules; i++) {
++ module = &kcontext->preauth_context->modules[i];
++ /* skip over those which don't match the preauth type */
++ if (module->pa_type != in_padata->pa_type)
++ continue;
++ /* skip over those which don't match the flags (INFO vs REAL, mainly) */
++ if ((module->flags & module_required_flags) == 0)
++ continue;
++ /* if it's a REAL module, try to call it only once per library call */
++ if (module_required_flags & PA_REAL) {
++ if (module->use_count > 0) {
++#ifdef DEBUG
++ fprintf(stderr, "skipping already-used module \"%s\"(%d)\n",
++ module->name, module->pa_type);
++#endif
++ continue;
++ }
++ module->use_count++;
++ }
++ /* run the module's callback function */
++ out_pa_data = NULL;
++#ifdef DEBUG
++ fprintf(stderr, "using module \"%s\" (%d), flags = %d\n",
++ module->name, module->pa_type, module->flags);
++#endif
++ ret = module->client_process(kcontext,
++ module->plugin_context,
++ *module->request_context_pp,
++ client_data_proc,
++ get_data_rock,
++ request,
++ encoded_request_body,
++ encoded_previous_request,
++ in_padata,
++ prompter, prompter_data,
++ gak_fct, gak_data, salt, s2kparams,
++ as_key,
++ &out_pa_data);
++ /* Make note of the module's flags and status. */
++ *module_flags = module->flags;
++ *module_ret = ret;
++ /* Save the new preauth data item. */
++ if (out_pa_data != NULL) {
++ ret = grow_pa_list(out_pa_list, out_pa_list_size, out_pa_data);
++ if (ret != 0)
++ return ret;
++ }
++ break;
++ }
++ if (i >= kcontext->preauth_context->n_modules) {
++ return ENOENT;
++ }
++ return 0;
++}
+
+ static
+ krb5_error_code pa_salt(krb5_context context,
+@@ -101,8 +563,8 @@ krb5_error_code pa_enc_timestamp(krb5_co
+ #ifdef DEBUG
+ fprintf (stderr, "%s:%d: salt len=%d", __FILE__, __LINE__,
+ salt->length);
+- if (salt->length > 0)
+- fprintf (stderr, " '%*s'", salt->length, salt->data);
++ 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
+@@ -819,15 +1281,88 @@ static const pa_types_t pa_types[] = {
+ },
+ };
+
+-krb5_error_code
++/*
++ * If one of the modules can adjust its AS_REQ data using the contents of the
++ * err_reply, return 0. If it's the sort of correction which requires that we
++ * ask the user another question, we let the calling application deal with it.
++ */
++krb5_error_code KRB5_CALLCONV
++krb5_do_preauth_tryagain(krb5_context kcontext,
++ krb5_kdc_req *request,
++ krb5_data *encoded_request_body,
++ krb5_data *encoded_previous_request,
++ 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_preauth_client_rock *get_data_rock)
++{
++ krb5_error_code ret;
++ krb5_pa_data *out_padata;
++ krb5_preauth_context *context;
++ struct _krb5_preauth_context_module *module;
++ int i, j;
++ int out_pa_list_size = 0;
++
++ ret = KRB5KRB_ERR_GENERIC;
++ if (kcontext->preauth_context == NULL) {
++ return KRB5KRB_ERR_GENERIC;
++ }
++ context = kcontext->preauth_context;
++ if (context == NULL) {
++ return KRB5KRB_ERR_GENERIC;
++ }
++
++ for (i = 0; padata[i] != NULL && padata[i]->pa_type != 0; i++) {
++ out_padata = NULL;
++ for (j = 0; j < context->n_modules; j++) {
++ module = &context->modules[j];
++ if (module->pa_type != padata[i]->pa_type) {
++ continue;
++ }
++ if (module->client_tryagain == NULL) {
++ continue;
++ }
++ if ((*module->client_tryagain)(kcontext,
++ module->plugin_context,
++ *module->request_context_pp,
++ client_data_proc,
++ get_data_rock,
++ request,
++ encoded_request_body,
++ encoded_previous_request,
++ padata[i],
++ err_reply,
++ prompter, prompter_data,
++ gak_fct, gak_data, salt, s2kparams,
++ as_key,
++ &out_padata) == 0) {
++ if (out_padata != NULL) {
++ grow_pa_list(return_padata, &out_pa_list_size, out_padata);
++ return 0;
++ }
++ }
++ }
++ }
++ return ret;
++}
++
++krb5_error_code KRB5_CALLCONV
+ 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_gic_get_as_key_fct gak_fct, void *gak_data,
++ krb5_preauth_client_rock *get_data_rock)
+ {
+ int h, i, j, out_pa_list_size;
+ int seen_etype_info2 = 0;
+@@ -844,9 +1379,9 @@ krb5_do_preauth(krb5_context context,
+ }
+
+ #ifdef DEBUG
+- fprintf (stderr, "salt len=%d", salt->length);
+- if (salt->length > 0)
+- fprintf (stderr, " '%*s'", salt->length, salt->data);
++ fprintf (stderr, "salt len=%d", (int) salt->length);
++ if ((int) salt->length > 0)
++ fprintf (stderr, " '%.*s'", salt->length, salt->data);
+ fprintf (stderr, "; preauth data types:");
+ for (i = 0; in_padata[i]; i++) {
+ fprintf (stderr, " %d", in_padata[i]->pa_type);
+@@ -953,7 +1488,7 @@ krb5_do_preauth(krb5_context context,
+ fprintf (stderr, "etype info %d: etype %d salt len=%d",
+ j, e->etype, e->length);
+ if (e->length > 0 && e->length != KRB5_ETYPE_NO_SALT)
+- fprintf (stderr, " '%*s'", e->length, e->salt);
++ fprintf (stderr, " '%.*s'", e->length, e->salt);
+ fprintf (stderr, "\n");
+ }
+ #endif
+@@ -967,9 +1502,14 @@ krb5_do_preauth(krb5_context context,
+ default:
+ ;
+ }
+- for (j=0; pa_types[j].type >= 0; j++) {
++ /* Try the internally-provided preauth type list. */
++ if (!realdone) for (j=0; pa_types[j].type >= 0; j++) {
+ if ((in_padata[i]->pa_type == pa_types[j].type) &&
+ (pa_types[j].flags & paorder[h])) {
++#ifdef DEBUG
++ fprintf (stderr, "calling internal function for pa_type "
++ "%d, flag %d\n", pa_types[j].type, paorder[h]);
++#endif
+ out_pa = NULL;
+
+ if ((ret = ((*pa_types[j].fct)(context, request,
+@@ -980,41 +1520,54 @@ krb5_do_preauth(krb5_context context,
+ goto cleanup;
+ }
+
+- if (out_pa) {
+- if (out_pa_list == NULL) {
+- if ((out_pa_list =
+- (krb5_pa_data **)
+- malloc(2*sizeof(krb5_pa_data *)))
+- == NULL) {
+- ret = ENOMEM;
+- goto cleanup;
+- }
+- } else {
+- if ((out_pa_list =
+- (krb5_pa_data **)
+- realloc(out_pa_list,
+- (out_pa_list_size+2)*
+- sizeof(krb5_pa_data *)))
+- == NULL) {
+- /* XXX this will leak the pointers which
+- have already been allocated. oh well. */
+- ret = ENOMEM;
+- goto cleanup;
+- }
+- }
+-
+- out_pa_list[out_pa_list_size++] = out_pa;
++ ret = grow_pa_list(&out_pa_list, &out_pa_list_size,
++ out_pa);
++ if (ret != 0) {
++ goto cleanup;
+ }
+ if (paorder[h] == PA_REAL)
+ realdone = 1;
+ }
+ }
++
++ /* Try to use plugins now. */
++ if (!realdone) {
++ krb5_init_preauth_context(context);
++ if (context->preauth_context != NULL) {
++ int module_ret, module_flags;
++#ifdef DEBUG
++ fprintf (stderr, "trying modules for pa_type %d, flag %d\n",
++ in_padata[i]->pa_type, paorder[h]);
++#endif
++ ret = krb5_run_preauth_plugins(context,
++ paorder[h],
++ request,
++ encoded_request_body,
++ encoded_previous_request,
++ in_padata[i],
++ prompter,
++ prompter_data,
++ gak_fct,
++ salt, s2kparams,
++ gak_data,
++ get_data_rock,
++ as_key,
++ &out_pa_list,
++ &out_pa_list_size,
++ &module_ret,
++ &module_flags);
++ if (ret == 0) {
++ if (module_ret == 0) {
++ if (paorder[h] == PA_REAL) {
++ realdone = 1;
++ }
++ }
++ }
++ }
++ }
+ }
+ }
+
+- if (out_pa_list)
+- out_pa_list[out_pa_list_size++] = NULL;
+-
+ *out_padata = out_pa_list;
+ if (etype_info)
+ krb5_free_etype_info(context, etype_info);
+--- krb5/src/lib/krb5/krb/Makefile.in
++++ krb5/src/lib/krb5/krb/Makefile.in
+@@ -6,7 +6,7 @@ RUN_SETUP = @KRB5_RUN_ENV@
+ PROG_LIBPATH=-L$(TOPLIBD)
+ PROG_RPATH=$(KRB5_LIBDIR)
+ LOCALINCLUDES = -I$(srcdir)/../os -I$(SRCTOP)
+-DEFS=
++DEFS=-DLIBDIR=\"$(KRB5_LIBDIR)\"
+
+ ##DOS##BUILDTOP = ..\..\..
+ ##DOS##PREFIXDIR=krb
+--- krb5/src/lib/krb5/krb/get_in_tkt.c 2005-08-15 20:38:17.000000000 -0400
++++ krb5/src/lib/krb5/krb/get_in_tkt.c
+@@ -78,6 +78,9 @@ typedef krb5_error_code (*git_decrypt_pr
+ static krb5_error_code make_preauth_list (krb5_context,
+ krb5_preauthtype *,
+ int, krb5_pa_data ***);
++static krb5_error_code sort_krb5_padata_sequence(krb5_context context,
++ krb5_data *realm,
++ krb5_pa_data **padata);
+
+ /*
+ * This function performs 32 bit bounded addition so we can generate
+@@ -105,7 +108,6 @@ static krb5_int32 krb5int_addint32 (krb5
+ static krb5_error_code
+ send_as_request(krb5_context context,
+ krb5_kdc_req *request,
+- krb5_timestamp *time_now,
+ krb5_error ** ret_err_reply,
+ krb5_kdc_rep ** ret_as_reply,
+ int *use_master)
+@@ -116,17 +118,16 @@ send_as_request(krb5_context context,
+ krb5_data reply;
+ char k4_version; /* same type as *(krb5_data::data) */
+ int tcp_only = 0;
++ krb5_timestamp time_now;
+
+ reply.data = 0;
+-
+- if ((retval = krb5_timeofday(context, time_now)))
+- goto cleanup;
+
+- /*
+- * XXX we know they are the same size... and we should do
+- * something better than just the current time
+- */
+- request->nonce = (krb5_int32) *time_now;
++ /* set the nonce if the caller expects us to do it */
++ if (request->nonce == 0) {
++ if ((retval = krb5_timeofday(context, &time_now)))
++ goto cleanup;
++ request->nonce = (krb5_int32) time_now;
++ }
+
+ /* encode & send to KDC */
+ if ((retval = encode_krb5_as_req(request, &packet)) != 0)
+@@ -437,7 +438,6 @@ static const krb5_enctype get_in_tkt_enc
+ 0
+ };
+
+-
+ krb5_error_code KRB5_CALLCONV
+ krb5_get_in_tkt(krb5_context context,
+ const krb5_flags options,
+@@ -486,6 +486,7 @@ krb5_get_in_tkt(krb5_context context,
+ request.kdc_options = options;
+ request.client = creds->client;
+ request.server = creds->server;
++ request.nonce = 0;
+ request.from = creds->times.starttime;
+ request.till = creds->times.endtime;
+ request.rtime = creds->times.renew_till;
+@@ -553,7 +554,17 @@ krb5_get_in_tkt(krb5_context context,
+
+ err_reply = 0;
+ as_reply = 0;
+- if ((retval = send_as_request(context, &request, &time_now, &err_reply,
++
++ if ((retval = krb5_timeofday(context, &time_now)))
++ goto cleanup;
++
++ /*
++ * XXX we know they are the same size... and we should do
++ * something better than just the current time
++ */
++ request.nonce = (krb5_int32) time_now;
++
++ if ((retval = send_as_request(context, &request, &err_reply,
+ &as_reply, &use_master)))
+ goto cleanup;
+
+@@ -565,6 +576,11 @@ krb5_get_in_tkt(krb5_context context,
+ krb5_free_error(context, err_reply);
+ if (retval)
+ goto cleanup;
++ retval = sort_krb5_padata_sequence(context,
++ &request.server->realm,
++ padata);
++ if (retval)
++ goto cleanup;
+ continue;
+ } else {
+ retval = (krb5_error_code) err_reply->error
+@@ -746,6 +762,79 @@ krb5_libdefault_boolean(krb5_context con
+ return(0);
+ }
+
++/* Sort a pa_data sequence so that types named in the "preferred_preauth_types"
++ * libdefaults entry are listed before any others. */
++static krb5_error_code
++sort_krb5_padata_sequence(krb5_context context, krb5_data *realm,
++ krb5_pa_data **padata)
++{
++ int i, j, base;
++ krb5_error_code ret;
++ const char *p;
++ long l;
++ char *q, *preauth_types = NULL;
++ krb5_pa_data *tmp;
++ int need_free_string = 1;
++
++ if ((padata == NULL) || (padata[0] == NULL)) {
++ return 0;
++ }
++
++ ret = krb5_libdefault_string(context, realm, "preferred_preauth_types",
++ &preauth_types);
++ if ((ret != 0) || (preauth_types == NULL)) {
++ /* Try to use PKINIT first. */
++ preauth_types = "17, 16, 15, 14";
++ need_free_string = 0;
++ }
++
++#ifdef DEBUG
++ fprintf (stderr, "preauth data types before sorting:");
++ for (i = 0; padata[i]; i++) {
++ fprintf (stderr, " %d", padata[i]->pa_type);
++ }
++ fprintf (stderr, "\n");
++#endif
++
++ base = 0;
++ for (p = preauth_types; *p != '\0';) {
++ /* skip whitespace to find an entry */
++ p += strspn(p, ", ");
++ if (*p != '\0') {
++ /* see if we can extract a number */
++ l = strtol(p, &q, 10);
++ if ((q != NULL) && (q > p)) {
++ /* got a valid number; search for a matchin entry */
++ for (i = base; padata[i] != NULL; i++) {
++ /* bubble the matching entry to the front of the list */
++ if (padata[i]->pa_type == l) {
++ tmp = padata[i];
++ for (j = i; j > base; j--)
++ padata[j] = padata[j - 1];
++ padata[base] = tmp;
++ base++;
++ break;
++ }
++ }
++ p = q;
++ } else {
++ break;
++ }
++ }
++ }
++ if (need_free_string)
++ free(preauth_types);
++
++#ifdef DEBUG
++ fprintf (stderr, "preauth data types after sorting:");
++ for (i = 0; padata[i]; i++)
++ fprintf (stderr, " %d", padata[i]->pa_type);
++ fprintf (stderr, "\n");
++#endif
++
++ return 0;
++}
++
+ krb5_error_code KRB5_CALLCONV
+ krb5_get_init_creds(krb5_context context,
+ krb5_creds *creds,
+@@ -762,7 +851,8 @@ krb5_get_init_creds(krb5_context context
+ {
+ krb5_error_code ret;
+ krb5_kdc_req request;
+- krb5_pa_data **padata;
++ krb5_data *encoded_request_body, *encoded_previous_request;
++ krb5_pa_data **preauth_to_use, **kdc_padata;
+ int tempint;
+ char *tempstr;
+ krb5_deltat tkt_life;
+@@ -775,6 +865,7 @@ krb5_get_init_creds(krb5_context context
+ krb5_kdc_rep *local_as_reply;
+ krb5_timestamp time_now;
+ krb5_enctype etype = 0;
++ krb5_preauth_client_rock get_data_rock;
+
+ /* initialize everything which will be freed at cleanup */
+
+@@ -784,19 +875,27 @@ krb5_get_init_creds(krb5_context context
+ request.ktype = NULL;
+ request.addresses = NULL;
+ request.padata = NULL;
+- padata = NULL;
++ encoded_request_body = NULL;
++ encoded_previous_request = NULL;
++ preauth_to_use = NULL;
++ kdc_padata = NULL;
+ as_key.length = 0;
+ salt.length = 0;
+ salt.data = NULL;
+
+ local_as_reply = 0;
+
++ err_reply = NULL;
++
+ /*
+ * Set up the basic request structure
+ */
+ request.magic = KV5M_KDC_REQ;
+ request.msg_type = KRB5_AS_REQ;
+
++ /* request.nonce is filled in when we send a request to the kdc */
++ request.nonce = 0;
++
+ /* request.padata is filled in later */
+
+ request.kdc_options = context->kdc_default_options;
+@@ -921,7 +1020,9 @@ krb5_get_init_creds(krb5_context context
+ goto cleanup;
+ }
+
+- /* nonce is filled in by send_as_request */
++ krb5_preauth_request_context_init(context);
++
++ /* nonce is filled in by send_as_request if we don't take care of it */
+
+ if (options && (options->flags & KRB5_GET_INIT_CREDS_OPT_ETYPE_LIST)) {
+ request.ktype = options->etype_list;
+@@ -960,8 +1061,8 @@ krb5_get_init_creds(krb5_context context
+
+ if (options && (options->flags & KRB5_GET_INIT_CREDS_OPT_PREAUTH_LIST)) {
+ if ((ret = make_preauth_list(context, options->preauth_list,
+- options->preauth_list_length,
+- &padata)))
++ options->preauth_list_length,
++ &preauth_to_use)))
+ goto cleanup;
+ }
+
+@@ -975,44 +1076,118 @@ krb5_get_init_creds(krb5_context context
+ salt.data = NULL;
+ }
+
+- /* now, loop processing preauth data and talking to the kdc */
+
++ /* set the request nonce */
++ if ((ret = krb5_timeofday(context, &time_now)))
++ goto cleanup;
++ /*
++ * XXX we know they are the same size... and we should do
++ * something better than just the current time
++ */
++ request.nonce = (krb5_int32) time_now;
++
++ /* give the preauth plugins a chance to prep the request body */
++ krb5_preauth_prepare_request(context, options, &request);
++ ret = encode_krb5_kdc_req_body(&request, &encoded_request_body);
++ if (ret)
++ goto cleanup;
++
++ get_data_rock.magic = CLIENT_ROCK_MAGIC;
++ get_data_rock.as_reply = NULL;
++
++ /* now, loop processing preauth data and talking to the kdc */
+ for (loopcount = 0; loopcount < MAX_IN_TKT_LOOPS; loopcount++) {
+ if (request.padata) {
+ krb5_free_pa_data(context, request.padata);
+ request.padata = NULL;
+ }
++ if (!err_reply) {
++ /* either our first attempt, or retrying after PREAUTH_NEEDED */
++ if ((ret = krb5_do_preauth(context,
++ &request,
++ encoded_request_body,
++ encoded_previous_request,
++ preauth_to_use, &request.padata,
++ &salt, &s2kparams, &etype, &as_key,
++ prompter, prompter_data,
++ gak_fct, gak_data,
++ &get_data_rock)))
++ goto cleanup;
++ } else {
++ if (preauth_to_use != NULL) {
++ /*
++ * Retry after an error other than PREAUTH_NEEDED,
++ * using e-data to figure out what to change.
++ */
++ ret = krb5_do_preauth_tryagain(context,
++ &request,
++ encoded_request_body,
++ encoded_previous_request,
++ preauth_to_use, &request.padata,
++ err_reply,
++ &salt, &s2kparams, &etype,
++ &as_key,
++ prompter, prompter_data,
++ gak_fct, gak_data,
++ &get_data_rock);
++ } else {
++ /* No preauth supplied, so can't query the plug-ins. */
++ ret = KRB5KRB_ERR_GENERIC;
++ }
++ if (ret) {
++ /* couldn't come up with anything better */
++ ret = err_reply->error + ERROR_TABLE_BASE_krb5;
++ }
++ krb5_free_error(context, err_reply);
++ err_reply = NULL;
++ if (ret)
++ goto cleanup;
++ }
+
+- if ((ret = krb5_do_preauth(context, &request,
+- padata, &request.padata,
+- &salt, &s2kparams, &etype, &as_key, prompter,
+- prompter_data, gak_fct, gak_data)))
++ if (encoded_previous_request != NULL) {
++ krb5_free_data(context, encoded_previous_request);
++ encoded_previous_request = NULL;
++ }
++ ret = encode_krb5_as_req(&request, &encoded_previous_request);
++ if (ret)
+ goto cleanup;
+
+- if (padata) {
+- krb5_free_pa_data(context, padata);
+- padata = 0;
+- }
+-
+ err_reply = 0;
+ local_as_reply = 0;
+- if ((ret = send_as_request(context, &request, &time_now, &err_reply,
++ if ((ret = send_as_request(context, &request, &err_reply,
+ &local_as_reply, use_master)))
+ goto cleanup;
+
+ if (err_reply) {
+ if (err_reply->error == KDC_ERR_PREAUTH_REQUIRED &&
+ err_reply->e_data.length > 0) {
++ /* reset the list of preauth types to try */
++ if (preauth_to_use) {
++ krb5_free_pa_data(context, preauth_to_use);
++ preauth_to_use = NULL;
++ }
+ ret = decode_krb5_padata_sequence(&err_reply->e_data,
+- &padata);
++ &preauth_to_use);
+ krb5_free_error(context, err_reply);
++ err_reply = NULL;
++ if (ret)
++ goto cleanup;
++ ret = sort_krb5_padata_sequence(context,
++ &request.server->realm,
++ preauth_to_use);
+ if (ret)
+ goto cleanup;
++ /* continue to next iteration */
+ } else {
+- ret = (krb5_error_code) err_reply->error
+- + ERROR_TABLE_BASE_krb5;
+- krb5_free_error(context, err_reply);
+- goto cleanup;
++ if (err_reply->e_data.length > 0) {
++ /* continue to next iteration */
++ } else {
++ /* error + no hints = give up */
++ ret = (krb5_error_code) err_reply->error
++ + ERROR_TABLE_BASE_krb5;
++ krb5_free_error(context, err_reply);
++ goto cleanup;
++ }
+ }
+ } else if (local_as_reply) {
+ break;
+@@ -1028,16 +1203,20 @@ krb5_get_init_creds(krb5_context context
+ }
+
+ /* process any preauth data in the as_reply */
+-
+- if ((ret = krb5_do_preauth(context, &request,
+- local_as_reply->padata, &padata,
++ krb5_clear_preauth_context_use_counts(context);
++ if ((ret = sort_krb5_padata_sequence(context, &request.server->realm,
++ local_as_reply->padata)))
++ goto cleanup;
++ get_data_rock.as_reply = local_as_reply;
++ if ((ret = krb5_do_preauth(context,
++ &request,
++ encoded_request_body, encoded_previous_request,
++ local_as_reply->padata, &kdc_padata,
+ &salt, &s2kparams, &etype, &as_key, prompter,
+- prompter_data, gak_fct, gak_data)))
++ prompter_data, gak_fct, gak_data,
++ &get_data_rock)))
+ goto cleanup;
+
+- /* XXX if there's padata on output, something is wrong, but it's
+- not obviously an error */
+-
+ /* XXX For 1.1.1 and prior KDC's, when SAM is used w/ USE_SAD_AS_KEY,
+ the AS_REP comes back encrypted in the user's longterm key
+ instead of in the SAD. If there was a SAM preauth, there
+@@ -1090,6 +1269,15 @@ krb5_get_init_creds(krb5_context context
+ ret = 0;
+
+ cleanup:
++ krb5_preauth_request_context_fini(context);
++ if (encoded_previous_request != NULL) {
++ krb5_free_data(context, encoded_previous_request);
++ encoded_previous_request = NULL;
++ }
++ if (encoded_request_body != NULL) {
++ krb5_free_data(context, encoded_request_body);
++ encoded_request_body = NULL;
++ }
+ if (request.server)
+ krb5_free_principal(context, request.server);
+ if (request.ktype &&
+@@ -1099,8 +1287,10 @@ cleanup:
+ (!(options &&
+ (options->flags & KRB5_GET_INIT_CREDS_OPT_ADDRESS_LIST))))
+ krb5_free_addresses(context, request.addresses);
+- if (padata)
+- krb5_free_pa_data(context, padata);
++ if (preauth_to_use)
++ krb5_free_pa_data(context, preauth_to_use);
++ if (kdc_padata)
++ krb5_free_pa_data(context, kdc_padata);
+ if (request.padata)
+ krb5_free_pa_data(context, request.padata);
+ if (as_key.length)
+--- krb5/src/lib/krb5/error_tables/krb5_err.et
++++ krb5/src/lib/krb5/error_tables/krb5_err.et
+@@ -103,26 +103,26 @@ error_code KRB5PLACEHOLD_58, "KRB5 error
+ error_code KRB5PLACEHOLD_59, "KRB5 error code 59"
+ error_code KRB5KRB_ERR_GENERIC, "Generic error (see e-text)"
+ error_code KRB5KRB_ERR_FIELD_TOOLONG, "Field is too long for this implementation"
+-error_code KRB5PLACEHOLD_62, "KRB5 error code 62"
+-error_code KRB5PLACEHOLD_63, "KRB5 error code 63"
+-error_code KRB5PLACEHOLD_64, "KRB5 error code 64"
+-error_code KRB5PLACEHOLD_65, "KRB5 error code 65"
+-error_code KRB5PLACEHOLD_66, "KRB5 error code 66"
++error_code KRB5KDC_ERR_CLIENT_NOT_TRUSTED, "Client not trusted"
++error_code KRB5KDC_ERR_KDC_NOT_TRUSTED, "KDC not trusted"
++error_code KRB5KDC_ERR_INVALID_SIG, "Invalid signature"
++error_code KRB5KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED, "Key parameters not accepted"
++error_code KRB5KDC_ERR_CERTIFICATE_MISMATCH, "Certificate mismatch"
+ error_code KRB5PLACEHOLD_67, "KRB5 error code 67"
+ error_code KRB5PLACEHOLD_68, "KRB5 error code 68"
+ error_code KRB5PLACEHOLD_69, "KRB5 error code 69"
+-error_code KRB5PLACEHOLD_70, "KRB5 error code 70"
+-error_code KRB5PLACEHOLD_71, "KRB5 error code 71"
+-error_code KRB5PLACEHOLD_72, "KRB5 error code 72"
+-error_code KRB5PLACEHOLD_73, "KRB5 error code 73"
+-error_code KRB5PLACEHOLD_74, "KRB5 error code 74"
+-error_code KRB5PLACEHOLD_75, "KRB5 error code 75"
+-error_code KRB5PLACEHOLD_76, "KRB5 error code 76"
+-error_code KRB5PLACEHOLD_77, "KRB5 error code 77"
+-error_code KRB5PLACEHOLD_78, "KRB5 error code 78"
+-error_code KRB5PLACEHOLD_79, "KRB5 error code 79"
+-error_code KRB5PLACEHOLD_80, "KRB5 error code 80"
+-error_code KRB5PLACEHOLD_81, "KRB5 error code 81"
++error_code KRB5KDC_ERR_CANT_VERIFY_CERTIFICATE, "Can't verify certificate"
++error_code KRB5KDC_ERR_INVALID_CERTIFICATE, "Invalid certificate"
++error_code KRB5KDC_ERR_REVOKED_CERTIFICATE, "Revoked certificate"
++error_code KRB5KDC_ERR_REVOCATION_STATUS_UNKNOWN, "Revocation status unknown"
++error_code KRB5KDC_ERR_REVOCATION_STATUS_UNAVAILABLE, "Revocation status unavailable"
++error_code KRB5KDC_ERR_CLIENT_NAME_MISMATCH, "Client name mismatch"
++error_code KRB5KDC_ERR_KDC_NAME_MISMATCH, "KDC name mismatch"
++error_code KRB5KDC_ERR_INCONSISTENT_KEY_PURPOSE, "Inconsistent key purpose"
++error_code KRB5KDC_ERR_DIGEST_IN_CERT_NOT_ACCEPTED, "Digest in certificate not accepted"
++error_code KRB5KDC_ERR_PA_CHECKSUM_MUST_BE_INCLUDED, "Checksum must be included"
++error_code KRB5KDC_ERR_DIGEST_IN_SIGNED_DATA_NOT_ACCEPTED, "Digest in signed-data not accepted"
++error_code KRB5KDC_ERR_PUBLIC_KEY_ENCRYPTION_NOT_SUPPORTED, "Public key encryption not supported"
+ error_code KRB5PLACEHOLD_82, "KRB5 error code 82"
+ error_code KRB5PLACEHOLD_83, "KRB5 error code 83"
+ error_code KRB5PLACEHOLD_84, "KRB5 error code 84"
+--- krb5/src/include/k5-int.h
++++ krb5/src/include/k5-int.h
+@@ -835,6 +835,90 @@ error(MIT_DES_KEYSIZE does not equal KRB
+ #ifndef KRB5_PREAUTH__
+ #define KRB5_PREAUTH__
+
++#include <krb5/preauth_plugin.h>
++
++#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. */
++typedef struct _krb5_preauth_client_rock {
++ krb5_magic magic;
++ krb5_kdc_rep *as_reply;
++} krb5_preauth_client_rock;
++
++/* This structure lets us keep track of all of the modules which are loaded,
++ * turning the list of modules and their lists of implemented preauth types
++ * into a single list which we can walk easily. */
++typedef struct _krb5_preauth_context {
++ int n_modules;
++ struct _krb5_preauth_context_module {
++ /* Which of the possibly more than one preauth types which the
++ * module supports we're using at this point in the list. */
++ krb5_preauthtype pa_type;
++ /* Encryption types which the client claims to support -- we
++ * copy them directly into the krb5_kdc_req structure during
++ * krb5_preauth_prepare_request(). */
++ krb5_enctype *enctypes;
++ /* The plugin's per-plugin context and a function to clear it. */
++ void *plugin_context;
++ void (*client_fini)(krb5_context context, void *plugin_context);
++ /* The module's table, and some of its members, copied here for
++ * convenience when we populated the list. */
++ struct krb5plugin_preauth_client_ftable_v0 *ftable;
++ const char *name;
++ int flags, use_count;
++ krb5_error_code (*client_process)(krb5_context context,
++ void *plugin_context,
++ void *request_context,
++ preauth_get_client_data_proc get_data_proc,
++ krb5_preauth_client_rock *rock,
++ krb5_kdc_req *request,
++ krb5_data *encoded_request_body,
++ krb5_data *encoded_previous_request,
++ krb5_pa_data *pa_data,
++ krb5_prompter_fct prompter,
++ void *prompter_data,
++ preauth_get_as_key_proc gak_fct,
++ void *gak_data,
++ krb5_data *salt,
++ krb5_data *s2kparams,
++ krb5_keyblock *as_key,
++ krb5_pa_data **out_pa_data);
++ krb5_error_code (*client_tryagain)(krb5_context context,
++ void *plugin_context,
++ void *request_context,
++ preauth_get_client_data_proc get_data_proc,
++ krb5_preauth_client_rock *rock,
++ krb5_kdc_req *request,
++ krb5_data *encoded_request_body,
++ krb5_data *encoded_previous_request,
++ krb5_pa_data *old_pa_data,
++ krb5_error *err_reply,
++ krb5_prompter_fct prompter,
++ void *prompter_data,
++ preauth_get_as_key_proc gak_fct,
++ void *gak_data,
++ krb5_data *salt,
++ krb5_data *s2kparams,
++ krb5_keyblock *as_key,
++ krb5_pa_data **new_pa_data);
++ void (*client_req_init)(krb5_context context, void *plugin_context,
++ void **request_context);
++ void (*client_req_fini)(krb5_context context, void *plugin_context,
++ void *request_context);
++ /* The per-request context which the client_req_init() function
++ * might allocate, which we'll need to clean up later by
++ * calling the client_req_fini() function. */
++ void *request_context;
++ /* A pointer to the request_context pointer. All modules within
++ * a plugin will point at the request_context of the first
++ * module within the plugin. */
++ void **request_context_pp;
++ } *modules;
++} krb5_preauth_context;
++
+ typedef struct _krb5_pa_enc_ts {
+ krb5_timestamp patimestamp;
+ krb5_int32 pausec;
+@@ -961,14 +1045,41 @@ void krb5int_populate_gic_opt (
+ krb5_preauthtype *pre_auth_types, krb5_creds *creds);
+
+
+-krb5_error_code krb5_do_preauth
+-(krb5_context, krb5_kdc_req *,
+- krb5_pa_data **, krb5_pa_data ***,
+- krb5_data *salt, krb5_data *s2kparams,
+- krb5_enctype *,
+- krb5_keyblock *,
+- krb5_prompter_fct, void *,
+- krb5_gic_get_as_key_fct, void *);
++krb5_error_code KRB5_CALLCONV 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_preauth_client_rock *get_data_rock);
++krb5_error_code KRB5_CALLCONV krb5_do_preauth_tryagain
++ (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_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_preauth_client_rock *get_data_rock);
++void KRB5_CALLCONV krb5_init_preauth_context
++ (krb5_context);
++void KRB5_CALLCONV krb5_free_preauth_context
++ (krb5_context);
++void KRB5_CALLCONV krb5_clear_preauth_context_use_counts
++ (krb5_context);
++void KRB5_CALLCONV krb5_preauth_prepare_request
++ (krb5_context, krb5_get_init_creds_opt *, krb5_kdc_req *);
++void KRB5_CALLCONV krb5_preauth_request_context_init
++ (krb5_context);
++void KRB5_CALLCONV krb5_preauth_request_context_fini
++ (krb5_context);
+
+ void KRB5_CALLCONV krb5_free_sam_challenge
+ (krb5_context, krb5_sam_challenge * );
+@@ -1059,6 +1174,10 @@ struct _krb5_context {
+ struct krb5plugin_service_locate_ftable *vtbl;
+ void (**locate_fptrs)(void);
+
++ /* preauth module stuff */
++ struct plugin_dir_handle preauth_plugins;
++ krb5_preauth_context *preauth_context;
++
+ /* error detail info */
+ struct errinfo err;
+ };
+--- krb5/src/include/kdb.h
++++ krb5/src/include/kdb.h
+@@ -171,6 +171,7 @@ typedef struct __krb5_key_salt_tuple {
+ #define KRB5_TL_SECURID_STATE 0x0006
+ #define KRB5_TL_DB_ARGS 0x7fff
+ #endif /* SECURID */
++#define KRB5_TL_USER_CERTIFICATE 0x0007
+
+ /*
+ * Determines the number of failed KDC requests before DISALLOW_ALL_TIX is set
+--- /dev/null 2007-01-10 09:59:42.964619257 -0500
++++ krb5/src/include/krb5/preauth_plugin.h
+@@ -0,0 +1,326 @@
++/*
++ * <krb5/preauth_plugin.h>
++ *
++ * Copyright (c) 2006 Red Hat, Inc.
++ * Portions copyright (c) 2006 Massachusetts Institute of Technology
++ * All Rights Reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions are met:
++ *
++ * * Redistributions of source code must retain the above copyright
++ * notice, this list of conditions and the following disclaimer.
++ * * Redistributions in binary form must reproduce the above copyright
++ * notice, this list of conditions and the following disclaimer in
++ * the documentation and/or other materials provided with the
++ * distribution.
++ * * Neither the name of Red Hat, Inc., nor the names of its
++ * contributors may be used to endorse or promote products derived
++ * from this software without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
++ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
++ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
++ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
++ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
++ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
++ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
++ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
++ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
++ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
++ *
++ * Preauthentication plugin definitions for Kerberos 5.
++ */
++
++#ifndef KRB5_PREAUTH_PLUGIN_H_INCLUDED
++#define KRB5_PREAUTH_PLUGIN_H_INCLUDED
++#include <krb5/krb5.h>
++
++/*
++ * While arguments of these types are passed-in, for the most part a preauth
++ * module can treat them as opaque. If we need keying data, we can ask for
++ * it directly.
++ */
++struct _krb5_db_entry_new;
++struct _krb5_key_data;
++struct _krb5_preauth_client_rock;
++
++/*
++ * Preauth mechanism property flags, unified from previous definitions in the
++ * KDC and libkrb5 sources.
++ */
++
++/* Provides a real answer which we can send back to the KDC (client-only). The
++ * client assumes that one real answer will be enough. */
++#define PA_REAL 0x00000001
++
++/* Doesn't provide a real answer, but must be given a chance to run before any
++ * REAL mechanism callbacks (client-only). */
++#define PA_INFO 0x00000002
++
++/* Causes the KDC to include this mechanism in a list of supported preauth
++ * types if the user's DB entry flags the user as requiring hardware-based
++ * preauthentication (server-only). */
++#define PA_HARDWARE 0x00000004
++
++/* Causes the KDC to include this mechanism in a list of supported preauth
++ * types if the user's DB entry flags the user as requiring preauthentication,
++ * and to fail preauthentication if we can't verify the client data. The
++ * flipside of PA_SUFFICIENT (server-only). */
++#define PA_REQUIRED 0x00000008
++
++/* Causes the KDC to include this mechanism in a list of supported preauth
++ * types if the user's DB entry flags the user as requiring preauthentication,
++ * and to mark preauthentication as successful if we can verify the client
++ * data. The flipside of PA_REQUIRED (server-only). */
++#define PA_SUFFICIENT 0x00000010
++
++/* Marks this preauthentication mechanism as one which changes the key which is
++ * used for encrypting the response to the client. Modules which have this
++ * flag have their server_return_proc called before modules which do not, and
++ * are passed over if a previously-called module has modified the encrypting
++ * key (server-only). */
++#define PA_REPLACES_KEY 0x00000020
++
++/* Causes the KDC to check with this preauthentication module even if the
++ * client has no entry in the realm database. If the module returns a success
++ * code, continue processing and assume that its return_padata callback will
++ * supply us with a key for encrypting the AS reply (server-only). */
++/* #define PA_VIRTUAL (0x00000040 | PA_REPLACES_KEY) */
++
++/* Not really a padata type, so don't include it in any list of preauth types
++ * which gets sent over the wire. */
++#define PA_PSEUDO 0x00000080
++
++/*
++ * A server module's callback functions are allowed to request specific types
++ * of information about the given client or server record or request, even
++ * though the database records themselves are opaque to the module.
++ */
++enum krb5plugin_preauth_entry_request_type {
++ /* The returned krb5_data item holds a DER-encoded X.509 certificate. */
++ krb5plugin_preauth_entry_request_certificate = 1,
++ /* The returned krb5_data_item holds a krb5_deltat. */
++ krb5plugin_preauth_entry_max_time_skew = 2,
++ /* The returned krb5_data_item holds an array of krb5_keyblock structures,
++ * terminated by an entry with key type = 0.
++ * Each keyblock should have its contents freed in turn, and then the data
++ * item itself should be freed. */
++ krb5plugin_preauth_keys = 3,
++ /* The returned krb5_data_item holds the request structure, re-encoded
++ * using DER. Unless the client implementation is the same as the server
++ * implementation, there's a good chance that the result will not match
++ * what the client sent, so don't go creating any fatal errors if it
++ * doesn't match up. */
++ krb5plugin_preauth_request_body = 4
++};
++typedef krb5_error_code
++(*preauth_get_entry_data_proc)(krb5_context,
++ krb5_kdc_req *,
++ struct _krb5_db_entry_new *,
++ krb5_int32 request_type,
++ krb5_data **);
++
++/*
++ * A client module's callback functions are allowed to request various
++ * information to enable it to process a request.
++ */
++enum krb5plugin_preauth_client_request_type {
++ /* The returned krb5_data item holds the enctype used to encrypt the
++ * encrypted portion of the AS_REP packet. */
++ krb5plugin_preauth_client_get_etype = 1,
++ /* Free the data returned from krb5plugin_preauth_client_req_get_etype */
++ krb5plugin_preauth_client_free_etype = 2
++};
++typedef krb5_error_code
++(*preauth_get_client_data_proc)(krb5_context,
++ struct _krb5_preauth_client_rock *,
++ krb5_int32 request_type,
++ krb5_data **);
++
++/*
++ * A callback which will obtain the user's long-term AS key by prompting the
++ * user for the password, then salting it properly, and so on. For the moment,
++ * it's identical to the get_as_key callback used inside of libkrb5, but we
++ * define a new typedef here instead of making the existing one public to
++ * isolate ourselves from potential future changes.
++ */
++typedef krb5_error_code
++(*preauth_get_as_key_proc)(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);
++
++/*
++ * The function table / structure which a preauth client module must export as
++ * "preauthentication_client_0_backport_1_6". If the interfaces work correctly, future
++ * versions of the table will add either more callbacks or more arguments to
++ * callbacks, and in both cases we'll be able to wrap the v0 functions.
++ */
++typedef struct krb5plugin_preauth_client_ftable_v0 {
++ /* Not-usually-visible name. */
++ char *name;
++
++ /* Pointer to zero-terminated list of pa_types which this module can
++ * provide services for. */
++ krb5_preauthtype *pa_type_list;
++
++ /* Pointer to zero-terminated list of enc_types which this module claims
++ * to add support for. */
++ krb5_enctype *enctype_list;
++
++ /* Per-plugin initialization/cleanup. The init function is called
++ * by libkrb5 when the plugin is loaded, and the fini function is
++ * called before the plugin is unloaded. Both are optional and
++ * may be called multiple times in case the plugin is used in
++ * multiple contexts. The returned context lives the lifetime of
++ * the krb5_context */
++ krb5_error_code (*init)(krb5_context context, void **plugin_context);
++ void (*fini)(krb5_context context, void *plugin_context);
++ /* A callback which returns flags indicating if the module is a "real" or
++ * an "info" mechanism, and so on. This function is called for each entry
++ * in the client_pa_type_list. */
++ int (*flags)(krb5_context context, krb5_preauthtype pa_type);
++ /* Per-request initialization/cleanup. The request_init function is
++ * called when beginning to process a get_init_creds request and the
++ * request_fini function is called when processing of the request is
++ * complete. This is optional. It may be called multiple times in
++ * the lifetime of a krb5_context. */
++ void (*request_init)(krb5_context context, void *plugin_context,
++ void **request_context);
++ void (*request_fini)(krb5_context context, void *plugin_context,
++ void *request_context);
++ /* Client function which processes server-supplied data in pa_data,
++ * returns created data in out_pa_data, storing any of its own state in
++ * client_context if data for the associated preauthentication type is
++ * needed. It is also called after the AS-REP is received if the AS-REP
++ * includes preauthentication data of the associated type.
++ * NOTE! the encoded_previous_request will be NULL the first time this
++ * function is called, because it is expected to only ever contain the data
++ * obtained from a previous call to this function. */
++ krb5_error_code (*process)(krb5_context context,
++ void *plugin_context,
++ void *request_context,
++ preauth_get_client_data_proc get_data_proc,
++ struct _krb5_preauth_client_rock *rock,
++ krb5_kdc_req *request,
++ krb5_data *encoded_request_body,
++ krb5_data *encoded_previous_request,
++ krb5_pa_data *pa_data,
++ krb5_prompter_fct prompter,
++ void *prompter_data,
++ preauth_get_as_key_proc gak_fct,
++ void *gak_data,
++ krb5_data *salt, krb5_data *s2kparams,
++ krb5_keyblock *as_key,
++ krb5_pa_data **out_pa_data);
++ /* Client function which can attempt to use e-data in the error response to
++ * try to recover from the given error. If this function is not NULL, and
++ * it stores data in out_pa_data which is different data from the contents
++ * of in_pa_data, then the client library will retransmit the request. */
++ krb5_error_code (*tryagain)(krb5_context context,
++ void *plugin_context,
++ void *request_context,
++ preauth_get_client_data_proc get_data_proc,
++ struct _krb5_preauth_client_rock *rock,
++ krb5_kdc_req *request,
++ krb5_data *encoded_request_body,
++ krb5_data *encoded_previous_request,
++ krb5_pa_data *in_pa_data,
++ krb5_error *error,
++ krb5_prompter_fct prompter,
++ void *prompter_data,
++ preauth_get_as_key_proc gak_fct,
++ void *gak_data,
++ krb5_data *salt, krb5_data *s2kparams,
++ krb5_keyblock *as_key,
++ krb5_pa_data **out_pa_data);
++} krb5plugin_preauth_client_ftable_v0;
++
++/*
++ * The function table / structure which a preauth server module must export as
++ * "preauthentication_server_0_backport_1_6". NOTE: replace "0" with "1" for the type and
++ * variable names if this gets picked up by upstream. If the interfaces work
++ * correctly, future versions of the table will add either more callbacks or
++ * more arguments to callbacks, and in both cases we'll be able to wrap the v0
++ * functions.
++ */
++typedef struct krb5plugin_preauth_server_ftable_v0 {
++ /* Not-usually-visible name. */
++ char *name;
++
++ /* Pointer to zero-terminated list of pa_types which this module can
++ * provide services for. */
++ krb5_preauthtype *pa_type_list;
++
++ /* Per-plugin initialization/cleanup. The init function is called by the
++ * KDC when the plugin is loaded, and the fini function is called before
++ * the plugin is unloaded. Both are optional. */
++ krb5_error_code (*init_proc)(krb5_context, void **);
++ void (*fini_proc)(krb5_context, void *);
++ /* Return the flags which the KDC should use for this module. This is a
++ * callback instead of a static value because the module may or may not
++ * wish to count itself as a hardware preauthentication module (in other
++ * words, the flags may be affected by the configuration, for example if a
++ * site administrator can force a particular preauthentication type to be
++ * supported using only hardware). This function is called for each entry
++ * entry in the server_pa_type_list. */
++ int (*flags_proc)(krb5_context, krb5_preauthtype);
++ /* Get preauthentication data to send to the client as part of the "you
++ * need to use preauthentication" error. The module doesn't need to
++ * actually provide data if the protocol doesn't require it, but it should
++ * return either zero or non-zero to control whether its padata type is
++ * included in the list which is sent back to the client. Is not allowed
++ * to create a context because we have no guarantee that the client will
++ * ever call again (or that it will hit this server if it does), in which
++ * case a context might otherwise hang around forever. */
++ krb5_error_code (*edata_proc)(krb5_context, krb5_kdc_req *request,
++ struct _krb5_db_entry_new *client,
++ struct _krb5_db_entry_new *server,
++ preauth_get_entry_data_proc,
++ void *pa_module_context,
++ krb5_pa_data *data);
++ /* Verify preauthentication data sent by the client, setting the
++ * TKT_FLG_PRE_AUTH or TKT_FLG_HW_AUTH flag in the enc_tkt_reply's "flags"
++ * field as appropriate, and returning nonzero on failure. Can create
++ * context data for consumption by the return_proc or freepa_proc below. */
++ krb5_error_code (*verify_proc)(krb5_context,
++ struct _krb5_db_entry_new *client,
++ krb5_data *req_pkt,
++ krb5_kdc_req *request,
++ krb5_enc_tkt_part *enc_tkt_reply,
++ krb5_pa_data *data,
++ preauth_get_entry_data_proc,
++ void *pa_module_context,
++ void **pa_request_context,
++ krb5_data **e_data);
++ /* Generate preauthentication response data to send to the client as part
++ * of the AS-REP. If it needs to override the key which is used to encrypt
++ * the response, it can do so. The module is expected (but not required,
++ * if a freepa_proc is also provided) to free any context data it saved in
++ * "request_pa_context". */
++ krb5_error_code (*return_proc)(krb5_context, krb5_pa_data * padata,
++ struct _krb5_db_entry_new *client,
++ krb5_data *req_pkt,
++ krb5_kdc_req *request,
++ krb5_kdc_rep *reply,
++ struct _krb5_key_data *client_keys,
++ krb5_keyblock *encrypting_key,
++ krb5_pa_data **send_pa,
++ preauth_get_entry_data_proc,
++ void *pa_module_context,
++ void **pa_request_context);
++ /* Free up the server-side per-request context, in cases where
++ * server_return_proc() didn't or for whatever reason was not called. Can
++ * be NULL. */
++ krb5_error_code (*freepa_reqcontext_proc)(krb5_context,
++ void *pa_module_context,
++ void **request_pa_context);
++} krb5plugin_preauth_server_ftable_v0;
++#endif /* KRB5_PREAUTH_PLUGIN_H_INCLUDED */
+--- krb5/src/config/pre.in
++++ krb5/src/config/pre.in
+@@ -194,6 +194,7 @@ prefix=@prefix@
+ INSTALL_PREFIX=$(prefix)
+ INSTALL_EXEC_PREFIX=@exec_prefix@
+ exec_prefix=@exec_prefix@
++datarootdir=@datarootdir@
+ SHLIB_TAIL_COMP=@SHLIB_TAIL_COMP@
+
+ datadir = @datadir@
+@@ -212,6 +213,7 @@ KRB5_SHLIBDIR = @libdir@$(SHLIB_TAIL_COM
+ KRB5_INCDIR = @includedir@
+ MODULE_DIR = @libdir@/krb5/plugins
+ KRB5_DB_MODULE_DIR = $(MODULE_DIR)/kdb
++KRB5_PA_MODULE_DIR = $(MODULE_DIR)/preauth
+ KRB5_LIBKRB5_MODULE_DIR = $(MODULE_DIR)/libkrb5
+ KRB5_INCSUBDIRS = \
+ $(KRB5_INCDIR)/krb5 \
+--- /dev/null 2007-01-10 09:59:42.964619257 -0500
++++ krb5/src/plugins/preauth/wpse/configure.in
+@@ -0,0 +1,14 @@
++K5_AC_INIT(configure.in)
++enable_shared=yes
++build_dynobj=yes
++CONFIG_RULES
++
++AC_CHECK_HEADERS(errno.h string.h)
++
++KRB5_RUN_FLAGS
++dnl The following is for check...
++KRB5_BUILD_PROGRAM
++KRB5_BUILD_LIBOBJS
++KRB5_BUILD_LIBRARY_WITH_DEPS
++AC_CONFIG_HEADERS(config.h)
++V5_AC_OUTPUT_MAKEFILE
+--- /dev/null 2007-01-10 09:59:42.964619257 -0500
++++ krb5/src/plugins/preauth/wpse/Makefile.in
+@@ -0,0 +1,42 @@
++thisconfigdir=.
++myfulldir=plugins/preauth/wpse
++mydir=.
++BUILDTOP=$(REL)..$(S)..$(S)..
++KRB5_RUN_ENV = @KRB5_RUN_ENV@
++KRB5_CONFIG_SETUP = KRB5_CONFIG=$(SRCTOP)/config-files/krb5.conf ; export KRB5_CONFIG ;
++PROG_LIBPATH=-L$(TOPLIBD)
++PROG_RPATH=$(KRB5_LIBDIR)
++MODULE_INSTALL_DIR = $(KRB5_PA_MODULE_DIR)
++DEFS=@DEFS@
++
++LOCALINCLUDES = -I../../../include/krb5 -I.
++
++LIBBASE=wpse
++LIBMAJOR=0
++LIBMINOR=0
++SO_EXT=.so
++RELDIR=../plugins/preauth/wpse
++# Depends on libk5crypto and libkrb5
++SHLIB_EXPDEPS = \
++ $(TOPLIBD)/libk5crypto$(SHLIBEXT) \
++ $(TOPLIBD)/libkrb5$(SHLIBEXT)
++SHLIB_EXPLIBS= -lkrb5 -lcom_err -lk5crypto $(SUPPORT_LIB) $(LIBS)
++
++SHLIB_DIRS=-L$(TOPLIBD)
++SHLIB_RDIRS=$(KRB5_LIBDIR)
++STOBJLISTS=OBJS.ST
++STLIBOBJS=wpse_main.o
++
++SRCS=wpse_main.c
++
++all-unix:: $(LIBBASE)$(SO_EXT)
++install-unix:: install-libs
++clean-unix:: clean-libs clean-libobjs
++
++clean::
++ $(RM) lib$(LIBBASE)$(SO_EXT)
++
++@libnover_frag@
++@libobj_frag@
++
++# +++ Dependency line eater +++
+--- /dev/null 2007-01-10 09:59:42.964619257 -0500
++++ krb5/src/plugins/preauth/wpse/wpse_main.c
+@@ -0,0 +1,393 @@
++/*
++ * Copyright (C) 2006 Red Hat, Inc.
++ * All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions are met:
++ *
++ * * Redistributions of source code must retain the above copyright
++ * notice, this list of conditions and the following disclaimer.
++ * * Redistributions in binary form must reproduce the above copyright
++ * notice, this list of conditions and the following disclaimer in
++ * the documentation and/or other materials provided with the
++ * distribution.
++ * * Neither the name of Red Hat, Inc., nor the names of its
++ * contributors may be used to endorse or promote products derived
++ * from this software without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
++ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
++ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
++ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
++ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
++ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
++ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
++ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
++ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
++ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
++ */
++
++/* Worst. Preauthentication. Scheme. Ever. */
++
++#ident "$Id$"
++
++#ifdef HAVE_CONFIG_H
++#include "config.h"
++#endif
++
++#ifdef HAVE_ERRNO_H
++#include <errno.h>
++#endif
++#ifdef HAVE_STRING_H
++#include <string.h>
++#endif
++
++#include <arpa/inet.h>
++#include <stdio.h>
++
++#include <krb5/krb5.h>
++#include <krb5/preauth_plugin.h>
++
++/* This is not a standardized value. It's defined here only to make it easier
++ * to change in this module. */
++#define KRB5_PADATA_WPSE_REQ 131
++
++static int
++client_get_flags(krb5_context kcontext, krb5_preauthtype pa_type)
++{
++ return PA_REAL;
++}
++
++static krb5_error_code
++client_init(krb5_context kcontext, void **ctx)
++{
++ int *pctx;
++
++ pctx = malloc(sizeof(int));
++ if (pctx == NULL)
++ return ENOMEM;
++ *pctx = 0;
++ *ctx = pctx;
++ return 0;
++}
++
++static void
++client_fini(krb5_context kcontext, void *ctx)
++{
++ int *pctx;
++
++ pctx = ctx;
++ if (pctx) {
++#ifdef DEBUG
++ fprintf(stderr, "wpse module called total of %d times\n", *pctx);
++#endif
++ free(pctx);
++ }
++}
++
++static krb5_error_code
++client_process(krb5_context kcontext,
++ void *plugin_context,
++ void *request_context,
++ preauth_get_client_data_proc client_get_data_proc,
++ struct _krb5_preauth_client_rock *rock,
++ krb5_kdc_req *request,
++ krb5_data *encoded_request_body,
++ krb5_data *encoded_previous_request,
++ krb5_pa_data *pa_data,
++ krb5_prompter_fct prompter,
++ void *prompter_data,
++ preauth_get_as_key_proc 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_int32 nnonce, enctype;
++ krb5_keyblock *kb;
++ krb5_error_code status;
++ int *pctx;
++
++#ifdef DEBUG
++ fprintf(stderr, "%d bytes of preauthentication data (type %d)\n",
++ pa_data->length, pa_data->pa_type);
++#endif
++
++ pctx = plugin_context;
++ if (pctx) {
++ (*pctx)++;
++ }
++
++ if (pa_data->length == 0) {
++ /* Create preauth data. */
++ send_pa = malloc(sizeof(krb5_pa_data));
++ if (send_pa == NULL)
++ return ENOMEM;
++ send_pa->pa_type = KRB5_PADATA_WPSE_REQ;
++ send_pa->length = 4;
++ send_pa->contents = malloc(4);
++ if (send_pa->contents == NULL) {
++ free(send_pa);
++ return ENOMEM;
++ }
++ /* Store the preauth data. */
++ nnonce = htonl(request->nonce);
++ memcpy(send_pa->contents, &nnonce, 4);
++ *out_pa_data = send_pa;
++ } else {
++ /* A reply from the KDC. Conventionally this would be
++ * indicated by a different preauthentication type, but this
++ * mechanism/implementation doesn't do that. */
++ if (pa_data->length > 4) {
++ memcpy(&enctype, pa_data->contents, 4);
++ kb = NULL;
++ status = krb5_init_keyblock(kcontext, ntohl(enctype),
++ pa_data->length - 4, &kb);
++ if (status != 0)
++ return status;
++ memcpy(kb->contents, pa_data->contents + 4, pa_data->length - 4);
++#ifdef DEBUG
++ fprintf(stderr, "Recovered key type=%d, length=%d.\n",
++ kb->enctype, kb->length);
++#endif
++ status = krb5_copy_keyblock_contents(kcontext, kb, as_key);
++ krb5_free_keyblock(kcontext, kb);
++ return status;
++ }
++ return KRB5KRB_ERR_GENERIC;
++ }
++ return 0;
++}
++
++#define WPSE_MAGIC 0x77707365
++typedef struct _wpse_req_ctx
++{
++ int magic;
++ int value;
++} wpse_req_ctx;
++
++static void
++client_req_init(krb5_context kcontext, void *plugin_context, void **req_context_p)
++{
++ wpse_req_ctx *ctx;
++
++ *req_context_p = NULL;
++
++ /* Allocate a request context. Useful for verifying that we do in fact
++ * do per-request cleanup. */
++ ctx = (wpse_req_ctx *) malloc(sizeof(*ctx));
++ if (ctx == NULL)
++ return;
++ ctx->magic = WPSE_MAGIC;
++ ctx->value = 0xc0dec0de;
++
++ *req_context_p = ctx;
++}
++
++static void
++client_req_cleanup(krb5_context kcontext, void *plugin_context, void *req_context)
++{
++ wpse_req_ctx *ctx = (wpse_req_ctx *)req_context;
++
++ if (ctx) {
++#ifdef DEBUG
++ fprintf(stderr, "client_req_cleanup: req_ctx at %p has magic %x and value %x\n",
++ ctx, ctx->magic, ctx->value);
++#endif
++ if (ctx->magic != WPSE_MAGIC) {
++#ifdef DEBUG
++ fprintf(stderr, "client_req_cleanup: req_context at %p has bad magic value %x\n",
++ ctx, ctx->magic);
++#endif
++ return;
++ }
++ free(ctx);
++ }
++ return;
++}
++
++/* Free state. */
++static krb5_error_code
++server_free_pa_request_context(krb5_context kcontext, void *plugin_context,
++ void **request_context)
++{
++ if (*request_context != NULL) {
++ free(*request_context);
++ *request_context = NULL;
++ }
++ return 0;
++}
++
++/* Obtain and return any preauthentication data (which is destined for the
++ * client) which matches type data->pa_type. */
++static krb5_error_code
++server_get_edata(krb5_context kcontext,
++ krb5_kdc_req *request,
++ struct _krb5_db_entry_new *client,
++ struct _krb5_db_entry_new *server,
++ preauth_get_entry_data_proc server_get_entry_data,
++ void *pa_module_context,
++ krb5_pa_data *data)
++{
++ /* Return zero bytes of data. */
++ data->length = 0;
++ data->contents = NULL;
++ return 0;
++}
++
++/* Verify a request from a client. */
++static krb5_error_code
++server_verify(krb5_context kcontext,
++ struct _krb5_db_entry_new *client,
++ krb5_data *req_pkt,
++ krb5_kdc_req *request,
++ krb5_enc_tkt_part *enc_tkt_reply,
++ krb5_pa_data *data,
++ preauth_get_entry_data_proc server_get_entry_data,
++ void *pa_module_context,
++ void **pa_request_context,
++ krb5_data **e_data)
++{
++ krb5_int32 nnonce;
++ krb5_data *test_edata;
++
++ /* Verify the preauth data. */
++ if (data->length != 4)
++ return KRB5KDC_ERR_PREAUTH_FAILED;
++ memcpy(&nnonce, data->contents, 4);
++ nnonce = ntohl(nnonce);
++ if (memcmp(&nnonce, &request->nonce, 4) != 0)
++ return KRB5KDC_ERR_PREAUTH_FAILED;
++ /* Note that preauthentication succeeded. */
++ enc_tkt_reply->flags |= TKT_FLG_PRE_AUTH;
++ enc_tkt_reply->flags |= TKT_FLG_HW_AUTH;
++ /* Allocate a context. Useful for verifying that we do in fact do
++ * per-request cleanup. */
++ if (*pa_request_context == NULL)
++ *pa_request_context = malloc(4);
++
++ /* Return edata to exercise code that handles edata... */
++ test_edata = malloc(sizeof(*test_edata));
++ if (test_edata != NULL) {
++ test_edata->data = malloc(20);
++ if (test_edata->data == NULL) {
++ free(test_edata);
++ } else {
++ test_edata->length = 20;
++ memset(test_edata->data, '#', 20); /* fill it with junk */
++ *e_data = test_edata;
++ }
++ }
++ return 0;
++}
++
++/* Create the response for a client. */
++static krb5_error_code
++server_return(krb5_context kcontext,
++ krb5_pa_data *padata,
++ struct _krb5_db_entry_new *client,
++ krb5_data *req_pkt,
++ krb5_kdc_req *request,
++ krb5_kdc_rep *reply,
++ struct _krb5_key_data *client_key,
++ krb5_keyblock *encrypting_key,
++ krb5_pa_data **send_pa,
++ preauth_get_entry_data_proc server_get_entry_data,
++ void *pa_module_context,
++ void **pa_request_context)
++{
++ /* This module does a couple of dumb things. It tags its reply with
++ * the same type as the initial challenge (expecting the client to sort
++ * out whether there's anything useful in there). Oh, and it replaces
++ * the AS reply key with one which is sent in the clear. */
++ krb5_keyblock *kb;
++ krb5_int32 enctype;
++ int i;
++
++ *send_pa = NULL;
++
++ /* We'll want a key with the first supported enctype. */
++ for (i = 0; i < request->nktypes; i++) {
++ kb = NULL;
++ if (krb5_init_keyblock(kcontext, request->ktype[i], 0, &kb) == 0) {
++ break;
++ }
++ }
++ if (i >= request->nktypes) {
++ /* No matching cipher type found. */
++ return 0;
++ }
++
++ /* Randomize a key and save it for the client. */
++ if (krb5_c_make_random_key(kcontext, request->ktype[i], kb) != 0) {
++ krb5_free_keyblock(kcontext, kb);
++ return 0;
++ }
++#ifdef DEBUG
++ fprintf(stderr, "Generated random key, type=%d, length=%d.\n",
++ kb->enctype, kb->length);
++#endif
++
++ *send_pa = malloc(sizeof(krb5_pa_data));
++ if (*send_pa == NULL) {
++ krb5_free_keyblock(kcontext, kb);
++ return ENOMEM;
++ }
++ (*send_pa)->pa_type = KRB5_PADATA_WPSE_REQ;
++ (*send_pa)->length = 4 + kb->length;
++ (*send_pa)->contents = malloc(4 + kb->length);
++ if ((*send_pa)->contents == NULL) {
++ free(*send_pa);
++ *send_pa = NULL;
++ krb5_free_keyblock(kcontext, kb);
++ return ENOMEM;
++ }
++
++ /* Store the preauth data. */
++ enctype = htonl(kb->enctype);
++ memcpy((*send_pa)->contents, &enctype, 4);
++ memcpy((*send_pa)->contents + 4, kb->contents, kb->length);
++ krb5_free_keyblock_contents(kcontext, encrypting_key);
++ krb5_copy_keyblock_contents(kcontext, kb, encrypting_key);
++
++ /* Clean up. */
++ krb5_free_keyblock(kcontext, kb);
++
++ return 0;
++}
++
++static int
++server_get_flags(krb5_context kcontext, krb5_preauthtype pa_type)
++{
++ return PA_HARDWARE | PA_REPLACES_KEY;
++}
++
++static krb5_preauthtype supported_client_pa_types[] = {KRB5_PADATA_WPSE_REQ, 0};
++static krb5_preauthtype supported_server_pa_types[] = {KRB5_PADATA_WPSE_REQ, 0};
++
++struct krb5plugin_preauth_client_ftable_v0 preauthentication_client_0_backport_1_6 = {
++ "wpse", /* name */
++ &supported_client_pa_types[0], /* pa_type_list */
++ NULL, /* enctype_list */
++ client_init, /* plugin init function */
++ client_fini, /* plugin fini function */
++ client_get_flags, /* get flags function */
++ client_req_init, /* request init function */
++ client_req_cleanup, /* request fini function */
++ client_process, /* process function */
++ NULL, /* try_again function */
++};
++
++struct krb5plugin_preauth_server_ftable_v0 preauthentication_server_0_backport_1_6 = {
++ "wpse",
++ &supported_server_pa_types[0],
++ NULL,
++ NULL,
++ server_get_flags,
++ server_get_edata,
++ server_verify,
++ server_return,
++ server_free_pa_request_context,
++};
+--- /dev/null 2007-01-10 09:59:42.964619257 -0500
++++ krb5/src/plugins/preauth/wpse/wpse.exports
+@@ -0,0 +1,2 @@
++preauthentication_client_0_backport_1_6
++preauthentication_server_0_backport_1_6
+--- /dev/null 2007-01-10 09:59:42.964619257 -0500
++++ krb5/src/plugins/preauth/cksum_body/cksum_body.exports
+@@ -0,0 +1,2 @@
++preauthentication_client_0_backport_1_6
++preauthentication_server_0_backport_1_6
+--- /dev/null 2007-01-10 09:59:42.964619257 -0500
++++ krb5/src/plugins/preauth/cksum_body/configure.in
+@@ -0,0 +1,14 @@
++K5_AC_INIT(configure.in)
++enable_shared=yes
++build_dynobj=yes
++CONFIG_RULES
++
++AC_CHECK_HEADERS(errno.h string.h)
++
++KRB5_RUN_FLAGS
++dnl The following is for check...
++KRB5_BUILD_PROGRAM
++KRB5_BUILD_LIBOBJS
++KRB5_BUILD_LIBRARY_WITH_DEPS
++AC_CONFIG_HEADERS(config.h)
++V5_AC_OUTPUT_MAKEFILE
+--- /dev/null 2007-01-10 09:59:42.964619257 -0500
++++ krb5/src/plugins/preauth/cksum_body/Makefile.in
+@@ -0,0 +1,42 @@
++thisconfigdir=.
++myfulldir=plugins/preauth/cksum_body
++mydir=.
++BUILDTOP=$(REL)..$(S)..$(S)..
++KRB5_RUN_ENV = @KRB5_RUN_ENV@
++KRB5_CONFIG_SETUP = KRB5_CONFIG=$(SRCTOP)/config-files/krb5.conf ; export KRB5_CONFIG ;
++PROG_LIBPATH=-L$(TOPLIBD)
++PROG_RPATH=$(KRB5_LIBDIR)
++MODULE_INSTALL_DIR = $(KRB5_PA_MODULE_DIR)
++DEFS=@DEFS@
++
++LOCALINCLUDES = -I../../../include/krb5 -I.
++
++LIBBASE=cksum_body
++LIBMAJOR=0
++LIBMINOR=0
++SO_EXT=.so
++RELDIR=../plugins/preauth/cksum_body
++# Depends on libk5crypto and libkrb5
++SHLIB_EXPDEPS = \
++ $(TOPLIBD)/libk5crypto$(SHLIBEXT) \
++ $(TOPLIBD)/libkrb5$(SHLIBEXT)
++SHLIB_EXPLIBS= -lkrb5 -lcom_err -lk5crypto $(SUPPORT_LIB) $(LIBS)
++
++SHLIB_DIRS=-L$(TOPLIBD)
++SHLIB_RDIRS=$(KRB5_LIBDIR)
++STOBJLISTS=OBJS.ST
++STLIBOBJS=cksum_body_main.o
++
++SRCS= $(srcdir)/cksum_body_main.c
++
++all-unix:: $(LIBBASE)$(SO_EXT)
++install-unix:: install-libs
++clean-unix:: clean-libs clean-libobjs
++
++clean::
++ $(RM) lib$(LIBBASE)$(SO_EXT)
++
++@libnover_frag@
++@libobj_frag@
++
++# +++ Dependency line eater +++
+--- /dev/null 2007-01-10 09:59:42.964619257 -0500
++++ krb5/src/plugins/preauth/cksum_body/cksum_body_main.c
+@@ -0,0 +1,521 @@
++/*
++ * Copyright (C) 2006 Red Hat, Inc.
++ * All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions are met:
++ *
++ * * Redistributions of source code must retain the above copyright
++ * notice, this list of conditions and the following disclaimer.
++ * * Redistributions in binary form must reproduce the above copyright
++ * notice, this list of conditions and the following disclaimer in
++ * the documentation and/or other materials provided with the
++ * distribution.
++ * * Neither the name of Red Hat, Inc., nor the names of its
++ * contributors may be used to endorse or promote products derived
++ * from this software without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
++ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
++ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
++ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
++ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
++ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
++ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
++ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
++ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
++ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
++ */
++
++/*
++ * Checksum the request body with the user's long-term key.
++ *
++ * The e-data from the KDC is a list of network-byte-order 32-bit integers
++ * listing key types which the KDC has for the user.
++ *
++ * The client uses one of these key types to generate a checksum over the body
++ * of the request, and includes the checksum in the AS-REQ as preauthentication
++ * data.
++ *
++ * The AS-REP carries no preauthentication data for this scheme.
++ */
++
++#ident "$Id$"
++
++#ifdef HAVE_CONFIG_H
++#include "config.h"
++#endif
++
++#ifdef HAVE_ERRNO_H
++#include <errno.h>
++#endif
++#ifdef HAVE_STRING_H
++#include <string.h>
++#endif
++
++#include <arpa/inet.h>
++#include <stdio.h>
++
++#include <krb5/krb5.h>
++#include <krb5/preauth_plugin.h>
++
++/* This is not a standardized value. It's defined here only to make it easier
++ * to change in this module. */
++#define KRB5_PADATA_CKSUM_BODY_REQ 130
++
++struct server_stats{
++ int successes, failures;
++};
++
++static int
++client_get_flags(krb5_context kcontext, krb5_preauthtype pa_type)
++{
++ return PA_REAL;
++}
++
++static krb5_error_code
++client_process(krb5_context kcontext,
++ void *client_plugin_context,
++ void *client_request_context,
++ preauth_get_client_data_proc client_get_data_proc,
++ struct _krb5_preauth_client_rock *rock,
++ krb5_kdc_req *request,
++ krb5_data *encoded_request_body,
++ krb5_data *encoded_previous_request,
++ krb5_pa_data *pa_data,
++ krb5_prompter_fct prompter,
++ void *prompter_data,
++ preauth_get_as_key_proc 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;
++
++ 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;
++ }
++#ifdef DEBUG
++ fprintf(stderr, "Got AS key (type = %d).\n", as_key->enctype);
++#endif
++
++ /* Determine an appropriate checksum type for this key. */
++ cksumtype_count = 0;
++ cksumtypes = NULL;
++ status = krb5_c_keyed_checksum_types(kcontext, as_key->enctype,
++ &cksumtype_count, &cksumtypes);
++ if (status != 0)
++ return status;
++
++ /* Generate the checksum. */
++ for (i = 0; i < cksumtype_count; i++) {
++ status = krb5_c_make_checksum(kcontext, cksumtypes[i], as_key,
++ KRB5_KEYUSAGE_TGS_REQ_AUTH_CKSUM,
++ encoded_request_body,
++ &checksum);
++ if (status == 0) {
++#ifdef DEBUG
++ fprintf(stderr, "Made checksum (type = %d, %d bytes).\n",
++ checksum.checksum_type, encoded_request_body->length);
++#endif
++ break;
++ }
++ }
++ cksumtype = htonl(cksumtypes[i]);
++ krb5_free_cksumtypes(kcontext, cksumtypes);
++ if (status != 0) {
++ if (checksum.length > 0)
++ krb5_free_checksum_contents(kcontext, &checksum);
++ return status;
++ }
++
++ /* Allocate the preauth data structure. */
++ send_pa = malloc(sizeof(krb5_pa_data));
++ if (send_pa == NULL) {
++ krb5_free_checksum_contents(kcontext, &checksum);
++ return ENOMEM;
++ }
++ send_pa->pa_type = KRB5_PADATA_CKSUM_BODY_REQ;
++ send_pa->length = 4 + checksum.length;
++ send_pa->contents = malloc(4 + checksum.length);
++ if (send_pa->contents == NULL) {
++ krb5_free_checksum_contents(kcontext, &checksum);
++ free(send_pa);
++ return ENOMEM;
++ }
++
++ /* Store the checksum. */
++ memcpy(send_pa->contents, &cksumtype, 4);
++ memcpy(send_pa->contents + 4, checksum.contents, checksum.length);
++ *out_pa_data = send_pa;
++
++ /* Clean up. */
++ krb5_free_checksum_contents(kcontext, &checksum);
++
++ return 0;
++}
++
++/* Initialize and tear down the server-side module, and do stat tracking. */
++static krb5_error_code
++server_init(krb5_context kcontext, void **module_context)
++{
++ struct server_stats *stats;
++ stats = malloc(sizeof(struct server_stats));
++ if (stats == NULL)
++ return ENOMEM;
++ stats->successes = 0;
++ stats->failures = 0;
++ *module_context = stats;
++ return 0;
++}
++static void
++server_fini(krb5_context kcontext, void *module_context)
++{
++ struct server_stats *stats;
++ stats = module_context;
++ if (stats != NULL) {
++#ifdef DEBUG
++ fprintf(stderr, "Total: %d clients failed, %d succeeded.\n",
++ stats->failures, stats->successes);
++#endif
++ free(stats);
++ }
++}
++
++/* Obtain and return any preauthentication data (which is destined for the
++ * client) which matches type data->pa_type. */
++static krb5_error_code
++server_get_edata(krb5_context kcontext,
++ krb5_kdc_req *request,
++ struct _krb5_db_entry_new *client,
++ struct _krb5_db_entry_new *server,
++ preauth_get_entry_data_proc server_get_entry_data,
++ void *pa_module_context,
++ krb5_pa_data *data)
++{
++ krb5_data *key_data;
++ krb5_keyblock *keys, *key;
++ krb5_int32 *enctypes, enctype;
++ int i;
++
++ /* Retrieve the client's keys. */
++ key_data = NULL;
++ if ((*server_get_entry_data)(kcontext, request, client,
++ krb5plugin_preauth_keys, &key_data) != 0) {
++#ifdef DEBUG
++ fprintf(stderr, "Error retrieving client keys.\n");
++#endif
++ return KRB5KDC_ERR_PADATA_TYPE_NOSUPP;
++ }
++
++ /* Count which types of keys we've got, freeing the contents, which we
++ * don't need at this point. */
++ keys = (krb5_keyblock *) key_data->data;
++ key = NULL;
++ for (i = 0; keys[i].enctype != 0; i++)
++ krb5_free_keyblock_contents(kcontext, &keys[i]);
++
++ /* Return the list of encryption types. */
++ enctypes = malloc((unsigned)i * 4);
++ if (enctypes == NULL) {
++ krb5_free_data(kcontext, key_data);
++ return ENOMEM;
++ }
++#ifdef DEBUG
++ fprintf(stderr, "Supported enctypes = {");
++#endif
++ for (i = 0; keys[i].enctype != 0; i++) {
++#ifdef DEBUG
++ fprintf(stderr, "%s%d", (i > 0) ? ", " : "", keys[i].enctype);
++#endif
++ enctype = htonl(keys[i].enctype);
++ memcpy(&enctypes[i], &enctype, 4);
++ }
++#ifdef DEBUG
++ fprintf(stderr, "}.\n");
++#endif
++ data->pa_type = KRB5_PADATA_CKSUM_BODY_REQ;
++ data->length = (i * 4);
++ data->contents = (unsigned char *) enctypes;
++ krb5_free_data(kcontext, key_data);
++ return 0;
++}
++
++/* Verify a request from a client. */
++static krb5_error_code
++server_verify(krb5_context kcontext,
++ struct _krb5_db_entry_new *client,
++ krb5_data *req_pkt,
++ krb5_kdc_req *request,
++ krb5_enc_tkt_part *enc_tkt_reply,
++ krb5_pa_data *data,
++ preauth_get_entry_data_proc server_get_entry_data,
++ void *pa_module_context,
++ void **pa_request_context,
++ krb5_data **e_data)
++{
++ krb5_int32 cksumtype;
++ krb5_checksum checksum;
++ krb5_boolean valid;
++ krb5_data *key_data, *req_body;
++ krb5_keyblock *keys, *key;
++ size_t length;
++ int i;
++ unsigned int j, cksumtypes_count;
++ krb5_cksumtype *cksumtypes;
++ krb5_error_code status;
++ struct server_stats *stats;
++ krb5_data *test_edata;
++
++ stats = pa_module_context;
++
++ /* Verify the preauth data. Start with the checksum type. */
++ if (data->length < 4) {
++ stats->failures++;
++ return KRB5KDC_ERR_PREAUTH_FAILED;
++ }
++ memcpy(&cksumtype, data->contents, 4);
++ memset(&checksum, 0, sizeof(checksum));
++ checksum.checksum_type = ntohl(cksumtype);
++
++ /* Verify that the amount of data we have left is what we expect. */
++ if (krb5_c_checksum_length(kcontext, checksum.checksum_type,
++ &length) != 0) {
++#ifdef DEBUG
++ fprintf(stderr, "Error determining checksum size (type = %d). "
++ "Is it supported?\n", checksum.checksum_type);
++#endif
++ stats->failures++;
++ return KRB5KDC_ERR_SUMTYPE_NOSUPP;
++ }
++ if (data->length - 4 != length) {
++#ifdef DEBUG
++ fprintf(stderr, "Checksum size doesn't match client packet size.\n");
++#endif
++ stats->failures++;
++ return KRB5KDC_ERR_PREAUTH_FAILED;
++ }
++ checksum.length = length;
++
++ /* Pull up the client's keys. */
++ key_data = NULL;
++ if ((*server_get_entry_data)(kcontext, request, client,
++ krb5plugin_preauth_keys, &key_data) != 0) {
++#ifdef DEBUG
++ fprintf(stderr, "Error retrieving client keys.\n");
++#endif
++ stats->failures++;
++ return KRB5KDC_ERR_PREAUTH_FAILED;
++ }
++
++ /* Find the key which would have been used to generate the checksum. */
++ keys = (krb5_keyblock *) key_data->data;
++ key = NULL;
++ for (i = 0; keys[i].enctype != 0; i++) {
++ key = &keys[i];
++ cksumtypes_count = 0;
++ cksumtypes = NULL;
++ if (krb5_c_keyed_checksum_types(kcontext, key->enctype,
++ &cksumtypes_count, &cksumtypes) != 0)
++ continue;
++ for (j = 0; j < cksumtypes_count; j++) {
++ if (cksumtypes[j] == checksum.checksum_type)
++ break;
++ }
++ if (cksumtypes != NULL)
++ krb5_free_cksumtypes(kcontext, cksumtypes);
++ if (j < cksumtypes_count) {
++#ifdef DEBUG
++ fprintf(stderr, "Found checksum key.\n");
++#endif
++ break;
++ }
++ }
++ if ((key == NULL) || (key->enctype == 0)) {
++ for (i = 0; keys[i].enctype != 0; i++)
++ krb5_free_keyblock_contents(kcontext, &keys[i]);
++ krb5_free_data(kcontext, key_data);
++ stats->failures++;
++ return KRB5KDC_ERR_SUMTYPE_NOSUPP;
++ }
++
++ /* Save a copy of the key. */
++ if (krb5_copy_keyblock(kcontext, &keys[i], &key) != 0) {
++ for (i = 0; keys[i].enctype != 0; i++)
++ krb5_free_keyblock_contents(kcontext, &keys[i]);
++ krb5_free_data(kcontext, key_data);
++ stats->failures++;
++ return KRB5KDC_ERR_SUMTYPE_NOSUPP;
++ }
++ for (i = 0; keys[i].enctype != 0; i++)
++ krb5_free_keyblock_contents(kcontext, &keys[i]);
++ krb5_free_data(kcontext, key_data);
++
++ /* Rebuild a copy of the client's request-body. If we were serious
++ * about doing this with any chance of working interoperability, we'd
++ * extract the structure directly from the req_pkt structure. This
++ * will probably work if it's us on both ends, though. */
++ req_body = NULL;
++ if ((*server_get_entry_data)(kcontext, request, client,
++ krb5plugin_preauth_request_body,
++ &req_body) != 0) {
++ krb5_free_keyblock(kcontext, key);
++ stats->failures++;
++ return KRB5KDC_ERR_PREAUTH_FAILED;
++ }
++
++#ifdef DEBUG
++ fprintf(stderr, "AS key type %d, checksum type %d, %d bytes.\n",
++ key->enctype, checksum.checksum_type, req_body->length);
++#endif
++
++ /* Verify the checksum itself. */
++ checksum.contents = data->contents + 4;
++ valid = FALSE;
++ status = krb5_c_verify_checksum(kcontext, key,
++ KRB5_KEYUSAGE_TGS_REQ_AUTH_CKSUM,
++ req_body, &checksum, &valid);
++
++ /* Clean up. */
++ krb5_free_data(kcontext, req_body);
++ krb5_free_keyblock(kcontext, key);
++
++ /* Evaluate our results. */
++ if ((status != 0) || (!valid)) {
++#ifdef DEBUG
++ if (status != 0) {
++ fprintf(stderr, "Error in checksum verification.\n");
++ } else {
++ fprintf(stderr, "Checksum mismatch.\n");
++ }
++#endif
++ /* Return edata to exercise code that handles edata... */
++ test_edata = malloc(sizeof(*test_edata));
++ if (test_edata != NULL) {
++ test_edata->data = malloc(20);
++ if (test_edata->data == NULL) {
++ free(test_edata);
++ } else {
++ test_edata->length = 20;
++ memset(test_edata->data, 'F', 20); /* fill it with junk */
++ *e_data = test_edata;
++ }
++ }
++ stats->failures++;
++ return KRB5KDC_ERR_PREAUTH_FAILED;
++ }
++
++ /* Return edata to exercise code that handles edata... */
++ test_edata = malloc(sizeof(*test_edata));
++ if (test_edata != NULL) {
++ test_edata->data = malloc(20);
++ if (test_edata->data == NULL) {
++ free(test_edata);
++ } else {
++ test_edata->length = 20;
++ memset(test_edata->data, 'S', 20); /* fill it with junk */
++ *e_data = test_edata;
++ }
++ }
++
++ /* Note that preauthentication succeeded. */
++ enc_tkt_reply->flags |= TKT_FLG_PRE_AUTH;
++ stats->successes++;
++ return 0;
++}
++
++/* Create the response for a client. */
++static krb5_error_code
++server_return(krb5_context kcontext,
++ krb5_pa_data *padata,
++ struct _krb5_db_entry_new *client,
++ krb5_data *req_pkt,
++ krb5_kdc_req *request,
++ krb5_kdc_rep *reply,
++ struct _krb5_key_data *client_key,
++ krb5_keyblock *encrypting_key,
++ krb5_pa_data **send_pa,
++ preauth_get_entry_data_proc server_get_entry_data,
++ void *pa_module_context,
++ void **pa_request_context)
++{
++ /* We don't need to send data back on the return trip. */
++ *send_pa = NULL;
++ return 0;
++}
++
++static int
++server_get_flags(krb5_context kcontext, krb5_preauthtype pa_type)
++{
++ return PA_SUFFICIENT;
++}
++
++static krb5_preauthtype supported_client_pa_types[] = {
++ KRB5_PADATA_CKSUM_BODY_REQ, 0,
++};
++static krb5_preauthtype supported_server_pa_types[] = {
++ KRB5_PADATA_CKSUM_BODY_REQ, 0,
++};
++
++struct krb5plugin_preauth_client_ftable_v0 preauthentication_client_0_backport_1_6 = {
++ "cksum_body", /* name */
++ &supported_client_pa_types[0], /* pa_type_list */
++ NULL, /* enctype_list */
++ NULL, /* plugin init function */
++ NULL, /* plugin fini function */
++ client_get_flags, /* get flags function */
++ NULL, /* request init function */
++ NULL, /* request fini function */
++ client_process, /* process function */
++ NULL, /* try_again function */
++};
++
++struct krb5plugin_preauth_server_ftable_v0 preauthentication_server_0_backport_1_6 = {
++ "cksum_body",
++ &supported_server_pa_types[0],
++ server_init,
++ server_fini,
++ server_get_flags,
++ server_get_edata,
++ server_verify,
++ server_return,
++ NULL
++};
+--- krb5/src/configure.in
++++ krb5/src/configure.in
+@@ -900,7 +900,7 @@ fi
+ if test -n "$KRB4_LIB"; then
+ K5_GEN_MAKEFILE(lib/krb4)
+ fi
+-AC_CONFIG_SUBDIRS(lib/apputils plugins/kdb/db2 appl tests)
++AC_CONFIG_SUBDIRS(lib/apputils plugins/kdb/db2 plugins/preauth/wpse plugins/preauth/cksum_body appl tests)
+ dnl
+ if false; then
+ AC_CHECK_HEADERS(Python.h python2.3/Python.h)
+--- krb5/src/Makefile.in
++++ krb5/src/Makefile.in
+@@ -3,8 +3,8 @@ datadir=@datadir@
+ thisconfigdir=.
+ myfulldir=.
+ mydir=.
+-# Don't build sample by default: plugins/locate/python
+-SUBDIRS=util include lib @krb524@ kdc kadmin slave clients \
++# Don't build sample by default: plugins/locate/python plugins/preauth/wpse plugins/preauth/cksum_body
++SUBDIRS=util include lib @krb524@ kdc kadmin slave clients \
+ plugins/kdb/db2 \
+ appl tests \
+ config-files gen-manpages
+--- krb5/src/config-files/krb5.conf.M
++++ krb5/src/config-files/krb5.conf.M
+@@ -156,6 +156,12 @@ libraries, use a value of 3 to use the C
+ instead. This field is ignored when its value is incompatible with
+ the session key type.
+
++.IP preferred_preauth_types
++This allows you to set the preferred preauthentication types which the
++client will attempt before others which may be advertised by a KDC. The
++default value for this setting is "17, 16, 15, 14", which forces libkrb5
++to attempt to use PKINIT if it is supported.
++
+ .IP ccache_type
+ User this parameter on systems which are DCE clients, to specify the
+ type of cache to be created by kinit, or when forwarded tickets are
+@@ -169,7 +175,7 @@ Specifies the location of the Kerberos V
+ "/etc/srvtab".
+
+ .IP krb4_config
+-Specifies the location of hte Kerberos V4 configuration file. Default
++Specifies the location of the Kerberos V4 configuration file. Default
+ is "/etc/krb.conf".
+
+ .IP krb4_realms