#include <string.h>
#include "k5-int.h"
#include "int-proto.h"
#include "os-proto.h"
#define IN_TKT_DEBUG 0
#if IN_TKT_DEBUG
#define inTktDebug(args...) printf(args)
#else
#define inTktDebug(args...)
#endif
typedef krb5_error_code (*git_key_proc) (krb5_context,
const krb5_enctype,
krb5_data *,
krb5_const_pointer,
krb5_keyblock **);
typedef krb5_error_code (*git_decrypt_proc) (krb5_context,
const krb5_keyblock *,
krb5_const_pointer,
krb5_kdc_rep * );
static krb5_error_code make_preauth_list (krb5_context,
krb5_preauthtype *,
int, krb5_pa_data ***);
static krb5_int32 krb5int_addint32 (krb5_int32 x, krb5_int32 y)
{
if ((x > 0) && (y > (KRB5_INT32_MAX - x))) {
return KRB5_INT32_MAX;
} else if ((x < 0) && (y < (KRB5_INT32_MIN - x))) {
return KRB5_INT32_MIN;
}
return x + y;
}
static krb5_error_code
gen_nonce(krb5_context context,
krb5_int32 *nonce)
{
krb5_int32 time_now;
krb5_error_code retval = krb5_timeofday(context, &time_now);
if(retval) {
return retval;
}
*nonce = time_now;
return 0;
}
static krb5_error_code
send_as_request(krb5_context context,
krb5_kdc_req *request,
krb5_error ** ret_err_reply,
krb5_kdc_rep ** ret_as_reply,
int *use_master)
{
krb5_kdc_rep *as_reply = 0;
krb5_error_code retval;
krb5_data *packet = 0;
krb5_data reply;
char k4_version;
int tcp_only = 0;
inTktDebug("send_as_request top\n");
reply.data = 0;
if ((retval = encode_krb5_as_req(request, &packet)) != 0)
goto cleanup;
k4_version = packet->data[0];
send_again:
retval = krb5_sendto_kdc(context, packet,
krb5_princ_realm(context, request->client),
&reply, use_master, tcp_only);
inTktDebug("krb5_sendto_kdc returned %d\n", (int)retval);
if (retval)
goto cleanup;
if (krb5_is_krb_error(&reply)) {
krb5_error *err_reply;
if ((retval = decode_krb5_error(&reply, &err_reply)))
goto cleanup;
if (ret_err_reply) {
if (err_reply->error == KRB_ERR_RESPONSE_TOO_BIG
&& tcp_only == 0) {
tcp_only = 1;
krb5_free_error(context, err_reply);
free(reply.data);
reply.data = 0;
goto send_again;
}
*ret_err_reply = err_reply;
} else
krb5_free_error(context, err_reply);
goto cleanup;
}
if (!krb5_is_as_rep(&reply)) {
#define V4_KRB_PROT_VERSION 4
#define V4_AUTH_MSG_ERR_REPLY (5<<1)
unsigned int t_switch;
t_switch = reply.data[1];
t_switch &= ~1;
if (t_switch == V4_AUTH_MSG_ERR_REPLY
&& (reply.data[0] == V4_KRB_PROT_VERSION
|| reply.data[0] == k4_version)) {
retval = KRB5KRB_AP_ERR_V4_REPLY;
} else {
retval = KRB5KRB_AP_ERR_MSG_TYPE;
}
goto cleanup;
}
if ((retval = decode_krb5_as_rep(&reply, &as_reply)))
goto cleanup;
if (as_reply->msg_type != KRB5_AS_REP) {
retval = KRB5KRB_AP_ERR_MSG_TYPE;
krb5_free_kdc_rep(context, as_reply);
goto cleanup;
}
if (ret_as_reply)
*ret_as_reply = as_reply;
else
krb5_free_kdc_rep(context, as_reply);
cleanup:
if (packet)
krb5_free_data(context, packet);
if (reply.data)
free(reply.data);
return retval;
}
static krb5_error_code
decrypt_as_reply(krb5_context context,
krb5_kdc_req *request,
krb5_kdc_rep *as_reply,
git_key_proc key_proc,
krb5_const_pointer keyseed,
krb5_keyblock * key,
git_decrypt_proc decrypt_proc,
krb5_const_pointer decryptarg)
{
krb5_error_code retval;
krb5_keyblock * decrypt_key = 0;
krb5_data salt;
if (as_reply->enc_part2)
return 0;
if (key)
decrypt_key = key;
else {
if ((retval = krb5_principal2salt(context, request->client, &salt)))
return(retval);
retval = (*key_proc)(context, as_reply->enc_part.enctype,
&salt, keyseed, &decrypt_key);
krb5_xfree(salt.data);
if (retval)
goto cleanup;
}
if ((retval = (*decrypt_proc)(context, decrypt_key, decryptarg, as_reply)))
goto cleanup;
cleanup:
if (!key && decrypt_key)
krb5_free_keyblock(context, decrypt_key);
return (retval);
}
static krb5_error_code
verify_as_reply(krb5_context context,
krb5_timestamp time_now,
krb5_kdc_req *request,
krb5_kdc_rep *as_reply)
{
krb5_error_code retval;
if (!as_reply->enc_part2->times.starttime)
as_reply->enc_part2->times.starttime =
as_reply->enc_part2->times.authtime;
if (!krb5_principal_compare(context, as_reply->client, request->client)
|| !krb5_principal_compare(context, as_reply->enc_part2->server, request->server)
|| !krb5_principal_compare(context, as_reply->ticket->server, request->server)
|| (request->nonce != as_reply->enc_part2->nonce)
|| ((request->kdc_options & KDC_OPT_POSTDATED) &&
(request->from != 0) &&
(request->from != as_reply->enc_part2->times.starttime))
|| ((request->till != 0) &&
(as_reply->enc_part2->times.endtime > request->till))
|| ((request->kdc_options & KDC_OPT_RENEWABLE) &&
(request->rtime != 0) &&
(as_reply->enc_part2->times.renew_till > request->rtime))
|| ((request->kdc_options & KDC_OPT_RENEWABLE_OK) &&
!(request->kdc_options & KDC_OPT_RENEWABLE) &&
(as_reply->enc_part2->flags & KDC_OPT_RENEWABLE) &&
(request->till != 0) &&
(as_reply->enc_part2->times.renew_till > request->till))
) {
inTktDebug("verify_as_reply: KDCREP_MODIFIED\n");
#if IN_TKT_DEBUG
if(request->client->realm.length && request->client->data->length)
inTktDebug("request: name %s realm %s\n",
request->client->realm.data, request->client->data->data);
if(as_reply->client->realm.length && as_reply->client->data->length)
inTktDebug("reply : name %s realm %s\n",
as_reply->client->realm.data, as_reply->client->data->data);
#endif
return KRB5_KDCREP_MODIFIED;
}
if (context->library_options & KRB5_LIBOPT_SYNC_KDCTIME) {
retval = krb5_set_real_time(context,
as_reply->enc_part2->times.authtime, 0);
if (retval)
return retval;
} else {
if ((request->from == 0) &&
(labs(as_reply->enc_part2->times.starttime - time_now)
> context->clockskew))
return (KRB5_KDCREP_SKEW);
}
return 0;
}
static krb5_error_code
stash_as_reply(krb5_context context,
krb5_timestamp time_now,
krb5_kdc_req *request,
krb5_kdc_rep *as_reply,
krb5_creds * creds,
krb5_ccache ccache)
{
krb5_error_code retval;
krb5_data * packet;
krb5_principal client;
krb5_principal server;
client = NULL;
server = NULL;
if (!creds->client)
if ((retval = krb5_copy_principal(context, as_reply->client, &client)))
goto cleanup;
if (!creds->server)
if ((retval = krb5_copy_principal(context, as_reply->enc_part2->server,
&server)))
goto cleanup;
if ((retval = krb5_copy_keyblock_contents(context,
as_reply->enc_part2->session,
&creds->keyblock)))
goto cleanup;
creds->times = as_reply->enc_part2->times;
creds->is_skey = FALSE;
creds->ticket_flags = as_reply->enc_part2->flags;
if ((retval = krb5_copy_addresses(context, as_reply->enc_part2->caddrs,
&creds->addresses)))
goto cleanup;
creds->second_ticket.length = 0;
creds->second_ticket.data = 0;
if ((retval = encode_krb5_ticket(as_reply->ticket, &packet)))
goto cleanup;
creds->ticket = *packet;
krb5_xfree(packet);
if (ccache)
if ((retval = krb5_cc_store_cred(context, ccache, creds)))
goto cleanup;
if (!creds->client)
creds->client = client;
if (!creds->server)
creds->server = server;
cleanup:
if (retval) {
if (client)
krb5_free_principal(context, client);
if (server)
krb5_free_principal(context, server);
if (creds->keyblock.contents) {
memset((char *)creds->keyblock.contents, 0,
creds->keyblock.length);
krb5_xfree(creds->keyblock.contents);
creds->keyblock.contents = 0;
creds->keyblock.length = 0;
}
if (creds->ticket.data) {
krb5_xfree(creds->ticket.data);
creds->ticket.data = 0;
}
if (creds->addresses) {
krb5_free_addresses(context, creds->addresses);
creds->addresses = 0;
}
}
return (retval);
}
static krb5_error_code
make_preauth_list(krb5_context context,
krb5_preauthtype * ptypes,
int nptypes,
krb5_pa_data *** ret_list)
{
krb5_preauthtype * ptypep;
krb5_pa_data ** preauthp;
int i;
if (nptypes < 0) {
for (nptypes=0, ptypep = ptypes; *ptypep; ptypep++, nptypes++)
;
}
if ((preauthp =
(krb5_pa_data **) malloc((nptypes+1)*sizeof(krb5_pa_data *))) == NULL)
return(ENOMEM);
for (i=0; i<nptypes; i++) {
if ((preauthp[i] =
(krb5_pa_data *) malloc(sizeof(krb5_pa_data))) == NULL) {
for (; i>=0; i++)
free(preauthp[i]);
free(preauthp);
return (ENOMEM);
}
preauthp[i]->magic = KV5M_PA_DATA;
preauthp[i]->pa_type = ptypes[i];
preauthp[i]->length = 0;
preauthp[i]->contents = 0;
}
preauthp[nptypes] = NULL;
*ret_list = preauthp;
return 0;
}
#define MAX_IN_TKT_LOOPS 16
static const krb5_enctype get_in_tkt_enctypes[] = {
ENCTYPE_DES3_CBC_SHA1,
ENCTYPE_ARCFOUR_HMAC,
ENCTYPE_DES_CBC_MD5,
ENCTYPE_DES_CBC_MD4,
ENCTYPE_DES_CBC_CRC,
0
};
krb5_error_code KRB5_CALLCONV
krb5_get_in_tkt(krb5_context context,
const krb5_flags options,
krb5_address * const * addrs,
krb5_enctype * ktypes,
krb5_preauthtype * ptypes,
git_key_proc key_proc,
krb5_const_pointer keyseed,
git_decrypt_proc decrypt_proc,
krb5_const_pointer decryptarg,
krb5_creds * creds,
krb5_ccache ccache,
krb5_kdc_rep ** ret_as_reply)
{
krb5_error_code retval;
krb5_timestamp time_now;
krb5_keyblock * decrypt_key = 0;
krb5_kdc_req request;
krb5_pa_data **padata = 0;
krb5_error * err_reply;
krb5_kdc_rep * as_reply = 0;
krb5_pa_data ** preauth_to_use = 0;
int loopcount = 0;
krb5_int32 do_more = 0;
int use_master = 0;
inTktDebug("krb5_get_in_tkt top\n");
if (! krb5_realm_compare(context, creds->client, creds->server))
return KRB5_IN_TKT_REALM_MISMATCH;
if (ret_as_reply)
*ret_as_reply = 0;
request.magic = KV5M_KDC_REQ;
request.msg_type = KRB5_AS_REQ;
request.addresses = 0;
request.ktype = 0;
request.padata = 0;
if (addrs)
request.addresses = (krb5_address **) addrs;
else
if ((retval = krb5_os_localaddr(context, &request.addresses)))
goto cleanup;
request.kdc_options = options;
request.client = creds->client;
request.server = creds->server;
request.from = creds->times.starttime;
request.till = creds->times.endtime;
request.rtime = creds->times.renew_till;
retval = gen_nonce(context, (krb5_int32 *)&time_now);
if(retval) {
goto cleanup;
}
request.nonce = time_now;
request.ktype = malloc (sizeof(get_in_tkt_enctypes));
if (request.ktype == NULL) {
retval = ENOMEM;
goto cleanup;
}
memcpy(request.ktype, get_in_tkt_enctypes, sizeof(get_in_tkt_enctypes));
for (request.nktypes = 0;request.ktype[request.nktypes];request.nktypes++);
if (ktypes) {
int i, req, next = 0;
for (req = 0; ktypes[req]; req++) {
if (ktypes[req] == request.ktype[next]) {
next++;
continue;
}
for (i = next + 1; i < request.nktypes; i++)
if (ktypes[req] == request.ktype[i]) {
krb5_enctype t;
t = request.ktype[next];
request.ktype[next] = request.ktype[i];
request.ktype[i] = t;
next++;
break;
}
}
request.ktype[next] = 0;
request.nktypes = next;
}
request.authorization_data.ciphertext.length = 0;
request.authorization_data.ciphertext.data = 0;
request.unenc_authdata = 0;
request.second_ticket = 0;
if (ptypes) {
retval = make_preauth_list(context, ptypes, -1, &preauth_to_use);
if (retval)
goto cleanup;
}
while (1) {
if (loopcount++ > MAX_IN_TKT_LOOPS) {
retval = KRB5_GET_IN_TKT_LOOP;
goto cleanup;
}
inTktDebug("krb5_get_in_tkt calling krb5_obtain_padata\n");
if ((retval = krb5_obtain_padata(context, preauth_to_use, key_proc,
keyseed, creds, &request)) != 0)
goto cleanup;
if (preauth_to_use)
krb5_free_pa_data(context, preauth_to_use);
preauth_to_use = 0;
err_reply = 0;
as_reply = 0;
if ((retval = send_as_request(context, &request, &err_reply,
&as_reply, &use_master)))
goto cleanup;
if (err_reply) {
if (err_reply->error == KDC_ERR_PREAUTH_REQUIRED &&
err_reply->e_data.length > 0) {
retval = decode_krb5_padata_sequence(&err_reply->e_data,
&preauth_to_use);
krb5_free_error(context, err_reply);
if (retval)
goto cleanup;
continue;
} else {
retval = (krb5_error_code) err_reply->error
+ ERROR_TABLE_BASE_krb5;
krb5_free_error(context, err_reply);
goto cleanup;
}
} else if (!as_reply) {
retval = KRB5KRB_AP_ERR_MSG_TYPE;
goto cleanup;
}
if ((retval = krb5_process_padata(context, &request, as_reply,
key_proc, keyseed, decrypt_proc,
&decrypt_key, creds,
&do_more)) != 0)
goto cleanup;
if (!do_more)
break;
}
if ((retval = decrypt_as_reply(context, &request, as_reply, key_proc,
keyseed, decrypt_key, decrypt_proc,
decryptarg)))
goto cleanup;
if ((retval = verify_as_reply(context, time_now, &request, as_reply)))
goto cleanup;
if ((retval = stash_as_reply(context, time_now, &request, as_reply,
creds, ccache)))
goto cleanup;
cleanup:
if (request.ktype)
free(request.ktype);
if (!addrs && request.addresses)
krb5_free_addresses(context, request.addresses);
if (request.padata)
krb5_free_pa_data(context, request.padata);
if (padata)
krb5_free_pa_data(context, padata);
if (preauth_to_use)
krb5_free_pa_data(context, preauth_to_use);
if (decrypt_key)
krb5_free_keyblock(context, decrypt_key);
if (as_reply) {
if (ret_as_reply)
*ret_as_reply = as_reply;
else
krb5_free_kdc_rep(context, as_reply);
}
return (retval);
}
static const char *const conf_yes[] = {
"y", "yes", "true", "t", "1", "on",
0,
};
static const char *const conf_no[] = {
"n", "no", "false", "nil", "0", "off",
0,
};
int
_krb5_conf_boolean(const char *s)
{
const char *const *p;
for(p=conf_yes; *p; p++) {
if (!strcasecmp(*p,s))
return 1;
}
for(p=conf_no; *p; p++) {
if (!strcasecmp(*p,s))
return 0;
}
return 0;
}
static krb5_error_code
krb5_libdefault_string(krb5_context context, const krb5_data *realm,
const char *option, char **ret_value)
{
profile_t profile;
const char *names[5];
char **nameval = NULL;
krb5_error_code retval;
char realmstr[1024];
if (realm->length > sizeof(realmstr)-1)
return(EINVAL);
strncpy(realmstr, realm->data, realm->length);
realmstr[realm->length] = '\0';
if (!context || (context->magic != KV5M_CONTEXT))
return KV5M_CONTEXT;
profile = context->profile;
names[0] = "libdefaults";
names[1] = realmstr;
names[2] = option;
names[3] = 0;
retval = profile_get_values(profile, names, &nameval);
if (retval == 0 && nameval && nameval[0])
goto goodbye;
names[1] = option;
names[2] = 0;
retval = profile_get_values(profile, names, &nameval);
if (retval == 0 && nameval && nameval[0])
goto goodbye;
goodbye:
if (!nameval)
return(ENOENT);
if (!nameval[0]) {
retval = ENOENT;
} else {
*ret_value = malloc(strlen(nameval[0]) + 1);
if (!*ret_value)
retval = ENOMEM;
else
strcpy(*ret_value, nameval[0]);
}
profile_free_list(nameval);
return retval;
}
krb5_error_code
krb5_libdefault_boolean(krb5_context context, const krb5_data *realm,
const char *option, int *ret_value)
{
char *string = NULL;
krb5_error_code retval;
retval = krb5_libdefault_string(context, realm, option, &string);
if (retval)
return(retval);
*ret_value = _krb5_conf_boolean(string);
free(string);
return(0);
}
krb5_error_code KRB5_CALLCONV
krb5_get_init_creds(krb5_context context,
krb5_creds *creds,
krb5_principal client,
krb5_prompter_fct prompter,
void *prompter_data,
krb5_deltat start_time,
char *in_tkt_service,
krb5_get_init_creds_opt *options,
krb5_gic_get_as_key_fct gak_fct,
void *gak_data,
int *use_master,
krb5_kdc_rep **as_reply)
{
krb5_error_code ret;
krb5_kdc_req request;
krb5_pa_data **padata;
int tempint;
char *tempstr;
krb5_deltat tkt_life;
krb5_deltat renew_life;
int loopcount;
krb5_data salt;
krb5_data s2kparams;
krb5_keyblock as_key;
krb5_error *err_reply;
krb5_kdc_rep *local_as_reply;
krb5_timestamp time_now;
krb5_enctype etype = 0;
s2kparams.data = NULL;
s2kparams.length = 0;
request.server = NULL;
request.ktype = NULL;
request.addresses = NULL;
request.padata = NULL;
padata = NULL;
as_key.length = 0;
salt.length = 0;
salt.data = NULL;
local_as_reply = 0;
inTktDebug("krb5_get_init_creds top\n");
request.magic = KV5M_KDC_REQ;
request.msg_type = KRB5_AS_REQ;
request.kdc_options = context->kdc_default_options;
if (options && (options->flags & KRB5_GET_INIT_CREDS_OPT_FORWARDABLE))
tempint = options->forwardable;
else if ((ret = krb5_libdefault_boolean(context, &client->realm,
"forwardable", &tempint)) == 0)
;
else
tempint = 0;
if (tempint)
request.kdc_options |= KDC_OPT_FORWARDABLE;
if (options && (options->flags & KRB5_GET_INIT_CREDS_OPT_PROXIABLE))
tempint = options->proxiable;
else if ((ret = krb5_libdefault_boolean(context, &client->realm,
"proxiable", &tempint)) == 0)
;
else
tempint = 0;
if (tempint)
request.kdc_options |= KDC_OPT_PROXIABLE;
if (start_time > 0)
request.kdc_options |= (KDC_OPT_ALLOW_POSTDATE|KDC_OPT_POSTDATED);
if ((ret = krb5_timeofday(context, &request.from)))
goto cleanup;
request.from = krb5int_addint32(request.from, start_time);
if (options && (options->flags & KRB5_GET_INIT_CREDS_OPT_TKT_LIFE)) {
tkt_life = options->tkt_life;
} else if ((ret = krb5_libdefault_string(context, &client->realm,
"ticket_lifetime", &tempstr))
== 0) {
if ((ret = krb5_string_to_deltat(tempstr, &tkt_life))) {
free(tempstr);
goto cleanup;
}
} else {
tkt_life = 24*60*60;
}
request.till = krb5int_addint32(request.from, tkt_life);
if (options && (options->flags & KRB5_GET_INIT_CREDS_OPT_RENEW_LIFE)) {
renew_life = options->renew_life;
} else if ((ret = krb5_libdefault_string(context, &client->realm,
"renew_lifetime", &tempstr))
== 0) {
if ((ret = krb5_string_to_deltat(tempstr, &renew_life))) {
free(tempstr);
goto cleanup;
}
} else {
renew_life = 0;
}
if (renew_life > 0)
request.kdc_options |= KDC_OPT_RENEWABLE;
if (renew_life > 0) {
request.rtime = krb5int_addint32(request.from, renew_life);
if (request.rtime < request.till) {
request.rtime = request.till;
}
request.kdc_options &= ~(KDC_OPT_RENEWABLE_OK);
} else {
request.rtime = 0;
}
request.client = client;
if (in_tkt_service) {
if ((ret = krb5_parse_name(context, in_tkt_service, &request.server)))
goto cleanup;
if (request.server->realm.length < request.client->realm.length)
if ((request.server->realm.data =
(char *) realloc(request.server->realm.data,
request.client->realm.length)) == NULL) {
ret = ENOMEM;
goto cleanup;
}
request.server->realm.length = request.client->realm.length;
memcpy(request.server->realm.data, request.client->realm.data,
request.client->realm.length);
} else {
if ((ret = krb5_build_principal_ext(context, &request.server,
request.client->realm.length,
request.client->realm.data,
KRB5_TGS_NAME_SIZE,
KRB5_TGS_NAME,
request.client->realm.length,
request.client->realm.data,
0)))
goto cleanup;
}
ret = gen_nonce(context, (krb5_int32 *)&time_now);
if(ret) {
goto cleanup;
}
request.nonce = time_now;
if (options && (options->flags & KRB5_GET_INIT_CREDS_OPT_ETYPE_LIST)) {
request.ktype = options->etype_list;
request.nktypes = options->etype_list_length;
} else if ((ret = krb5_get_default_in_tkt_ktypes(context,
&request.ktype)) == 0) {
for (request.nktypes = 0;
request.ktype[request.nktypes];
request.nktypes++)
;
} else {
goto cleanup;
}
if (options && (options->flags & KRB5_GET_INIT_CREDS_OPT_ADDRESS_LIST)) {
request.addresses = options->address_list;
}
else if (((ret = krb5_libdefault_boolean(context, &client->realm,
"noaddresses", &tempint)) != 0)
|| (tempint == 1)) {
;
} else {
if ((ret = krb5_os_localaddr(context, &request.addresses)))
goto cleanup;
}
request.authorization_data.ciphertext.length = 0;
request.authorization_data.ciphertext.data = 0;
request.unenc_authdata = 0;
request.second_ticket = 0;
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)))
goto cleanup;
}
if (options && (options->flags & KRB5_GET_INIT_CREDS_OPT_SALT)) {
salt = *options->salt;
} else {
salt.length = SALT_TYPE_AFS_LENGTH;
salt.data = NULL;
}
for (loopcount = 0; loopcount < MAX_IN_TKT_LOOPS; loopcount++) {
if (request.padata) {
krb5_free_pa_data(context, request.padata);
request.padata = NULL;
}
inTktDebug("krb5_get_init_creds calling do_preauth (%d)\n", (int)loopcount);
if ((ret = krb5_do_preauth(context, &request,
padata, &request.padata,
&salt, &s2kparams, &etype, &as_key, prompter,
prompter_data, gak_fct, gak_data)))
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, &err_reply,
&local_as_reply, use_master))) {
inTktDebug("krb5_get_init_creds send_as_request err %d\n", (int)ret);
goto cleanup;
}
if (err_reply) {
if (err_reply->error == KDC_ERR_PREAUTH_REQUIRED &&
err_reply->e_data.length > 0) {
inTktDebug("krb5_get_init_creds decoding padata\n");
ret = decode_krb5_padata_sequence(&err_reply->e_data,
&padata);
krb5_free_error(context, err_reply);
if (ret)
goto cleanup;
} else {
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;
} else {
ret = KRB5KRB_AP_ERR_MSG_TYPE;
goto cleanup;
}
}
inTktDebug("krb5_get_init_creds done with send_as_request loop lc %d\n",
(int)loopcount);
if (loopcount == MAX_IN_TKT_LOOPS) {
ret = KRB5_GET_IN_TKT_LOOP;
goto cleanup;
}
if ((ret = krb5_do_preauth(context, &request,
local_as_reply->padata, &padata,
&salt, &s2kparams, &etype, &as_key, prompter,
prompter_data, gak_fct, gak_data))) {
inTktDebug("krb5_get_init_creds krb5_do_preauth returned %d\n", (int)ret);
goto cleanup;
}
if (as_key.length)
ret = decrypt_as_reply(context, NULL, local_as_reply, NULL,
NULL, &as_key, krb5_kdc_rep_decrypt_proc,
NULL);
else
ret = -1;
if (ret) {
if ((ret = ((*gak_fct)(context, request.client,
local_as_reply->enc_part.enctype,
prompter, prompter_data, &salt, &s2kparams,
&as_key, gak_data))))
goto cleanup;
if ((ret = decrypt_as_reply(context, NULL, local_as_reply, NULL,
NULL, &as_key, krb5_kdc_rep_decrypt_proc,
NULL)))
goto cleanup;
}
if ((ret = verify_as_reply(context, time_now, &request, local_as_reply)))
goto cleanup;
memset(creds, 0, sizeof(*creds));
if ((ret = stash_as_reply(context, time_now, &request, local_as_reply,
creds, NULL)))
goto cleanup;
ret = 0;
cleanup:
inTktDebug("krb5_get_init_creds returning %d\n", (int)ret);
if (request.server)
krb5_free_principal(context, request.server);
if (request.ktype &&
(!(options && (options->flags & KRB5_GET_INIT_CREDS_OPT_ETYPE_LIST))))
free(request.ktype);
if (request.addresses &&
(!(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 (request.padata)
krb5_free_pa_data(context, request.padata);
if (as_key.length)
krb5_free_keyblock_contents(context, &as_key);
if (salt.data &&
(!(options && (options->flags & KRB5_GET_INIT_CREDS_OPT_SALT))))
krb5_xfree(salt.data);
krb5_free_data_contents(context, &s2kparams);
if (as_reply)
*as_reply = local_as_reply;
else if (local_as_reply)
krb5_free_kdc_rep(context, local_as_reply);
return(ret);
}