Fix for bug 4914.

I've tested it locally and seem to do exactly what is needed. I couldn't
detect any side effects, except that if you use kadmin to get a
randomized password for a service then you'll get a key for all
supported types (currently aes256, aes128, des3, rc4, camellia128,
camellia256) instead of just the default ones (aes256, aes128, des3,
rc4) if you do not specify enctypes. I think that is fine, we use
ipa-getkeytab anyway in the normal course of business and that one uses
a different code path.

Simo.

-- 
Simo Sorce * Red Hat, Inc * New York
>From 44935747078330df36d4063ab1caa2688efe1c89 Mon Sep 17 00:00:00 2001
From: Simo Sorce <s...@redhat.com>
Date: Sat, 4 Apr 2015 10:53:52 -0400
Subject: [PATCH] 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
---
 daemons/ipa-kdb/ipa_kdb.c            | 38 ++++++++++++++++
 daemons/ipa-kdb/ipa_kdb.h            |  2 +
 daemons/ipa-kdb/ipa_kdb_passwords.c  | 16 +++++++
 daemons/ipa-kdb/ipa_kdb_principals.c | 80 +++++++++++++++++++++++++++++++++
 daemons/ipa-kdb/ipa_kdb_pwdpolicy.c  |  8 ++++
 util/ipa_krb5.c                      | 85 ++++++++++++++++++++++++++++++++++++
 util/ipa_krb5.h                      |  2 +
 7 files changed, 231 insertions(+)

diff --git a/daemons/ipa-kdb/ipa_kdb.c b/daemons/ipa-kdb/ipa_kdb.c
index d20b6a1f4666a40f1f0523c5ee9b729e27b666ad..fff35c9c9b4cf0a1c7fd9a4e13d1029aa01160c3 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 ba9968bce7cff87f9f4a7fcd056ff7a906ce9e82..3c6138599fe202029cfc47d3f635525e4701b4be 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 974ae8fc872f13d8fcae133527f7a505ea23340d..ad57181d5049f36c69044bb2c9cfe183d7e4ea25 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 d0fd3e29181d82d476fd9f871f15158f5c7c1269..b3f8b1ad7784f55f55b4d6edd05f778a9389de27 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 6f3992be69ccec565079315c0a2c7259a8c3ffa3..076314a12840881a340763ab5693131aaccafec6 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 feb23eae9099b83b96a26cf36b7cbc31b478c4f6..65e10dd401edf6b54988fc4bfa5a2e08789b7b75 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 2153bd57142d1468031d0aa4b5d3f59ef5c890b5..c2a0dde2d24493132889548c6fb9d18f7a0b909e 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_ */
-- 
2.3.4

-- 
Manage your subscription for the Freeipa-devel mailing list:
https://www.redhat.com/mailman/listinfo/freeipa-devel
Contribute to FreeIPA: http://www.freeipa.org/page/Contribute/Code

Reply via email to