#include "k5-int.h"
#include "com_err.h"
#include "init_creds_ctx.h"
#include "int-proto.h"
#include "os-proto.h"
krb5_error_code
krb5_get_as_key_password(krb5_context context,
krb5_principal client,
krb5_enctype etype,
krb5_prompter_fct prompter,
void *prompter_data,
krb5_data *salt,
krb5_data *params,
krb5_keyblock *as_key,
void *gak_data,
k5_response_items *ritems)
{
struct gak_password *gp = gak_data;
krb5_error_code ret;
krb5_data defsalt;
char *clientstr;
char promptstr[1024], pwbuf[1024];
krb5_data pw;
krb5_prompt prompt;
krb5_prompt_type prompt_type;
const char *rpass;
if (as_key == NULL) {
if (gp->password != NULL)
return 0;
return k5_response_items_ask_question(ritems,
KRB5_RESPONDER_QUESTION_PASSWORD,
"");
}
if (as_key->length) {
if (as_key->enctype != etype) {
krb5_free_keyblock_contents (context, as_key);
as_key->length = 0;
}
}
if (gp->password == NULL) {
rpass = k5_response_items_get_answer(ritems,
KRB5_RESPONDER_QUESTION_PASSWORD);
if (rpass != NULL) {
ret = alloc_data(&gp->storage, strlen(rpass));
if (ret)
return ret;
memcpy(gp->storage.data, rpass, strlen(rpass));
gp->password = &gp->storage;
}
}
if (gp->password == NULL) {
if (prompter == NULL)
return(EIO);
if ((ret = krb5_unparse_name(context, client, &clientstr)))
return(ret);
snprintf(promptstr, sizeof(promptstr), _("Password for %s"),
clientstr);
free(clientstr);
pw = make_data(pwbuf, sizeof(pwbuf));
prompt.prompt = promptstr;
prompt.hidden = 1;
prompt.reply = &pw;
prompt_type = KRB5_PROMPT_TYPE_PASSWORD;
k5_set_prompt_types(context, &prompt_type);
ret = (*prompter)(context, prompter_data, NULL, NULL, 1, &prompt);
k5_set_prompt_types(context, 0);
if (ret)
return(ret);
ret = krb5int_copy_data_contents(context, &pw, &gp->storage);
zap(pw.data, pw.length);
if (ret)
return ret;
gp->password = &gp->storage;
}
if (salt == NULL) {
if ((ret = krb5_principal2salt(context, client, &defsalt)))
return(ret);
salt = &defsalt;
} else {
defsalt.length = 0;
}
ret = krb5_c_string_to_key_with_params(context, etype, gp->password, salt,
params->data?params:NULL, as_key);
if (defsalt.length)
free(defsalt.data);
return(ret);
}
krb5_error_code KRB5_CALLCONV
krb5_init_creds_set_password(krb5_context context,
krb5_init_creds_context ctx,
const char *password)
{
char *s;
s = strdup(password);
if (s == NULL)
return ENOMEM;
zapfree(ctx->gakpw.storage.data, ctx->gakpw.storage.length);
ctx->gakpw.storage = string2data(s);
ctx->gakpw.password = &ctx->gakpw.storage;
ctx->gak_fct = krb5_get_as_key_password;
ctx->gak_data = &ctx->gakpw;
return 0;
}
static krb5_error_code
make_chpw_options(krb5_context context, krb5_get_init_creds_opt *in,
krb5_get_init_creds_opt **out)
{
krb5_get_init_creds_opt *opt;
*out = NULL;
opt = k5_gic_opt_shallow_copy(in);
if (opt == NULL)
return ENOMEM;
krb5_get_init_creds_opt_set_tkt_life(opt, 5 * 60);
krb5_get_init_creds_opt_set_renew_life(opt, 0);
krb5_get_init_creds_opt_set_forwardable(opt, 0);
krb5_get_init_creds_opt_set_proxiable(opt, 0);
opt->flags &= ~KRB5_GET_INIT_CREDS_OPT_ADDRESS_LIST;
opt->flags &= ~KRB5_GET_INIT_CREDS_OPT_ANONYMOUS;
krb5_get_init_creds_opt_set_out_ccache(context, opt, NULL);
*out = opt;
return 0;
}
krb5_error_code KRB5_CALLCONV
krb5_get_init_creds_password(krb5_context context,
krb5_creds *creds,
krb5_principal client,
const char *password,
krb5_prompter_fct prompter,
void *data,
krb5_deltat start_time,
const char *in_tkt_service,
krb5_get_init_creds_opt *options)
{
krb5_error_code ret;
krb5_kdc_rep *as_reply;
int tries;
krb5_creds chpw_creds;
krb5_get_init_creds_opt *chpw_opts = NULL;
struct gak_password gakpw;
krb5_data pw0, pw1;
char banner[1024], pw0array[1024], pw1array[1024];
krb5_prompt prompt[2];
krb5_prompt_type prompt_types[sizeof(prompt)/sizeof(prompt[0])];
char *message;
as_reply = NULL;
memset(&chpw_creds, 0, sizeof(chpw_creds));
memset(&gakpw, 0, sizeof(gakpw));
if (password != NULL) {
pw0 = string2data((char *)password);
gakpw.password = &pw0;
}
ret = k5_get_init_creds(context, creds, client, prompter, data, start_time,
in_tkt_service, options, krb5_get_as_key_password,
&gakpw, &as_reply);
if (!ret)
goto cleanup;
if (ret != KRB5KDC_ERR_KEY_EXP || prompter == NULL)
goto cleanup;
if (options && !(options->flags & KRB5_GET_INIT_CREDS_OPT_CHG_PWD_PRMPT))
goto cleanup;
TRACE_GIC_PWD_EXPIRED(context);
ret = make_chpw_options(context, options, &chpw_opts);
if (ret)
goto cleanup;
ret = k5_get_init_creds(context, &chpw_creds, client, prompter, data,
start_time, "kadmin/changepw", chpw_opts,
krb5_get_as_key_password, &gakpw, NULL);
if (ret)
goto cleanup;
pw0.data = pw0array;
pw0.data[0] = '\0';
pw0.length = sizeof(pw0array);
prompt[0].prompt = _("Enter new password");
prompt[0].hidden = 1;
prompt[0].reply = &pw0;
prompt_types[0] = KRB5_PROMPT_TYPE_NEW_PASSWORD;
pw1.data = pw1array;
pw1.data[0] = '\0';
pw1.length = sizeof(pw1array);
prompt[1].prompt = _("Enter it again");
prompt[1].hidden = 1;
prompt[1].reply = &pw1;
prompt_types[1] = KRB5_PROMPT_TYPE_NEW_PASSWORD_AGAIN;
strlcpy(banner, _("Password expired. You must change it now."),
sizeof(banner));
for (tries = 3; tries; tries--) {
TRACE_GIC_PWD_CHANGEPW(context, tries);
pw0.length = sizeof(pw0array);
pw1.length = sizeof(pw1array);
k5_set_prompt_types(context, prompt_types);
ret = (*prompter)(context, data, 0, banner,
sizeof(prompt)/sizeof(prompt[0]), prompt);
k5_set_prompt_types(context, 0);
if (ret)
goto cleanup;
if (strcmp(pw0.data, pw1.data) != 0) {
ret = KRB5_LIBOS_BADPWDMATCH;
snprintf(banner, sizeof(banner),
_("%s. Please try again."), error_message(ret));
} else if (pw0.length == 0) {
ret = KRB5_CHPW_PWDNULL;
snprintf(banner, sizeof(banner),
_("%s. Please try again."), error_message(ret));
} else {
int result_code;
krb5_data code_string;
krb5_data result_string;
if ((ret = krb5_change_password(context, &chpw_creds, pw0array,
&result_code, &code_string,
&result_string)))
goto cleanup;
if (result_code == 0) {
free(code_string.data);
free(result_string.data);
break;
}
ret = KRB5_CHPW_FAIL;
if (result_code != KRB5_KPASSWD_SOFTERROR) {
free(code_string.data);
free(result_string.data);
goto cleanup;
}
if (krb5_chpw_message(context, &result_string, &message) != 0)
message = NULL;
if (message != NULL && strlen(message) > (sizeof(banner) - 100))
message[sizeof(banner) - 100] = '\0';
snprintf(banner, sizeof(banner),
_("%.*s%s%s. Please try again.\n"),
(int) code_string.length, code_string.data,
message ? ": " : "", message ? message : "");
free(message);
free(code_string.data);
free(result_string.data);
}
}
if (ret)
goto cleanup;
TRACE_GIC_PWD_CHANGED(context);
gakpw.password = &pw0;
ret = k5_get_init_creds(context, creds, client, prompter, data,
start_time, in_tkt_service, options,
krb5_get_as_key_password, &gakpw, &as_reply);
if (ret)
goto cleanup;
cleanup:
free(chpw_opts);
zapfree(gakpw.storage.data, gakpw.storage.length);
memset(pw0array, 0, sizeof(pw0array));
memset(pw1array, 0, sizeof(pw1array));
krb5_free_cred_contents(context, &chpw_creds);
if (as_reply)
krb5_free_kdc_rep(context, as_reply);
return(ret);
}
krb5_error_code KRB5_CALLCONV
krb5_get_in_tkt_with_password(krb5_context context, krb5_flags options,
krb5_address *const *addrs, krb5_enctype *ktypes,
krb5_preauthtype *pre_auth_types,
const char *password, krb5_ccache ccache,
krb5_creds *creds, krb5_kdc_rep **ret_as_reply)
{
krb5_error_code retval;
struct gak_password gakpw;
krb5_data pw;
char * server;
krb5_principal server_princ, client_princ;
krb5_get_init_creds_opt *opts = NULL;
memset(&gakpw, 0, sizeof(gakpw));
if (password != NULL) {
pw = string2data((char *)password);
gakpw.password = &pw;
}
retval = k5_populate_gic_opt(context, &opts, options, addrs, ktypes,
pre_auth_types, creds);
if (retval)
return (retval);
retval = krb5_unparse_name( context, creds->server, &server);
if (retval) {
krb5_get_init_creds_opt_free(context, opts);
return (retval);
}
server_princ = creds->server;
client_princ = creds->client;
retval = k5_get_init_creds(context, creds, creds->client,
krb5_prompter_posix, NULL, 0, server, opts,
krb5_get_as_key_password, &gakpw, ret_as_reply);
krb5_free_unparsed_name( context, server);
krb5_get_init_creds_opt_free(context, opts);
zapfree(gakpw.storage.data, gakpw.storage.length);
if (retval) {
return (retval);
}
krb5_free_principal( context, creds->server);
krb5_free_principal( context, creds->client);
creds->client = client_princ;
creds->server = server_princ;
if (ccache)
if ((retval = krb5_cc_store_cred(context, ccache, creds)))
return (retval);
return retval;
}