When installing the adtrust code we need to be able to get the ipaNTHash
populated as in some cases we may need it to authenticate connections
over SMB w/o using kerberos during the trust setup phase.

The NT hash is really just the same thing as the rc4-hmac key we already
have by default in the Kerberos Keys.

This patch-set implements a check in the password plugin for the pre-mod
operation to catch the attempt to replace the attribute with the value
'MagicRegen' in the ipaNThash attribute.

If no previous ipaNTHash value is present, and the kerberos keys are
available, then we attempt to find the rc4-hmac key and if we find it we
store it in the ipaNTHash.

This will allow us to give the admin user (and potentially any other
user) the NT hash samba requires without forcing them to reset their
password, assuming the rc4-hmac key is present (currently it is by
default).

I marked this patch-set as RFC as I want opinions on the method (LDAP
modify with replace operation) I utilized to perform the extraction.

If it bode well with everybody we can consider the patch-set for
inclusion.

I tested it and extracting the hash works fine and it works later on
using smbclient to access a share.

This patchset implements task  #2867:
https://fedorahosted.org/freeipa/ticket/2867

Simo.

-- 
Simo Sorce * Red Hat, Inc * New York
>From c3d1c24413698e6d371b1de17b6efde9e1b7acb0 Mon Sep 17 00:00:00 2001
From: Simo Sorce <sso...@redhat.com>
Date: Fri, 6 Jul 2012 11:15:15 -0400
Subject: [PATCH 1/7] Move code into common krb5 utils

This moves the decoding function that reads the keys from the ber format
into a structure in the common krb5 util code right below the function
that encodes the same data structure into a ber format.
This way the 2 functions are in the same place and can be both used by
all ia components.
---
 daemons/ipa-kdb/ipa_kdb_principals.c |  148 ++-------------------------------
 util/ipa_krb5.c                      |  150 ++++++++++++++++++++++++++++++++++
 util/ipa_krb5.h                      |    2 +
 3 files changed, 159 insertions(+), 141 deletions(-)

diff --git a/daemons/ipa-kdb/ipa_kdb_principals.c b/daemons/ipa-kdb/ipa_kdb_principals.c
index d87d6fe9f82b479db6ab8e6b59a8b5ee580b9a27..6f8b296fa4cb19cbfe5c37536316d6f0e7f83b9c 100644
--- a/daemons/ipa-kdb/ipa_kdb_principals.c
+++ b/daemons/ipa-kdb/ipa_kdb_principals.c
@@ -205,152 +205,18 @@ static int ipadb_ldap_attr_to_key_data(LDAP *lcontext, LDAPMessage *le,
                                        krb5_kvno *res_mkvno)
 {
     struct berval **vals;
-    krb5_key_data *keys = NULL;
-    BerElement *be = NULL;
-    void *tmp;
-    int i = 0;
-    int ret = ENOENT;
+    int mkvno;
+    int ret;
 
     vals = ldap_get_values_len(lcontext, le, attrname);
-    if (vals) {
-        ber_tag_t tag;
-        ber_int_t major_vno;
-        ber_int_t minor_vno;
-        ber_int_t kvno;
-        ber_int_t mkvno;
-        ber_int_t type;
-        ber_tag_t seqtag;
-        ber_len_t seqlen;
-        ber_len_t setlen;
-        ber_tag_t retag;
-        ber_tag_t opttag;
-        struct berval tval;
-
-        be = ber_alloc_t(LBER_USE_DER);
-        if (!be) {
-            return ENOMEM;
-        }
-
-        /* reinit the ber element with the new val */
-        ber_init2(be, vals[0], LBER_USE_DER);
-
-        /* fill key_data struct with the data */
-        retag = ber_scanf(be, "{t[i]t[i]t[i]t[i]t[{",
-                          &tag, &major_vno,
-                          &tag, &minor_vno,
-                          &tag, &kvno,
-                          &tag, &mkvno,
-                          &seqtag);
-        if (retag == LBER_ERROR ||
-                major_vno != 1 ||
-                minor_vno != 1 ||
-                seqtag != (LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 4)) {
-            ret = EINVAL;
-            goto done;
-        }
-
-        retag = ber_skip_tag(be, &seqlen);
-
-        /* sequence of keys */
-        for (i = 0; retag == LBER_SEQUENCE; i++) {
-
-            tmp = realloc(keys, (i + 1) * sizeof(krb5_key_data));
-            if (!tmp) {
-                ret = ENOMEM;
-                goto done;
-            }
-            keys = tmp;
-
-            memset(&keys[i], 0, sizeof(krb5_key_data));
-
-            keys[i].key_data_kvno = kvno;
-
-            /* do we have a salt type ? (optional) */
-            retag = ber_scanf(be, "t", &opttag);
-            if (retag == LBER_ERROR) {
-                ret = EINVAL;
-                goto done;
-            }
-            if (opttag == (LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 0)) {
-                keys[i].key_data_ver = 2;
-
-                retag = ber_scanf(be, "[l{tl[i]",
-                                  &seqlen, &tag, &setlen, &type);
-                if (tag != (LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 0)) {
-                    ret = EINVAL;
-                    goto done;
-                }
-                keys[i].key_data_type[1] = type;
-
-                /* do we have salt data ? (optional) */
-                if (seqlen > setlen + 2) {
-                    retag = ber_scanf(be, "t[o]", &tag, &tval);
-                    if (retag == LBER_ERROR ||
-                        tag != (LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 1)) {
-                        ret = EINVAL;
-                        goto done;
-                    }
-                    keys[i].key_data_length[1] = tval.bv_len;
-                    keys[i].key_data_contents[1] = (krb5_octet *)tval.bv_val;
-                }
-
-                retag = ber_scanf(be, "}]t", &opttag);
-                if (retag == LBER_ERROR) {
-                    ret = EINVAL;
-                    goto done;
-                }
-
-            } else {
-                keys[i].key_data_ver = 1;
-            }
-
-            if (opttag != (LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 1)) {
-                ret = EINVAL;
-                goto done;
-            }
-
-            /* get the key */
-            retag = ber_scanf(be, "[{t[i]t[o]}]", &tag, &type, &tag, &tval);
-            if (retag == LBER_ERROR) {
-                ret = EINVAL;
-                goto done;
-            }
-            keys[i].key_data_type[0] = type;
-            keys[i].key_data_length[0] = tval.bv_len;
-            keys[i].key_data_contents[0] = (krb5_octet *)tval.bv_val;
-
-            /* check for sk2params */
-            retag = ber_peek_tag(be, &setlen);
-            if (retag == (LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 2)) {
-                /* not supported yet, skip */
-                retag = ber_scanf(be, "t[x]}");
-            } else {
-                retag = ber_scanf(be, "}");
-            }
-            if (retag == LBER_ERROR) {
-                ret = EINVAL;
-                goto done;
-            }
-
-            retag = ber_skip_tag(be, &seqlen);
-        }
-        *result = keys;
-        *num = i;
-        *res_mkvno = mkvno;
-        ret = 0;
+    if (!vals) {
+        return ENOENT;
     }
 
-done:
-    ber_free(be, 0); /* internal buffer is 'vals[0]' */
+    ret = ber_decode_krb5_key_data(vals[0], &mkvno, num, result);
     ldap_value_free_len(vals);
-    if (ret) {
-        for (i -= 1; keys && i >= 0; i--) {
-            free(keys[i].key_data_contents[0]);
-            free(keys[i].key_data_contents[1]);
-        }
-        free(keys);
-        *result = NULL;
-        *num = 0;
+    if (ret == 0) {
+        *res_mkvno = mkvno;
     }
     return ret;
 }
diff --git a/util/ipa_krb5.c b/util/ipa_krb5.c
index 0240c079ecd38271e5dbb36ec8c6a091001cfce7..934fd27d80cdd846f4de631b2dd587b0ad0f325c 100644
--- a/util/ipa_krb5.c
+++ b/util/ipa_krb5.c
@@ -427,6 +427,156 @@ done:
     return ret;
 }
 
+int ber_decode_krb5_key_data(struct berval *encoded, int *m_kvno,
+                             int *numk, krb5_key_data **data)
+{
+    krb5_key_data *keys = NULL;
+    BerElement *be = NULL;
+    void *tmp;
+    int i = 0;
+    ber_tag_t tag;
+    ber_int_t major_vno;
+    ber_int_t minor_vno;
+    ber_int_t kvno;
+    ber_int_t mkvno;
+    ber_int_t type;
+    ber_tag_t seqtag;
+    ber_len_t seqlen;
+    ber_len_t setlen;
+    ber_tag_t retag;
+    ber_tag_t opttag;
+    struct berval tval;
+    int ret;
+
+    be = ber_alloc_t(LBER_USE_DER);
+    if (!be) {
+        return ENOMEM;
+    }
+
+    /* reinit the ber element with the new val */
+    ber_init2(be, encoded, LBER_USE_DER);
+
+    /* fill key_data struct with the data */
+    retag = ber_scanf(be, "{t[i]t[i]t[i]t[i]t[{",
+                      &tag, &major_vno,
+                      &tag, &minor_vno,
+                      &tag, &kvno,
+                      &tag, &mkvno,
+                      &seqtag);
+    if (retag == LBER_ERROR ||
+            major_vno != 1 ||
+            minor_vno != 1 ||
+            seqtag != (LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 4)) {
+        ret = EINVAL;
+        goto done;
+    }
+
+    retag = ber_skip_tag(be, &seqlen);
+
+    /* sequence of keys */
+    for (i = 0; retag == LBER_SEQUENCE; i++) {
+
+        tmp = realloc(keys, (i + 1) * sizeof(krb5_key_data));
+        if (!tmp) {
+            ret = ENOMEM;
+            goto done;
+        }
+        keys = tmp;
+
+        memset(&keys[i], 0, sizeof(krb5_key_data));
+
+        keys[i].key_data_kvno = kvno;
+
+        /* do we have a salt type ? (optional) */
+        retag = ber_scanf(be, "t", &opttag);
+        if (retag == LBER_ERROR) {
+            ret = EINVAL;
+            goto done;
+        }
+        if (opttag == (LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 0)) {
+            keys[i].key_data_ver = 2;
+
+            retag = ber_scanf(be, "[l{tl[i]",
+                              &seqlen, &tag, &setlen, &type);
+            if (tag != (LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 0)) {
+                ret = EINVAL;
+                goto done;
+            }
+            keys[i].key_data_type[1] = type;
+
+            /* do we have salt data ? (optional) */
+            if (seqlen > setlen + 2) {
+                retag = ber_scanf(be, "t[o]", &tag, &tval);
+                if (retag == LBER_ERROR ||
+                    tag != (LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 1)) {
+                    ret = EINVAL;
+                    goto done;
+                }
+                keys[i].key_data_length[1] = tval.bv_len;
+                keys[i].key_data_contents[1] = (krb5_octet *)tval.bv_val;
+            }
+
+            retag = ber_scanf(be, "}]t", &opttag);
+            if (retag == LBER_ERROR) {
+                ret = EINVAL;
+                goto done;
+            }
+
+        } else {
+            keys[i].key_data_ver = 1;
+        }
+
+        if (opttag != (LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 1)) {
+            ret = EINVAL;
+            goto done;
+        }
+
+        /* get the key */
+        retag = ber_scanf(be, "[{t[i]t[o]}]", &tag, &type, &tag, &tval);
+        if (retag == LBER_ERROR) {
+            ret = EINVAL;
+            goto done;
+        }
+        keys[i].key_data_type[0] = type;
+        keys[i].key_data_length[0] = tval.bv_len;
+        keys[i].key_data_contents[0] = (krb5_octet *)tval.bv_val;
+
+        /* check for sk2params */
+        retag = ber_peek_tag(be, &setlen);
+        if (retag == (LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 2)) {
+            /* not supported yet, skip */
+            retag = ber_scanf(be, "t[x]}");
+        } else {
+            retag = ber_scanf(be, "}");
+        }
+        if (retag == LBER_ERROR) {
+            ret = EINVAL;
+            goto done;
+        }
+
+        retag = ber_skip_tag(be, &seqlen);
+    }
+
+    ret = 0;
+
+done:
+    ber_free(be, 0); /* internal buffer is 'encoded' */
+    if (ret) {
+        for (i -= 1; keys && i >= 0; i--) {
+            free(keys[i].key_data_contents[0]);
+            free(keys[i].key_data_contents[1]);
+        }
+        free(keys);
+        keys = NULL;
+        mkvno = 0;
+    }
+    *m_kvno = mkvno;
+    *numk = i;
+    *data = keys;
+    return ret;
+}
+
+
 krb5_error_code parse_bval_key_salt_tuples(krb5_context kcontext,
                                            const char * const *vals,
                                            int n_vals,
diff --git a/util/ipa_krb5.h b/util/ipa_krb5.h
index 97ffc47b5017507cd58e130755cfe050a287b30c..7fb0355724e3c4f522097df417d2686b85619319 100644
--- a/util/ipa_krb5.h
+++ b/util/ipa_krb5.h
@@ -49,6 +49,8 @@ void ipa_krb5_free_key_data(krb5_key_data *keys, int num_keys);
 int ber_encode_krb5_key_data(krb5_key_data *data,
                              int numk, int mkvno,
                              struct berval **encoded);
+int ber_decode_krb5_key_data(struct berval *encoded, int *m_kvno,
+                             int *numk, krb5_key_data **data);
 
 krb5_error_code parse_bval_key_salt_tuples(krb5_context kcontext,
                                            const char * const *vals,
-- 
1.7.10.4

>From edd59aeb353d63173a585e003acb008d74443bde Mon Sep 17 00:00:00 2001
From: Simo Sorce <sso...@redhat.com>
Date: Fri, 6 Jul 2012 17:03:19 -0400
Subject: [PATCH 2/7] Improve loops around slapi mods

Avoid the need to allocate/free a Slapi_Mod and avoid checking for attribute
equvalence after a match (use if/else)
---
 .../ipa-pwd-extop/ipapwd_prepost.c                 |  130 ++++++++++----------
 1 file changed, 62 insertions(+), 68 deletions(-)

diff --git a/daemons/ipa-slapi-plugins/ipa-pwd-extop/ipapwd_prepost.c b/daemons/ipa-slapi-plugins/ipa-pwd-extop/ipapwd_prepost.c
index 181bd0ee785784c52865c03cb696a8a72fb9905a..deae6477772f82edcc4674a1c9580661c3dae94b 100644
--- a/daemons/ipa-slapi-plugins/ipa-pwd-extop/ipapwd_prepost.c
+++ b/daemons/ipa-slapi-plugins/ipa-pwd-extop/ipapwd_prepost.c
@@ -394,7 +394,7 @@ static int ipapwd_pre_mod(Slapi_PBlock *pb)
     struct ipapwd_krbcfg *krbcfg = NULL;
     char *errMesg = NULL;
     LDAPMod **mods;
-    Slapi_Mod *smod, *tmod;
+    LDAPMod *lmod;
     Slapi_Mods *smods = NULL;
     char *userpw = NULL;
     char *unhashedpw = NULL;
@@ -434,52 +434,43 @@ static int ipapwd_pre_mod(Slapi_PBlock *pb)
     /* In the first pass,
      * only check there is anything we are interested in */
     is_pwd_op = 0;
-    tmod = slapi_mod_new();
-    smod = slapi_mods_get_first_smod(smods, tmod);
-    while (smod) {
+    lmod = slapi_mods_get_first_mod(smods);
+    while (lmod) {
         struct berval *bv;
-        const char *type;
-        int mop;
 
-        type = slapi_mod_get_type(smod);
-        if (slapi_attr_types_equivalent(type, SLAPI_USERPWD_ATTR)) {
-            mop = slapi_mod_get_operation(smod);
+        if (slapi_attr_types_equivalent(lmod->mod_type, SLAPI_USERPWD_ATTR)) {
             /* check op filtering out LDAP_MOD_BVALUES */
-            switch (mop & 0x0f) {
+            switch (lmod->mod_op & 0x0f) {
             case LDAP_MOD_ADD:
             case LDAP_MOD_REPLACE:
                 is_pwd_op = 1;
             default:
                 break;
             }
-        }
-
-        /* we check for unahsehd password here so that we are sure to catch them
-         * early, before further checks go on, this helps checking
-         * LDAP_MOD_DELETE operations in some corner cases later */
-        /* we keep only the last one if multiple are provided for any absurd
-	 * reason */
-        if (slapi_attr_types_equivalent(type, "unhashed#user#password")) {
-            bv = slapi_mod_get_first_value(smod);
-            if (!bv) {
-                slapi_mod_free(&tmod);
+        } else if (slapi_attr_types_equivalent(lmod->mod_type,
+                                                "unhashed#user#password")) {
+            /* we check for unahsehd password here so that we are sure to
+             * catch them early, before further checks go on, this helps
+             * checking LDAP_MOD_DELETE operations in some corner cases later.
+             * We keep only the last one if multiple are provided for any
+             * reason */
+            if (!lmod->mod_bvalues ||
+                !lmod->mod_bvalues[0]) {
                 rc = LDAP_OPERATIONS_ERROR;
                 goto done;
             }
+            bv = lmod->mod_bvalues[0];
             slapi_ch_free_string(&unhashedpw);
             unhashedpw = slapi_ch_malloc(bv->bv_len+1);
             if (!unhashedpw) {
-                slapi_mod_free(&tmod);
                 rc = LDAP_OPERATIONS_ERROR;
                 goto done;
             }
             memcpy(unhashedpw, bv->bv_val, bv->bv_len);
             unhashedpw[bv->bv_len] = '\0';
         }
-        slapi_mod_done(tmod);
-        smod = slapi_mods_get_next_smod(smods, tmod);
+        lmod = slapi_mods_get_next_mod(smods);
     }
-    slapi_mod_free(&tmod);
 
     /* If userPassword is not modified we are done here */
     if (! is_pwd_op) {
@@ -487,7 +478,7 @@ static int ipapwd_pre_mod(Slapi_PBlock *pb)
         goto done;
     }
 
-    /* OK swe have something interesting here, start checking for
+    /* OK we have something interesting here, start checking for
      * pre-requisites */
 
     /* Get target DN */
@@ -532,33 +523,27 @@ static int ipapwd_pre_mod(Slapi_PBlock *pb)
     }
 
     /* run through the mods again and adjust flags if operations affect them */
-    tmod = slapi_mod_new();
-    smod = slapi_mods_get_first_smod(smods, tmod);
-    while (smod) {
+    lmod = slapi_mods_get_first_mod(smods);
+    while (lmod) {
         struct berval *bv;
-        const char *type;
-        int mop;
 
-        type = slapi_mod_get_type(smod);
-        if (slapi_attr_types_equivalent(type, SLAPI_USERPWD_ATTR)) {
-            mop = slapi_mod_get_operation(smod);
+        if (slapi_attr_types_equivalent(lmod->mod_type, SLAPI_USERPWD_ATTR)) {
             /* check op filtering out LDAP_MOD_BVALUES */
-            switch (mop & 0x0f) {
+            switch (lmod->mod_op & 0x0f) {
             case LDAP_MOD_ADD:
                 /* FIXME: should we try to track cases where we would end up
                  * with multiple userPassword entries ?? */
             case LDAP_MOD_REPLACE:
                 is_pwd_op = 1;
-                bv = slapi_mod_get_first_value(smod);
-                if (!bv) {
-                    slapi_mod_free(&tmod);
+                if (!lmod->mod_bvalues ||
+                    !lmod->mod_bvalues[0]) {
                     rc = LDAP_OPERATIONS_ERROR;
                     goto done;
                 }
+                bv = lmod->mod_bvalues[0];
                 slapi_ch_free_string(&userpw);
                 userpw = slapi_ch_malloc(bv->bv_len+1);
                 if (!userpw) {
-                    slapi_mod_free(&tmod);
                     rc = LDAP_OPERATIONS_ERROR;
                     goto done;
                 }
@@ -569,23 +554,27 @@ static int ipapwd_pre_mod(Slapi_PBlock *pb)
                 /* reset only if we are deleting all values, or the exact
                  * same value previously set, otherwise we are just trying to
                  * add a new value and delete an existing one */
-                bv = slapi_mod_get_first_value(smod);
-                if (!bv) {
+                if (!lmod->mod_bvalues ||
+                    !lmod->mod_bvalues[0]) {
                     is_pwd_op = 0;
                 } else {
-                    if ((userpw && 0 == strncmp(userpw, bv->bv_val, bv->bv_len)) ||
-                        (unhashedpw && 0 == strncmp(unhashedpw, bv->bv_val, bv->bv_len)))
+                    bv = lmod->mod_bvalues[0];
+                    if ((userpw &&
+                         strncmp(userpw, bv->bv_val, bv->bv_len) == 0) ||
+                        (unhashedpw &&
+                         strncmp(unhashedpw, bv->bv_val, bv->bv_len) == 0)) {
                         is_pwd_op = 0;
+                    }
                 }
             default:
                 break;
             }
-        }
 
-        if (slapi_attr_types_equivalent(type, SLAPI_ATTR_OBJECTCLASS)) {
-            mop = slapi_mod_get_operation(smod);
+        } else if (slapi_attr_types_equivalent(lmod->mod_type,
+                                                SLAPI_ATTR_OBJECTCLASS)) {
+            int i;
             /* check op filtering out LDAP_MOD_BVALUES */
-            switch (mop & 0x0f) {
+            switch (lmod->mod_op & 0x0f) {
             case LDAP_MOD_REPLACE:
                 /* if objectclasses are replaced we need to start clean with
                  * flags, so we sero them out and see if they get set again */
@@ -594,20 +583,23 @@ static int ipapwd_pre_mod(Slapi_PBlock *pb)
                 is_ipant = 0;
 
             case LDAP_MOD_ADD:
-                bv = slapi_mod_get_first_value(smod);
-                if (!bv) {
-                    slapi_mod_free(&tmod);
+                if (!lmod->mod_bvalues ||
+                    !lmod->mod_bvalues[0]) {
                     rc = LDAP_OPERATIONS_ERROR;
                     goto done;
                 }
-                do {
-                    if (0 == strncasecmp("krbPrincipalAux", bv->bv_val, bv->bv_len))
+                for (i = 0; (bv = lmod->mod_bvalues[i]) != NULL; i++) {
+                    if (strncasecmp("krbPrincipalAux",
+                                    bv->bv_val, bv->bv_len) == 0) {
                         is_krb = 1;
-                    if (0 == strncasecmp("sambaSamAccount", bv->bv_val, bv->bv_len))
+                    } else if (strncasecmp("sambaSamAccount",
+                                           bv->bv_val, bv->bv_len) == 0) {
                         is_smb = 1;
-                    if (0 == strncasecmp("ipaNTUserAttrs", bv->bv_val, bv->bv_len))
+                    } else if (strncasecmp("ipaNTUserAttrs",
+                                           bv->bv_val, bv->bv_len) == 0) {
                         is_ipant = 1;
-                } while ((bv = slapi_mod_get_next_value(smod)) != NULL);
+                    }
+                }
 
                 break;
 
@@ -620,32 +612,34 @@ static int ipapwd_pre_mod(Slapi_PBlock *pb)
             default:
                 break;
             }
-        }
 
-        /* if we are getting a krbPrincipalKey, also avoid regenerating the keys,
-         * it means kadmin has alredy done the job and is simply keeping
-         * userPassword and sambaXXPAssword in sync */
-        if (slapi_attr_types_equivalent(type, "krbPrincipalKey")) {
+        } else if (slapi_attr_types_equivalent(lmod->mod_type,
+                                               "krbPrincipalKey")) {
+
+            /* if we are getting a krbPrincipalKey, also avoid regenerating
+             * the keys, it means kadmin has alredy done the job and is simply
+             * keeping userPassword and sambaXXPAssword in sync */
+
             /* we also check we have enough authority */
             if (is_root) {
                 has_krb_keys = 1;
             }
-        }
 
-        /* if we are getting a passwordHistory, also avoid regenerating the hashes,
-         * it means kadmin has alredy done the job and is simply keeping
-         * userPassword and sambaXXPAssword in sync */
-        if (slapi_attr_types_equivalent(type, "passwordHistory")) {
+        } else if (slapi_attr_types_equivalent(lmod->mod_type,
+                                               "passwordHistory")) {
+
+            /* if we are getting a passwordHistory, also avoid regenerating
+             * the hashes, it means kadmin has alredy done the job and is
+             * simply keeping userPassword and sambaXXPAssword in sync */
+
             /* we also check we have enough authority */
             if (is_root) {
                 has_history = 1;
             }
         }
 
-        slapi_mod_done(tmod);
-        smod = slapi_mods_get_next_smod(smods, tmod);
+        lmod = slapi_mods_get_next_mod(smods);
     }
-    slapi_mod_free(&tmod);
 
     if (is_krb) {
         if (has_krb_keys) {
-- 
1.7.10.4

>From d2626dec5388172dfed54e31540c2925582dae66 Mon Sep 17 00:00:00 2001
From: Simo Sorce <sso...@redhat.com>
Date: Fri, 6 Jul 2012 16:18:29 -0400
Subject: [PATCH 3/7] Add special modify op to regen ipaNTHash

The NT Hash is the same thing as the RC4-HMAC key, so we add a function to
extract it from krb5 keys if they are available to avoid forcing a password
change when configuring trust relationships.
---
 .../ipa-pwd-extop/ipapwd_prepost.c                 |  147 +++++++++++++++++++-
 1 file changed, 144 insertions(+), 3 deletions(-)

diff --git a/daemons/ipa-slapi-plugins/ipa-pwd-extop/ipapwd_prepost.c b/daemons/ipa-slapi-plugins/ipa-pwd-extop/ipapwd_prepost.c
index deae6477772f82edcc4674a1c9580661c3dae94b..24fa52eb9ac92004576ccdba4f576162c358770d 100644
--- a/daemons/ipa-slapi-plugins/ipa-pwd-extop/ipapwd_prepost.c
+++ b/daemons/ipa-slapi-plugins/ipa-pwd-extop/ipapwd_prepost.c
@@ -41,7 +41,12 @@
 #  include <config.h>
 #endif
 
-#define _XOPEN_SOURCE /* strptime needs this */
+/* strptime needs _XOPEN_SOURCE and endian.h needs __USE_BSD
+ * _GNU_SOURCE imply both, and we use it elsewhere, so use this */
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE 1
+#endif
+
 #include <stdio.h>
 #include <string.h>
 #include <strings.h>
@@ -53,6 +58,7 @@
 #include <dirsrv/slapi-plugin.h>
 #include <lber.h>
 #include <time.h>
+#include <endian.h>
 
 #include "ipapwd.h"
 #include "util.h"
@@ -379,6 +385,12 @@ done:
     return 0;
 }
 
+#define NTHASH_REGEN_VAL "MagicRegen"
+#define NTHASH_REGEN_LEN sizeof(NTHASH_REGEN_VAL)
+static int ipapwd_regen_nthash(Slapi_PBlock *pb, Slapi_Mods *smods,
+                               char *dn, struct slapi_entry *entry,
+                               struct ipapwd_krbcfg *krbcfg);
+
 /* PRE MOD Operation:
  * Gets the clean text password (fail the operation if the password came
  * pre-hashed, unless this is a replicated operation).
@@ -407,6 +419,7 @@ static int ipapwd_pre_mod(Slapi_PBlock *pb)
     int has_krb_keys = 0;
     int has_history = 0;
     int gen_krb_keys = 0;
+    int is_magic_regen = 0;
     int ret, rc;
 
     LOG_TRACE( "=>\n");
@@ -447,6 +460,27 @@ static int ipapwd_pre_mod(Slapi_PBlock *pb)
             default:
                 break;
             }
+        } else if (slapi_attr_types_equivalent(lmod->mod_type, "ipaNTHash")) {
+            /* check op filtering out LDAP_MOD_BVALUES */
+            switch (lmod->mod_op & 0x0f) {
+            case LDAP_MOD_REPLACE:
+                if (!lmod->mod_bvalues ||
+                    !lmod->mod_bvalues[0]) {
+                    rc = LDAP_OPERATIONS_ERROR;
+                    goto done;
+                }
+                bv = lmod->mod_bvalues[0];
+                if ((bv->bv_len >= NTHASH_REGEN_LEN -1) &&
+                    (bv->bv_len <= NTHASH_REGEN_LEN) &&
+                    (strncmp(NTHASH_REGEN_VAL,
+                             bv->bv_val, bv->bv_len) == 0)) {
+                    is_magic_regen = 1;
+                    /* make sure the database will later ignore this mod */
+                    slapi_mods_remove(smods);
+                }
+            default:
+                break;
+            }
         } else if (slapi_attr_types_equivalent(lmod->mod_type,
                                                 "unhashed#user#password")) {
             /* we check for unahsehd password here so that we are sure to
@@ -472,8 +506,9 @@ static int ipapwd_pre_mod(Slapi_PBlock *pb)
         lmod = slapi_mods_get_next_mod(smods);
     }
 
-    /* If userPassword is not modified we are done here */
-    if (! is_pwd_op) {
+    /* If userPassword is not modified check if this is a request to generate
+     * NT hashes otherwise we are done here */
+    if (!is_pwd_op && !is_magic_regen) {
         rc = LDAP_SUCCESS;
         goto done;
     }
@@ -522,6 +557,22 @@ static int ipapwd_pre_mod(Slapi_PBlock *pb)
         goto done;
     }
 
+    if (!is_pwd_op) {
+        /* This may be a magic op to ask us to generate the NT hashes */
+        if (is_magic_regen) {
+            /* Make sense to call only if this entry has krb keys to source
+             * the nthash from */
+            if (is_krb) {
+                rc = ipapwd_regen_nthash(pb, smods, dn, e, krbcfg);
+            } else {
+                rc = LDAP_UNWILLING_TO_PERFORM;
+            }
+        } else {
+            rc = LDAP_OPERATIONS_ERROR;
+        }
+        goto done;
+    }
+
     /* run through the mods again and adjust flags if operations affect them */
     lmod = slapi_mods_get_first_mod(smods);
     while (lmod) {
@@ -831,6 +882,96 @@ done:
     return 0;
 }
 
+static int ipapwd_regen_nthash(Slapi_PBlock *pb, Slapi_Mods *smods,
+                               char *dn, struct slapi_entry *entry,
+                               struct ipapwd_krbcfg *krbcfg)
+{
+    Slapi_Attr *attr;
+    Slapi_Value *value;
+    const struct berval *val;
+    struct berval *ntvals[2] = { NULL, NULL };
+    struct berval bval;
+    krb5_key_data *keys;
+    int num_keys;
+    int mkvno;
+    int ret;
+    int i;
+
+    ret = slapi_entry_attr_find(entry, "ipaNTHash", &attr);
+    if (ret == 0) {
+        /* We refuse to regen if there is already a value */
+        return LDAP_CONSTRAINT_VIOLATION;
+    }
+
+    /* ok let's see if we can find the RC4 hash in the keys */
+    ret = slapi_entry_attr_find(entry, "krbPrincipalKey", &attr);
+    if (ret) {
+        return LDAP_UNWILLING_TO_PERFORM;
+    }
+
+    ret = slapi_attr_first_value(attr, &value);
+    if (ret) {
+        return LDAP_OPERATIONS_ERROR;
+    }
+
+    val = slapi_value_get_berval(value);
+    if (!val) {
+        return LDAP_OPERATIONS_ERROR;
+    }
+
+    ret = ber_decode_krb5_key_data((struct berval *)val,
+                                    &mkvno, &num_keys, &keys);
+    if (ret) {
+        return LDAP_OPERATIONS_ERROR;
+    }
+
+    ret = LDAP_UNWILLING_TO_PERFORM;
+
+    for (i = 0; i < num_keys; i++) {
+        char nthash[16];
+        krb5_enc_data cipher;
+        krb5_data plain;
+        krb5_int16 t;
+
+        if (keys[i].key_data_type[0] != ENCTYPE_ARCFOUR_HMAC) {
+            continue;
+        }
+
+        memcpy(&t, keys[i].key_data_contents[0], 2);
+        plain.length = le16toh(t);
+        if (plain.length != 16) {
+            continue;
+        }
+        plain.data = nthash;
+
+        memset(&cipher, 0, sizeof(krb5_enc_data));
+        cipher.enctype = krbcfg->kmkey->enctype;
+        cipher.ciphertext.length = keys[i].key_data_length[0] - 2;
+        cipher.ciphertext.data = ((char *)keys[i].key_data_contents[0]) + 2;
+
+        ret = krb5_c_decrypt(krbcfg->krbctx, krbcfg->kmkey,
+                             0, NULL, &cipher, &plain);
+        if (ret) {
+            ret = LDAP_OPERATIONS_ERROR;
+            break;
+        }
+
+        bval.bv_val = nthash;
+        bval.bv_len = 16;
+        ntvals[0] = &bval;
+
+        slapi_mods_add_modbvps(smods, LDAP_MOD_REPLACE,
+                                  "ipaNTHash", ntvals);
+
+        ret = LDAP_SUCCESS;
+        break;
+    }
+
+    ipa_krb5_free_key_data(keys, num_keys);
+
+    return ret;
+}
+
 static int ipapwd_post_op(Slapi_PBlock *pb)
 {
     void *op;
-- 
1.7.10.4

_______________________________________________
Freeipa-devel mailing list
Freeipa-devel@redhat.com
https://www.redhat.com/mailman/listinfo/freeipa-devel

Reply via email to