r1388: Adding password history code for ldap backend, based on a patch from
authorJeremy Allison <jra@samba.org>
Wed, 7 Jul 2004 22:46:51 +0000 (22:46 +0000)
committerGerald (Jerry) Carter <jerry@samba.org>
Wed, 10 Oct 2007 15:52:09 +0000 (10:52 -0500)
"Jianliang Lu" <j.lu@tiesse.com>. Multi-string attribute changed to
linearised pstring due to ordering issues. A few other changes to
fix race conditions. I will add the tdb backend code next. This code
compiles but has not yet been tested with password history policy
set to greater than zero. Targeted for 3.0.6.
Jeremy.
(This used to be commit dd54b2a3c45e202e504ad69d170eb798da4e6fc9)

examples/LDAP/samba.schema
source3/include/passdb.h
source3/include/smbldap.h
source3/lib/smbldap.c
source3/passdb/pdb_get_set.c
source3/passdb/pdb_ldap.c
source3/passdb/pdb_tdb.c
source3/smbd/chgpasswd.c

index 71c954a0c008fde245e9929333e8d5a454d5ef84..0ad94f973df0ce5c82e65d4f227c1c46a79f559b 100644 (file)
@@ -251,6 +251,11 @@ attributetype ( 1.3.6.1.4.1.7165.2.1.47 NAME 'sambaMungedDial'
        EQUALITY caseExactMatch
        SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{1050} )
 
+attributetype ( 1.3.6.1.4.1.7165.2.1.50 NAME 'sambaPasswordHistory'
+       DESC 'MD4 hash of the unicode password'
+       EQUALITY caseIgnoreIA5Match
+       SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{32} )
+
 ##
 ## SID, of any type
 ##
@@ -329,7 +334,7 @@ objectclass ( 1.3.6.1.4.1.7165.2.2.6 NAME 'sambaSamAccount' SUP top AUXILIARY
                displayName $ sambaHomePath $ sambaHomeDrive $ sambaLogonScript $
               sambaProfilePath $ description $ sambaUserWorkstations $
               sambaPrimaryGroupSID $ sambaDomainName $ sambaMungedDial $
-              sambaBadPasswordCount $ sambaBadPasswordTime))
+              sambaBadPasswordCount $ sambaBadPasswordTime $ sambaPasswordHistory))
 
 ##
 ## Group mapping info
index d08fd13a72321e59e77bf0be26ba40dd0601eb3f..7d3e0014b6524ce00405fa083beccdd83e012f9a 100644 (file)
@@ -99,6 +99,7 @@ enum pdb_elements {
        PDB_UNKNOWN6,
        PDB_LMPASSWD,
        PDB_NTPASSWD,
+       PDB_PWHISTORY,
        PDB_BACKEND_PRIVATE_DATA,
 
        /* this must be the last element */
@@ -165,16 +166,17 @@ typedef struct sam_passwd
                const char * dir_drive;    /* home directory drive string */
                const char * logon_script; /* logon script string */
                const char * profile_path; /* profile path string */
-               const char * acct_desc  ;  /* user description string */
+               const char * acct_desc;    /* user description string */
                const char * workstations; /* login from workstations string */
-               const char * unknown_str ; /* don't know what this is, yet. */
-               const char * munged_dial ; /* munged path name and dial-back tel number */
+               const char * unknown_str /* don't know what this is, yet. */
+               const char * munged_dial /* munged path name and dial-back tel number */
                
                DOM_SID user_sid;    /* Primary User SID */
                DOM_SID group_sid;   /* Primary Group SID */
                
                DATA_BLOB lm_pw; /* .data is Null if no password */
                DATA_BLOB nt_pw; /* .data is Null if no password */
+               DATA_BLOB nt_pw_his; /* nt hashed password history .data is Null if not available */
                char* plaintext_pw; /* is Null if not available */
                
                uint16 acct_ctrl; /* account info (ACB_xxxx bit-mask) */
index c7de7d84b375226b5243d7d9ece202246e4496e8..b94577178b34d191500bfb696bebb4b2aae1ed28 100644 (file)
@@ -93,8 +93,9 @@
 #define LDAP_ATTR_LOGON_COUNT          36
 #define LDAP_ATTR_MUNGED_DIAL          37
 #define LDAP_ATTR_BAD_PASSWORD_TIME    38
-
-#define LDAP_ATTR_SID_LIST              40
+#define LDAP_ATTR_PWD_HISTORY          39
+#define LDAP_ATTR_SID_LIST             40
+#define LDAP_ATTR_MOD_TIMESTAMP                41
 
 typedef struct _attrib_map_entry {
        int             attrib;
index d058613f004e8a9c5a1f7989d721f27ed5a99dab..9b6d5976060b83e9e5245a42085ecbc80d8eda14 100644 (file)
@@ -100,6 +100,8 @@ ATTRIB_MAP_ENTRY attrib_map_v30[] = {
        { LDAP_ATTR_MUNGED_DIAL,        "sambaMungedDial"       },
        { LDAP_ATTR_BAD_PASSWORD_COUNT, "sambaBadPasswordCount" },
        { LDAP_ATTR_BAD_PASSWORD_TIME,  "sambaBadPasswordTime"  },
+       { LDAP_ATTR_PWD_HISTORY,        "sambaPasswordHistory"  },
+       { LDAP_ATTR_MOD_TIMESTAMP,      "modifyTimestamp"       },
        { LDAP_ATTR_LIST_END,           NULL                    }
 };
 
@@ -345,19 +347,19 @@ static BOOL fetch_ldap_pw(char **dn, char** pw)
 
        /* sanity checks on the mod values */
 
-       if (attribute == NULL || *attribute == '\0')
+       if (attribute == NULL || *attribute == '\0') {
                return; 
+       }
+
 #if 0  /* commented out after discussion with abartlet.  Do not reenable.
           left here so other so re-add similar code   --jerry */
                if (value == NULL || *value == '\0')
                return;
 #endif
 
-       if (mods == NULL) 
-       {
+       if (mods == NULL) {
                mods = (LDAPMod **) malloc(sizeof(LDAPMod *));
-               if (mods == NULL)
-               {
+               if (mods == NULL) {
                        DEBUG(0, ("make_a_mod: out of memory!\n"));
                        return;
                }
@@ -369,17 +371,14 @@ static BOOL fetch_ldap_pw(char **dn, char** pw)
                        break;
        }
 
-       if (mods[i] == NULL)
-       {
+       if (mods[i] == NULL) {
                mods = (LDAPMod **) Realloc (mods, (i + 2) * sizeof (LDAPMod *));
-               if (mods == NULL)
-               {
+               if (mods == NULL) {
                        DEBUG(0, ("make_a_mod: out of memory!\n"));
                        return;
                }
                mods[i] = (LDAPMod *) malloc(sizeof(LDAPMod));
-               if (mods[i] == NULL)
-               {
+               if (mods[i] == NULL) {
                        DEBUG(0, ("make_a_mod: out of memory!\n"));
                        return;
                }
@@ -389,8 +388,7 @@ static BOOL fetch_ldap_pw(char **dn, char** pw)
                mods[i + 1] = NULL;
        }
 
-       if (value != NULL)
-       {
+       if (value != NULL) {
                char *utf8_value = NULL;
 
                j = 0;
index e69dac524f0260ce27704080d59426ac979a2ae9..4abb951d7b14bf97301c798388d1459e2990c796 100644 (file)
@@ -150,6 +150,19 @@ const uint8* pdb_get_lanman_passwd (const SAM_ACCOUNT *sampass)
                return (NULL);
 }
 
+const uint8* pdb_get_pw_history (const SAM_ACCOUNT *sampass, uint32 *current_hist_len)
+{
+       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;
+               return ((uint8*)sampass->private.nt_pw_his.data);
+       } else {
+               *current_hist_len = 0;
+               return (NULL);
+       }
+}
+
 /* Return the plaintext password if known.  Most of the time
    it isn't, so don't assume anything magic about this function.
    
@@ -981,6 +994,30 @@ BOOL pdb_set_lanman_passwd (SAM_ACCOUNT *sampass, const uint8 pwd[LM_HASH_LEN],
        return pdb_set_init_flags(sampass, PDB_LMPASSWD, flag);
 }
 
+/*********************************************************************
+ Set the user's password history hash. historyLen is the number of NT_HASH_LEN
+ entries to store in the history - this must match the size of the uint8 array
+ in pwd.
+********************************************************************/
+
+BOOL pdb_set_pw_history (SAM_ACCOUNT *sampass, const uint8 *pwd, uint32 historyLen, enum pdb_value_state flag)
+{
+       if (!sampass)
+               return False;
+
+       if (historyLen && pwd){
+               sampass->private.nt_pw_his = data_blob_talloc(sampass->mem_ctx, pwd, historyLen*NT_HASH_LEN);
+               if (!sampass->private.nt_pw_his.length) {
+                       DEBUG(0, ("pdb_set_pw_history: data_blob_talloc() failed!\n"));
+                       return False;
+               }
+       } else {
+               sampass->private.nt_pw_his = data_blob_talloc(sampass->mem_ctx, NULL, 0);
+       }
+
+       return pdb_set_init_flags(sampass, PDB_PWHISTORY, flag);
+}
+
 /*********************************************************************
  Set the user's plaintext password only (base procedure, see helper
  below)
@@ -1133,12 +1170,20 @@ BOOL pdb_set_pass_changed_now (SAM_ACCOUNT *sampass)
 
 BOOL pdb_set_plaintext_passwd (SAM_ACCOUNT *sampass, const char *plaintext)
 {
-       uchar new_lanman_p16[16];
-       uchar new_nt_p16[16];
+       uchar new_lanman_p16[LM_HASH_LEN];
+       uchar new_nt_p16[NT_HASH_LEN];
+       uchar current_ntpw_copy[NT_HASH_LEN];
+       uchar *current_ntpw = NULL;
 
        if (!sampass || !plaintext)
                return False;
-       
+
+       /* Store the current password for history purposes. */
+       current_ntpw = (uint8 *)pdb_get_nt_passwd(sampass);
+       if (current_ntpw) {
+               memcpy (current_ntpw_copy, current_ntpw, NT_HASH_LEN);
+       }
+
        /* Calculate the MD4 hash (NT compatible) of the password */
        E_md4hash(plaintext, new_nt_p16);
 
@@ -1164,6 +1209,45 @@ BOOL pdb_set_plaintext_passwd (SAM_ACCOUNT *sampass, const char *plaintext)
        if (!pdb_set_pass_changed_now (sampass))
                return False;
 
+       /* Store the password history. */
+       if (pdb_get_acct_ctrl(sampass) & ACB_NORMAL) {
+               uchar *pwhistory;
+               uint32 pwHistLen;
+               account_policy_get(AP_PASSWORD_HISTORY, &pwHistLen);
+               if (pwHistLen != 0){
+                       uint32 current_history_len;
+                       /* We need to make sure we don't have a race condition here - the
+                          account policy history length can change between when the pw_history
+                          was first loaded into the SAM_ACCOUNT struct and now.... JRA. */
+                       pwhistory = (uchar *)pdb_get_pw_history(sampass, &current_history_len);
+
+                       if (current_history_len != pwHistLen) {
+                               /* After closing and reopening SAM_ACCOUNT the history
+                                       values will sync up. We can't do this here. */
+
+                               /* current_history_len > pwHistLen is not a problem - we
+                                       have more history than we need. */
+
+                               if (current_history_len < pwHistLen) {
+                                       /* We only have room for current_history_len entries. */
+                                       pwHistLen = current_history_len;
+                               }
+                       }
+                       if (pwhistory && current_ntpw && pwHistLen){
+                               if (pwHistLen > 1) {
+                                       memmove(&pwhistory[NT_HASH_LEN], pwhistory, (pwHistLen -1)*NT_HASH_LEN );
+                               }
+                               memcpy(pwhistory, current_ntpw_copy, NT_HASH_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"));
+                       }
+               } else {
+                       /* Set the history length to zero. */
+                       pdb_set_pw_history(sampass, NULL, 0, PDB_CHANGED);
+               }
+       }
+
        return True;
 }
 
index d2ee9a2d9dc92b975684d3d7ce30cec29d3631c2..fed92cea568c008a74c5bd02c2ddced73913081d 100644 (file)
@@ -81,8 +81,6 @@
 #define SAM_ACCOUNT struct sam_passwd
 #endif
 
-#define MODIFY_TIMESTAMP_STRING "modifyTimestamp"
-
 #include "smbldap.h"
 
 struct ldapsam_privates {
@@ -301,7 +299,9 @@ static NTSTATUS ldapsam_delete_entry(struct ldapsam_privates *ldap_state,
                   really exist. */
 
                for (attrib = attrs; *attrib != NULL; attrib++) {
-                       if (StrCaseCmp(*attrib, name) == 0) {
+                       if ((StrCaseCmp(*attrib, name) == 0) &&
+                                       !(StrCaseCmp(*attrib,
+                                               get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_MOD_TIMESTAMP)))) {
                                DEBUG(10, ("ldapsam_delete_entry: deleting attribute %s\n", name));
                                smbldap_set_mod(&mods, LDAP_MOD_DELETE, name, NULL);
                        }
@@ -400,8 +400,9 @@ static time_t ldapsam_get_entry_timestamp(
        pstring temp;   
        struct tm tm;
 
-       if (!smbldap_get_single_pstring(ldap_state->smbldap_state->ldap_struct,
-                                       entry, MODIFY_TIMESTAMP_STRING, temp)) 
+       if (!smbldap_get_single_pstring(ldap_state->smbldap_state->ldap_struct, entry,
+                       get_userattr_key2string(ldap_state->schema_ver,LDAP_ATTR_MOD_TIMESTAMP),
+                       temp))
                return (time_t) 0;
 
        strptime(temp, "%Y%m%d%H%M%SZ", &tm);
@@ -448,6 +449,7 @@ static BOOL init_sam_from_ldap (struct ldapsam_privates *ldap_state,
        uint8           hours[MAX_HOURS_LEN];
        pstring temp;
        LOGIN_CACHE     *cache_entry = NULL;
+       int pwHistLen;
 
        /*
         * do a little initialization
@@ -694,6 +696,37 @@ static BOOL init_sam_from_ldap (struct ldapsam_privates *ldap_state,
                ZERO_STRUCT(smbntpwd);
        }
 
+       account_policy_get(AP_PASSWORD_HISTORY, &pwHistLen);
+       if (pwHistLen > 0){
+               uint8 *pwhist = NULL;
+               int i;
+
+               if ((pwhist = malloc(NT_HASH_LEN * pwHistLen)) == NULL){
+                       DEBUG(0, ("init_sam_from_ldap: malloc failed!\n"));
+                       return False;
+               }
+               memset(pwhist, '\0', NT_HASH_LEN * pwHistLen);
+
+               if (!smbldap_get_single_pstring (ldap_state->smbldap_state->ldap_struct, entry, 
+                       get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_PWD_HISTORY), temp)) {
+                       /* leave as default - zeros */
+               } else {
+                       for (i = 0; i < pwHistLen; i++){
+                               if (!pdb_gethexpwd(&temp[i*32], smbntpwd)) {
+                                       break;
+                               }
+                               memset(&temp[i*32], '\0', 32);
+                               memcpy(&pwhist[i*NT_HASH_LEN], smbntpwd, NT_HASH_LEN);
+                               ZERO_STRUCT(smbntpwd);
+                       }
+               }
+               if (!pdb_set_pw_history(sampass, pwhist, pwHistLen, PDB_SET)){
+                       SAFE_FREE(pwhist);
+                       return False;
+               }
+               SAFE_FREE(pwhist);
+       }
+
        if (!smbldap_get_single_pstring (ldap_state->smbldap_state->ldap_struct, entry,
                        get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_ACB_INFO), temp)) {
                acct_ctrl |= ACB_NORMAL;
@@ -781,7 +814,7 @@ static BOOL init_sam_from_ldap (struct ldapsam_privates *ldap_state,
 }
 
 /**********************************************************************
- Initialize SAM_ACCOUNT from an LDAP query.
+ Initialize the ldap db from a SAM_ACCOUNT. Called on update.
  (Based on init_buffer_from_sam in pdb_tdb.c)
 *********************************************************************/
 
@@ -985,6 +1018,29 @@ static BOOL init_ldap_from_sam (struct ldapsam_privates *ldap_state,
                        }
                }
 
+               if (need_update(sampass, PDB_PWHISTORY)) {
+                       int pwHistLen = 0;
+                       account_policy_get(AP_PASSWORD_HISTORY, &pwHistLen);
+                       if (pwHistLen == 0) {
+                               /* Remove any password history from the LDAP store. */
+                               pstrcpy(temp, "00000000000000000000000000000000");
+                       } else {
+                               int i, currHistLen = 0;
+                               const uint8 *pwhist = pdb_get_pw_history(sampass, &currHistLen);
+                               if (pwhist != NULL) {
+                                       /* We can only store (sizeof(pstring)-1)/32 password history entries. */
+                                       pwHistLen = MIN(pwHistLen, ((sizeof(temp)-1)/32));
+                                       for (i=0; i< pwHistLen && i < currHistLen; i++) {
+                                               pdb_sethexpwd (&temp[i*32], &pwhist[i*NT_HASH_LEN], 0);
+                                               DEBUG(100, ("temp=%s\n", temp));
+                                       }
+                               } 
+                       }
+                       smbldap_make_mod(ldap_state->smbldap_state->ldap_struct, existing, mods,
+                                        get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_PWD_HISTORY), 
+                                        temp);
+               }
+
                if (need_update(sampass, PDB_PASSLASTSET)) {
                        slprintf (temp, sizeof (temp) - 1, "%li", pdb_get_pass_last_set_time(sampass));
                        smbldap_make_mod(ldap_state->smbldap_state->ldap_struct, existing, mods,
@@ -1162,7 +1218,7 @@ static NTSTATUS ldapsam_getsampwnam(struct pdb_methods *my_methods, SAM_ACCOUNT
        int rc;
        
        attr_list = get_userattr_list( ldap_state->schema_ver );
-       append_attr(&attr_list, MODIFY_TIMESTAMP_STRING);
+       append_attr(&attr_list, get_userattr_key2string(ldap_state->schema_ver,LDAP_ATTR_MOD_TIMESTAMP));
        rc = ldapsam_search_suffix_by_name(ldap_state, sname, &result, attr_list);
        free_attr_list( attr_list );
 
@@ -1208,7 +1264,7 @@ static int ldapsam_get_ldap_user_by_sid(struct ldapsam_privates *ldap_state,
        switch ( ldap_state->schema_ver ) {
                case SCHEMAVER_SAMBASAMACCOUNT:
                        attr_list = get_userattr_list(ldap_state->schema_ver);
-                       append_attr(&attr_list, MODIFY_TIMESTAMP_STRING);
+                       append_attr(&attr_list, get_userattr_key2string(ldap_state->schema_ver,LDAP_ATTR_MOD_TIMESTAMP));
                        rc = ldapsam_search_suffix_by_sid(ldap_state, sid, result, attr_list);
                        free_attr_list( attr_list );
 
index 9bfb10c400951d1fbff81dfb19e07cf6838b6bc5..2cf7c55049f354914f01e9c9134bd3b6249f25ee 100644 (file)
@@ -746,4 +746,3 @@ NTSTATUS pdb_tdbsam_init(void)
 {
        return smb_register_passdb(PASSDB_INTERFACE_VERSION, "tdbsam", pdb_init_tdbsam);
 }
-
index ca13a167fb0670df6ab8ae43413fd1ed8b95e353..a1b90c8fed4608a0e339aad96398af933211389a 100644 (file)
@@ -932,6 +932,65 @@ static NTSTATUS check_oem_password(const char *user,
        return NT_STATUS_WRONG_PASSWORD;
 }
 
+/***********************************************************
+ This routine takes the given password and checks it against
+ the password history. Returns True if this password has been
+ found in the history list.
+************************************************************/
+
+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];
+       const uint8 *nt_pw;
+       const uint8 *pwhistory;
+       BOOL found = False;
+       int i, pwHisLen, curr_pwHisLen;
+
+       account_policy_get(AP_PASSWORD_HISTORY, &pwHisLen);
+       if (pwHisLen == 0) {
+               return False;
+       }
+
+       pwhistory = pdb_get_pw_history(sampass, &curr_pwHisLen);
+       if (!pwhistory || curr_pwHisLen == 0) {
+               return False;
+       }
+
+       /* Only examine the minimum of the current history len and
+          the stored history len. Avoids race conditions. */
+       pwHisLen = MIN(pwHisLen,curr_pwHisLen);
+
+       nt_pw = pdb_get_nt_passwd(sampass);
+
+       E_md4hash(plaintext, new_nt_p16);
+
+       if (!memcmp(nt_pw, new_nt_p16, NT_HASH_LEN)) {
+               DEBUG(10,("check_passwd_history: proposed new password for user %s is the same as the current password !\n",
+                       pdb_get_username(sampass) ));
+               return True;
+       }
+
+       dump_data(100, new_nt_p16, NT_HASH_LEN);
+       dump_data(100, pwhistory, NT_HASH_LEN*pwHisLen);
+
+       memset(zero_nt_pw, '\0', NT_HASH_LEN);
+       for (i=0; i<pwHisLen; i++) {
+               if (!memcmp(&pwhistory[i*NT_HASH_LEN], zero_nt_pw, NT_HASH_LEN)) {
+                       /* Ignore zero entries. */
+                       continue;
+               }
+               if (!memcmp(&pwhistory[i*NT_HASH_LEN], new_nt_p16, NT_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;
+}
+
 /***********************************************************
  Code to change the oem password. Changes both the lanman
  and NT hashes.  Old_passwd is almost always NULL.
@@ -941,20 +1000,21 @@ static NTSTATUS check_oem_password(const char *user,
 
 NTSTATUS change_oem_password(SAM_ACCOUNT *hnd, char *old_passwd, char *new_passwd, BOOL as_root)
 {
-       struct passwd *pass;
-
        BOOL ret;
        uint32 min_len;
+       struct passwd *pass = NULL;
+       const char *username = pdb_get_username(hnd);
+       time_t can_change_time = pdb_get_pass_can_change_time(hnd);
 
-       if (time(NULL) < pdb_get_pass_can_change_time(hnd)) {
+       if ((can_change_time != 0) && (time(NULL) < can_change_time)) {
                DEBUG(1, ("user %s cannot change password now, must wait until %s\n", 
-                         pdb_get_username(hnd), http_timestring(pdb_get_pass_can_change_time(hnd))));
-               return NT_STATUS_PASSWORD_RESTRICTION;
+                         username, http_timestring(can_change_time)));
+               return NT_STATUS_ACCOUNT_RESTRICTION;
        }
 
        if (account_policy_get(AP_MIN_PASSWORD_LEN, &min_len) && (strlen(new_passwd) < min_len)) {
                DEBUG(1, ("user %s cannot change password - password too short\n", 
-                         pdb_get_username(hnd)));
+                         username));
                DEBUGADD(1, (" account policy min password len = %d\n", min_len));
                return NT_STATUS_PASSWORD_RESTRICTION;
 /*             return NT_STATUS_PWD_TOO_SHORT; */
@@ -965,14 +1025,19 @@ NTSTATUS change_oem_password(SAM_ACCOUNT *hnd, char *old_passwd, char *new_passw
        if (strlen(new_passwd) < lp_min_passwd_length()) {
                /* too short, must be at least MINPASSWDLENGTH */
                DEBUG(1, ("Password Change: user %s, New password is shorter than minimum password length = %d\n",
-                      pdb_get_username(hnd), lp_min_passwd_length()));
+                      username, lp_min_passwd_length()));
                return NT_STATUS_PASSWORD_RESTRICTION;
 /*             return NT_STATUS_PWD_TOO_SHORT; */
        }
 
-       pass = Get_Pwnam(pdb_get_username(hnd));
+       if (check_passwd_history(hnd,new_passwd)) {
+               return NT_STATUS_PASSWORD_RESTRICTION;
+       }
+
+       pass = Get_Pwnam(username);
        if (!pass) {
-               DEBUG(1, ("check_oem_password: Username does not exist in system !?!\n"));
+               DEBUG(1, ("check_oem_password: Username %s does not exist in system !?!\n", username));
+               return NT_STATUS_ACCESS_DENIED;
        }
 
        /*
@@ -988,7 +1053,7 @@ NTSTATUS change_oem_password(SAM_ACCOUNT *hnd, char *old_passwd, char *new_passw
         */
        
        if(lp_unix_password_sync() &&
-               !chgpasswd(pdb_get_username(hnd), pass, old_passwd, new_passwd, as_root)) {
+               !chgpasswd(username, pass, old_passwd, new_passwd, as_root)) {
                return NT_STATUS_ACCESS_DENIED;
        }