RCSID("$Id$");
-static krb5_error_code
-change_pw_and_update_keytab(krb5_context context, kcm_ccache ccache);
-
/*
* Get a new ticket using a keytab/cached key and swap it into
* an existing redentials cache
krb5_get_init_creds_opt *opt = NULL;
krb5_ccache_data ccdata;
char *in_tkt_service = NULL;
- int done = 0;
memset(&cred, 0, sizeof(cred));
opt);
} else {
/* loosely based on lib/krb5/init_creds_pw.c */
- while (!done) {
- ret = krb5_get_init_creds_keytab(context,
- &cred,
- ccache->client,
- ccache->key.keytab,
- 0,
- in_tkt_service,
- opt);
- switch (ret) {
- case KRB5KDC_ERR_KEY_EXPIRED:
- if (in_tkt_service != NULL &&
- strcmp(in_tkt_service, "kadmin/changepw") == 0) {
- goto out;
- }
-
- ret = change_pw_and_update_keytab(context, ccache);
- if (ret)
- goto out;
- break;
- case 0:
- default:
- done = 1;
- break;
- }
- }
+ ret = krb5_get_init_creds_keytab(context,
+ &cred,
+ ccache->client,
+ ccache->key.keytab,
+ 0,
+ in_tkt_service,
+ opt);
}
if (ret) {
return ret;
}
-
-static krb5_error_code
-change_pw(krb5_context context,
- kcm_ccache ccache,
- char *cpn,
- char *newpw)
-{
- krb5_error_code ret;
- krb5_creds cpw_cred;
- int result_code;
- krb5_data result_code_string;
- krb5_data result_string;
- krb5_get_init_creds_opt *options;
-
- memset(&cpw_cred, 0, sizeof(cpw_cred));
-
- ret = krb5_get_init_creds_opt_alloc(context, &options);
- if (ret)
- return ret;
- krb5_get_init_creds_opt_set_tkt_life(options, 60);
- krb5_get_init_creds_opt_set_forwardable(options, FALSE);
- krb5_get_init_creds_opt_set_proxiable(options, FALSE);
-
- krb5_data_zero(&result_code_string);
- krb5_data_zero(&result_string);
-
- ret = krb5_get_init_creds_keytab(context,
- &cpw_cred,
- ccache->client,
- ccache->key.keytab,
- 0,
- "kadmin/changepw",
- options);
- krb5_get_init_creds_opt_free(context, options);
- if (ret) {
- kcm_log(0, "Failed to acquire password change credentials "
- "for principal %s: %s",
- cpn, krb5_get_err_text(context, ret));
- goto out;
- }
-
- ret = krb5_set_password(context,
- &cpw_cred,
- newpw,
- ccache->client,
- &result_code,
- &result_code_string,
- &result_string);
- if (ret) {
- kcm_log(0, "Failed to change password for principal %s: %s",
- cpn, krb5_get_err_text(context, ret));
- goto out;
- }
-
- if (result_code) {
- kcm_log(0, "Failed to change password for principal %s: %.*s",
- cpn,
- (int)result_string.length,
- result_string.length > 0 ? (char *)result_string.data : "");
- goto out;
- }
-
-out:
- krb5_data_free(&result_string);
- krb5_data_free(&result_code_string);
- krb5_free_cred_contents(context, &cpw_cred);
-
- return ret;
-}
-
-struct kcm_keyseed_data {
- krb5_salt salt;
- const char *password;
-};
-
-static krb5_error_code
-kcm_password_key_proc(krb5_context context,
- krb5_enctype etype,
- krb5_salt salt,
- krb5_const_pointer keyseed,
- krb5_keyblock **key)
-{
- krb5_error_code ret;
- struct kcm_keyseed_data *s = (struct kcm_keyseed_data *)keyseed;
-
- /* we may be called multiple times */
- krb5_free_salt(context, s->salt);
- krb5_data_zero(&s->salt.saltvalue);
-
- /* stash the salt */
- s->salt.salttype = salt.salttype;
-
- ret = krb5_data_copy(&s->salt.saltvalue,
- salt.saltvalue.data,
- salt.saltvalue.length);
- if (ret)
- return ret;
-
- *key = (krb5_keyblock *)malloc(sizeof(**key));
- if (*key == NULL) {
- return ENOMEM;
- }
-
- ret = krb5_string_to_key_salt(context, etype, s->password,
- s->salt, *key);
- if (ret) {
- free(*key);
- *key = NULL;
- }
-
- return ret;
-}
-
-static krb5_error_code
-get_salt_and_kvno(krb5_context context,
- kcm_ccache ccache,
- krb5_enctype *etypes,
- char *cpn,
- char *newpw,
- krb5_salt *salt,
- unsigned *kvno)
-{
- krb5_error_code ret;
- krb5_creds creds;
- krb5_ccache_data ccdata;
- krb5_flags options = 0;
- krb5_kdc_rep reply;
- struct kcm_keyseed_data s;
-
- memset(&creds, 0, sizeof(creds));
- memset(&reply, 0, sizeof(reply));
-
- s.password = NULL;
- s.salt.salttype = (int)ETYPE_NULL;
- krb5_data_zero(&s.salt.saltvalue);
-
- *kvno = 0;
- kcm_internal_ccache(context, ccache, &ccdata);
- s.password = newpw;
-
- /* Do an AS-REQ to determine salt and key version number */
- ret = krb5_copy_principal(context, ccache->client, &creds.client);
- if (ret)
- return ret;
-
- /* Yes, get a ticket to ourselves */
- ret = krb5_copy_principal(context, ccache->client, &creds.server);
- if (ret) {
- krb5_free_principal(context, creds.client);
- return ret;
- }
-
- ret = krb5_get_in_tkt(context,
- options,
- NULL,
- etypes,
- NULL,
- kcm_password_key_proc,
- &s,
- NULL,
- NULL,
- &creds,
- &ccdata,
- &reply);
- if (ret) {
- kcm_log(0, "Failed to get self ticket for principal %s: %s",
- cpn, krb5_get_err_text(context, ret));
- krb5_free_salt(context, s.salt);
- } else {
- *salt = s.salt; /* retrieve stashed salt */
- if (reply.kdc_rep.enc_part.kvno != NULL)
- *kvno = *(reply.kdc_rep.enc_part.kvno);
- }
- /* ccache may have been modified but it will get trashed anyway */
-
- krb5_free_cred_contents(context, &creds);
- krb5_free_kdc_rep(context, &reply);
-
- return ret;
-}
-
-static krb5_error_code
-update_keytab_entry(krb5_context context,
- kcm_ccache ccache,
- krb5_enctype etype,
- char *cpn,
- char *spn,
- char *newpw,
- krb5_salt salt,
- unsigned kvno)
-{
- krb5_error_code ret;
- krb5_keytab_entry entry;
- krb5_data pw;
-
- memset(&entry, 0, sizeof(entry));
-
- pw.data = (char *)newpw;
- pw.length = strlen(newpw);
-
- ret = krb5_string_to_key_data_salt(context, etype, pw,
- salt, &entry.keyblock);
- if (ret) {
- kcm_log(0, "String to key conversion failed for principal %s "
- "and etype %d: %s",
- cpn, etype, krb5_get_err_text(context, ret));
- return ret;
- }
-
- if (spn == NULL) {
- ret = krb5_copy_principal(context, ccache->client,
- &entry.principal);
- if (ret) {
- kcm_log(0, "Failed to copy principal name %s: %s",
- cpn, krb5_get_err_text(context, ret));
- return ret;
- }
- } else {
- ret = krb5_parse_name(context, spn, &entry.principal);
- if (ret) {
- kcm_log(0, "Failed to parse SPN alias %s: %s",
- spn, krb5_get_err_text(context, ret));
- return ret;
- }
- }
-
- entry.vno = kvno;
- entry.timestamp = time(NULL);
-
- ret = krb5_kt_add_entry(context, ccache->key.keytab, &entry);
- if (ret) {
- kcm_log(0, "Failed to update keytab for principal %s "
- "and etype %d: %s",
- cpn, etype, krb5_get_err_text(context, ret));
- }
-
- krb5_kt_free_entry(context, &entry);
-
- return ret;
-}
-
-static krb5_error_code
-update_keytab_entries(krb5_context context,
- kcm_ccache ccache,
- krb5_enctype *etypes,
- char *cpn,
- char *spn,
- char *newpw,
- krb5_salt salt,
- unsigned kvno)
-{
- krb5_error_code ret = 0;
- int i;
-
- for (i = 0; etypes[i] != ETYPE_NULL; i++) {
- ret = update_keytab_entry(context, ccache, etypes[i],
- cpn, spn, newpw, salt, kvno);
- if (ret)
- break;
- }
-
- return ret;
-}
-
-static void
-generate_random_pw(krb5_context context,
- char *buf,
- size_t bufsiz)
-{
- unsigned char x[512], *p;
- size_t i;
-
- memset(x, 0, sizeof(x));
- krb5_generate_random_block(x, sizeof(x));
- p = x;
-
- for (i = 0; i < bufsiz; i++) {
- while (isprint(*p) == 0)
- p++;
-
- if (p - x >= sizeof(x)) {
- krb5_generate_random_block(x, sizeof(x));
- p = x;
- }
- buf[i] = (char)*p++;
- }
- buf[bufsiz - 1] = '\0';
- memset(x, 0, sizeof(x));
-}
-
-static krb5_error_code
-change_pw_and_update_keytab(krb5_context context,
- kcm_ccache ccache)
-{
- char newpw[121];
- krb5_error_code ret;
- unsigned kvno;
- krb5_salt salt;
- krb5_enctype *etypes = NULL;
- int i;
- char *cpn = NULL;
- char **spns = NULL;
-
- krb5_data_zero(&salt.saltvalue);
-
- ret = krb5_unparse_name(context, ccache->client, &cpn);
- if (ret) {
- kcm_log(0, "Failed to unparse name: %s",
- krb5_get_err_text(context, ret));
- goto out;
- }
-
- ret = krb5_get_default_in_tkt_etypes(context, &etypes);
- if (ret) {
- kcm_log(0, "Failed to determine default encryption types: %s",
- krb5_get_err_text(context, ret));
- goto out;
- }
-
- /* Generate a random password (there is no set keys protocol) */
- generate_random_pw(context, newpw, sizeof(newpw));
-
- /* Change it */
- ret = change_pw(context, ccache, cpn, newpw);
- if (ret)
- goto out;
-
- /* Do an AS-REQ to determine salt and key version number */
- ret = get_salt_and_kvno(context, ccache, etypes, cpn, newpw,
- &salt, &kvno);
- if (ret) {
- kcm_log(0, "Failed to determine salting principal for principal %s: %s",
- cpn, krb5_get_err_text(context, ret));
- goto out;
- }
-
- /* Add canonical name */
- ret = update_keytab_entries(context, ccache, etypes, cpn,
- NULL, newpw, salt, kvno);
- if (ret)
- goto out;
-
- /* Add SPN aliases, if any */
- spns = krb5_config_get_strings(context, NULL, "kcm",
- "system_ccache", "spn_aliases", NULL);
- if (spns != NULL) {
- for (i = 0; spns[i] != NULL; i++) {
- ret = update_keytab_entries(context, ccache, etypes, cpn,
- spns[i], newpw, salt, kvno);
- if (ret)
- goto out;
- }
- }
-
- kcm_log(0, "Changed expired password for principal %s in cache %s",
- cpn, ccache->name);
-
-out:
- if (cpn != NULL)
- free(cpn);
- if (spns != NULL)
- krb5_config_free_strings(spns);
- if (etypes != NULL)
- free(etypes);
- krb5_free_salt(context, salt);
- memset(newpw, 0, sizeof(newpw));
-
- return ret;
-}
-