r1661: Changed the password history format so that each history entry
authorJeremy Allison <jra@samba.org>
Thu, 5 Aug 2004 19:57:41 +0000 (19:57 +0000)
committerGerald (Jerry) Carter <jerry@samba.org>
Wed, 10 Oct 2007 15:52:17 +0000 (10:52 -0500)
consists of a 16 byte salt, followed by the 16 byte MD5 hash of
the concatination of the salt plus the NThash of the historical
password. Allows these to be exposed in LDAP without security issues.
Jeremy.
(This used to be commit 82e4036aaa2d283534a5bd8149857320fcf0d0dc)

source3/include/smb.h
source3/libsmb/smbencrypt.c
source3/passdb/passdb.c
source3/passdb/pdb_get_set.c
source3/smbd/chgpasswd.c

index a802e9622662c431614eb9becbc7b8a66dc3b00d..32dba0cf78f02a3aa0b59d9e249c1466e7ba6172 100644 (file)
@@ -625,6 +625,11 @@ typedef struct {
 #define NT_HASH_LEN 16
 #define LM_HASH_LEN 16
 
+/* Password history contants. */
+#define PW_HISTORY_SALT_LEN 16
+#define SALTED_MD5_HASH_LEN 16
+#define PW_HISTORY_ENTRY_LEN (PW_HISTORY_SALT_LEN+SALTED_MD5_HASH_LEN)
+
 /*
  * Flags for account policy.
  */
index 9f936b77aec973322f79a0054087c0f3cc456ec5..d4b05574118a1337285e9c9eadbc2333c8c33890 100644 (file)
@@ -72,6 +72,26 @@ void E_md4hash(const char *passwd, uchar p16[16])
        ZERO_STRUCT(wpwd);      
 }
 
+/**
+ * Creates the MD5 Hash of a combination of 16 byte salt and 16 byte NT hash.
+ * @param 16 byte salt.
+ * @param 16 byte NT hash.
+ * @param 16 byte return hashed with md5, caller allocated 16 byte buffer
+ */
+
+void E_md5hash(const uchar salt[16], const uchar nthash[16], uchar hash_out[16])
+{
+       struct MD5Context tctx;
+       uchar array[32];
+       
+       memset(hash_out, '\0', 16);
+       memcpy(array, salt, 16);
+       memcpy(&array[16], nthash, 16);
+       MD5Init(&tctx);
+       MD5Update(&tctx, array, 32);
+       MD5Final(hash_out, &tctx);
+}
+
 /**
  * Creates the DES forward-only Hash of the users password in DOS ASCII charset
  * @param passwd password in 'unix' charset.
index 2f9742e17da90e9c7021db606f9a2f6770473d7d..e404f5af3f9145f3443f59d7fdc8e1098ad3cc7d 100644 (file)
@@ -1841,18 +1841,20 @@ BOOL init_sam_from_buffer_v2(SAM_ACCOUNT *sampass, uint8 *buf, uint32 buflen)
        /* Change from V1 is addition of password history field. */
        account_policy_get(AP_PASSWORD_HISTORY, &pwHistLen);
        if (pwHistLen) {
-               char *pw_hist = malloc(pwHistLen * NT_HASH_LEN);
+               char *pw_hist = malloc(pwHistLen * PW_HISTORY_ENTRY_LEN);
                if (!pw_hist) {
                        ret = False;
                        goto done;
                }
-               memset(pw_hist, '\0', pwHistLen * NT_HASH_LEN);
+               memset(pw_hist, '\0', pwHistLen * PW_HISTORY_ENTRY_LEN);
                if (nt_pw_hist_ptr && nt_pw_hist_len) {
                        int i;
-                       SMB_ASSERT((nt_pw_hist_len % NT_HASH_LEN) == 0);
-                       nt_pw_hist_len /= NT_HASH_LEN;
+                       SMB_ASSERT((nt_pw_hist_len % PW_HISTORY_ENTRY_LEN) == 0);
+                       nt_pw_hist_len /= PW_HISTORY_ENTRY_LEN;
                        for (i = 0; (i < pwHistLen) && (i < nt_pw_hist_len); i++) {
-                               memcpy(&pw_hist[i*NT_HASH_LEN], &nt_pw_hist_ptr[i*NT_HASH_LEN], NT_HASH_LEN);
+                               memcpy(&pw_hist[i*PW_HISTORY_ENTRY_LEN],
+                                       &nt_pw_hist_ptr[i*PW_HISTORY_ENTRY_LEN],
+                                       PW_HISTORY_ENTRY_LEN);
                        }
                }
                if (!pdb_set_pw_history(sampass, pw_hist, pwHistLen, PDB_SET)) {
@@ -2048,7 +2050,7 @@ uint32 init_buffer_from_sam_v2 (uint8 **buf, const SAM_ACCOUNT *sampass, BOOL si
        account_policy_get(AP_PASSWORD_HISTORY, &pwHistLen);
        nt_pw_hist =  pdb_get_pw_history(sampass, &nt_pw_hist_len);
        if (pwHistLen && nt_pw_hist && nt_pw_hist_len) {
-               nt_pw_hist_len *= NT_HASH_LEN;
+               nt_pw_hist_len *= PW_HISTORY_ENTRY_LEN;
        } else {
                nt_pw_hist_len = 0;
        }
index dc8a2f68d21d1cc62dca826332ee12dbb929c4a2..51c408e6d51b4bd5998a6f92d9ffa52277356833 100644 (file)
@@ -154,8 +154,8 @@ const uint8* pdb_get_pw_history (const SAM_ACCOUNT *sampass, uint32 *current_his
 {
        if (sampass) {
                SMB_ASSERT((!sampass->private.nt_pw_his.data) 
-                          || ((sampass->private.nt_pw_his.length % NT_HASH_LEN) == 0));
-               *current_hist_len = sampass->private.nt_pw_his.length / NT_HASH_LEN;
+                  || ((sampass->private.nt_pw_his.length % PW_HISTORY_ENTRY_LEN) == 0));
+               *current_hist_len = sampass->private.nt_pw_his.length / PW_HISTORY_ENTRY_LEN;
                return ((uint8*)sampass->private.nt_pw_his.data);
        } else {
                *current_hist_len = 0;
@@ -995,7 +995,8 @@ BOOL pdb_set_lanman_passwd (SAM_ACCOUNT *sampass, const uint8 pwd[LM_HASH_LEN],
 }
 
 /*********************************************************************
- Set the user's password history hash. historyLen is the number of NT_HASH_LEN
+ Set the user's password history hash. historyLen is the number of 
+ PW_HISTORY_SALT_LEN+SALTED_MD5_HASH_LEN length
  entries to store in the history - this must match the size of the uint8 array
  in pwd.
 ********************************************************************/
@@ -1006,7 +1007,8 @@ BOOL pdb_set_pw_history (SAM_ACCOUNT *sampass, const uint8 *pwd, uint32 historyL
                return False;
 
        if (historyLen && pwd){
-               sampass->private.nt_pw_his = data_blob_talloc(sampass->mem_ctx, pwd, historyLen*NT_HASH_LEN);
+               sampass->private.nt_pw_his = data_blob_talloc(sampass->mem_ctx,
+                                               pwd, historyLen*PW_HISTORY_ENTRY_LEN);
                if (!sampass->private.nt_pw_his.length) {
                        DEBUG(0, ("pdb_set_pw_history: data_blob_talloc() failed!\n"));
                        return False;
@@ -1221,17 +1223,34 @@ BOOL pdb_set_plaintext_passwd (SAM_ACCOUNT *sampass, const char *plaintext)
                                        have more history than we need. */
 
                                if (current_history_len < pwHistLen) {
-                                       /* We only have room for current_history_len entries. */
-                                       pwHistLen = current_history_len;
+                                       /* Ensure we have space for the needed history. */
+                                       uchar *new_history = talloc(sampass->mem_ctx,
+                                                               pwHistLen*PW_HISTORY_ENTRY_LEN);
+                                       /* And copy it into the new buffer. */
+                                       if (current_history_len) {
+                                               memcpy(new_history, pwhistory,
+                                                       current_history_len*PW_HISTORY_ENTRY_LEN);
+                                       }
+                                       /* Clearing out any extra space. */
+                                       memset(&new_history[current_history_len*PW_HISTORY_ENTRY_LEN],
+                                               '\0', (pwHistLen-current_history_len)*PW_HISTORY_ENTRY_LEN);
+                                       /* Finally replace it. */
+                                       pwhistory = new_history;
                                }
                        }
                        if (pwhistory && pwHistLen){
                                /* Make room for the new password in the history list. */
                                if (pwHistLen > 1) {
-                                       memmove(&pwhistory[NT_HASH_LEN], pwhistory, (pwHistLen -1)*NT_HASH_LEN );
+                                       memmove(&pwhistory[PW_HISTORY_ENTRY_LEN],
+                                               pwhistory, (pwHistLen -1)*PW_HISTORY_ENTRY_LEN );
                                }
-                               /* Ensure we have a copy of the new password as the first history entry. */
-                               memcpy(pwhistory, new_nt_p16, NT_HASH_LEN);
+                               /* Create the new salt as the first part of the history entry. */
+                               generate_random_buffer(pwhistory, PW_HISTORY_SALT_LEN);
+
+                               /* Generate the md5 hash of the salt+new password as the second
+                                       part of the history entry. */
+
+                               E_md5hash(pwhistory, new_nt_p16, &pwhistory[PW_HISTORY_SALT_LEN]);
                                pdb_set_pw_history(sampass, pwhistory, pwHistLen, PDB_CHANGED);
                        } else {
                                DEBUG (10,("pdb_get_set.c: pdb_set_plaintext_passwd: pwhistory was NULL!\n"));
index a1b90c8fed4608a0e339aad96398af933211389a..5c1d66abc44d542d13c6f75e78c2bfdcedb74a0c 100644 (file)
@@ -941,7 +941,7 @@ static NTSTATUS check_oem_password(const char *user,
 static BOOL check_passwd_history(SAM_ACCOUNT *sampass, const char *plaintext)
 {
        uchar new_nt_p16[NT_HASH_LEN];
-       uchar zero_nt_pw[NT_HASH_LEN];
+       uchar zero_md5_nt_pw[SALTED_MD5_HASH_LEN];
        const uint8 *nt_pw;
        const uint8 *pwhistory;
        BOOL found = False;
@@ -972,22 +972,28 @@ static BOOL check_passwd_history(SAM_ACCOUNT *sampass, const char *plaintext)
        }
 
        dump_data(100, new_nt_p16, NT_HASH_LEN);
-       dump_data(100, pwhistory, NT_HASH_LEN*pwHisLen);
+       dump_data(100, pwhistory, PW_HISTORY_ENTRY_LEN*pwHisLen);
 
-       memset(zero_nt_pw, '\0', NT_HASH_LEN);
+       memset(zero_md5_nt_pw, '\0', SALTED_MD5_HASH_LEN);
        for (i=0; i<pwHisLen; i++) {
-               if (!memcmp(&pwhistory[i*NT_HASH_LEN], zero_nt_pw, NT_HASH_LEN)) {
-                       /* Ignore zero entries. */
+               uchar new_nt_pw_salted_md5_hash[SALTED_MD5_HASH_LEN];
+               const uchar *current_salt = &pwhistory[i*PW_HISTORY_ENTRY_LEN];
+               const uchar *old_nt_pw_salted_md5_hash = &pwhistory[(i*PW_HISTORY_ENTRY_LEN)+
+                                                       PW_HISTORY_SALT_LEN];
+               if (!memcmp(zero_md5_nt_pw, old_nt_pw_salted_md5_hash, SALTED_MD5_HASH_LEN)) {
+                       /* Ignore zero valued entries. */
                        continue;
                }
-               if (!memcmp(&pwhistory[i*NT_HASH_LEN], new_nt_p16, NT_HASH_LEN)) {
+               /* Create salted versions of new to compare. */
+               E_md5hash(current_salt, new_nt_p16, new_nt_pw_salted_md5_hash);
+
+               if (!memcmp(new_nt_pw_salted_md5_hash, old_nt_pw_salted_md5_hash, SALTED_MD5_HASH_LEN)) {
                        DEBUG(1,("check_passwd_history: proposed new password for user %s found in history list !\n",
                                pdb_get_username(sampass) ));
                        found = True;
                        break;
                }
        }
-
        return found;
 }