diff options
-rw-r--r-- | source3/include/smb.h | 5 | ||||
-rw-r--r-- | source3/libsmb/smbencrypt.c | 20 | ||||
-rw-r--r-- | source3/passdb/passdb.c | 14 | ||||
-rw-r--r-- | source3/passdb/pdb_get_set.c | 37 | ||||
-rw-r--r-- | source3/smbd/chgpasswd.c | 20 |
5 files changed, 74 insertions, 22 deletions
diff --git a/source3/include/smb.h b/source3/include/smb.h index a802e96226..32dba0cf78 100644 --- a/source3/include/smb.h +++ b/source3/include/smb.h @@ -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. */ diff --git a/source3/libsmb/smbencrypt.c b/source3/libsmb/smbencrypt.c index 9f936b77ae..d4b0557411 100644 --- a/source3/libsmb/smbencrypt.c +++ b/source3/libsmb/smbencrypt.c @@ -73,6 +73,26 @@ void E_md4hash(const char *passwd, uchar p16[16]) } /** + * 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. * @param p16 return password hashed with DES, caller allocated 16 byte buffer diff --git a/source3/passdb/passdb.c b/source3/passdb/passdb.c index 2f9742e17d..e404f5af3f 100644 --- a/source3/passdb/passdb.c +++ b/source3/passdb/passdb.c @@ -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; } diff --git a/source3/passdb/pdb_get_set.c b/source3/passdb/pdb_get_set.c index dc8a2f68d2..51c408e6d5 100644 --- a/source3/passdb/pdb_get_set.c +++ b/source3/passdb/pdb_get_set.c @@ -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")); diff --git a/source3/smbd/chgpasswd.c b/source3/smbd/chgpasswd.c index a1b90c8fed..5c1d66abc4 100644 --- a/source3/smbd/chgpasswd.c +++ b/source3/smbd/chgpasswd.c @@ -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; } |