#include "k5-int.h"
#if APPLE_PKINIT
#include "pkinit_client.h"
#include "pkinit_cert_store.h"
#endif
#include "osconf.h"
#include <krb5/preauth_plugin.h>
#include "int-proto.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 };
#else
static const char *objdirs[] = { LIBDIR "/krb5/plugins/preauth", NULL };
#endif
typedef krb5_error_code (*pa_function)(krb5_context,
krb5_kdc_req *request,
krb5_pa_data *in_padata,
krb5_pa_data ***out_padata,
krb5_data *encoded_request_body,
krb5_data *encoded_previous_request,
krb5_data *salt, krb5_data *s2kparams,
krb5_enctype *etype,
krb5_keyblock *as_key,
krb5_prompter_fct prompter_fct,
void *prompter_data,
krb5_gic_get_as_key_fct gak_fct,
void *gak_data);
typedef struct _pa_types_t {
krb5_preauthtype type;
pa_function fct;
int flags;
} pa_types_t;
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_v1 *table;
krb5_preauth_context *context = NULL;
void *plugin_context;
krb5_preauthtype pa_type;
void **rcpp;
if (kcontext->preauth_context != NULL)
return;
if (PLUGIN_DIR_OPEN(&kcontext->preauth_plugins) == 0) {
if (krb5int_open_plugin_dirs(objdirs, NULL,
&kcontext->preauth_plugins,
&kcontext->err) != 0) {
return;
}
}
tables = NULL;
if (krb5int_get_plugin_dir_data(&kcontext->preauth_plugins,
"preauthentication_client_1",
&tables,
&kcontext->err) != 0) {
return;
}
if (tables == NULL) {
return;
}
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++;
}
}
}
context = malloc(sizeof(*context));
if (context == NULL) {
krb5int_free_plugin_dir_data(tables);
return;
}
context->modules = calloc(n_modules, sizeof(context->modules[0]));
if (context->modules == NULL) {
krb5int_free_plugin_dir_data(tables);
free(context);
return;
}
context->n_modules = n_modules;
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;
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;
if (j == 0)
context->modules[k].client_supply_gic_opts = table->gic_opts;
else
context->modules[k].client_supply_gic_opts = NULL;
context->modules[k].request_context = NULL;
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);
kcontext->preauth_context = context;
}
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;
}
}
}
krb5_error_code
krb5_preauth_supply_preauth_data(krb5_context context,
krb5_gic_opt_ext *opte,
const char *attr,
const char *value)
{
krb5_error_code retval = 0;
int i;
void *pctx;
const char *emsg = NULL;
if (context->preauth_context == NULL)
krb5_init_preauth_context(context);
if (context->preauth_context == NULL) {
retval = EINVAL;
krb5int_set_error(&context->err, retval,
"krb5_preauth_supply_preauth_data: "
"Unable to initialize preauth context");
return retval;
}
for (i = 0; i < context->preauth_context->n_modules; i++) {
if (context->preauth_context->modules[i].client_supply_gic_opts == NULL)
continue;
pctx = context->preauth_context->modules[i].plugin_context;
retval = (*context->preauth_context->modules[i].client_supply_gic_opts)
(context, pctx,
(krb5_get_init_creds_opt *)opte, attr, value);
if (retval) {
emsg = krb5_get_error_message(context, retval);
krb5int_set_error(&context->err, retval, "Preauth plugin %s: %s",
context->preauth_context->modules[i].name, emsg);
break;
}
}
return retval;
}
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;
}
}
void KRB5_CALLCONV
krb5_preauth_request_context_init(krb5_context context)
{
int i;
void *rctx, *pctx;
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);
}
}
}
}
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;
}
}
}
}
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;
}
}
static int
grow_pa_list(krb5_pa_data ***out_pa_list, int *out_pa_list_size,
krb5_pa_data **addition, int num_addition)
{
krb5_pa_data **pa_list;
int i, j;
if (out_pa_list == NULL || addition == NULL) {
return EINVAL;
}
if (*out_pa_list == NULL) {
pa_list = malloc((num_addition + 1) * sizeof(krb5_pa_data *));
if (pa_list == NULL)
return ENOMEM;
for (i = 0; i < num_addition; i++)
pa_list[i] = addition[i];
pa_list[i] = NULL;
*out_pa_list = pa_list;
*out_pa_list_size = num_addition;
} else {
pa_list = malloc((*out_pa_list_size + num_addition + 1)
* 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];
for (j = 0; j < num_addition;)
pa_list[i++] = addition[j++];
pa_list[i] = NULL;
free(*out_pa_list);
*out_pa_list = pa_list;
*out_pa_list_size = i;
}
return 0;
}
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;
}
}
void KRB5_CALLCONV
krb5_preauth_prepare_request(krb5_context kcontext,
krb5_gic_opt_ext *opte,
krb5_kdc_req *request)
{
int i, j;
if (kcontext->preauth_context == NULL) {
return;
}
if (!(opte && (opte->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]);
}
}
}
}
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,
krb5_gic_opt_ext *opte)
{
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;
}
for (i = 0; i < kcontext->preauth_context->n_modules; i++) {
module = &kcontext->preauth_context->modules[i];
if (module->pa_type != in_padata->pa_type)
continue;
if ((module->flags & module_required_flags) == 0)
continue;
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++;
}
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,
(krb5_get_init_creds_opt *)opte,
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);
*module_flags = module->flags;
*module_ret = ret;
if (out_pa_data != NULL) {
int j;
for (j = 0; out_pa_data[j] != NULL; j++);
ret = grow_pa_list(out_pa_list, out_pa_list_size, out_pa_data, j);
free(out_pa_data);
if (ret != 0)
return ret;
}
break;
}
if (i >= kcontext->preauth_context->n_modules) {
return ENOENT;
}
return 0;
}
static inline krb5_data
padata2data(krb5_pa_data p)
{
krb5_data d;
d.magic = KV5M_DATA;
d.length = p.length;
d.data = (char *) p.contents;
return d;
}
static
krb5_error_code pa_salt(krb5_context context,
krb5_kdc_req *request,
krb5_pa_data *in_padata,
krb5_pa_data ***out_padata,
krb5_data *encoded_request_body,
krb5_data *encoded_previous_request,
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_data tmp;
*out_padata = NULL;
tmp = padata2data(*in_padata);
krb5_free_data_contents(context, salt);
krb5int_copy_data_contents(context, &tmp, salt);
if (in_padata->pa_type == KRB5_PADATA_AFS3_SALT)
salt->length = SALT_TYPE_AFS_LENGTH;
return(0);
}
static
krb5_error_code pa_enc_timestamp(krb5_context context,
krb5_kdc_req *request,
krb5_pa_data *in_padata,
krb5_pa_data ***out_padata,
krb5_data *encoded_request_body,
krb5_data *encoded_previous_request,
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_error_code ret;
krb5_pa_enc_ts pa_enc;
krb5_data *tmp;
krb5_enc_data enc_data;
krb5_pa_data **pa;
if (as_key->length == 0) {
#ifdef DEBUG
fprintf (stderr, "%s:%d: salt len=%d", __FILE__, __LINE__,
salt->length);
if ((int) salt->length > 0)
fprintf (stderr, " '%.*s'", salt->length, salt->data);
fprintf (stderr, "; *etype=%d request->ktype[0]=%d\n",
*etype, request->ktype[0]);
#endif
if ((ret = ((*gak_fct)(context, request->client,
*etype ? *etype : request->ktype[0],
prompter, prompter_data,
salt, s2kparams, as_key, gak_data))))
return(ret);
}
if ((ret = krb5_us_timeofday(context, &pa_enc.patimestamp, &pa_enc.pausec)))
return(ret);
if ((ret = encode_krb5_pa_enc_ts(&pa_enc, &tmp)))
return(ret);
#ifdef DEBUG
fprintf (stderr, "key type %d bytes %02x %02x ...\n",
as_key->enctype,
as_key->contents[0], as_key->contents[1]);
#endif
ret = krb5_encrypt_helper(context, as_key,
KRB5_KEYUSAGE_AS_REQ_PA_ENC_TS,
tmp, &enc_data);
#ifdef DEBUG
fprintf (stderr, "enc data { type=%d kvno=%d data=%02x %02x ... }\n",
enc_data.enctype, enc_data.kvno,
0xff & enc_data.ciphertext.data[0],
0xff & enc_data.ciphertext.data[1]);
#endif
krb5_free_data(context, tmp);
if (ret) {
krb5_xfree(enc_data.ciphertext.data);
return(ret);
}
ret = encode_krb5_enc_data(&enc_data, &tmp);
krb5_xfree(enc_data.ciphertext.data);
if (ret)
return(ret);
if ((pa = calloc(2, sizeof(pa[0]))) == NULL) {
krb5_free_data(context, tmp);
return(ENOMEM);
}
pa[0] = malloc(sizeof(*pa[0]));
if (pa[0] == NULL) {
free(pa);
krb5_free_data(context, tmp);
return(ENOMEM);
}
pa[0]->magic = KV5M_PA_DATA;
pa[0]->pa_type = KRB5_PADATA_ENC_TIMESTAMP;
pa[0]->length = tmp->length;
pa[0]->contents = (krb5_octet *) tmp->data;
*out_padata = pa;
krb5_xfree(tmp);
return(0);
}
static
char *sam_challenge_banner(krb5_int32 sam_type)
{
char *label;
switch (sam_type) {
case PA_SAM_TYPE_ENIGMA:
label = "Challenge for Enigma Logic mechanism";
break;
case PA_SAM_TYPE_DIGI_PATH:
case PA_SAM_TYPE_DIGI_PATH_HEX:
label = "Challenge for Digital Pathways mechanism";
break;
case PA_SAM_TYPE_ACTIVCARD_DEC:
case PA_SAM_TYPE_ACTIVCARD_HEX:
label = "Challenge for Activcard mechanism";
break;
case PA_SAM_TYPE_SKEY_K0:
label = "Challenge for Enhanced S/Key mechanism";
break;
case PA_SAM_TYPE_SKEY:
label = "Challenge for Traditional S/Key mechanism";
break;
case PA_SAM_TYPE_SECURID:
label = "Challenge for Security Dynamics mechanism";
break;
case PA_SAM_TYPE_SECURID_PREDICT:
label = "Challenge for Security Dynamics mechanism";
break;
default:
label = "Challenge from authentication server";
break;
}
return(label);
}
#define SAMDATA(kdata, str, maxsize) \
(int)((kdata.length)? \
((((kdata.length)<=(maxsize))?(kdata.length):strlen(str))): \
strlen(str)), \
(kdata.length)? \
((((kdata.length)<=(maxsize))?(kdata.data):(str))):(str)
static
krb5_error_code pa_sam(krb5_context context,
krb5_kdc_req *request,
krb5_pa_data *in_padata,
krb5_pa_data ***out_padata,
krb5_data *encoded_request_body,
krb5_data *encoded_previous_request,
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_error_code ret;
krb5_data tmpsam;
char name[100], banner[100];
char prompt[100], response[100];
krb5_data response_data;
krb5_prompt kprompt;
krb5_prompt_type prompt_type;
krb5_data defsalt;
krb5_sam_challenge *sam_challenge = 0;
krb5_sam_response sam_response;
krb5_enc_sam_response_enc enc_sam_response_enc;
krb5_data * scratch;
krb5_pa_data ** pa;
if (prompter == NULL)
return EIO;
tmpsam.length = in_padata->length;
tmpsam.data = (char *) in_padata->contents;
if ((ret = decode_krb5_sam_challenge(&tmpsam, &sam_challenge)))
return(ret);
if (sam_challenge->sam_flags & KRB5_SAM_MUST_PK_ENCRYPT_SAD) {
krb5_xfree(sam_challenge);
return(KRB5_SAM_UNSUPPORTED);
}
if (!(sam_challenge->sam_flags & KRB5_SAM_USE_SAD_AS_KEY) ||
(sam_challenge->sam_type == PA_SAM_TYPE_CRYPTOCARD)) {
if (etype && *etype == 0)
*etype = ENCTYPE_DES_CBC_CRC;
if ((ret = (gak_fct)(context, request->client, *etype, prompter,
prompter_data, salt, s2kparams, as_key, gak_data)))
return(ret);
}
snprintf(name, sizeof(name), "%.*s",
SAMDATA(sam_challenge->sam_type_name, "SAM Authentication",
sizeof(name) - 1));
snprintf(banner, sizeof(banner), "%.*s",
SAMDATA(sam_challenge->sam_challenge_label,
sam_challenge_banner(sam_challenge->sam_type),
sizeof(banner)-1));
snprintf(prompt, sizeof(prompt), "%s%.*s%s%.*s",
sam_challenge->sam_challenge.length?"Challenge is [":"",
SAMDATA(sam_challenge->sam_challenge, "", 20),
sam_challenge->sam_challenge.length?"], ":"",
SAMDATA(sam_challenge->sam_response_prompt, "passcode", 55));
response_data.data = response;
response_data.length = sizeof(response);
kprompt.prompt = prompt;
kprompt.hidden = 1;
kprompt.reply = &response_data;
prompt_type = KRB5_PROMPT_TYPE_PREAUTH;
krb5int_set_prompt_types(context, &prompt_type);
if ((ret = ((*prompter)(context, prompter_data, name,
banner, 1, &kprompt)))) {
krb5_xfree(sam_challenge);
krb5int_set_prompt_types(context, 0);
return(ret);
}
krb5int_set_prompt_types(context, 0);
enc_sam_response_enc.sam_nonce = sam_challenge->sam_nonce;
if (sam_challenge->sam_nonce == 0) {
if ((ret = krb5_us_timeofday(context,
&enc_sam_response_enc.sam_timestamp,
&enc_sam_response_enc.sam_usec))) {
krb5_xfree(sam_challenge);
return(ret);
}
sam_response.sam_patimestamp = enc_sam_response_enc.sam_timestamp;
}
if (sam_challenge->sam_flags & KRB5_SAM_SEND_ENCRYPTED_SAD) {
if (as_key->length) {
krb5_free_keyblock_contents(context, as_key);
as_key->length = 0;
}
if ((salt->length == -1 || salt->length == SALT_TYPE_AFS_LENGTH) && (salt->data == NULL)) {
if ((ret = krb5_principal2salt(context, request->client,
&defsalt))) {
krb5_xfree(sam_challenge);
return(ret);
}
salt = &defsalt;
} else {
defsalt.length = 0;
}
ret = krb5_c_string_to_key(context, ENCTYPE_DES_CBC_MD5,
(krb5_data *)gak_data, salt, as_key);
if (defsalt.length)
krb5_xfree(defsalt.data);
if (ret) {
krb5_xfree(sam_challenge);
return(ret);
}
enc_sam_response_enc.sam_sad = response_data;
} else if (sam_challenge->sam_flags & KRB5_SAM_USE_SAD_AS_KEY) {
if (as_key->length) {
krb5_free_keyblock_contents(context, as_key);
as_key->length = 0;
}
#if 0
if ((salt->length == SALT_TYPE_AFS_LENGTH) && (salt->data == NULL)) {
if (ret = krb5_principal2salt(context, request->client,
&defsalt)) {
krb5_xfree(sam_challenge);
return(ret);
}
salt = &defsalt;
} else {
defsalt.length = 0;
}
#else
defsalt.length = 0;
salt = NULL;
#endif
ret = krb5_c_string_to_key(context, ENCTYPE_DES_CBC_MD5,
&response_data, salt, as_key);
if (defsalt.length)
krb5_xfree(defsalt.data);
if (ret) {
krb5_xfree(sam_challenge);
return(ret);
}
enc_sam_response_enc.sam_sad.length = 0;
} else {
return KRB5_PREAUTH_BAD_TYPE;
}
sam_response.sam_nonce = sam_challenge->sam_nonce;
sam_response.sam_flags = sam_challenge->sam_flags;
sam_response.sam_track_id = sam_challenge->sam_track_id;
sam_response.sam_type = sam_challenge->sam_type;
sam_response.magic = KV5M_SAM_RESPONSE;
krb5_xfree(sam_challenge);
if ((ret = encode_krb5_enc_sam_response_enc(&enc_sam_response_enc,
&scratch)))
return(ret);
ret = krb5_encrypt_data(context, as_key, 0, scratch,
&sam_response.sam_enc_nonce_or_ts);
krb5_free_data(context, scratch);
if (ret)
return(ret);
sam_response.sam_enc_key.ciphertext.length = 0;
if ((pa = calloc(2, sizeof(pa[0]))) == NULL) {
return(ENOMEM);
}
pa[0] = malloc(sizeof(*pa[0]));
if (pa[0] == NULL) {
free(pa);
return(ENOMEM);
}
if ((ret = encode_krb5_sam_response(&sam_response, &scratch))) {
free(pa[0]);
free(pa);
return(ret);
}
pa[0]->magic = KV5M_PA_DATA;
pa[0]->pa_type = KRB5_PADATA_SAM_RESPONSE;
pa[0]->length = scratch->length;
pa[0]->contents = (krb5_octet *) scratch->data;
*out_padata = pa;
return(0);
}
#if APPLE_PKINIT
#define PKINIT_DEBUG 0
#if PKINIT_DEBUG
#define kdcPkinitDebug(args...) printf(args)
#else
#define kdcPkinitDebug(args...)
#endif
static krb5_error_code pa_pkinit_gen_req(
krb5_context context,
krb5_kdc_req *request,
krb5_pa_data *in_padata,
krb5_pa_data ***out_padata,
krb5_data *encoded_request_body,
krb5_data *encoded_previous_request,
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_error_code krtn;
krb5_data out_data = {0, 0, NULL};
krb5_timestamp kctime = 0;
krb5_int32 cusec = 0;
krb5_ui_4 nonce = 0;
krb5_checksum cksum;
krb5_pkinit_signing_cert_t client_cert;
krb5_data *der_req = NULL;
char *client_principal = NULL;
char *server_principal = NULL;
unsigned char nonce_bytes[4];
krb5_data nonce_data = {0, 4, (char *)nonce_bytes};
int dex;
krb5_pa_data **pa;
krb5_data *trusted_CAs = NULL;
krb5_ui_4 num_trusted_CAs;
krb5_data kdc_cert = {0};
kdcPkinitDebug("pa_pkinit_gen_req\n");
if(request->client == NULL) {
kdcPkinitDebug("No request->client; aborting PKINIT\n");
return KRB5KDC_ERR_PREAUTH_FAILED;
}
krtn = krb5_unparse_name(context, request->client, &client_principal);
if(krtn) {
return krtn;
}
krtn = krb5_pkinit_get_client_cert(client_principal, &client_cert);
free(client_principal);
if(krtn) {
kdcPkinitDebug("No client cert; aborting PKINIT\n");
return krtn;
}
krtn = krb5_unparse_name(context, request->server, &server_principal);
if(krtn) {
goto cleanup;
}
krtn = krb5_pkinit_get_server_certs(client_principal, server_principal,
&trusted_CAs, &num_trusted_CAs, &kdc_cert);
if(krtn) {
goto cleanup;
}
krtn = encode_krb5_kdc_req_body(request, &der_req);
if(krtn) {
kdcPkinitDebug("encode_krb5_kdc_req_body returned %d\n", (int)krtn);
goto cleanup;
}
krtn = krb5_c_make_checksum(context, CKSUMTYPE_NIST_SHA, NULL, 0, der_req, &cksum);
if(krtn) {
goto cleanup;
}
krtn = krb5_us_timeofday(context, &kctime, &cusec);
if(krtn) {
goto cleanup;
}
krtn = krb5_c_random_make_octets(context, &nonce_data);
if(krtn) {
goto cleanup;
}
for(dex=0; dex<4; dex++) {
nonce <<= 8;
nonce |= nonce_bytes[dex];
}
krtn = krb5int_pkinit_as_req_create(context,
kctime, cusec, nonce, &cksum,
client_cert,
trusted_CAs, num_trusted_CAs,
(kdc_cert.data ? &kdc_cert : NULL),
&out_data);
if(krtn) {
kdcPkinitDebug("error %d on pkinit_as_req_create; aborting PKINIT\n", (int)krtn);
goto cleanup;
}
if ((pa = calloc(3, sizeof(pa[0]))) == NULL) {
free(out_data.data);
return(ENOMEM);
}
pa[0] = malloc(sizeof(*pa[0]));
if (pa[0] == NULL) {
free(pa);
free(out_data.data);
return(ENOMEM);
}
pa[1] = malloc(sizeof(*pa[0]));
if (pa[1] == NULL) {
free(pa[0]);
free(pa);
free(out_data.data);
return(ENOMEM);
}
pa[0]->magic = KV5M_PA_DATA;
pa[0]->pa_type = KRB5_PADATA_PK_AS_REQ;
pa[0]->length = out_data.length;
pa[0]->contents = (krb5_octet *)out_data.data;
pa[1]->magic = KV5M_PA_DATA;
pa[1]->pa_type = KRB5_PADATA_PK_AS_09_BINDING;
pa[1]->length = 0;
pa[1]->contents = NULL;
*out_padata = pa;
krtn = 0;
cleanup:
if(client_cert) {
krb5_pkinit_release_cert(client_cert);
}
if(cksum.contents) {
free(cksum.contents);
}
if (der_req) {
krb5_free_data(context, der_req);
}
if(server_principal) {
free(server_principal);
}
if(trusted_CAs) {
unsigned udex;
for(udex=0; udex<num_trusted_CAs; udex++) {
free(trusted_CAs[udex].data);
}
free(trusted_CAs);
}
if(kdc_cert.data) {
free(kdc_cert.data);
}
return krtn;
}
static krb5_boolean local_kdc_cert_match(
krb5_context context,
krb5_data *signer_cert,
krb5_principal client)
{
static const char lkdcprefix[] = "LKDC:SHA1.";
krb5_boolean match = FALSE;
size_t cert_hash_len;
char *cert_hash;
const char *realm_hash;
size_t realm_hash_len;
if (client->realm.length <= sizeof(lkdcprefix) ||
0 != memcmp(lkdcprefix, client->realm.data, sizeof(lkdcprefix)-1))
return match;
realm_hash = &client->realm.data[sizeof(lkdcprefix)-1];
realm_hash_len = client->realm.length - sizeof(lkdcprefix) + 1;
kdcPkinitDebug("checking realm versus certificate hash\n");
if (NULL != (cert_hash = krb5_pkinit_cert_hash_str(signer_cert))) {
kdcPkinitDebug("hash = %s\n", cert_hash);
cert_hash_len = strlen(cert_hash);
if (cert_hash_len == realm_hash_len &&
0 == memcmp(cert_hash, realm_hash, cert_hash_len))
match = TRUE;
free(cert_hash);
}
kdcPkinitDebug("result: %s\n", match ? "matches" : "does not match");
return match;
}
static krb5_error_code pa_pkinit_parse_rep(
krb5_context context,
krb5_kdc_req *request,
krb5_pa_data *in_padata,
krb5_pa_data ***out_padata,
krb5_data *encoded_request_body,
krb5_data *encoded_previous_request,
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)
{
krb5int_cert_sig_status sig_status = (krb5int_cert_sig_status)-999;
krb5_error_code krtn;
krb5_data asRep;
krb5_keyblock local_key = {0};
krb5_pkinit_signing_cert_t client_cert;
char *princ_name = NULL;
krb5_checksum as_req_checksum_rcd = {0};
krb5_checksum as_req_checksum_gen = {0};
krb5_data signer_cert = {0};
krb5_boolean valid = 0;
*out_padata = NULL;
kdcPkinitDebug("pa_pkinit_parse_rep\n");
if((in_padata == NULL) || (in_padata->length== 0)) {
kdcPkinitDebug("pa_pkinit_parse_rep: no in_padata\n");
return KRB5KDC_ERR_PREAUTH_FAILED;
}
if(request->client == NULL) {
kdcPkinitDebug("No request->client; aborting PKINIT\n");
return KRB5KDC_ERR_PREAUTH_FAILED;
}
krtn = krb5_unparse_name(context, request->client, &princ_name);
if(krtn) {
return krtn;
}
krtn = krb5_pkinit_get_client_cert(princ_name, &client_cert);
free(princ_name);
if(krtn) {
kdcPkinitDebug("No client cert; aborting PKINIT\n");
return krtn;
}
memset(&local_key, 0, sizeof(local_key));
asRep.data = (char *)in_padata->contents;
asRep.length = in_padata->length;
krtn = krb5int_pkinit_as_rep_parse(context, &asRep, client_cert,
&local_key, &as_req_checksum_rcd, &sig_status,
&signer_cert, NULL, NULL);
if(krtn) {
kdcPkinitDebug("pkinit_as_rep_parse returned %d\n", (int)krtn);
return krtn;
}
switch(sig_status) {
case pki_cs_good:
break;
case pki_cs_unknown_root:
if (local_kdc_cert_match(context, &signer_cert, request->client))
break;
default:
kdcPkinitDebug("pa_pkinit_parse_rep: bad cert/sig status %d\n",
(int)sig_status);
krtn = KRB5KDC_ERR_PREAUTH_FAILED;
goto error_out;
}
krtn = krb5_c_verify_checksum(context, &local_key, KRB5_KEYUSAGE_TGS_REQ_AUTH_CKSUM,
encoded_previous_request,
&as_req_checksum_rcd, &valid);
if (krtn)
goto error_out;
if (!valid) {
free(as_req_checksum_gen.contents);
memset(&as_req_checksum_gen, 0, sizeof(as_req_checksum_gen));
krtn = krb5_c_make_checksum(context, context->kdc_req_sumtype,
&local_key, KRB5_KEYUSAGE_TGS_REQ_AUTH_CKSUM,
encoded_previous_request, &as_req_checksum_gen);
if(krtn)
goto error_out;
if((as_req_checksum_gen.length != as_req_checksum_rcd.length) ||
memcmp(as_req_checksum_gen.contents,
as_req_checksum_rcd.contents,
as_req_checksum_gen.length))
{
kdcPkinitDebug("pa_pkinit_parse_rep: checksum miscompare\n");
krtn = KRB5KDC_ERR_PREAUTH_FAILED;
goto error_out;
}
}
if (as_key->length) {
krb5_free_keyblock_contents(context, as_key);
}
*as_key = local_key;
#if PKINIT_DEBUG
fprintf(stderr, "pa_pkinit_parse_rep: SUCCESS\n");
fprintf(stderr, "enctype %d keylen %d keydata %02x %02x %02x %02x...\n",
(int)as_key->enctype, (int)as_key->length,
as_key->contents[0], as_key->contents[1],
as_key->contents[2], as_key->contents[3]);
#endif
krtn = 0;
error_out:
if (signer_cert.data) {
free(signer_cert.data);
}
if(as_req_checksum_rcd.contents) {
free(as_req_checksum_rcd.contents);
}
if(as_req_checksum_gen.contents) {
free(as_req_checksum_gen.contents);
}
if(krtn && (local_key.contents != NULL)) {
krb5_free_keyblock_contents(context, &local_key);
}
return krtn;
}
#endif
static
krb5_error_code pa_sam_2(krb5_context context,
krb5_kdc_req *request,
krb5_pa_data *in_padata,
krb5_pa_data ***out_padata,
krb5_data *encoded_request_body,
krb5_data *encoded_previous_request,
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_error_code retval;
krb5_sam_challenge_2 *sc2 = NULL;
krb5_sam_challenge_2_body *sc2b = NULL;
krb5_data tmp_data;
krb5_data response_data;
char name[100], banner[100], prompt[100], response[100];
krb5_prompt kprompt;
krb5_prompt_type prompt_type;
krb5_data defsalt;
krb5_checksum **cksum;
krb5_data *scratch = NULL;
krb5_boolean valid_cksum = 0;
krb5_enc_sam_response_enc_2 enc_sam_response_enc_2;
krb5_sam_response_2 sr2;
size_t ciph_len;
krb5_pa_data **sam_padata;
if (prompter == NULL)
return KRB5_LIBOS_CANTREADPWD;
tmp_data.length = in_padata->length;
tmp_data.data = (char *)in_padata->contents;
if ((retval = decode_krb5_sam_challenge_2(&tmp_data, &sc2)))
return(retval);
retval = decode_krb5_sam_challenge_2_body(&sc2->sam_challenge_2_body, &sc2b);
if (retval)
return(retval);
if (!sc2->sam_cksum || ! *sc2->sam_cksum) {
krb5_free_sam_challenge_2(context, sc2);
krb5_free_sam_challenge_2_body(context, sc2b);
return(KRB5_SAM_NO_CHECKSUM);
}
if (sc2b->sam_flags & KRB5_SAM_MUST_PK_ENCRYPT_SAD) {
krb5_free_sam_challenge_2(context, sc2);
krb5_free_sam_challenge_2_body(context, sc2b);
return(KRB5_SAM_UNSUPPORTED);
}
if (!krb5_c_valid_enctype(sc2b->sam_etype)) {
krb5_free_sam_challenge_2(context, sc2);
krb5_free_sam_challenge_2_body(context, sc2b);
return(KRB5_SAM_INVALID_ETYPE);
}
if (!(sc2b->sam_flags & KRB5_SAM_USE_SAD_AS_KEY)) {
retval = (gak_fct)(context, request->client,
sc2b->sam_etype, prompter,
prompter_data, salt, s2kparams, as_key, gak_data);
if (retval) {
krb5_free_sam_challenge_2(context, sc2);
krb5_free_sam_challenge_2_body(context, sc2b);
return(retval);
}
}
snprintf(name, sizeof(name), "%.*s",
SAMDATA(sc2b->sam_type_name, "SAM Authentication",
sizeof(name) - 1));
snprintf(banner, sizeof(banner), "%.*s",
SAMDATA(sc2b->sam_challenge_label,
sam_challenge_banner(sc2b->sam_type),
sizeof(banner)-1));
snprintf(prompt, sizeof(prompt), "%s%.*s%s%.*s",
sc2b->sam_challenge.length?"Challenge is [":"",
SAMDATA(sc2b->sam_challenge, "", 20),
sc2b->sam_challenge.length?"], ":"",
SAMDATA(sc2b->sam_response_prompt, "passcode", 55));
response_data.data = response;
response_data.length = sizeof(response);
kprompt.prompt = prompt;
kprompt.hidden = 1;
kprompt.reply = &response_data;
prompt_type = KRB5_PROMPT_TYPE_PREAUTH;
krb5int_set_prompt_types(context, &prompt_type);
if ((retval = ((*prompter)(context, prompter_data, name,
banner, 1, &kprompt)))) {
krb5_free_sam_challenge_2(context, sc2);
krb5_free_sam_challenge_2_body(context, sc2b);
krb5int_set_prompt_types(context, 0);
return(retval);
}
krb5int_set_prompt_types(context, (krb5_prompt_type *)NULL);
if ((salt->length == -1) && (salt->data == NULL)) {
if ((retval =
krb5_principal2salt(context, request->client, &defsalt))) {
krb5_free_sam_challenge_2(context, sc2);
krb5_free_sam_challenge_2_body(context, sc2b);
return(retval);
}
salt = &defsalt;
} else {
defsalt.length = 0;
}
if (!(sc2b->sam_flags & KRB5_SAM_USE_SAD_AS_KEY)) {
if (as_key->length) {
krb5_free_keyblock_contents(context, as_key);
as_key->length = 0;
}
retval = krb5_c_string_to_key(context, sc2b->sam_etype,
(krb5_data *)gak_data, salt, as_key);
if (retval) {
krb5_free_sam_challenge_2(context, sc2);
krb5_free_sam_challenge_2_body(context, sc2b);
if (defsalt.length) krb5_xfree(defsalt.data);
return(retval);
}
if (!(sc2b->sam_flags & KRB5_SAM_SEND_ENCRYPTED_SAD)) {
krb5_keyblock tmp_kb;
retval = krb5_c_string_to_key(context, sc2b->sam_etype,
&response_data, salt, &tmp_kb);
if (retval) {
krb5_free_sam_challenge_2(context, sc2);
krb5_free_sam_challenge_2_body(context, sc2b);
if (defsalt.length) krb5_xfree(defsalt.data);
return(retval);
}
retval = krb5int_c_combine_keys(context, as_key, &tmp_kb, as_key);
if (retval) {
krb5_free_sam_challenge_2(context, sc2);
krb5_free_sam_challenge_2_body(context, sc2b);
if (defsalt.length) krb5_xfree(defsalt.data);
return(retval);
}
krb5_free_keyblock_contents(context, &tmp_kb);
}
if (defsalt.length)
krb5_xfree(defsalt.data);
} else {
if (as_key->length) {
krb5_free_keyblock_contents(context, as_key);
as_key->length = 0;
}
retval = krb5_c_string_to_key(context, sc2b->sam_etype,
&response_data, salt, as_key);
if (defsalt.length)
krb5_xfree(defsalt.data);
if (retval) {
krb5_free_sam_challenge_2(context, sc2);
krb5_free_sam_challenge_2_body(context, sc2b);
return(retval);
}
}
cksum = sc2->sam_cksum;
for (; *cksum; cksum++) {
if (!krb5_c_is_keyed_cksum((*cksum)->checksum_type))
continue;
retval = krb5_c_verify_checksum(context, as_key,
KRB5_KEYUSAGE_PA_SAM_CHALLENGE_CKSUM,
&sc2->sam_challenge_2_body,
*cksum, &valid_cksum);
if (retval) {
krb5_free_data(context, scratch);
krb5_free_sam_challenge_2(context, sc2);
krb5_free_sam_challenge_2_body(context, sc2b);
return(retval);
}
if (valid_cksum)
break;
}
if (!valid_cksum) {
if (!(sc2b->sam_flags & KRB5_SAM_SEND_ENCRYPTED_SAD))
krb5_free_sam_challenge_2(context, sc2);
krb5_free_sam_challenge_2_body(context, sc2b);
return(KRB5KRB_AP_ERR_BAD_INTEGRITY);
}
enc_sam_response_enc_2.magic = KV5M_ENC_SAM_RESPONSE_ENC_2;
enc_sam_response_enc_2.sam_nonce = sc2b->sam_nonce;
if (sc2b->sam_flags & KRB5_SAM_SEND_ENCRYPTED_SAD) {
enc_sam_response_enc_2.sam_sad = response_data;
} else {
enc_sam_response_enc_2.sam_sad.data = NULL;
enc_sam_response_enc_2.sam_sad.length = 0;
}
retval = encode_krb5_enc_sam_response_enc_2(&enc_sam_response_enc_2,
&scratch);
if (retval) {
krb5_free_sam_challenge_2(context, sc2);
krb5_free_sam_challenge_2_body(context, sc2b);
return(retval);
}
memset(&sr2, 0, sizeof(sr2));
sr2.sam_type = sc2b->sam_type;
sr2.sam_flags = sc2b->sam_flags;
sr2.sam_track_id = sc2b->sam_track_id;
sr2.sam_nonce = sc2b->sam_nonce;
retval = krb5_c_encrypt_length(context, as_key->enctype, scratch->length,
&ciph_len);
if (retval) {
krb5_free_sam_challenge_2(context, sc2);
krb5_free_sam_challenge_2_body(context, sc2b);
return(retval);
}
sr2.sam_enc_nonce_or_sad.ciphertext.length = ciph_len;
sr2.sam_enc_nonce_or_sad.ciphertext.data =
(char *)malloc(sr2.sam_enc_nonce_or_sad.ciphertext.length);
if (!sr2.sam_enc_nonce_or_sad.ciphertext.data) {
krb5_free_sam_challenge_2(context, sc2);
krb5_free_sam_challenge_2_body(context, sc2b);
return(ENOMEM);
}
retval = krb5_c_encrypt(context, as_key, KRB5_KEYUSAGE_PA_SAM_RESPONSE,
NULL, scratch, &sr2.sam_enc_nonce_or_sad);
if (retval) {
krb5_free_sam_challenge_2(context, sc2);
krb5_free_sam_challenge_2_body(context, sc2b);
krb5_free_data(context, scratch);
krb5_free_data_contents(context, &sr2.sam_enc_nonce_or_sad.ciphertext);
return(retval);
}
krb5_free_data(context, scratch);
scratch = NULL;
retval = encode_krb5_sam_response_2(&sr2, &scratch);
krb5_free_sam_challenge_2(context, sc2);
krb5_free_sam_challenge_2_body(context, sc2b);
krb5_free_data_contents(context, &sr2.sam_enc_nonce_or_sad.ciphertext);
if (retval) {
return (retval);
}
sam_padata = calloc(2, sizeof(sam_padata[0]));
if (sam_padata == NULL) {
krb5_free_data(context, scratch);
return(ENOMEM);
}
sam_padata[0] = malloc(sizeof(krb5_pa_data));
if (sam_padata[0] == NULL) {
free(sam_padata);
krb5_free_data(context, scratch);
return(ENOMEM);
}
sam_padata[0]->magic = KV5M_PA_DATA;
sam_padata[0]->pa_type = KRB5_PADATA_SAM_RESPONSE_2;
sam_padata[0]->length = scratch->length;
sam_padata[0]->contents = (krb5_octet *) scratch->data;
*out_padata = sam_padata;
return(0);
}
static const pa_types_t pa_types[] = {
{
KRB5_PADATA_PW_SALT,
pa_salt,
PA_INFO,
},
{
KRB5_PADATA_AFS3_SALT,
pa_salt,
PA_INFO,
},
#if APPLE_PKINIT
{
KRB5_PADATA_PK_AS_REQ,
pa_pkinit_gen_req,
PA_INFO,
},
{
KRB5_PADATA_PK_AS_REP,
pa_pkinit_parse_rep,
PA_REAL,
},
#endif
{
KRB5_PADATA_ENC_TIMESTAMP,
pa_enc_timestamp,
PA_REAL,
},
{
KRB5_PADATA_SAM_CHALLENGE_2,
pa_sam_2,
PA_REAL,
},
{
KRB5_PADATA_SAM_CHALLENGE,
pa_sam,
PA_REAL,
},
{
-1,
NULL,
0,
},
};
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_gic_opt_ext *opte)
{
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,
(krb5_get_init_creds_opt *)opte,
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) {
int k;
for (k = 0; out_padata[k] != NULL; k++);
grow_pa_list(return_padata, &out_pa_list_size,
out_padata, k);
free(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_preauth_client_rock *get_data_rock,
krb5_gic_opt_ext *opte)
{
unsigned int h;
int i, j, out_pa_list_size;
int seen_etype_info2 = 0;
krb5_pa_data **out_pa = NULL, **out_pa_list = NULL;
krb5_data scratch;
krb5_etype_info etype_info = NULL;
krb5_error_code ret;
static const int paorder[] = { PA_INFO, PA_REAL };
int realdone;
if (in_padata == NULL) {
*out_padata = NULL;
return(0);
}
#ifdef DEBUG
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);
}
fprintf (stderr, "\n");
#endif
out_pa_list = NULL;
out_pa_list_size = 0;
for (h=0; h<(sizeof(paorder)/sizeof(paorder[0])); h++) {
realdone = 0;
for (i=0; in_padata[i] && !realdone; i++) {
int k, l, etype_found, valid_etype_found;
switch (in_padata[i]->pa_type) {
case KRB5_PADATA_ETYPE_INFO:
case KRB5_PADATA_ETYPE_INFO2:
{
krb5_preauthtype pa_type = in_padata[i]->pa_type;
if (etype_info) {
if (seen_etype_info2 || pa_type != KRB5_PADATA_ETYPE_INFO2)
continue;
if (pa_type == KRB5_PADATA_ETYPE_INFO2) {
krb5_free_etype_info( context, etype_info);
etype_info = NULL;
}
}
scratch.length = in_padata[i]->length;
scratch.data = (char *) in_padata[i]->contents;
if (pa_type == KRB5_PADATA_ETYPE_INFO2) {
seen_etype_info2++;
ret = decode_krb5_etype_info2(&scratch, &etype_info);
}
else ret = decode_krb5_etype_info(&scratch, &etype_info);
if (ret) {
ret = 0;
if (etype_info)
krb5_free_etype_info( context, etype_info);
etype_info = NULL;
continue;
}
if (etype_info[0] == NULL) {
krb5_free_etype_info(context, etype_info);
etype_info = NULL;
break;
}
for (etype_found = 0, valid_etype_found = 0, k = 0;
!etype_found && k < request->nktypes; k++) {
for (l = 0; etype_info[l]; l++) {
if (etype_info[l]->etype == request->ktype[k]) {
etype_found++;
break;
}
if (krb5_c_valid_enctype(etype_info[l]->etype))
valid_etype_found++;
}
}
if (!etype_found) {
if (valid_etype_found) {
ret = KRB5_CONFIG_ETYPE_NOSUPP;
goto cleanup;
}
else {
ret = KRB5_PROG_ETYPE_NOSUPP;
goto cleanup;
}
}
scratch.data = (char *) etype_info[l]->salt;
scratch.length = etype_info[l]->length;
krb5_free_data_contents(context, salt);
if (scratch.length == KRB5_ETYPE_NO_SALT)
salt->data = NULL;
else
if ((ret = krb5int_copy_data_contents( context, &scratch, salt)) != 0)
goto cleanup;
*etype = etype_info[l]->etype;
krb5_free_data_contents(context, s2kparams);
if ((ret = krb5int_copy_data_contents(context,
&etype_info[l]->s2kparams,
s2kparams)) != 0)
goto cleanup;
#ifdef DEBUG
for (j = 0; etype_info[j]; j++) {
krb5_etype_info_entry *e = etype_info[j];
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, "\n");
}
#endif
break;
}
case KRB5_PADATA_PW_SALT:
case KRB5_PADATA_AFS3_SALT:
if (etype_info)
continue;
break;
default:
;
}
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,
in_padata[i], &out_pa,
encoded_request_body,
encoded_previous_request,
salt, s2kparams, etype,
as_key,
prompter, prompter_data,
gak_fct, gak_data)))) {
if (paorder[h] == PA_INFO) {
#ifdef DEBUG
fprintf (stderr,
"internal function for type %d, flag %d "
"failed with err %d\n",
in_padata[i]->pa_type, paorder[h], ret);
#endif
ret = 0;
continue;
}
goto cleanup;
}
if (out_pa) {
int l;
for (l = 0; out_pa != NULL && out_pa[l] != NULL; l++);
ret = grow_pa_list(&out_pa_list, &out_pa_list_size,
out_pa, l);
free(out_pa);
if (ret != 0) {
goto cleanup;
}
}
if (paorder[h] == PA_REAL)
realdone = 1;
}
}
if (!realdone) {
krb5_init_preauth_context(context);
if (context->preauth_context != NULL) {
int module_ret = 0, 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,
opte);
if (ret == 0) {
if (module_ret == 0) {
if (paorder[h] == PA_REAL) {
realdone = 1;
}
}
}
}
}
}
}
*out_padata = out_pa_list;
if (etype_info)
krb5_free_etype_info(context, etype_info);
return(0);
cleanup:
if (out_pa_list) {
out_pa_list[out_pa_list_size++] = NULL;
krb5_free_pa_data(context, out_pa_list);
}
if (etype_info)
krb5_free_etype_info(context, etype_info);
return (ret);
}