summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeremy Allison <jra@samba.org>2004-08-05 19:57:41 +0000
committerGerald (Jerry) Carter <jerry@samba.org>2007-10-10 10:52:17 -0500
commit2723be12397c1ddadecac501fb2484c5aa56a564 (patch)
treec872dcf5f1cbbeaf8f560b67ebeec39a2f3f2cff
parentab8139381eff04be5c5ce78bf1526c299c1278d8 (diff)
downloadsamba-2723be12397c1ddadecac501fb2484c5aa56a564.tar.gz
samba-2723be12397c1ddadecac501fb2484c5aa56a564.tar.bz2
samba-2723be12397c1ddadecac501fb2484c5aa56a564.zip
r1661: Changed the password history format so that each history entry
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)
-rw-r--r--source3/include/smb.h5
-rw-r--r--source3/libsmb/smbencrypt.c20
-rw-r--r--source3/passdb/passdb.c14
-rw-r--r--source3/passdb/pdb_get_set.c37
-rw-r--r--source3/smbd/chgpasswd.c20
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;
}