diff options
author | Simo Sorce <simo@redhat.com> | 2015-04-04 10:53:52 -0400 |
---|---|---|
committer | Simo Sorce <simo@redhat.com> | 2015-05-27 09:45:56 -0400 |
commit | d5b6c8360116857623b4b67a42ed3788df2ba24a (patch) | |
tree | 2c3f7a30cc26d0f28a84c30304480804baf3546e | |
parent | 01fa05dd4ec7bd79abee8df0dd3642eabf138bcf (diff) | |
download | freeipa-d5b6c8360116857623b4b67a42ed3788df2ba24a.tar.gz freeipa-d5b6c8360116857623b4b67a42ed3788df2ba24a.tar.xz freeipa-d5b6c8360116857623b4b67a42ed3788df2ba24a.zip |
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>
-rw-r--r-- | daemons/ipa-kdb/ipa_kdb.c | 38 | ||||
-rw-r--r-- | daemons/ipa-kdb/ipa_kdb.h | 2 | ||||
-rw-r--r-- | daemons/ipa-kdb/ipa_kdb_passwords.c | 16 | ||||
-rw-r--r-- | daemons/ipa-kdb/ipa_kdb_principals.c | 80 | ||||
-rw-r--r-- | daemons/ipa-kdb/ipa_kdb_pwdpolicy.c | 8 | ||||
-rw-r--r-- | util/ipa_krb5.c | 85 | ||||
-rw-r--r-- | util/ipa_krb5.h | 2 |
7 files changed, 231 insertions, 0 deletions
diff --git a/daemons/ipa-kdb/ipa_kdb.c b/daemons/ipa-kdb/ipa_kdb.c index d20b6a1f4..fff35c9c9 100644 --- a/daemons/ipa-kdb/ipa_kdb.c +++ b/daemons/ipa-kdb/ipa_kdb.c @@ -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]) { diff --git a/daemons/ipa-kdb/ipa_kdb.h b/daemons/ipa-kdb/ipa_kdb.h index ba9968bce..3c6138599 100644 --- a/daemons/ipa-kdb/ipa_kdb.h +++ b/daemons/ipa-kdb/ipa_kdb.h @@ -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(). */ diff --git a/daemons/ipa-kdb/ipa_kdb_passwords.c b/daemons/ipa-kdb/ipa_kdb_passwords.c index 974ae8fc8..ad57181d5 100644 --- a/daemons/ipa-kdb/ipa_kdb_passwords.c +++ b/daemons/ipa-kdb/ipa_kdb_passwords.c @@ -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, diff --git a/daemons/ipa-kdb/ipa_kdb_principals.c b/daemons/ipa-kdb/ipa_kdb_principals.c index d0fd3e291..b3f8b1ad7 100644 --- a/daemons/ipa-kdb/ipa_kdb_principals.c +++ b/daemons/ipa-kdb/ipa_kdb_principals.c @@ -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) { diff --git a/daemons/ipa-kdb/ipa_kdb_pwdpolicy.c b/daemons/ipa-kdb/ipa_kdb_pwdpolicy.c index 6f3992be6..076314a12 100644 --- a/daemons/ipa-kdb/ipa_kdb_pwdpolicy.c +++ b/daemons/ipa-kdb/ipa_kdb_pwdpolicy.c @@ -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); } } diff --git a/util/ipa_krb5.c b/util/ipa_krb5.c index feb23eae9..65e10dd40 100644 --- a/util/ipa_krb5.c +++ b/util/ipa_krb5.c @@ -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; +} diff --git a/util/ipa_krb5.h b/util/ipa_krb5.h index 2153bd571..c2a0dde2d 100644 --- a/util/ipa_krb5.h +++ b/util/ipa_krb5.h @@ -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_ */ |