Detect default encsalts kadmin password change

When kadmin tries to change a password it will get the allowed keysalts
from the password policy. Failure to provide them will result in kadmin
using the defaults specified in the kdc.conf file or hardcoded defaults
(the default salt is then of type NORMAL).

This patch provides the supported values that have been read out of the
appropriate LDAP attribute when we read the server configuration.

Then at actual password change, check if kadmin is handing us back the exact
list of supported encsalts we sent it, and in that case replace it with the
real default encsalts.

Fixes https://fedorahosted.org/freeipa/ticket/4914

Signed-off-by: Simo Sorce <simo@redhat.com>
Reviewed-by: Martin Babinsky <mbabinsk@redhat.com>
This commit is contained in:
Simo Sorce 2015-04-04 10:53:52 -04:00
parent 01fa05dd4e
commit d5b6c83601
7 changed files with 231 additions and 0 deletions

View File

@ -56,6 +56,7 @@ static void ipadb_context_free(krb5_context kcontext,
ldap_unbind_ext_s((*ctx)->lcontext, NULL, NULL);
}
free((*ctx)->supp_encs);
free((*ctx)->def_encs);
ipadb_mspac_struct_free(&(*ctx)->mspac);
krb5_free_default_realm(kcontext, (*ctx)->realm);
@ -383,6 +384,43 @@ int ipadb_get_connection(struct ipadb_context *ipactx)
goto done;
}
/* defaults first, this is used to tell what default enc:salts to use
* for kadmin password changes */
vals = ldap_get_values_len(ipactx->lcontext, first,
"krbDefaultEncSaltTypes");
if (!vals || !vals[0]) {
goto done;
}
for (c = 0; vals[c]; c++) /* count */ ;
cvals = calloc(c, sizeof(char *));
if (!cvals) {
ret = ENOMEM;
goto done;
}
for (i = 0; i < c; i++) {
cvals[i] = strndup(vals[i]->bv_val, vals[i]->bv_len);
if (!cvals[i]) {
ret = ENOMEM;
goto done;
}
}
ret = parse_bval_key_salt_tuples(ipactx->kcontext,
(const char * const *)cvals, c,
&kst, &n_kst);
if (ret) {
goto done;
}
if (ipactx->def_encs) {
free(ipactx->def_encs);
}
ipactx->def_encs = kst;
ipactx->n_def_encs = n_kst;
/* supported enc salt types, use to tell kadmin what to accept
* but also to detect if kadmin is requesting the default set */
vals = ldap_get_values_len(ipactx->lcontext, first,
"krbSupportedEncSaltTypes");
if (!vals || !vals[0]) {

View File

@ -106,6 +106,8 @@ struct ipadb_context {
bool override_restrictions;
krb5_key_salt_tuple *supp_encs;
int n_supp_encs;
krb5_key_salt_tuple *def_encs;
int n_def_encs;
struct ipadb_mspac *mspac;
/* Don't access this directly, use ipadb_get_global_config(). */

View File

@ -159,6 +159,22 @@ krb5_error_code ipadb_change_pwd(krb5_context context,
pwd.data = passwd;
pwd.length = strlen(passwd);
/* detect if kadmin is just passing along the default set */
if (ks_tuple_count == ipactx->n_supp_encs) {
for (i = 0; i < ks_tuple_count; i++) {
if (ks_tuple[i].ks_enctype != ipactx->supp_encs[i].ks_enctype)
break;
if (ks_tuple[i].ks_salttype != ipactx->supp_encs[i].ks_salttype)
break;
}
if (i == ks_tuple_count) {
/* we got passed the default supported enctypes, replace with
* the actual default enctypes to use */
ks_tuple = ipactx->def_encs;
ks_tuple_count = ipactx->n_def_encs;
}
}
/* We further filter supported enctypes to restrict to the list
* we have in ldap */
kerr = filter_key_salt_tuples(context, ks_tuple, ks_tuple_count,

View File

@ -349,6 +349,83 @@ static enum ipadb_user_auth ipadb_get_user_auth(struct ipadb_context *ipactx,
return ua;
}
#define OSA_ADB_PRINC_VERSION_1 0x12345C01
/* The XDR encoding of OSA_PRINC_ENC is as follows:
version: int (signed 32 bit integer)
name: nullstring (null terminated variable string)
aux_attributes: long (signed 32 bit integer)
old_key_next: u_int (unsigned 32 bit integer)
adm_hist_kvno: u_char (unisgned char)
old_keys: array of keys, we do not care so alway u_int of 0
*/
#define OSA_PRINC_ENC_BASE_SIZE 20
static krb5_error_code ipadb_policydn_to_kdam_tl_data(const char *policydn,
krb5_db_entry *entry)
{
krb5_error_code kerr;
uint32_t tmp;
char *policy_name = NULL;
char *p;
uint8_t *buf = NULL;
size_t buf_len;
int slen;
int plen;
int cur;
/* policy objects must use cn as the RDN */
if (strncmp(policydn, "cn=", 3) != 0) {
return KRB5_KDB_INTERNAL_ERROR;
}
/* Should we try to consider the case where a ',' is part of the polict
* name ? */
policy_name = strdup(&policydn[3]);
if (!policy_name) {
kerr = ENOMEM;
goto done;
}
p = strchr(policy_name, ',');
if (p) *p = '\0';
/* Now we open code a basic KRB5_TL_KADM_DATA which is a XDR encoded
* structure in MIT code */
slen = strlen(policy_name) + 1;
/* A xdr varstring is preceeded by a 32bit len field and is always 32
* bit aligned */
plen = slen + 4;
plen = (((plen + 3) / 4) * 4);
buf_len = OSA_PRINC_ENC_BASE_SIZE + plen;
buf = calloc(1, buf_len);
if (!buf) {
kerr = ENOMEM;
goto done;
}
/* version */
cur = 0;
tmp = htobe32(OSA_ADB_PRINC_VERSION_1);
memcpy(&buf[cur], &tmp, 4);
cur += 4;
/* name */
tmp = htobe32(slen);
memcpy(&buf[cur], &tmp, 4);
memcpy(&buf[cur + 4], policy_name, slen);
cur += plen;
/* All the other fileds are left empty */
kerr = ipadb_set_tl_data(entry, KRB5_TL_KADM_DATA, buf_len, buf);
done:
free(policy_name);
free(buf);
return kerr;
}
static krb5_error_code ipadb_parse_ldap_entry(krb5_context kcontext,
char *principal,
LDAPMessage *lentry,
@ -617,6 +694,9 @@ static krb5_error_code ipadb_parse_ldap_entry(krb5_context kcontext,
}
ied->pw_policy_dn = restring;
kerr = ipadb_policydn_to_kdam_tl_data(restring, entry);
if (kerr) goto done;
ret = ipadb_ldap_attr_to_strlist(lcontext, lentry,
"passwordHistory", &restrlist);
if (ret != 0 && ret != ENOENT) {

View File

@ -237,6 +237,13 @@ krb5_error_code ipadb_get_pwd_policy(krb5_context kcontext, char *name,
pentry->pw_lockout_duration = result;
}
ret = ipa_kstuples_to_string(ipactx->supp_encs, ipactx->n_supp_encs,
&pentry->allowed_keysalts);
if (ret != 0) {
kerr = KRB5_KDB_INTERNAL_ERROR;
goto done;
}
*policy = pentry;
done:
@ -274,6 +281,7 @@ void ipadb_free_pwd_policy(krb5_context kcontext, osa_policy_ent_t val)
{
if (val) {
free(val->name);
free(val->allowed_keysalts);
free(val);
}
}

View File

@ -1075,3 +1075,88 @@ int create_keys(krb5_context krbctx,
return nkeys;
}
int ipa_kstuples_to_string(krb5_key_salt_tuple *kst, int n_kst, char **str)
{
char *buf = NULL;
char *tmp;
int buf_avail;
int buf_size;
int buf_cur;
int len;
int ret = 0;
int i;
buf_size = 512; /* should be enough for the default supported enctypes */
buf = malloc(buf_size);
if (!buf) {
ret = ENOMEM;
goto done;
}
buf_cur = 0;
for (i = 0; i < n_kst; i++) {
/* grow if too tight */
if (ret == ENOMEM) {
buf_size *= 2;
/* hard limit at 8k, do not eat all memory by mistake */
if (buf_size > 8192) goto done;
tmp = realloc(buf, buf_size);
if (!tmp) {
ret = ENOMEM;
goto done;
}
buf = tmp;
}
buf_avail = buf_size - buf_cur;
len = 0;
/* append separator if necessary */
if (buf_cur > 0) {
buf[buf_cur] = ',';
len++;
}
ret = krb5_enctype_to_name(kst[i].ks_enctype, 0,
&buf[buf_cur + len], buf_avail - len);
if (ret == ENOMEM) {
i--;
continue;
} else if (ret != 0) {
goto done;
}
len += strlen(&buf[buf_cur + len]);
buf[buf_cur + len] = ':';
len++;
ret = krb5_salttype_to_string(kst[i].ks_salttype,
&buf[buf_cur + len], buf_avail - len);
if (ret == ENOMEM) {
i--;
continue;
} else if (ret != 0) {
goto done;
}
len += strlen(&buf[buf_cur + len]);
if (buf_avail - len < 2) {
ret = ENOMEM;
i--;
continue;
}
buf_cur += len;
}
buf[buf_cur] = '\0';
*str = buf;
ret = 0;
done:
if (ret) {
free(buf);
}
return ret;
}

View File

@ -81,4 +81,6 @@ int create_keys(krb5_context krbctx,
const char *enctypes_string,
struct keys_container *keys,
char **err_msg);
int ipa_kstuples_to_string(krb5_key_salt_tuple *kst, int n_kst, char **str);
#endif /* __IPA_KRB5_H_ */