diff options
author | Nalin Dahyabhai <nalin@fedoraproject.org> | 2007-01-22 21:24:18 +0000 |
---|---|---|
committer | Nalin Dahyabhai <nalin@fedoraproject.org> | 2007-01-22 21:24:18 +0000 |
commit | 3108b87f7256524698119beb790bea5bfade77ac (patch) | |
tree | c2147380d1315e1f4c2b45d965e18b99412178aa | |
parent | f3820b972d502161542e121eee4b18bc64e36037 (diff) | |
download | krb5-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.patch | 4175 |
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 |