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.

examples/LDAP/samba.schema
source/include/passdb.h
source/include/smbldap.h
source/lib/smbldap.c
source/passdb/pdb_get_set.c
source/passdb/pdb_ldap.c
source/passdb/pdb_tdb.c
source/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;
        }