Passowrd policy patch on Samba-3.0.2 for LDAP backend

Jianliang Lu j.lu at tiesse.com
Tue Feb 17 11:25:07 GMT 2004


I've finished the work on password policy (LDAP backend), that comprised 
bad password lockout and password history.

Three attributes were added in samba.schema for sambaSAMAccount object: 
"sambaBadPwdCount" and "sambaBadPwdTime" for bad password lockout, and 
a multi-value attribute "sambaPwdHistory" for password history (Uniqueness). 

During the logon process, the autolock flag is updated if the "lockout 
duration" time is reached. Also, the sambaBadPwdCount is reset as well when 
the "reset count minutes" is reached. When the passord check results wrong 
password then the sambaBadPwdCount is incremented and the account will be 
lockout if the "bad lockout attempt" is reached.
The autolock flag is also checked and updated when a SAMR_QUERY_USERINFO is 
called and the level of query is 10, 12, 20 or 21, while the sambaBadPwdCount 
and the sambaBadPwdTime is updated only for the query level 21.
Autolock and sambaBadPwdCount are updated when you would show the user 
using pdbedit -u user -v. You can reset the sambaBadPwdCount using 
pdbedit -u user -z.

When user would change his password, the new password is checked against
passwords in history list, which the length is "password histroy". The new 
password is accepted only if the check results that no password in history 
list is matched the new one.

Jianliang Lu

PATCH:
--------------------
--- ./examples/LDAP/samba.schema.orig	Wed Feb 11 09:43:56 2004
+++ ./examples/LDAP/samba.schema	Wed Feb 11 09:44:26 2004
@@ -202,6 +202,15 @@
 	EQUALITY integerMatch
 	SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
 
+attributetype ( 1.3.6.1.4.1.7165.2.1.50 NAME 'sambaBadPwdCount'
+	DESC 'NT bad password attempt count'
+	EQUALITY integerMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.4.1.7165.2.1.51 NAME 'sambaBadPwdTime'
+	DESC 'Timestamp of the last bad password attempt update'
+	EQUALITY integerMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
 
 ##
 ## string settings
@@ -241,6 +250,10 @@
 	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.52 NAME 'sambaPwdHistory'
+	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
 ##
@@ -313,7 +326,8 @@
 	       sambaPwdCanChange $ sambaPwdMustChange $ sambaAcctFlags $
                displayName $ sambaHomePath $ sambaHomeDrive $ 
sambaLogonScript $
 	       sambaProfilePath $ description $ sambaUserWorkstations $
-	       sambaPrimaryGroupSID $ sambaDomainName $ sambaMungedDial))
+	       sambaPrimaryGroupSID $ sambaDomainName $ sambaMungedDial $
+		   sambaBadPwdCount $ sambaBadPwdTime $ sambaPwdHistory))
 
 ##
 ## Group mapping info
--- ./source/auth/auth_sam.c.orig	Wed Feb 11 09:35:05 2004
+++ ./source/auth/auth_sam.c	Wed Feb 11 09:35:24 2004
@@ -74,10 +74,12 @@
 			       SAM_ACCOUNT *sampass, 
 			       const auth_usersupplied_info *user_info)
 {
-	uint16	acct_ctrl = pdb_get_acct_ctrl(sampass);
+	uint16	acct_ctrl;
 	char *workstation_list;
 	time_t kickoff_time;
 	
+	acct_ctrl = pdb_get_acct_ctrl(sampass);
+
 	DEBUG(4,("sam_account_ok: Checking SMB password for user %
s\n",pdb_get_username(sampass)));
 
 	/* Quit if the account was disabled. */
@@ -86,12 +88,6 @@
 		return NT_STATUS_ACCOUNT_DISABLED;
 	}
 
-	/* Quit if the account was locked out. */
-	if (acct_ctrl & ACB_AUTOLOCK) {
-		DEBUG(1,("sam_account_ok: Account for user %s was locked 
out.\n", pdb_get_username(sampass)));
-		return NT_STATUS_ACCOUNT_LOCKED_OUT;
-	}
-
 	/* Test account expire time */
 	
 	kickoff_time = pdb_get_kickoff_time(sampass);
@@ -177,8 +173,11 @@
 	SAM_ACCOUNT *sampass=NULL;
 	BOOL ret;
 	NTSTATUS nt_status;
+	GROUP_MAP map;
+	pstring grname;
 	DATA_BLOB user_sess_key = data_blob(NULL, 0);
 	DATA_BLOB lm_sess_key = data_blob(NULL, 0);
+	BOOL updated=False, updated1=False;
 
 	if (!user_info || !auth_context) {
 		return NT_STATUS_UNSUCCESSFUL;
@@ -202,14 +201,58 @@
 		return NT_STATUS_NO_SUCH_USER;
 	}
 
+	if (pdb_get_acct_ctrl(sampass) & ACB_NORMAL)
+		pdb_update_autolock_flag(sampass, &updated);
+
+	/* Quit if the account was locked out. */
+	if (pdb_get_acct_ctrl(sampass) & ACB_AUTOLOCK) {
+		DEBUG(3,("check_sam_security: Account for user %s was locked 
out.\n", pdb_get_username(sampass)));
+		return NT_STATUS_ACCOUNT_LOCKED_OUT;
+	}
+
+
 	nt_status = sam_password_ok(auth_context, mem_ctx, sampass, 
 				    user_info, &user_sess_key, &lm_sess_key);
 	
 	if (!NT_STATUS_IS_OK(nt_status)) {
+		if (NT_STATUS_EQUAL(nt_status,NT_STATUS_WRONG_PASSWORD) && 
pdb_get_acct_ctrl(sampass) &ACB_NORMAL) {  
+
+			if (!pdb_getgrnam(&map, "Domain Admins"))
+                DEBUG(1, ("auth_sam.c: Failed to get groupmap for Domain 
Admins"));
+			else
+	            pstrcpy(grname, gidtoname(map.gid));
+			if(!user_in_group_list(user_info-
>internal_username.str,grname,NULL,0)){
+				pdb_increment_bad_password_count(sampass);
+				updated1 = True;
+			}
+		} else {
+				if (!pdb_update_bad_password_count(sampass, 
&updated1))
+					DEBUG(2,
("pdb_update_bad_password_count failed.\n"));
+		}
+		if (updated || updated1){
+			become_root();
+			if(!pdb_update_sam_account(sampass))
+				DEBUG(1, ("Failed to modify entry.\n"));
+			unbecome_root();
+		}
 		pdb_free_sam(&sampass);
 		return nt_status;
 	}
 
+	if ((pdb_get_acct_ctrl(sampass) & ACB_NORMAL) && 
+								
	(pdb_get_bad_password_count(sampass) > 0)){
+		pdb_set_bad_password_count(sampass, 0, PDB_CHANGED);
+		pdb_set_bad_password_time(sampass, 0, PDB_CHANGED);
+		updated1 = True;
+	}
+
+	if (updated || updated1){
+		become_root();
+		if(!pdb_update_sam_account(sampass))
+			DEBUG(1, ("Failed to modify entry.\n"));
+		unbecome_root();
+	}
+
 	nt_status = sam_account_ok(mem_ctx, sampass, user_info);
 
 	if (!NT_STATUS_IS_OK(nt_status)) {
--- ./source/include/passdb.h.orig	Wed Feb 11 09:38:22 2004
+++ ./source/include/passdb.h	Wed Feb 11 09:38:52 2004
@@ -50,6 +50,7 @@
 	PDB_GROUPSID,
 	PDB_ACCTCTRL,
 	PDB_PASSLASTSET,
+	PDB_BAD_PASSWORD_TIME,	
 	PDB_UNIXHOMEDIR,
 	PDB_ACCTDESC,
 	PDB_WORKSTATIONS,
@@ -62,6 +63,7 @@
 	PDB_UNKNOWN6,
 	PDB_LMPASSWD,
 	PDB_NTPASSWD,
+	PDB_PWHISTORY,
 	PDB_BACKEND_PRIVATE_DATA,
 
 	/* this must be the last element */
@@ -108,6 +110,7 @@
 		time_t pass_last_set_time;    /* password last set time */
 		time_t pass_can_change_time;  /* password can change time */
 		time_t pass_must_change_time; /* password must change time */
+		time_t bad_password_time; 	  /* last bad password 
attempt time */
 		
 		const char * username;     /* UNIX username string */
 		const char * domain;       /* Windows Domain name */
@@ -122,6 +125,7 @@
 		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 uint8 * nt_pw_his; /* nt hashed password history */
 		
 		DOM_SID user_sid;    /* Primary User SID */
 		DOM_SID group_sid;   /* Primary Group SID */
--- ./source/include/smbldap.h.orig	Wed Feb 11 09:40:15 2004
+++ ./source/include/smbldap.h	Wed Feb 11 09:40:28 2004
@@ -91,6 +91,8 @@
 #define LDAP_ATTR_BAD_PASSWORD_COUNT	35
 #define LDAP_ATTR_LOGON_COUNT		36
 #define LDAP_ATTR_MUNGED_DIAL		37
+#define LDAP_ATTR_BAD_PASSWORD_TIME		38
+#define LDAP_ATTR_PWD_HISTORY		39
 
 typedef struct _attrib_map_entry {
 	int		attrib;
--- ./source/lib/smbldap.c.orig	Wed Feb 11 09:39:28 2004
+++ ./source/lib/smbldap.c	Wed Feb 11 09:39:52 2004
@@ -98,6 +98,9 @@
 	{ LDAP_ATTR_OBJCLASS,		"objectClass"		},
 	{ LDAP_ATTR_ACB_INFO,		"sambaAcctFlags"	},
 	{ LDAP_ATTR_MUNGED_DIAL,	"sambaMungedDial"	},
+	{ LDAP_ATTR_BAD_PASSWORD_COUNT,	"sambaBadPwdCount"	},
+	{ LDAP_ATTR_BAD_PASSWORD_TIME,	"sambaBadPwdTime"	},
+	{ LDAP_ATTR_PWD_HISTORY,	"sambaPwdHistory"	},
 	{ LDAP_ATTR_LIST_END,		NULL 			}
 };
 
@@ -313,6 +316,44 @@
 	ldap_value_free(values);
 #ifdef DEBUG_PASSWORDS
 	DEBUG (100, ("smbldap_get_single_attribute: [%s] = [%s]\n", 
attribute, value));
+#endif	
+	return True;
+}
+
+/*******************************************************************
+ Search an attribute and return the value found.
+******************************************************************/
+
+BOOL smbldap_get_single_attribute_multivalue (LDAP * ldap_struct, 
LDAPMessage * entry,
+				   const char *attribute, char *value, int 
len, int * count)
+{
+	char **values;
+	int i;  
+	
+	if ( !attribute )
+		return False;
+		
+	value[0] = '\0';
+
+	if ((values = ldap_get_values (ldap_struct, entry, attribute)) == 
NULL) {
+		DEBUG (10, ("smbldap_get_single_attribute_multivalue: [%s] = 
[<does not exist>]\n", attribute));
+		
+		return False;
+	}
+	
+	for (i = 0; (values[i] != NULL) && (i < len); i++){
+		if (convert_string(CH_UTF8, CH_UNIX,values[i], -1,(value + 
i*sizeof(pstring)), sizeof(pstring)) == (size_t)-1) {
+			DEBUG(1, ("smbldap_get_single_attribute_multivalue: 
string conversion of [%s] = [%s] failed!\n", 
+				  attribute, values[i]));
+			ldap_value_free(values);
+			return False;
+		}
+	}
+	
+	*count = i;
+	ldap_value_free(values);
+#ifdef DEBUG_PASSWORDS
+	DEBUG (100, ("smbldap_get_single_attribute: [%s] = [%s]\n", 
attribute, value));
 #endif	
 	return True;
 }
--- ./source/passdb/passdb.c.orig	Wed Feb 11 09:37:39 2004
+++ ./source/passdb/passdb.c	Wed Feb 11 09:38:00 2004
@@ -79,6 +79,7 @@
 	user->private.logoff_time           = 
 	user->private.kickoff_time          = 
 	user->private.pass_must_change_time = get_time_t_max();
+	user->private.bad_password_time     = (time_t)0;
 	user->private.unknown_3 = 0x00ffffff; 	/* don't know */
 	user->private.logon_divs = 168; 	/* hours per week */
 	user->private.hours_len = 21; 		/* 21 times 8 bits = 168 */
@@ -1765,3 +1766,107 @@
 }
 
 
+/*********************************************************************
+ Update the bad password count checking the AP_RESET_COUNT_TIME 
+ ********************************************************************/
+
+BOOL pdb_update_bad_password_count(SAM_ACCOUNT *sampass, BOOL *updated)
+{
+	time_t LastBadPassword;
+	uint16 BadPasswordCount;
+	uint32 resettime; 
+
+	BadPasswordCount = pdb_get_bad_password_count(sampass);
+	if(BadPasswordCount > 0){
+		if (!account_policy_get(AP_RESET_COUNT_TIME, &resettime)) {
+				DEBUG(0, ("account_policy_get failed.\n"));
+				return False;
+		} else {
+				LastBadPassword = pdb_get_bad_password_time
(sampass);	
+				DEBUG(10, ("LastBadPassword=%d, resettime=%
d.\n", LastBadPassword, resettime));
+				if ((resettime < 0xffffffff) && (time(NULL) > 
(LastBadPassword + resettime*60))){
+					pdb_set_bad_password_count(sampass, 
0, PDB_CHANGED);
+					pdb_set_bad_password_time(sampass, 0, 
PDB_CHANGED);
+					*updated =True;
+				}		
+		}
+	}
+	return True;
+}
+
+/*********************************************************************
+ Update the ACB_AUTOLOCK flag checking the AP_LOCK_ACCOUNT_DURATION 
+ ********************************************************************/
+
+BOOL pdb_update_autolock_flag(SAM_ACCOUNT *sampass, BOOL *updated)
+{
+	uint32 duration;
+	time_t LastBadPassword;
+
+	if (!sampass)
+		return False;
+
+	if (pdb_get_acct_ctrl(sampass) & ACB_AUTOLOCK)
+		if (!account_policy_get(AP_LOCK_ACCOUNT_DURATION, &duration)) 
{
+				DEBUG(0, ("pdb_update_autolock_flag: 
account_policy_get failed.\n"));
+				return False;
+		} else {
+				LastBadPassword = pdb_get_bad_password_time
(sampass);	
+				DEBUG(10, ("LastBadPassword=%d, duration=%
d.\n",LastBadPassword,duration*60 ));
+				if ((duration < 0xffffffff) && (time(NULL) > 
(LastBadPassword + duration*60))){
+					DEBUG(10, ("LastBadPassword=%d, 
duration=%d.\n",LastBadPassword,duration*60 ));
+					pdb_set_acct_ctrl (sampass,
+							
	pdb_get_acct_ctrl(sampass) & ~ACB_AUTOLOCK,
+								PDB_CHANGED);
+					pdb_set_bad_password_count(sampass, 
0, PDB_CHANGED);
+					pdb_set_bad_password_time(sampass, 0, 
PDB_CHANGED);
+
+					*updated =True;
+				}		
+		}
+
+	return True;
+}
+
+/*********************************************************************
+ Increment the bad_password_count 
+ ********************************************************************/
+
+BOOL pdb_increment_bad_password_count(SAM_ACCOUNT *sampass)
+{
+	uint32 resettime;
+	uint32 account_policy_lockout;
+	time_t LastBadPassword;
+
+	if (!sampass)
+		return False;
+	
+	if (!account_policy_get(AP_RESET_COUNT_TIME, &resettime)) {
+			DEBUG(0, ("account_policy_get failed.\n"));
+			return False;
+	} else {
+			LastBadPassword = pdb_get_bad_password_time(sampass);
	
+			DEBUG(10, ("LastBadPassword=%d, resettime=%d.\n", 
LastBadPassword, resettime));
+			if (time(NULL) > (LastBadPassword + resettime*60)){
+				pdb_set_bad_password_count(sampass, 1, 
PDB_CHANGED);
+			}else		
+				pdb_set_bad_password_count(sampass, 
+									
	pdb_get_bad_password_count(sampass)+1,
+									
	PDB_CHANGED);
+			pdb_set_bad_password_time(sampass, time(NULL), 
PDB_CHANGED);
+			if (!account_policy_get
(AP_BAD_ATTEMPT_LOCKOUT,&account_policy_lockout)) {
+				DEBUG(0, ("account_policy_get failed.\n"));
+				return False;
+			} else {
+				if (account_policy_lockout && 
+					(pdb_get_bad_password_count(sampass) 
>= account_policy_lockout))
+					if (!pdb_set_acct_ctrl (sampass,
+								
	pdb_get_acct_ctrl(sampass) |ACB_AUTOLOCK,
+								
	PDB_CHANGED)) {
+						DEBUG(1, 
("pdb_increment_bad_password_count:failed to set 'autolock' flag. \n")); 
+						return False;
+					} 
+			}		
+			return True;
+	}
+}
--- ./source/passdb/pdb_get_set.c.orig	Tue Feb 10 16:46:12 2004
+++ ./source/passdb/pdb_get_set.c	Wed Feb 11 09:50:54 2004
@@ -96,6 +96,14 @@
 		return (-1);
 }
 
+time_t pdb_get_bad_password_time (const SAM_ACCOUNT *sampass)
+{
+	if (sampass)
+		return (sampass->private.bad_password_time);
+	else
+		return (-1);
+}
+
 uint16 pdb_get_logon_divs (const SAM_ACCOUNT *sampass)
 {
 	if (sampass)
@@ -131,6 +139,15 @@
 		return (NULL);
 }
 
+const uint8* pdb_get_pw_history (const SAM_ACCOUNT *sampass)
+{
+	if (sampass) {
+		return ((uint8*)sampass->private.nt_pw_his);
+	}
+	else
+		return (NULL);
+}
+
 const uint8* pdb_get_lanman_passwd (const SAM_ACCOUNT *sampass)
 {
 	if (sampass) {
@@ -420,6 +437,16 @@
 	return pdb_set_init_flags(sampass, PDB_PASSLASTSET, flag);
 }
 
+BOOL pdb_set_bad_password_time (SAM_ACCOUNT *sampass, time_t mytime, enum 
pdb_value_state flag)
+{
+	if (!sampass)
+		return False;
+
+	sampass->private.bad_password_time = mytime;
+
+	return pdb_set_init_flags(sampass, PDB_BAD_PASSWORD_TIME, flag);
+}
+
 BOOL pdb_set_hours_len (SAM_ACCOUNT *sampass, uint32 len, enum 
pdb_value_state flag)
 {
 	if (!sampass)
@@ -943,6 +970,31 @@
 }
 
 /*********************************************************************
+ Set the user's password history hash.
+ ********************************************************************/
+
+BOOL pdb_set_pw_history (SAM_ACCOUNT *sampass, const uint8 * pwd, int 
historyLen, enum pdb_value_state flag)
+{
+	if (!sampass)
+		return False;
+	
+	if (historyLen > 0){
+		if (pwd){
+			sampass->private.nt_pw_his = (uint8 *) talloc_memdup
(sampass->mem_ctx, pwd, historyLen*16);
+
+			if (!sampass->private.nt_pw_his) {
+				DEBUG(0, ("pdb_set_pw_his: talloc_memdup() 
failed!\n")); 
+				return False;
+			}
+		} else {
+			sampass->private.nt_pw_his = NULL;  
+		}
+	}
+
+	return pdb_set_init_flags(sampass, PDB_PWHISTORY, flag);
+}
+
+/*********************************************************************
  Set the user's LM hash.
  ********************************************************************/
 
@@ -1116,10 +1168,18 @@
 {
 	uchar new_lanman_p16[16];
 	uchar new_nt_p16[16];
+	uchar *pwhistory;
+	uchar nt_pw[16];
+	uchar * ntpw;
+	int pwHistLen;
 
 	if (!sampass || !plaintext)
 		return False;
 	
+	ntpw = (uint8 *)pdb_get_nt_passwd(sampass);
+	if (ntpw)
+		memcpy ((uchar *)nt_pw, (uchar *)ntpw, 16);  
+	
 	nt_lm_owf_gen (plaintext, new_nt_p16, new_lanman_p16);
 
 	if (!pdb_set_nt_passwd (sampass, new_nt_p16, PDB_CHANGED)) 
@@ -1134,5 +1194,20 @@
 	if (!pdb_set_pass_changed_now (sampass))
 		return False;
 
+	if (pdb_get_acct_ctrl(sampass) & ACB_NORMAL) { 
+		account_policy_get(AP_PASSWORD_HISTORY, &pwHistLen);
+			if (pwHistLen > 0){
+			pwhistory = (uint8 *)pdb_get_pw_history (sampass);
+			if (pwhistory){
+				memmove((uchar *) (pwhistory + 16), 
pwhistory, (pwHistLen -1)*16 );
+				memcpy((uchar *)pwhistory, (uchar *)nt_pw, 
16);
+				pdb_set_pw_history (sampass, pwhistory, 
pwHistLen, PDB_CHANGED);
+			}
+			else {
+				DEBUG (100,("pdb_get_set.c: 
pdb_set_plaintext_passwd: pwhistory was NULL!\n"));
+			}
+		}
+	}
 	return True;
 }
+
--- ./source/passdb/pdb_ldap.c.orig	Tue Feb 10 16:46:12 2004
+++ ./source/passdb/pdb_ldap.c	Wed Feb 11 10:18:02 2004
@@ -405,7 +405,8 @@
 			kickoff_time,
 			pass_last_set_time, 
 			pass_can_change_time, 
-			pass_must_change_time;
+			pass_must_change_time,
+			bad_password_time;
 	pstring 	username, 
 			domain,
 			nt_username,
@@ -427,6 +428,9 @@
 	uint32 hours_len;
 	uint8 		hours[MAX_HOURS_LEN];
 	pstring temp;
+	uint8   *pwhist;
+	int     i, pwcount, pwHistLen; 
+	char    *temp1;
 
 	/*
 	 * do a little initialization
@@ -695,6 +699,37 @@
 		ZERO_STRUCT(smbntpwd);
 	}
 
+	account_policy_get(AP_PASSWORD_HISTORY, &pwHistLen);
+	if (pwHistLen > 0){
+		if ((temp1 = (char  *) malloc(sizeof(pstring) * pwHistLen)) 
== NULL) {
+			DEBUG(0, ("pdb_ldap.c: init_sam_from_ldap: malloc 
failed!\n"));
+			return False;
+		}
+		memset(temp1, 0, sizeof(pstring)*pwHistLen);
+		if ((pwhist = (uint8 *) malloc(16 * pwHistLen)) == NULL){
+			DEBUG(0, ("pdb_ldap.c: init_sam_from_ldap: malloc 
failed!\n"));
+			return False;
+		}
+		memset((uint8 *) pwhist, 0, 16*pwHistLen);
+		if (!smbldap_get_single_attribute_multivalue (ldap_state-
>smbldap_state->ldap_struct, entry, get_userattr_key2string(ldap_state-
>schema_ver, LDAP_ATTR_PWD_HISTORY), temp1, pwHistLen, &pwcount)) {
+			/* leave as default */
+		} else {
+			for (i = 0; (i < pwHistLen) && (i < pwcount); i++){
+				pdb_gethexpwd((char *)(temp1 + i*sizeof
(pstring)), smbntpwd);
+				memcpy((unsigned char *) (pwhist + i*16), 
smbntpwd, 16);
+				ZERO_STRUCT(smbntpwd);
+			}
+		}
+		if (!pdb_set_pw_history(sampass, pwhist, pwHistLen, PDB_SET)){
+			SAFE_FREE (temp1);
+			SAFE_FREE (pwhist);
+			return False;
+		}
+		SAFE_FREE (temp1);
+		SAFE_FREE (pwhist);
+	}
+
+
 	if (!smbldap_get_single_attribute (ldap_state->smbldap_state-
>ldap_struct, entry,
 			get_userattr_key2string(ldap_state->schema_ver, 
LDAP_ATTR_ACB_INFO), temp)) {
 		acct_ctrl |= ACB_NORMAL;
@@ -722,6 +757,14 @@
 		pdb_set_bad_password_count(sampass, bad_password_count, 
PDB_SET);
 	}
 
+	if (!smbldap_get_single_attribute(ldap_state->smbldap_state-
>ldap_struct, entry, 
+			get_userattr_key2string(ldap_state->schema_ver, 
LDAP_ATTR_BAD_PASSWORD_TIME), temp)) {
+		/* leave as default */
+	} else {
+		bad_password_time = (time_t) atol(temp);
+		pdb_set_bad_password_time(sampass, bad_password_time, 
PDB_SET);
+	}
+
 	if (!smbldap_get_single_attribute(ldap_state->smbldap_state-
>ldap_struct, entry,
 			get_userattr_key2string(ldap_state->schema_ver, 
LDAP_ATTR_LOGON_COUNT), temp)) {
 			/* leave as default */
@@ -750,6 +793,8 @@
 {
 	pstring temp;
 	uint32 rid;
+	uint8 * temp1; 
+	int pwHistLen, i;
 
 	if (mods == NULL || sampass == NULL) {
 		DEBUG(0, ("init_ldap_from_sam: NULL parameters found!\n"));
@@ -909,6 +954,16 @@
 		smbldap_make_mod(ldap_state->smbldap_state->ldap_struct, 
existing, mods,
 			get_userattr_key2string(ldap_state->schema_ver, 
LDAP_ATTR_PWD_MUST_CHANGE), temp);
 
+	slprintf (temp, sizeof (temp) - 1, "%li", pdb_get_bad_password_time
(sampass));
+	if (need_update(sampass, PDB_BAD_PASSWORD_TIME))
+		smbldap_make_mod(ldap_state->smbldap_state->ldap_struct, 
existing, mods,
+			get_userattr_key2string(ldap_state->schema_ver, 
LDAP_ATTR_BAD_PASSWORD_TIME), temp);
+
+	slprintf (temp, sizeof (temp) - 1, "%li", pdb_get_bad_password_count
(sampass));
+	if (need_update(sampass, PDB_BAD_PASSWORD_COUNT))
+		smbldap_make_mod(ldap_state->smbldap_state->ldap_struct, 
existing, mods,
+			get_userattr_key2string(ldap_state->schema_ver, 
LDAP_ATTR_BAD_PASSWORD_COUNT), temp);
+
 	if ((pdb_get_acct_ctrl(sampass)&
(ACB_WSTRUST|ACB_SVRTRUST|ACB_DOMTRUST))
 			|| (lp_ldap_passwd_sync()!=LDAP_PASSWD_SYNC_ONLY)) {
 
@@ -941,6 +996,26 @@
 			}
 		}
 
+
+		if (need_update(sampass, PDB_PWHISTORY)) {
+			account_policy_get(AP_PASSWORD_HISTORY, &pwHistLen);
+			temp1 = (uint8 *) pdb_get_pw_history(sampass);
+			if (temp1 != NULL)
+				for (i=0; i< pwHistLen; i++){
+					pdb_sethexpwd (temp, (uint8 *)(temp1 
+ i*16), 0);
+					DEBUG(10, ("LUJ: temp1=%s, temp=%
s\n", temp1, temp));
+					if (!strncmp(temp,"0000000000",10))
+						break;
+					smbldap_set_mod 
(mods,LDAP_MOD_REPLACE, get_userattr_key2string(ldap_state-
>schema_ver,LDAP_ATTR_PWD_HISTORY),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,
--- ./source/rpc_server/srv_samr_nt.c.orig	Wed Feb 11 09:41:01 2004
+++ ./source/rpc_server/srv_samr_nt.c	Wed Feb 11 09:41:15 2004
@@ -1656,6 +1656,7 @@
 	SAM_ACCOUNT *smbpass=NULL;
 	BOOL ret;
 	NTSTATUS nt_status;
+	BOOL updated=False;
 
 	nt_status = pdb_init_sam_talloc(mem_ctx, &smbpass);
 	
@@ -1674,6 +1675,16 @@
 
 	DEBUG(3,("User:[%s]\n", pdb_get_username(smbpass) ));
 
+	pdb_update_autolock_flag(smbpass, &updated);
+	
+	if (updated){
+		become_root();
+		if(!pdb_update_sam_account(smbpass))
+			DEBUG(1, ("Failed to modify entry.\n"));
+		unbecome_root();
+	}
+
+
 	ZERO_STRUCTP(id10);
 	init_sam_user_info10(id10, pdb_get_acct_ctrl(smbpass) );
 
@@ -1693,6 +1704,7 @@
 	SAM_ACCOUNT *smbpass=NULL;
 	BOOL ret;
 	NTSTATUS nt_status;
+	BOOL updated=False;
 
 	if (!p->ntlmssp_auth_validated)
 		return NT_STATUS_ACCESS_DENIED;
@@ -1720,6 +1732,15 @@
 
 	DEBUG(3,("User:[%s] 0x%x\n", pdb_get_username(smbpass), 
pdb_get_acct_ctrl(smbpass) ));
 
+	pdb_update_autolock_flag(smbpass, &updated);
+	
+	if (updated){
+		become_root();
+		if(!pdb_update_sam_account(smbpass))
+			DEBUG(1, ("Failed to modify entry.\n"));
+		unbecome_root();
+	}
+
 	if ( pdb_get_acct_ctrl(smbpass) & ACB_DISABLED) {
 		pdb_free_sam(&smbpass);
 		return NT_STATUS_ACCOUNT_DISABLED;
@@ -1741,6 +1762,7 @@
 {
 	SAM_ACCOUNT *sampass=NULL;
 	BOOL ret;
+	BOOL updated=False;
 
 	pdb_init_sam_talloc(mem_ctx, &sampass);
 
@@ -1757,6 +1779,15 @@
 
 	DEBUG(3,("User:[%s]\n",  pdb_get_username(sampass) ));
 
+	pdb_update_autolock_flag(sampass, &updated);
+	
+	if (updated){
+		become_root();
+		if(!pdb_update_sam_account(sampass))
+			DEBUG(1, ("Failed to modify entry.\n"));
+		unbecome_root();
+	}
+
 	ZERO_STRUCTP(id20);
 	init_sam_user_info20A(id20, sampass);
 	
@@ -1775,6 +1806,7 @@
 	SAM_ACCOUNT *sampass=NULL;
 	BOOL ret;
 	NTSTATUS nt_status;
+	BOOL updated=False, updated1=False;
 
 	nt_status = pdb_init_sam_talloc(mem_ctx, &sampass);
 	if (!NT_STATUS_IS_OK(nt_status)) {
@@ -1794,6 +1826,16 @@
 
 	DEBUG(3,("User:[%s]\n",  pdb_get_username(sampass) ));
 
+	pdb_update_autolock_flag(sampass, &updated);
+	pdb_update_bad_password_count(sampass, &updated1);
+	
+	if (updated || updated1){
+		become_root();
+		if(!pdb_update_sam_account(sampass))
+			DEBUG(1, ("Failed to modify entry.\n"));
+		unbecome_root();
+	}
+
 	ZERO_STRUCTP(id21);
 	nt_status = init_sam_user_info21A(id21, sampass, domain_sid);
 	
--- ./source/smbd/chgpasswd.c.orig	Wed Feb 11 09:42:31 2004
+++ ./source/smbd/chgpasswd.c	Wed Feb 11 09:36:48 2004
@@ -723,6 +723,7 @@
 	if (!NT_STATUS_IS_OK(nt_status))
 		return nt_status;
 
+
 	/* We've already checked the old password here.... */
 	become_root();
 	nt_status = change_oem_password(sampass, NULL, new_passwd, True);
@@ -901,6 +902,45 @@
 }
 
 /***********************************************************
+ This routine takes the given password and checks it against
+ the password history.
+************************************************************/
+static BOOL check_passwd_history(SAM_ACCOUNT *sampass, const char *plaintext)
+{
+    uchar new_lanman_p16[16];
+    uchar new_nt_p16[16];
+	uint8 *nt_pw;
+	uint8 *pwhistory;
+	BOOL found = False;
+	int i, pwHisLen;
+
+	account_policy_get(AP_PASSWORD_HISTORY, &pwHisLen); 
+	if (pwHisLen == 0)
+		return False;
+
+
+	pwhistory = (uint8 *) pdb_get_pw_history (sampass);
+
+	if (!pwhistory)
+		return False;
+
+	nt_pw = (uint8*) pdb_get_nt_passwd(sampass);
+
+   	nt_lm_owf_gen (plaintext, new_nt_p16, new_lanman_p16);
+	
+	if (!memcmp(nt_pw, new_nt_p16, 16))
+			return True;
+
+	for (i=0; i<pwHisLen; i++){
+		if (!memcmp((uint8 *) (pwhistory + i*16), new_nt_p16, 16))
+			found = True;
+	}
+
+	return found;
+}
+
+/***********************************************************
+/***********************************************************
  Code to change the oem password. Changes both the lanman
  and NT hashes.  Old_passwd is almost always NULL.
  NOTE this function is designed to be called as root. Check the old password
@@ -936,6 +976,9 @@
 /* 		return NT_STATUS_PWD_TOO_SHORT; */
 	}
 
+	if (check_passwd_history(hnd,new_passwd))   
+		return NT_STATUS_PASSWORD_RESTRICTION; 
+
 	/* TODO:  Add cracklib support here */
 
 	/*
--- ./source/utils/pdbedit.c.orig	Tue Feb 10 16:46:12 2004
+++ ./source/utils/pdbedit.c	Wed Feb 11 10:35:40 2004
@@ -48,6 +48,7 @@
 #define BIT_IMPORT	0x01000000
 #define BIT_EXPORT	0x02000000
 #define BIT_FIX_INIT    0x04000000
+#define BIT_BADPWRESET  0x08000000
 
 #define MASK_ALWAYS_GOOD	0x0000001F
 #define MASK_USER_GOOD		0x00401F00
@@ -154,6 +155,10 @@
 		tmp = pdb_get_pass_must_change_time(sam_pwent);
 		printf ("Password must change: %s\n", tmp ? http_timestring
(tmp) : "0");
 		
+		tmp = pdb_get_bad_password_time(sam_pwent);
+		printf ("Last Bad Password Time: %s\n", tmp ? http_timestring
(tmp) : "0");
+		printf ("Bad Password Count: %d\n", pdb_get_bad_password_count
(sam_pwent));
+		
 	} else if (smbpwdstyle) {
 		char lm_passwd[33];
 		char nt_passwd[33];
@@ -186,6 +191,8 @@
 {
 	SAM_ACCOUNT *sam_pwent=NULL;
 	BOOL ret;
+	BOOL updated = False, updated1=False;
+
 	
 	if (!NT_STATUS_IS_OK(pdb_init_sam (&sam_pwent))) {
 		return -1;
@@ -198,6 +205,19 @@
 		pdb_free_sam(&sam_pwent);
 		return -1;
 	}
+
+	if (!pdb_update_autolock_flag(sam_pwent, &updated))
+	        DEBUG(2,("pdb_update_autolock_flag failed.\n"));
+
+	if (!pdb_update_bad_password_count(sam_pwent, &updated))
+	        DEBUG(2,("pdb_update_bad_password_count failed.\n"));
+	
+	if (updated || updated1){
+		become_root();
+		if(!pdb_update_sam_account(sam_pwent))
+			DEBUG(1, ("Failed to modify entry.\n"));
+		unbecome_root();
+	}
 	
 	ret=print_sam_info (sam_pwent, verbosity, smbpwdstyle);
 	pdb_free_sam(&sam_pwent);
@@ -275,7 +295,7 @@
 			  const char *fullname, const char *homedir, 
 			  const char *drive, const char *script, 
 			  const char *profile, const char *account_control,
-			  const char *user_sid, const char *group_sid)
+			  const char *user_sid, const char *group_sid,const 
BOOL badpw)
 {
 	SAM_ACCOUNT *sam_pwent=NULL;
 	BOOL ret;
@@ -347,6 +367,11 @@
 		pdb_set_group_sid (sam_pwent, &g_sid, PDB_CHANGED);
 	}
 	
+	if (badpw){
+	        pdb_set_bad_password_count(sam_pwent, 0, PDB_CHANGED);
+	        pdb_set_bad_password_time(sam_pwent, 0, PDB_CHANGED);
+	}
+
 	if (NT_STATUS_IS_OK(in->pdb_update_sam_account (in, sam_pwent)))
 		print_user_info (in, username, True, False);
 	else {
@@ -593,6 +618,7 @@
 	static char *group_sid = NULL;
 	static long int account_policy_value = 0;
 	BOOL account_policy_value_set = False;
+	static BOOL badpw_reset = False;  
 
 	struct pdb_context *bin;
 	struct pdb_context *bout;
@@ -623,6 +649,7 @@
 		{"value",       'C', POPT_ARG_LONG, 
&account_policy_value, 'C',"set the account policy to this value", NULL},
 		{"account-control",	'c', POPT_ARG_STRING, 
&account_control, 0, "Values of account control", NULL},
 		{"force-initialized-passwords", 0, POPT_ARG_NONE, 
&force_initialised_password, 0, "Force initialization of corrupt password 
strings in a passdb backend", NULL},
+		{"bad-password-count-reset", 'z', POPT_ARG_NONE, 
&badpw_reset, 0, "reset bad password count" , NULL},
 		POPT_COMMON_SAMBA
 		POPT_TABLEEND
 	};
@@ -675,7 +702,8 @@
 			(account_policy ? BIT_ACCPOLICY : 0) +
 			(account_policy_value_set ? BIT_ACCPOLVAL : 0) +
 			(backend_in ? BIT_IMPORT : 0) +
-			(backend_out ? BIT_EXPORT : 0);
+			(backend_out ? BIT_EXPORT : 0) +
+			(badpw_reset ? BIT_BADPWRESET : 0);
 
 	if (setparms & BIT_BACKEND) {
 		if (!NT_STATUS_IS_OK(make_pdb_context_string(&bdef, 
backend))) {
@@ -773,6 +801,11 @@
 	/* mask out users options */
 	checkparms &= ~MASK_USER_GOOD;
 	
+	if (checkparms & BIT_BADPWRESET){
+		checkparms |= BIT_MODIFY; 
+		checkparms &= ~BIT_BADPWRESET; 
+	}
+
 	/* account operation */
 	if ((checkparms & BIT_CREATE) || (checkparms & BIT_MODIFY) || 
(checkparms & BIT_DELETE)) {
 		/* check use of -u option */
@@ -808,7 +841,7 @@
 					      home_drive,
 					      logon_script,
 					      profile_path, account_control,
-					      user_sid, group_sid);
+					      user_sid, 
group_sid,badpw_reset);
 		}
 	}
 

Jianliang Lu
TieSse s.p.a.     Ivrea (To) - Italy
j.lu at tiesse.com   luj at libero.it
http://www.tiesse.com


More information about the samba-technical mailing list