[PATCH] Password history support

Aurélien Degrémont adegremont at idealx.com
Mon Dec 22 17:31:24 GMT 2003


Hi,

Here is a patch which implements password history support to Samba 3.

It add a new field pw_history[] to SAM_ACCOUNT struct. I think it's the 
last missing and needed SAM_ACCOUNT field. This patch complete the 
previous TDBSAM patch in order to create the new tdbsam format.

The user password history is stored in each sam_account struct. Each 
password is coded in a "salted" format. It is done by the following 
method : MD5( MD4_NT_PW_HASH + "salt") for security reasons. The 
transformation is done by crypt_salted(), I put it util_pw.c (I didn't 
know where put it, so it set it there, i let you move it if you know a 
better place).
I modified all the password modification code in Samba to add the 
password history call, maybe i miss some of it, please complete (the 
samba password managing code is far from clear and it is ... scattered :)).

Presently, only ldapsam and tdbsam are supported. SambaSamAccount got a 
new attribute.

Waiting for your comments.

Aurélien Degrémont

-------------- next part --------------
diff -ruN samba-3.0.1/examples/LDAP/samba.schema samba-3.0.1-patch/examples/LDAP/samba.schema
--- samba-3.0.1/examples/LDAP/samba.schema	2003-12-04 22:38:34.000000000 +0100
+++ samba-3.0.1-patch/examples/LDAP/samba.schema	2003-12-17 11:09:27.000000000 +0100
@@ -241,6 +241,11 @@
 	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.48 NAME 'sambaPasswordHistory'
+	DESC 'List of passwords from history'
+	EQUALITY caseIgnoreIA5Match
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{768} SINGLE-VALUE )
+
 ##
 ## SID, of any type
 ##
@@ -313,7 +318,8 @@
 	       sambaPwdCanChange $ sambaPwdMustChange $ sambaAcctFlags $
                displayName $ sambaHomePath $ sambaHomeDrive $ sambaLogonScript $
 	       sambaProfilePath $ description $ sambaUserWorkstations $
-	       sambaPrimaryGroupSID $ sambaDomainName $ sambaMungedDial))
+	       sambaPrimaryGroupSID $ sambaDomainName $ sambaMungedDial $
+	       sambaPasswordHistory))
 
 ##
 ## Group mapping info
diff -ruN samba-3.0.1/source/include/passdb.h samba-3.0.1-patch/source/include/passdb.h
--- samba-3.0.1/source/include/passdb.h	2003-12-04 22:38:36.000000000 +0100
+++ samba-3.0.1-patch/source/include/passdb.h	2003-12-17 11:04:17.000000000 +0100
@@ -62,6 +62,7 @@
 	PDB_UNKNOWN6,
 	PDB_LMPASSWD,
 	PDB_NTPASSWD,
+	PDB_PASSWD_HISTORY,
 	PDB_BACKEND_PRIVATE_DATA,
 
 	/* this must be the last element */
@@ -89,6 +90,9 @@
 #define IS_SAM_CHANGED(x, flag)	(pdb_get_init_flags(x, flag) == PDB_CHANGED)
 #define IS_SAM_DEFAULT(x, flag)	(pdb_get_init_flags(x, flag) == PDB_DEFAULT)
 		
+#define PW_HISTORY_SIZE NT_HASH_LEN	/* Size, in bytes, of a password in the history */
+#define PW_HISTORY_NBR 24			/* Number of password stored in the history */
+
 typedef struct sam_passwd
 {
 	TALLOC_CTX *mem_ctx;
@@ -128,6 +132,7 @@
 		
 		DATA_BLOB lm_pw; /* .data is Null if no password */
 		DATA_BLOB nt_pw; /* .data is Null if no password */
+		DATA_BLOB pw_history[PW_HISTORY_NBR]; /* .data is Null if no password */
 		char* plaintext_pw; /* is Null if not available */
 		
 		uint16 acct_ctrl; /* account info (ACB_xxxx bit-mask) */
diff -ruN samba-3.0.1/source/include/smbldap.h samba-3.0.1-patch/source/include/smbldap.h
--- samba-3.0.1/source/include/smbldap.h	2003-12-04 22:38:36.000000000 +0100
+++ samba-3.0.1-patch/source/include/smbldap.h	2003-12-17 11:04:17.000000000 +0100
@@ -91,6 +91,7 @@
 #define LDAP_ATTR_BAD_PASSWORD_COUNT	35
 #define LDAP_ATTR_LOGON_COUNT		36
 #define LDAP_ATTR_MUNGED_DIAL		37
+#define LDAP_ATTR_PASSWORD_HISTORY	39
 
 typedef struct _attrib_map_entry {
 	int		attrib;
@@ -145,4 +146,3 @@
 struct smbldap_state;
 
 #endif	/* _SMBLDAP_H */
-
diff -ruN samba-3.0.1/source/lib/smbldap.c samba-3.0.1-patch/source/lib/smbldap.c
--- samba-3.0.1/source/lib/smbldap.c	2003-12-04 22:38:37.000000000 +0100
+++ samba-3.0.1-patch/source/lib/smbldap.c	2003-12-17 11:04:17.000000000 +0100
@@ -98,6 +98,7 @@
 	{ LDAP_ATTR_OBJCLASS,		"objectClass"		},
 	{ LDAP_ATTR_ACB_INFO,		"sambaAcctFlags"	},
 	{ LDAP_ATTR_MUNGED_DIAL,	"sambaMungedDial"	},
+	{ LDAP_ATTR_PASSWORD_HISTORY,	"sambaPasswordHistory"	},
 	{ LDAP_ATTR_LIST_END,		NULL 			}
 };
 
diff -ruN samba-3.0.1/source/lib/util_pw.c samba-3.0.1-patch/source/lib/util_pw.c
--- samba-3.0.1/source/lib/util_pw.c	2003-06-07 19:57:33.000000000 +0200
+++ samba-3.0.1-patch/source/lib/util_pw.c	2003-12-17 11:04:17.000000000 +0100
@@ -87,3 +87,27 @@
 
 	return alloc_copy_passwd(temp);
 }
+
+/** 
+  * Crypt an NT password with some "salt" in MD5.
+  * @param nt_pw NT crypted password to be salted.
+  * @return a MD5 16-byte long hashed password salted. NULL if error.
+  */
+uint8 * crypt_salted(const uint8 *nt_pw) 
+{
+	uint8 *crypt_pw = NULL;
+	struct MD5Context md5_ctx;
+	
+	if ((crypt_pw = (uint8 *) malloc(PW_HISTORY_SIZE*sizeof(uint8))) == NULL) {
+		DEBUG(0,("crypt_salted: Unable to alloc crypt_pw\n"));
+		return(NULL);
+	}
+	
+	MD5Init(&md5_ctx);
+	MD5Update(&md5_ctx, nt_pw, 16);
+	/* Salt the crypted password */
+	MD5Update(&md5_ctx, "salt", strlen("salt"));
+	MD5Final((uchar *)crypt_pw, &md5_ctx);
+
+	return(crypt_pw);
+}
diff -ruN samba-3.0.1/source/passdb/passdb.c samba-3.0.1-patch/source/passdb/passdb.c
--- samba-3.0.1/source/passdb/passdb.c	2003-12-17 11:09:48.000000000 +0100
+++ samba-3.0.1-patch/source/passdb/passdb.c	2003-12-17 11:07:16.000000000 +0100
@@ -115,9 +115,14 @@
 
 static void destroy_pdb_talloc(SAM_ACCOUNT **user) 
 {
+	uint8 i;
+	
 	if (*user) {
 		data_blob_clear_free(&((*user)->private.lm_pw));
 		data_blob_clear_free(&((*user)->private.nt_pw));
+		for (i=0; i<PW_HISTORY_NBR; i++) 
+			if (pdb_get_passwd_history(*user,i) != NULL)
+				data_blob_clear_free(&((*user)->private.pw_history[i])); 
 
 		if((*user)->private.plaintext_pw!=NULL)
 			memset((*user)->private.plaintext_pw,'\0',strlen((*user)->private.plaintext_pw));
@@ -343,6 +348,7 @@
 
 static void pdb_free_sam_contents(SAM_ACCOUNT *user)
 {
+	uint8 i;
 
 	/* Kill off sensitive data.  Free()ed by the
 	   talloc mechinism */
@@ -352,6 +358,10 @@
 	if (user->private.plaintext_pw!=NULL)
 		memset(user->private.plaintext_pw,'\0',strlen(user->private.plaintext_pw));
 
+	for (i=0; i<PW_HISTORY_NBR; i++) 
+		if (pdb_get_passwd_history(user, i) != NULL)
+			data_blob_clear_free(&(user->private.pw_history[i])); 
+
 	if (user->private.backend_private_data && user->private.backend_private_data_free_fn) {
 		user->private.backend_private_data_free_fn(&user->private.backend_private_data);
 	}
@@ -1033,6 +1043,12 @@
 			pdb_free_sam(&sam_pass);
 			return False;
 		}
+		
+		if (!pdb_add_passwd_history (sam_pass, pdb_get_nt_passwd(sam_pass), PDB_CHANGED)) {
+			slprintf(err_str, err_str_len-1, "Failed to set password history for user %s.\n", user_name);
+			pdb_free_sam(&sam_pass);
+			return False;
+		}
 	}	
 
 	if (local_flags & LOCAL_ADD_USER) {
@@ -1296,7 +1312,7 @@
  *********************************************************************/
 
 #define TDB_FORMAT_STRING_V0       "ddddddBBBBBBBBBBBBddBBwdwdBwwd"
-#define TDB_FORMAT_STRING_V1       "dddddddBBBBBBBBBBBBddBBwdwdBwwd"
+#define TDB_FORMAT_STRING_V1       "dddddddBBBBBBBBBBBBddBBBwdwdBwwd"
 
 /**********************************************************************
  Intialize a SAM_ACCOUNT struct from a BYTE buffer of size len
@@ -1766,10 +1782,12 @@
 	uint16	acct_ctrl, logon_divs;
 	uint16	bad_password_count, logon_count;
 	uint8	*hours;
-	static uint8	*lm_pw_ptr, *nt_pw_ptr;
+	static uint8	*lm_pw_ptr, *nt_pw_ptr, *pw_history_ptr;
 	uint32		len = 0;
-	uint32		lm_pw_len, nt_pw_len, hourslen;
+	uint32		lm_pw_len, nt_pw_len, hourslen, pw_history_len;
+	uint8	*pw_history=NULL;
 	BOOL ret = True;
+	uint8 i=0;
 	
 	if(sampass == NULL || buf == NULL) {
 		DEBUG(0, ("init_sam_from_buffer: NULL parameters found!\n"));
@@ -1801,6 +1819,7 @@
 		&group_rid,
 		&lm_pw_len, &lm_pw_ptr,
 		&nt_pw_len, &nt_pw_ptr,
+		&pw_history_len, &pw_history_ptr,
 		&acct_ctrl,
 		&unknown_3,
 		&logon_divs,
@@ -1878,6 +1897,13 @@
 		}
 	}
 
+	if (pw_history_len && pw_history_ptr) {
+		pw_history_len /= PW_HISTORY_SIZE;
+		for (i=0; i < pw_history_len; i++) {
+			pdb_set_passwd_history(sampass, pw_history_ptr + i*PW_HISTORY_SIZE, i, PDB_SET);
+		}
+	}
+
 	pdb_set_user_sid_from_rid(sampass, user_rid, PDB_SET);
 	pdb_set_group_sid_from_rid(sampass, group_rid, PDB_SET);
 	pdb_set_unknown_3(sampass, unknown_3, PDB_SET);
@@ -1904,6 +1930,7 @@
 	SAFE_FREE(munged_dial);
 	SAFE_FREE(unknown_str);
 	SAFE_FREE(hours);
+	SAFE_FREE(pw_history_ptr);
 
 	return ret;
 }
@@ -1945,8 +1972,11 @@
 
 	const uint8 *lm_pw;
 	const uint8 *nt_pw;
+	uint8 *pw_history=NULL, *pw=NULL;
 	uint32	lm_pw_len = 16;
 	uint32	nt_pw_len = 16;
+	uint32  pw_history_len = 1;
+	uint8 i=0;
 
 	/* do we have a valid SAM_ACCOUNT pointer? */
 	if (sampass == NULL) {
@@ -2061,6 +2091,27 @@
 	else
 		munged_dial_len = 0;	
 		
+	
+	/* Alloc pointer to create the password history buffer */
+	if ((pw_history = (uint8 *) malloc(pw_history_len)) == NULL) {
+		DEBUG(0,("init_buffer_from_sam: Unable to malloc() memory for password history!\n"));
+		return (-1);
+	}
+	/* Store each password of the history into the buffer */
+	for(i=0; i < PW_HISTORY_NBR && ((pw = (uint8 *) pdb_get_passwd_history(sampass, i)) != NULL); i++) {
+		pw_history_len += PW_HISTORY_SIZE;
+		if ((pw_history = (uint8 *) realloc(pw_history, pw_history_len)) == NULL) {
+			DEBUG(0,("init_buffer_from_sam: Unable to realloc() memory for password history!\n"));
+			return (-1);
+		}
+		if (memcpy(pw_history + pw_history_len - PW_HISTORY_SIZE - 1, pw, PW_HISTORY_SIZE) == NULL) {
+			DEBUG(0,("init_buffer_from_sam: Unable to memcpy() memory of password history!\n"));
+			SAFE_FREE(pw_history);
+			return (-1);
+		}
+	}
+	pw_history[pw_history_len - 1] = '\0';
+	
 	/* one time to get the size needed */
 	len = tdb_pack(NULL, 0,  TDB_FORMAT_STRING_v1,
 		logon_time,
@@ -2086,6 +2137,7 @@
 		group_rid,
 		lm_pw_len, lm_pw,
 		nt_pw_len, nt_pw,
+		pw_history_len, pw_history,
 		pdb_get_acct_ctrl(sampass),
 		pdb_get_unknown_3(sampass),
 		pdb_get_logon_divs(sampass),
@@ -2102,6 +2154,7 @@
 	/* malloc the space needed */
 	if ( (*buf=(uint8*)malloc(len)) == NULL) {
 		DEBUG(0,("init_buffer_from_sam: Unable to malloc() memory for buffer!\n"));
+		SAFE_FREE(pw_history);
 		return (-1);
 	}
 	
@@ -2130,6 +2183,7 @@
 		group_rid,
 		lm_pw_len, lm_pw,
 		nt_pw_len, nt_pw,
+		pw_history_len, pw_history,
 		pdb_get_acct_ctrl(sampass),
 		pdb_get_unknown_3(sampass),
 		pdb_get_logon_divs(sampass),
@@ -2139,6 +2193,7 @@
 		pdb_get_logon_count(sampass),
 		pdb_get_unknown_6(sampass));
 	
+	SAFE_FREE(pw_history);
 	
 	/* check to make sure we got it correct */
 	if (buflen != len) {
diff -ruN samba-3.0.1/source/passdb/pdb_get_set.c samba-3.0.1-patch/source/passdb/pdb_get_set.c
--- samba-3.0.1/source/passdb/pdb_get_set.c	2003-11-07 18:37:36.000000000 +0100
+++ samba-3.0.1-patch/source/passdb/pdb_get_set.c	2003-12-17 11:04:17.000000000 +0100
@@ -338,6 +338,16 @@
 		return (-1);
 }
 
+const uint8 * pdb_get_passwd_history (const SAM_ACCOUNT *sampass, uint8 index)
+{
+
+	if (sampass && index < PW_HISTORY_NBR) {
+		return (sampass->private.pw_history[index].data);
+	}
+	else
+		return (NULL);
+}
+
 void *pdb_get_backend_private_data (const SAM_ACCOUNT *sampass, const struct pdb_methods *my_methods)
 {
 	if (sampass && my_methods == sampass->private.backend_private_methods)
@@ -1128,3 +1138,51 @@
 
 	return True;
 }
+
+
+/**
+  * Set a user's history password. The provided password must be correctly crypted.
+  * @param pwd must be a 16-byte NT_HASH already salted.
+  **/
+
+BOOL pdb_set_passwd_history (SAM_ACCOUNT *sampass, const uint8 *pwd, uint8 index, enum pdb_value_state flag)
+{
+
+	if (!sampass)
+		return False;
+
+	if (index >= PW_HISTORY_NBR) 
+		return False;
+	
+	data_blob_clear_free(&sampass->private.pw_history[index]);
+	sampass->private.pw_history[index] = data_blob(pwd, pwd ? PW_HISTORY_SIZE : 0);
+	
+	return pdb_set_init_flags(sampass, PDB_PASSWD_HISTORY, flag);
+}
+
+
+/**
+  * Add a password to the password history. The provided password will be salted.
+  * @param pwd must be a 16-byte NT_HASH non salted.
+  **/
+
+BOOL pdb_add_passwd_history (SAM_ACCOUNT *sampass, const uint8 *pwd, enum pdb_value_state flag)
+{
+	uint8 *salt_pw = NULL;
+
+	if (!sampass)
+		return False;
+
+	data_blob_clear_free(&sampass->private.pw_history[PW_HISTORY_NBR - 1]);
+	
+	memmove(&sampass->private.pw_history[1], sampass->private.pw_history, (sizeof(sampass->private.pw_history) - sizeof(sampass->private.pw_history[0])));
+
+	if (pwd)
+		salt_pw = crypt_salted(pwd);
+	
+	sampass->private.pw_history[0] = data_blob(salt_pw, salt_pw ? PW_HISTORY_SIZE : 0);
+
+	SAFE_FREE(salt_pw);
+
+	return pdb_set_init_flags(sampass, PDB_PASSWD_HISTORY, flag);
+}
diff -ruN samba-3.0.1/source/passdb/pdb_ldap.c samba-3.0.1-patch/source/passdb/pdb_ldap.c
--- samba-3.0.1/source/passdb/pdb_ldap.c	2003-12-04 22:38:38.000000000 +0100
+++ samba-3.0.1-patch/source/passdb/pdb_ldap.c	2003-12-17 11:04:17.000000000 +0100
@@ -426,7 +426,8 @@
 			logon_count = 0;
 	uint32 hours_len;
 	uint8 		hours[MAX_HOURS_LEN];
-	pstring temp;
+	pstring temp, pw_history;
+	uint8 i=0, pw_history_len=0;
 
 	/*
 	 * do a little initialization
@@ -696,6 +697,20 @@
 	}
 
 	if (!smbldap_get_single_attribute (ldap_state->smbldap_state->ldap_struct, entry,
+		get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_PASSWORD_HISTORY), temp)) {
+		/* leave as default */
+	} else {
+		pw_history_len = strlen(temp) / 32;
+		for(i=0; i<pw_history_len; i++) {
+			pdb_gethexpwd(temp+i*32, pw_history);
+			if (!pdb_set_passwd_history(sampass, pw_history, i, PDB_SET))
+				return False;
+		}
+		memset((char *)temp, '\0', strlen(temp)+1);
+		ZERO_STRUCT(pw_history);
+	}
+
+	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;
 	} else {
@@ -750,6 +765,7 @@
 {
 	pstring temp;
 	uint32 rid;
+	uint8 i=0;
 
 	if (mods == NULL || sampass == NULL) {
 		DEBUG(0, ("init_ldap_from_sam: NULL parameters found!\n"));
@@ -928,6 +944,15 @@
 				get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_NTPW), 
 				temp);
 
+		for(i=0;pdb_get_passwd_history(sampass,i) != NULL; i++) 
+			pdb_sethexpwd (temp+i*32, pdb_get_passwd_history(sampass, i),
+				       pdb_get_acct_ctrl(sampass));
+
+		if (need_update(sampass, PDB_PASSWD_HISTORY))
+			smbldap_make_mod(ldap_state->smbldap_state->ldap_struct, existing, mods,
+				get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_PASSWORD_HISTORY), 
+				temp);
+
 		slprintf (temp, sizeof (temp) - 1, "%li", pdb_get_pass_last_set_time(sampass));
 		if (need_update(sampass, PDB_PASSLASTSET))
 			smbldap_make_mod(ldap_state->smbldap_state->ldap_struct, existing, mods,
diff -ruN samba-3.0.1/source/rpc_server/srv_samr_nt.c samba-3.0.1-patch/source/rpc_server/srv_samr_nt.c
--- samba-3.0.1/source/rpc_server/srv_samr_nt.c	2003-12-10 22:59:18.000000000 +0100
+++ samba-3.0.1-patch/source/rpc_server/srv_samr_nt.c	2003-12-17 11:04:17.000000000 +0100
@@ -2667,6 +2667,11 @@
 		return False; 
 	}
  
+	if (!pdb_add_passwd_history(pwd, id12->nt_pwd, PDB_CHANGED)) {
+		pdb_free_sam(&pwd);
+		return False;
+	}
+ 
 	if(!pdb_update_sam_account(pwd)) {
 		pdb_free_sam(&pwd);
 		return False;
@@ -2825,6 +2830,11 @@
 		return False;
 	}
  
+	if (!pdb_add_passwd_history(pwd, pdb_get_nt_passwd(pwd), PDB_CHANGED)) {
+		pdb_free_sam(&pwd);
+		return False;
+	}
+ 
 	copy_id23_to_sam_passwd(pwd, id23);
  
 	/* if it's a trust account, don't update /etc/passwd */
@@ -2891,6 +2901,11 @@
 		return False;
 	}
  
+	if (!pdb_add_passwd_history(pwd, pdb_get_nt_passwd(pwd), PDB_CHANGED)) {
+		pdb_free_sam(&pwd);
+		return False;
+	}
+	
 	/* if it's a trust account, don't update /etc/passwd */
 	if ( ( (acct_ctrl &  ACB_DOMTRUST) == ACB_DOMTRUST ) ||
 		( (acct_ctrl &  ACB_WSTRUST) ==  ACB_WSTRUST) ||
diff -ruN samba-3.0.1/source/smbd/chgpasswd.c samba-3.0.1-patch/source/smbd/chgpasswd.c
--- samba-3.0.1/source/smbd/chgpasswd.c	2003-12-04 22:38:38.000000000 +0100
+++ samba-3.0.1-patch/source/smbd/chgpasswd.c	2003-12-17 11:04:17.000000000 +0100
@@ -908,8 +908,12 @@
 NTSTATUS change_oem_password(SAM_ACCOUNT *hnd, char *old_passwd, char *new_passwd, BOOL as_root)
 {
 	BOOL ret;
-	uint32 min_len;
+	uint32 min_len, nbr_pw;
+	uint8 crypt_pw[PW_HISTORY_SIZE];
+	uint8 *hist_pw=NULL, *salt_pw=NULL;
+	uint8 i=0;
 
+	
 	if (time(NULL) < pdb_get_pass_can_change_time(hnd)) {
 		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))));
@@ -934,6 +938,24 @@
 /* 		return NT_STATUS_PWD_TOO_SHORT; */
 	}
 
+	/* Test the password against the password history */
+	if (account_policy_get(AP_PASSWORD_HISTORY, &nbr_pw) && nbr_pw > 0) {
+		
+		/* Crypt and salt the clear password */
+		E_md4hash(new_passwd, crypt_pw);
+		salt_pw = crypt_salted(crypt_pw);
+		
+		for(i=0; i<nbr_pw && ((hist_pw = (uint8*) pdb_get_passwd_history(hnd, i)) != NULL); i++) {
+			if (hist_pw != NULL && memcmp(salt_pw, hist_pw, PW_HISTORY_SIZE) == 0) {
+				DEBUG(1, ("Password Change: the new password for user %s is already present in history, it cannot be used.\n", pdb_get_username(hnd)));
+				SAFE_FREE(salt_pw);
+				return NT_STATUS_PASSWORD_RESTRICTION;
+			}
+		}
+		SAFE_FREE(salt_pw);
+	}
+	
+	
 	/* TODO:  Add cracklib support here */
 
 	/*
@@ -957,6 +979,11 @@
 		return NT_STATUS_ACCESS_DENIED;
 	}
 
+	/* Add the password to the history */
+	if (!pdb_add_passwd_history(hnd, crypt_pw, PDB_CHANGED)) {
+		return NT_STATUS_ACCESS_DENIED;
+	}
+	
 	/* Now write it into the file. */
 	ret = pdb_update_sam_account (hnd);
 
diff -ruN samba-3.0.1/source/utils/pdbedit.c samba-3.0.1-patch/source/utils/pdbedit.c
--- samba-3.0.1/source/utils/pdbedit.c	2003-12-04 22:38:40.000000000 +0100
+++ samba-3.0.1-patch/source/utils/pdbedit.c	2003-12-17 11:04:17.000000000 +0100
@@ -358,6 +358,7 @@
 	}
 
 	pdb_set_plaintext_passwd(sam_pwent, password1);
+	pdb_add_passwd_history(sam_pwent, pdb_get_nt_passwd(sam_pwent), PDB_CHANGED);
 	memset(password1, 0, strlen(password1));
 	SAFE_FREE(password1);
 	memset(password2, 0, strlen(password2));


More information about the samba-technical mailing list