svn commit: samba r19058 - in branches/SAMBA_3_0/source: auth passdb rpc_server smbd

jmcd at samba.org jmcd at samba.org
Tue Oct 3 17:14:19 GMT 2006


Author: jmcd
Date: 2006-10-03 17:14:18 +0000 (Tue, 03 Oct 2006)
New Revision: 19058

WebSVN: http://websvn.samba.org/cgi-bin/viewcvs.cgi?view=rev&root=samba&rev=19058

Log:
Implement "user cannot change password", and complete "user must change
password at next logon" code.  The "password last set time" of zero now
means "user must change password", because that's how windows seems to
use it.  The "can change" and "must change" times are now calculated
based on the "last set" time and policies.  

We use the "can change" field now to indicate that a user cannot change
a password by putting MAX_TIME_T in it (so long as "last set" time isn't
zero).  Based on this, we set the password-can-change bit in the
faked secdesc.



Modified:
   branches/SAMBA_3_0/source/auth/auth_sam.c
   branches/SAMBA_3_0/source/passdb/passdb.c
   branches/SAMBA_3_0/source/passdb/pdb_get_set.c
   branches/SAMBA_3_0/source/passdb/pdb_interface.c
   branches/SAMBA_3_0/source/passdb/pdb_ldap.c
   branches/SAMBA_3_0/source/rpc_server/srv_netlog_nt.c
   branches/SAMBA_3_0/source/rpc_server/srv_samr_nt.c
   branches/SAMBA_3_0/source/smbd/chgpasswd.c


Changeset:
Modified: branches/SAMBA_3_0/source/auth/auth_sam.c
===================================================================
--- branches/SAMBA_3_0/source/auth/auth_sam.c	2006-10-03 16:48:02 UTC (rev 19057)
+++ branches/SAMBA_3_0/source/auth/auth_sam.c	2006-10-03 17:14:18 UTC (rev 19058)
@@ -168,7 +168,7 @@
 		time_t last_set_time = pdb_get_pass_last_set_time(sampass);
 
 		/* check for immediate expiry "must change at next logon" */
-		if (must_change_time == 0 && last_set_time != 0) {
+		if (last_set_time == 0) {
 			DEBUG(1,("sam_account_ok: Account for user '%s' password must change!.\n", pdb_get_username(sampass)));
 			return NT_STATUS_PASSWORD_MUST_CHANGE;
 		}

Modified: branches/SAMBA_3_0/source/passdb/passdb.c
===================================================================
--- branches/SAMBA_3_0/source/passdb/passdb.c	2006-10-03 16:48:02 UTC (rev 19057)
+++ branches/SAMBA_3_0/source/passdb/passdb.c	2006-10-03 17:14:18 UTC (rev 19058)
@@ -1106,7 +1106,7 @@
 	logoff_time = (uint32)pdb_get_logoff_time(sampass);
 	kickoff_time = (uint32)pdb_get_kickoff_time(sampass);
 	bad_password_time = (uint32)pdb_get_bad_password_time(sampass);
-	pass_can_change_time = (uint32)pdb_get_pass_can_change_time(sampass);
+	pass_can_change_time = (uint32)pdb_get_pass_can_change_time_noncalc(sampass);
 	pass_must_change_time = (uint32)pdb_get_pass_must_change_time(sampass);
 	pass_last_set_time = (uint32)pdb_get_pass_last_set_time(sampass);
 

Modified: branches/SAMBA_3_0/source/passdb/pdb_get_set.c
===================================================================
--- branches/SAMBA_3_0/source/passdb/pdb_get_set.c	2006-10-03 16:48:02 UTC (rev 19057)
+++ branches/SAMBA_3_0/source/passdb/pdb_get_set.c	2006-10-03 17:14:18 UTC (rev 19058)
@@ -74,15 +74,34 @@
 {
 	uint32 allow;
 
+	/* if the last set time is zero, it means the user cannot 
+	   change their password, and this time must be zero.   jmcd 
+	*/
 	if (sampass->pass_last_set_time == 0)
 		return (time_t) 0;
 	
+	/* if the time is max, and the field has been changed,
+	   we're trying to update this real value from the sampass
+	   to indicate that the user cannot change their password.  jmcd
+	*/
+	if (sampass->pass_can_change_time == get_time_t_max() &&
+	    pdb_get_init_flags(sampass, PDB_CANCHANGETIME) == PDB_CHANGED)
+		return sampass->pass_can_change_time;
+
 	if (!pdb_get_account_policy(AP_MIN_PASSWORD_AGE, &allow))
 		allow = 0;
 
+	/* in normal cases, just calculate it from policy */
 	return sampass->pass_last_set_time + allow;
 }
 
+/* we need this for loading from the backend, so that we don't overwrite
+   non-changed max times, otherwise the pass_can_change checking won't work */
+time_t pdb_get_pass_can_change_time_noncalc(const struct samu *sampass)
+{
+	return sampass->pass_can_change_time;
+}
+
 time_t pdb_get_pass_must_change_time(const struct samu *sampass)
 {
 	uint32 expire;
@@ -100,6 +119,14 @@
 	return sampass->pass_last_set_time + expire;
 }
 
+BOOL pdb_get_pass_can_change(const struct samu *sampass)
+{
+	if (sampass->pass_can_change_time == get_time_t_max() &&
+	    sampass->pass_last_set_time != 0)
+		return False;
+	return True;
+}
+
 uint16 pdb_get_logon_divs(const struct samu *sampass)
 {
 	return sampass->logon_divs;
@@ -944,43 +971,14 @@
 
 /* Helpful interfaces to the above */
 
-/*********************************************************************
- Sets the last changed times and must change times for a normal
- password change.
- ********************************************************************/
-
-BOOL pdb_set_pass_changed_now(struct samu *sampass)
+BOOL pdb_set_pass_can_change(struct samu *sampass, BOOL canchange)
 {
-	uint32 expire;
-	uint32 min_age;
-
-	if (!pdb_set_pass_last_set_time (sampass, time(NULL), PDB_CHANGED))
-		return False;
-
-	if (!pdb_get_account_policy(AP_MAX_PASSWORD_AGE, &expire) 
-	    || (expire==(uint32)-1) || (expire == 0)) {
-		if (!pdb_set_pass_must_change_time (sampass, get_time_t_max(), PDB_CHANGED))
-			return False;
-	} else {
-		if (!pdb_set_pass_must_change_time (sampass, 
-						    pdb_get_pass_last_set_time(sampass)
-						    + expire, PDB_CHANGED))
-			return False;
-	}
-	
-	if (!pdb_get_account_policy(AP_MIN_PASSWORD_AGE, &min_age) 
-	    || (min_age==(uint32)-1)) {
-		if (!pdb_set_pass_can_change_time (sampass, 0, PDB_CHANGED))
-			return False;
-	} else {
-		if (!pdb_set_pass_can_change_time (sampass, 
-						    pdb_get_pass_last_set_time(sampass)
-						    + min_age, PDB_CHANGED))
-			return False;
-	}
-	return True;
+	return pdb_set_pass_can_change_time(sampass, 
+				     canchange ? 0 : get_time_t_max(),
+				     PDB_CHANGED);
 }
 
+
 /*********************************************************************
  Set the user's PLAINTEXT password.  Used as an interface to the above.
  Also sets the last change time to NOW.
@@ -1016,7 +1014,7 @@
 	if (!pdb_set_plaintext_pw_only (sampass, plaintext, PDB_CHANGED)) 
 		return False;
 
-	if (!pdb_set_pass_changed_now (sampass))
+	if (!pdb_set_pass_last_set_time (sampass, time(NULL), PDB_CHANGED))
 		return False;
 
 	/* Store the password history. */

Modified: branches/SAMBA_3_0/source/passdb/pdb_interface.c
===================================================================
--- branches/SAMBA_3_0/source/passdb/pdb_interface.c	2006-10-03 16:48:02 UTC (rev 19057)
+++ branches/SAMBA_3_0/source/passdb/pdb_interface.c	2006-10-03 17:14:18 UTC (rev 19058)
@@ -48,44 +48,7 @@
 				  const char **name,
 				  enum lsa_SidType *psid_name_use,
 				  union unid_t *unix_id);
-/*******************************************************************
- Clean up uninitialised passwords.  The only way to tell 
- that these values are not 'real' is that they do not
- have a valid last set time.  Instead, the value is fixed at 0. 
- Therefore we use that as the key for 'is this a valid password'.
- However, it is perfectly valid to have a 'default' last change
- time, such LDAP with a missing attribute would produce.
-********************************************************************/
 
-static void pdb_force_pw_initialization(struct samu *pass) 
-{
-	const uint8 *lm_pwd, *nt_pwd;
-	
-	/* only reset a password if the last set time has been 
-	   explicitly been set to zero.  A default last set time 
-	   is ignored */
-
-	if ( (pdb_get_init_flags(pass, PDB_PASSLASTSET) != PDB_DEFAULT) 
-		&& (pdb_get_pass_last_set_time(pass) == 0) ) 
-	{
-		
-		if (pdb_get_init_flags(pass, PDB_LMPASSWD) != PDB_DEFAULT) 
-		{
-			lm_pwd = pdb_get_lanman_passwd(pass);
-			if (lm_pwd) 
-				pdb_set_lanman_passwd(pass, NULL, PDB_CHANGED);
-		}
-		if (pdb_get_init_flags(pass, PDB_NTPASSWD) != PDB_DEFAULT) 
-		{
-			nt_pwd = pdb_get_nt_passwd(pass);
-			if (nt_pwd) 
-				pdb_set_nt_passwd(pass, NULL, PDB_CHANGED);
-		}
-	}
-
-	return;
-}
-
 NTSTATUS smb_register_passdb(int version, const char *name, pdb_init_function init) 
 {
 	struct pdb_init_function_entry *entry = backends;
@@ -250,7 +213,7 @@
 	if ( !NT_STATUS_IS_OK(pdb->getsampwent(pdb, user) ) ) {
 		return False;
 	}
-	pdb_force_pw_initialization( user );
+
 	return True;
 }
 
@@ -266,8 +229,6 @@
 		TALLOC_FREE(csamuser);
 	}
 
-	pdb_force_pw_initialization( sam_acct );
-	
 	csamuser = samu_new( NULL );
 	if (!csamuser) {
 		return False;

Modified: branches/SAMBA_3_0/source/passdb/pdb_ldap.c
===================================================================
--- branches/SAMBA_3_0/source/passdb/pdb_ldap.c	2006-10-03 16:48:02 UTC (rev 19057)
+++ branches/SAMBA_3_0/source/passdb/pdb_ldap.c	2006-10-03 17:14:18 UTC (rev 19058)
@@ -1096,7 +1096,7 @@
 		smbldap_make_mod(ldap_state->smbldap_state->ldap_struct, existing, mods,
 			get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_KICKOFF_TIME), temp);
 
-	slprintf (temp, sizeof (temp) - 1, "%li", pdb_get_pass_can_change_time(sampass));
+	slprintf (temp, sizeof (temp) - 1, "%li", pdb_get_pass_can_change_time_noncalc(sampass));
 	if (need_update(sampass, PDB_CANCHANGETIME))
 		smbldap_make_mod(ldap_state->smbldap_state->ldap_struct, existing, mods,
 			get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_PWD_CAN_CHANGE), temp);

Modified: branches/SAMBA_3_0/source/rpc_server/srv_netlog_nt.c
===================================================================
--- branches/SAMBA_3_0/source/rpc_server/srv_netlog_nt.c	2006-10-03 16:48:02 UTC (rev 19057)
+++ branches/SAMBA_3_0/source/rpc_server/srv_netlog_nt.c	2006-10-03 17:14:18 UTC (rev 19058)
@@ -623,7 +623,7 @@
 			return NT_STATUS_NO_MEMORY;
 		}
 		
-		if (!pdb_set_pass_changed_now(sampass)) {
+		if (!pdb_set_pass_last_set_time(sampass, time(NULL), PDB_CHANGED)) {
 			TALLOC_FREE(sampass);
 			/* Not quite sure what this one qualifies as, but this will do */
 			return NT_STATUS_UNSUCCESSFUL; 

Modified: branches/SAMBA_3_0/source/rpc_server/srv_samr_nt.c
===================================================================
--- branches/SAMBA_3_0/source/rpc_server/srv_samr_nt.c	2006-10-03 16:48:02 UTC (rev 19057)
+++ branches/SAMBA_3_0/source/rpc_server/srv_samr_nt.c	2006-10-03 17:14:18 UTC (rev 19058)
@@ -40,6 +40,8 @@
 		( READ_CONTROL_ACCESS		| \
 		  SA_RIGHT_USER_CHANGE_PASSWORD	| \
 		  SA_RIGHT_USER_SET_LOC_COM )
+#define SAMR_USR_RIGHTS_CANT_WRITE_PW \
+		( READ_CONTROL_ACCESS | SA_RIGHT_USER_SET_LOC_COM )
 
 #define DISP_INFO_CACHE_TIMEOUT 10
 
@@ -90,6 +92,11 @@
 	GENERIC_RIGHTS_USER_WRITE,
 	GENERIC_RIGHTS_USER_EXECUTE,
 	GENERIC_RIGHTS_USER_ALL_ACCESS};
+static struct generic_mapping usr_nopwchange_generic_mapping = {
+	GENERIC_RIGHTS_USER_READ,
+	GENERIC_RIGHTS_USER_WRITE,
+	GENERIC_RIGHTS_USER_EXECUTE & ~SA_RIGHT_USER_CHANGE_PASSWORD,
+	GENERIC_RIGHTS_USER_ALL_ACCESS};
 static struct generic_mapping grp_generic_mapping = {
 	GENERIC_RIGHTS_GROUP_READ,
 	GENERIC_RIGHTS_GROUP_WRITE,
@@ -657,16 +664,6 @@
 }
 
 /*******************************************************************
- _samr_set_sec_obj
- ********************************************************************/
-
-NTSTATUS _samr_set_sec_obj(pipes_struct *p, SAMR_Q_SET_SEC_OBJ *q_u, SAMR_R_SET_SEC_OBJ *r_u)
-{
-	DEBUG(0,("_samr_set_sec_obj: Not yet implemented!\n"));
-	return NT_STATUS_NOT_IMPLEMENTED;
-}
-
-/*******************************************************************
 ********************************************************************/
 
 static BOOL get_lsa_policy_samr_sid( pipes_struct *p, POLICY_HND *pol, 
@@ -692,6 +689,97 @@
 }
 
 /*******************************************************************
+ _samr_set_sec_obj
+ ********************************************************************/
+
+NTSTATUS _samr_set_sec_obj(pipes_struct *p, SAMR_Q_SET_SEC_OBJ *q_u, SAMR_R_SET_SEC_OBJ *r_u)
+{
+	DOM_SID pol_sid;
+	uint32 acc_granted, i;
+	SEC_ACL *dacl;
+	BOOL ret;
+	struct samu *sampass=NULL;
+	NTSTATUS status;
+
+	r_u->status = NT_STATUS_OK;
+
+	if (!get_lsa_policy_samr_sid(p, &q_u->pol, &pol_sid, &acc_granted, NULL))
+		return NT_STATUS_INVALID_HANDLE;
+
+	if (!(sampass = samu_new( p->mem_ctx))) {
+		DEBUG(0,("No memory!\n"));
+		return NT_STATUS_NO_MEMORY;
+	}
+
+	/* get the user record */
+	become_root();
+	ret = pdb_getsampwsid(sampass, &pol_sid);
+	unbecome_root();
+
+	if (!ret) {
+		DEBUG(4, ("User %s not found\n", sid_string_static(&pol_sid)));
+		TALLOC_FREE(sampass);
+		return NT_STATUS_INVALID_HANDLE;
+	}
+
+	dacl = q_u->buf->sd->dacl;
+	for (i=0; i < dacl->num_aces; i++) {
+		if (sid_equal(&pol_sid, &dacl->aces[i].trustee)) {
+			ret = pdb_set_pass_can_change(sampass, 
+				(dacl->aces[i].access_mask & 
+				 SA_RIGHT_USER_CHANGE_PASSWORD) ? 
+						      True: False);
+			break;
+		}
+	}
+
+	if (!ret) {
+		TALLOC_FREE(sampass);
+		return NT_STATUS_ACCESS_DENIED;
+	}
+
+	status = pdb_update_sam_account(sampass);
+
+	TALLOC_FREE(sampass);
+
+	return status;
+}
+
+/*******************************************************************
+  build correct perms based on policies and password times for _samr_query_sec_obj
+*******************************************************************/
+static BOOL check_change_pw_access(TALLOC_CTX *mem_ctx, DOM_SID *user_sid)
+{
+	struct samu *sampass=NULL;
+	BOOL ret;
+
+	if ( !(sampass = samu_new( mem_ctx )) ) {
+		DEBUG(0,("No memory!\n"));
+		return False;
+	}
+
+	become_root();
+	ret = pdb_getsampwsid(sampass, user_sid);
+	unbecome_root();
+
+	if (ret == False) {
+		DEBUG(4,("User %s not found\n", sid_string_static(user_sid)));
+		TALLOC_FREE(sampass);
+		return False;
+	}
+
+	DEBUG(3,("User:[%s]\n",  pdb_get_username(sampass) ));
+
+	if (pdb_get_pass_can_change(sampass)) {
+		TALLOC_FREE(sampass);
+		return True;
+	}
+	TALLOC_FREE(sampass);
+	return False;
+}
+
+
+/*******************************************************************
  _samr_query_sec_obj
  ********************************************************************/
 
@@ -731,7 +819,13 @@
 		/* TODO: different SDs have to be generated for aliases groups and users.
 		         Currently all three get a default user SD  */
 		DEBUG(10,("_samr_query_sec_obj: querying security on Object with SID: %s\n", sid_to_string(str_sid, &pol_sid)));
-		r_u->status = make_samr_object_sd(p->mem_ctx, &psd, &sd_size, &usr_generic_mapping, &pol_sid, SAMR_USR_RIGHTS_WRITE_PW);
+		if (check_change_pw_access(p->mem_ctx, &pol_sid)) {
+			r_u->status = make_samr_object_sd(p->mem_ctx, &psd, &sd_size, &usr_generic_mapping, 
+							  &pol_sid, SAMR_USR_RIGHTS_WRITE_PW);
+		} else {
+			r_u->status = make_samr_object_sd(p->mem_ctx, &psd, &sd_size, &usr_nopwchange_generic_mapping, 
+							  &pol_sid, SAMR_USR_RIGHTS_CANT_WRITE_PW);
+		}
 	} else {
 		return NT_STATUS_OBJECT_TYPE_MISMATCH;
 	}
@@ -3056,7 +3150,7 @@
 		TALLOC_FREE(pwd);
 		return False;
 	}
- 	if (!pdb_set_pass_changed_now (pwd)) {
+ 	if (!pdb_set_pass_last_set_time (pwd, time(NULL), PDB_CHANGED)) {
 		TALLOC_FREE(pwd);
 		return False; 
 	}

Modified: branches/SAMBA_3_0/source/smbd/chgpasswd.c
===================================================================
--- branches/SAMBA_3_0/source/smbd/chgpasswd.c	2006-10-03 16:48:02 UTC (rev 19057)
+++ branches/SAMBA_3_0/source/smbd/chgpasswd.c	2006-10-03 17:14:18 UTC (rev 19058)
@@ -689,7 +689,7 @@
 		return False;	/* We lose the NT hash. Sorry. */
 	}
 
-	if (!pdb_set_pass_changed_now  (sampass)) {
+	if (!pdb_set_pass_last_set_time  (sampass, time(NULL), PDB_CHANGED)) {
 		TALLOC_FREE(sampass);
 		/* Not quite sure what this one qualifies as, but this will do */
 		return False; 
@@ -1018,41 +1018,34 @@
 
 NTSTATUS change_oem_password(struct samu *hnd, char *old_passwd, char *new_passwd, BOOL as_root, uint32 *samr_reject_reason)
 {
-	uint32 min_len, min_age;
+	uint32 min_len;
 	struct passwd *pass = NULL;
 	const char *username = pdb_get_username(hnd);
-	time_t last_change_time = pdb_get_pass_last_set_time(hnd);
 	time_t can_change_time = pdb_get_pass_can_change_time(hnd);
 
 	if (samr_reject_reason) {
 		*samr_reject_reason = Undefined;
 	}
 
-	if (pdb_get_account_policy(AP_MIN_PASSWORD_AGE, &min_age)) {
-		/*
-		 * Windows calculates the minimum password age check
-		 * dynamically, it basically ignores the pwdcanchange
-		 * timestamp. Do likewise.
-		 */
-		if (last_change_time + min_age > time(NULL)) {
-			DEBUG(1, ("user %s cannot change password now, must "
-				  "wait until %s\n", username,
-				  http_timestring(last_change_time+min_age)));
-			if (samr_reject_reason) {
-				*samr_reject_reason = REJECT_REASON_OTHER;
-			}
-			return NT_STATUS_ACCOUNT_RESTRICTION;
+	/* check to see if the secdesc has previously been set to disallow */
+	if (!pdb_get_pass_can_change(hnd)) {
+		DEBUG(1, ("user %s does not have permissions to change password\n"));
+		if (samr_reject_reason) {
+			*samr_reject_reason = REJECT_REASON_OTHER;
 		}
-	} else {
-		if ((can_change_time != 0) && (time(NULL) < can_change_time)) {
-			DEBUG(1, ("user %s cannot change password now, must "
-				  "wait until %s\n", username,
-				  http_timestring(can_change_time)));
-			if (samr_reject_reason) {
-				*samr_reject_reason = REJECT_REASON_OTHER;
-			}
-			return NT_STATUS_ACCOUNT_RESTRICTION;
+		return NT_STATUS_ACCOUNT_RESTRICTION;
+	}
+
+	/* removed calculation here, becuase passdb now calculates
+	   based on policy.  jmcd */
+	if ((can_change_time != 0) && (time(NULL) < can_change_time)) {
+		DEBUG(1, ("user %s cannot change password now, must "
+			  "wait until %s\n", username,
+			  http_timestring(can_change_time)));
+		if (samr_reject_reason) {
+			*samr_reject_reason = REJECT_REASON_OTHER;
 		}
+		return NT_STATUS_ACCOUNT_RESTRICTION;
 	}
 
 	if (pdb_get_account_policy(AP_MIN_PASSWORD_LEN, &min_len) && (str_charnum(new_passwd) < min_len)) {



More information about the samba-cvs mailing list