andykimpe / rpms / 389-ds-base

Forked from rpms/389-ds-base 5 months ago
Clone
dc8c34
From 51b3a4d6764fc460bf2b41678fe5232df15bed65 Mon Sep 17 00:00:00 2001
dc8c34
From: Noriko Hosoi <nhosoi@redhat.com>
dc8c34
Date: Mon, 23 Sep 2013 11:53:38 -0700
dc8c34
Subject: [PATCH 117/225] Ticket #47492 - PassSync removes User must change
dc8c34
 password flag on the Windows side
dc8c34
dc8c34
Bug description: Windows Sync sends password modify even if it is
dc8c34
from PassSync originated on AD.  The modify updates the pwdLastSet
dc8c34
attribute value to non-zero value.  The value 0 indicates the pass-
dc8c34
word must change at next logon on AD.
dc8c34
dc8c34
Fix description: Before sending the password modify, check whether
dc8c34
the current pwdLastSet value is 0 or not.  If it is 0 (means the
dc8c34
password must change), reset pwdLastSet value to 0 along with the
dc8c34
password modify.  This operation replaces the password on AD, but
dc8c34
the password still must change at next logon.
dc8c34
dc8c34
Note: If "password must change at next logon" on the both DS and AD,
dc8c34
the password needs to be changed by the user on the both servers to
dc8c34
enable it on each.
dc8c34
dc8c34
https://fedorahosted.org/389/ticket/47492
dc8c34
dc8c34
Reviewed by Rich (Thank you!!)
dc8c34
(cherry picked from commit f9d1d9e08225e5885b76cede4da677708892ee7a)
dc8c34
(cherry picked from commit 8d34f77f6d8d3c83dce1f29e6df709df1adef09d)
dc8c34
(cherry picked from commit 5005db538cc5ed33b906309715034a0d1cc62cc7)
dc8c34
---
dc8c34
 .../plugins/replication/windows_protocol_util.c    | 139 +++++++++++++++------
dc8c34
 1 file changed, 100 insertions(+), 39 deletions(-)
dc8c34
dc8c34
diff --git a/ldap/servers/plugins/replication/windows_protocol_util.c b/ldap/servers/plugins/replication/windows_protocol_util.c
dc8c34
index 75cb935..93246ec 100644
dc8c34
--- a/ldap/servers/plugins/replication/windows_protocol_util.c
dc8c34
+++ b/ldap/servers/plugins/replication/windows_protocol_util.c
dc8c34
@@ -70,7 +70,7 @@ static int windows_get_local_entry(const Slapi_DN* local_dn,Slapi_Entry **local_
dc8c34
 static int windows_get_local_entry_by_uniqueid(Private_Repl_Protocol *prp,const char* uniqueid,Slapi_Entry **local_entry, int is_global);
dc8c34
 static int windows_get_local_tombstone_by_uniqueid(Private_Repl_Protocol *prp,const char* uniqueid,Slapi_Entry **local_entry);
dc8c34
 static int windows_search_local_entry_by_uniqueid(Private_Repl_Protocol *prp, const char *uniqueid, char ** attrs, Slapi_Entry **ret_entry, int tombstone, void * component_identity, int is_global);
dc8c34
-static int map_entry_dn_outbound(Slapi_Entry *e, Slapi_DN **dn, Private_Repl_Protocol *prp, int *missing_entry, int want_guid);
dc8c34
+static int map_entry_dn_outbound(Slapi_Entry *e, Slapi_DN **dn, Private_Repl_Protocol *prp, int *missing_entry, int want_guid, Slapi_Entry **remote_entry);
dc8c34
 static char* extract_ntuserdomainid_from_entry(Slapi_Entry *e);
dc8c34
 static char* extract_container(const Slapi_DN *entry_dn, const Slapi_DN *suffix_dn);
dc8c34
 static int windows_get_remote_entry (Private_Repl_Protocol *prp, const Slapi_DN* remote_dn,Slapi_Entry **remote_entry);
dc8c34
@@ -448,7 +448,7 @@ map_dn_values(Private_Repl_Protocol *prp,Slapi_ValueSet *original_values, Slapi_
dc8c34
 				is_ours = is_subject_of_agreement_local(local_entry,prp->agmt);
dc8c34
 				if (is_ours)
dc8c34
 				{
dc8c34
-					map_entry_dn_outbound(local_entry,&remote_dn,prp,&missing_entry, 0 /* don't want GUID form here */);
dc8c34
+					map_entry_dn_outbound(local_entry,&remote_dn,prp,&missing_entry, 0 /* don't want GUID form here */, NULL);
dc8c34
 					if (remote_dn)
dc8c34
 					{
dc8c34
 						if (!missing_entry)
dc8c34
@@ -768,7 +768,10 @@ to_little_endian_double_bytes(UChar *unicode_password, int32_t unicode_password_
dc8c34
 /* this entry had a password, handle it seperately */
dc8c34
 /* http://support.microsoft.com/?kbid=269190 */
dc8c34
 static int
dc8c34
-send_password_modify(Slapi_DN *sdn, char *password, Private_Repl_Protocol *prp)
dc8c34
+send_password_modify(Slapi_DN *sdn,
dc8c34
+                     char *password,
dc8c34
+                     Private_Repl_Protocol *prp,
dc8c34
+                     Slapi_Entry *remote_entry)
dc8c34
 {
dc8c34
 		ConnResult pw_return = 0;
dc8c34
 
dc8c34
@@ -791,6 +794,35 @@ send_password_modify(Slapi_DN *sdn, char *password, Private_Repl_Protocol *prp)
dc8c34
 
dc8c34
 		} else
dc8c34
 		{
dc8c34
+			Slapi_Attr *attr = NULL;
dc8c34
+			int force_reset_pw = 0;
dc8c34
+			/* 
dc8c34
+			 * If AD entry has password must change flag is set,
dc8c34
+			 * we keep the flag (pwdLastSet == 0).
dc8c34
+			 * msdn.microsoft.com: Windows Dev Centor - Desktop
dc8c34
+			 * To force a user to change their password at next logon,
dc8c34
+			 * set the pwdLastSet attribute to zero (0).
dc8c34
+			 */
dc8c34
+			if (remote_entry &&
dc8c34
+				(0 == slapi_entry_attr_find(remote_entry, "pwdLastSet", &attr)) &&
dc8c34
+				attr) {
dc8c34
+				Slapi_Value *v = NULL;
dc8c34
+				int i = 0;
dc8c34
+				for (i = slapi_attr_first_value(attr, &v);
dc8c34
+					 v && (i != -1);
dc8c34
+					 i = slapi_attr_next_value(attr, i, &v)) {
dc8c34
+					const char *s = slapi_value_get_string(v);
dc8c34
+					if (NULL == s) {
dc8c34
+						continue;
dc8c34
+					}
dc8c34
+					if (0 == strcmp(s, "0")) {
dc8c34
+						slapi_log_error(SLAPI_LOG_REPL, windows_repl_plugin_name,
dc8c34
+						                "%s: AD entry %s set \"user must change password at next logon\". ",
dc8c34
+						                agmt_get_long_name(prp->agmt), slapi_entry_get_dn(remote_entry));
dc8c34
+						force_reset_pw = 1;
dc8c34
+					}
dc8c34
+				}
dc8c34
+			}
dc8c34
 			/* We will attempt to bind to AD with the new password first. We do
dc8c34
 			 * this to avoid playing a password change that originated from AD
dc8c34
 			 * back to AD.  If we just played the password change back, then
dc8c34
@@ -803,38 +835,53 @@ send_password_modify(Slapi_DN *sdn, char *password, Private_Repl_Protocol *prp)
dc8c34
 				quoted_password = PR_smprintf("\"%s\"",password);
dc8c34
 				if (quoted_password)
dc8c34
 				{
dc8c34
-					LDAPMod *pw_mods[2];
dc8c34
-					LDAPMod pw_mod;
dc8c34
-					struct berval bv = {0};
dc8c34
 					UChar *unicode_password = NULL;
dc8c34
 					int32_t unicode_password_length = 0; /* Length in _characters_ */
dc8c34
 					int32_t buffer_size = 0; /* Size in _characters_ */
dc8c34
 					UErrorCode error = U_ZERO_ERROR;
dc8c34
-					struct berval *bvals[2];
dc8c34
 					/* Need to UNICODE encode the password here */
dc8c34
 					/* It's one of those 'ask me first and I will tell you the buffer size' functions */
dc8c34
 					u_strFromUTF8(NULL, 0, &unicode_password_length, quoted_password, strlen(quoted_password), &error);
dc8c34
 					buffer_size = unicode_password_length;
dc8c34
 					unicode_password = (UChar *)slapi_ch_malloc(unicode_password_length * sizeof(UChar));
dc8c34
 					if (unicode_password) {
dc8c34
+						LDAPMod *pw_mods[3];
dc8c34
+						LDAPMod pw_mod;
dc8c34
+						LDAPMod reset_pw_mod;
dc8c34
+						struct berval bv = {0};
dc8c34
+						struct berval *bvals[2];
dc8c34
+						struct berval reset_bv = {0};
dc8c34
+						struct berval *reset_bvals[2];
dc8c34
 						error = U_ZERO_ERROR;
dc8c34
 						u_strFromUTF8(unicode_password, buffer_size, &unicode_password_length, quoted_password, strlen(quoted_password), &error);
dc8c34
-	
dc8c34
+
dc8c34
 						/* As an extra special twist, we need to send the unicode in little-endian order for AD to be happy */
dc8c34
 						to_little_endian_double_bytes(unicode_password, unicode_password_length);
dc8c34
-	
dc8c34
+
dc8c34
 						bv.bv_len = unicode_password_length * sizeof(UChar);
dc8c34
 						bv.bv_val = (char*)unicode_password;
dc8c34
-				
dc8c34
+
dc8c34
 						bvals[0] = &bv; 
dc8c34
 						bvals[1] = NULL;
dc8c34
 						
dc8c34
 						pw_mod.mod_type = "UnicodePwd";
dc8c34
 						pw_mod.mod_op = LDAP_MOD_REPLACE | LDAP_MOD_BVALUES;
dc8c34
 						pw_mod.mod_bvalues = bvals;
dc8c34
-					
dc8c34
+
dc8c34
 						pw_mods[0] = &pw_mod;
dc8c34
-						pw_mods[1] = NULL;
dc8c34
+						if (force_reset_pw) {
dc8c34
+							reset_bv.bv_len = 1;
dc8c34
+							reset_bv.bv_val = "0";
dc8c34
+							reset_bvals[0] = &reset_bv; 
dc8c34
+							reset_bvals[1] = NULL;
dc8c34
+							reset_pw_mod.mod_type = "pwdLastSet";
dc8c34
+							reset_pw_mod.mod_op = LDAP_MOD_REPLACE | LDAP_MOD_BVALUES;
dc8c34
+							reset_pw_mod.mod_bvalues = reset_bvals;
dc8c34
+							pw_mods[1] = &reset_pw_mod;
dc8c34
+							pw_mods[2] = NULL;
dc8c34
+						} else {
dc8c34
+							pw_mods[1] = NULL;
dc8c34
+						}
dc8c34
 
dc8c34
 						pw_return = windows_conn_send_modify(prp->conn, slapi_sdn_get_dn(sdn), pw_mods, NULL, NULL );
dc8c34
 
dc8c34
@@ -1414,6 +1461,7 @@ windows_replay_update(Private_Repl_Protocol *prp, slapi_operation_parameters *op
dc8c34
 	Slapi_DN *remote_dn = NULL;
dc8c34
 	Slapi_DN *local_dn = NULL;
dc8c34
 	Slapi_Entry *local_entry = NULL;
dc8c34
+	Slapi_Entry *remote_entry = NULL;
dc8c34
 		
dc8c34
 	LDAPDebug( LDAP_DEBUG_TRACE, "=> windows_replay_update\n", 0, 0, 0 );
dc8c34
 
dc8c34
@@ -1488,7 +1536,7 @@ windows_replay_update(Private_Repl_Protocol *prp, slapi_operation_parameters *op
dc8c34
 	if (is_ours && (is_user || is_group) ) {
dc8c34
 		int missing_entry = 0;
dc8c34
 		/* Make the entry's DN */
dc8c34
-		rc = map_entry_dn_outbound(local_entry,&remote_dn,prp,&missing_entry, 1);
dc8c34
+		rc = map_entry_dn_outbound(local_entry,&remote_dn,prp,&missing_entry, 1, &remote_entry);
dc8c34
 		if (rc || NULL == remote_dn) 
dc8c34
 		{
dc8c34
 			slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
dc8c34
@@ -1676,13 +1724,17 @@ windows_replay_update(Private_Repl_Protocol *prp, slapi_operation_parameters *op
dc8c34
 			 * seem to work over plain LDAP. */
dc8c34
 			if (is_guid_dn(remote_dn)) {
dc8c34
 				Slapi_DN *remote_dn_norm = NULL;
dc8c34
-				int norm_missing = 0;
dc8c34
 
dc8c34
-				map_entry_dn_outbound(local_entry,&remote_dn_norm,prp,&norm_missing, 0);
dc8c34
-				return_value = send_password_modify(remote_dn_norm, password, prp);
dc8c34
+				if (remote_entry) {
dc8c34
+					remote_dn_norm = slapi_sdn_dup(slapi_entry_get_sdn_const(remote_entry));
dc8c34
+				} else {
dc8c34
+					int norm_missing = 0;
dc8c34
+					map_entry_dn_outbound(local_entry,&remote_dn_norm,prp,&norm_missing, 0, &remote_entry);
dc8c34
+				}
dc8c34
+				return_value = send_password_modify(remote_dn_norm, password, prp, remote_entry);
dc8c34
 				slapi_sdn_free(&remote_dn_norm);
dc8c34
 			} else {
dc8c34
-				return_value = send_password_modify(remote_dn, password, prp);
dc8c34
+				return_value = send_password_modify(remote_dn, password, prp, remote_entry);
dc8c34
 			}
dc8c34
 
dc8c34
 			if (return_value)
dc8c34
@@ -1710,18 +1762,10 @@ windows_replay_update(Private_Repl_Protocol *prp, slapi_operation_parameters *op
dc8c34
 		/* We ignore operations that target entries outside of our sync'ed subtree, or which are not Windows users or groups */
dc8c34
 	}
dc8c34
 error:
dc8c34
-	if (local_entry)
dc8c34
-	{
dc8c34
-		slapi_entry_free(local_entry);
dc8c34
-	}
dc8c34
-	if (local_dn)
dc8c34
-	{
dc8c34
-		slapi_sdn_free (&local_dn);
dc8c34
-	}
dc8c34
-	if (remote_dn)
dc8c34
-	{
dc8c34
-		slapi_sdn_free(&remote_dn);
dc8c34
-	}
dc8c34
+	slapi_entry_free(remote_entry);
dc8c34
+	slapi_entry_free(local_entry);
dc8c34
+	slapi_sdn_free (&local_dn);
dc8c34
+	slapi_sdn_free(&remote_dn);
dc8c34
 	slapi_ch_free_string(&password);
dc8c34
 	return return_value;
dc8c34
 }
dc8c34
@@ -3388,13 +3432,27 @@ extract_container(const Slapi_DN *entry_dn, const Slapi_DN *suffix_dn)
dc8c34
 
dc8c34
 /* Given a non-tombstone entry, return the DN of its peer in AD (whether present or not) */
dc8c34
 static int 
dc8c34
-map_entry_dn_outbound(Slapi_Entry *e, Slapi_DN **dn, Private_Repl_Protocol *prp, int *missing_entry, int guid_form)
dc8c34
+map_entry_dn_outbound(Slapi_Entry *e,
dc8c34
+                      Slapi_DN **dn,
dc8c34
+                      Private_Repl_Protocol *prp,
dc8c34
+                      int *missing_entry,
dc8c34
+                      int guid_form,
dc8c34
+                      Slapi_Entry **remote_entry_to_return)
dc8c34
 {
dc8c34
 	int retval = 0;
dc8c34
 	char *guid = NULL;
dc8c34
 	Slapi_DN *new_dn = NULL;
dc8c34
 	int is_nt4 = windows_private_get_isnt4(prp->agmt);
dc8c34
-	const char *suffix = slapi_sdn_get_dn(windows_private_get_windows_subtree(prp->agmt));
dc8c34
+	const char *suffix = NULL;
dc8c34
+	Slapi_Entry *remote_entry = NULL;
dc8c34
+
dc8c34
+	if (NULL == e) {
dc8c34
+		slapi_log_error(SLAPI_LOG_REPL, windows_repl_plugin_name,
dc8c34
+		                "%s: map_entry_dn_outbound: NULL entry.\n",
dc8c34
+		                agmt_get_long_name(prp->agmt));
dc8c34
+		return -1;
dc8c34
+	}
dc8c34
+
dc8c34
 	/* To find the DN of the peer entry we first look for an ntUniqueId attribute
dc8c34
 	 * on the local entry. If that's present, we generate a GUID-form DN.
dc8c34
 	 * If there's no GUID, then we look for an ntUserDomainId attribute
dc8c34
@@ -3417,7 +3475,6 @@ map_entry_dn_outbound(Slapi_Entry *e, Slapi_DN **dn, Private_Repl_Protocol *prp,
dc8c34
 	if (guid && guid_form) 
dc8c34
 	{
dc8c34
 		int rc = 0;
dc8c34
-		Slapi_Entry *remote_entry = NULL;
dc8c34
 		new_dn = make_dn_from_guid(guid, is_nt4, suffix);
dc8c34
 		if (!new_dn) {
dc8c34
 			slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
dc8c34
@@ -3445,7 +3502,6 @@ map_entry_dn_outbound(Slapi_Entry *e, Slapi_DN **dn, Private_Repl_Protocol *prp,
dc8c34
 				slapi_sdn_free(&new_dn);
dc8c34
 				retval = -1;
dc8c34
 			}
dc8c34
-			slapi_entry_free(remote_entry);
dc8c34
 		} else {
dc8c34
 			slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
dc8c34
 					"%s: map_entry_dn_outbound: entry not found - rc %d\n",
dc8c34
@@ -3486,7 +3542,6 @@ map_entry_dn_outbound(Slapi_Entry *e, Slapi_DN **dn, Private_Repl_Protocol *prp,
dc8c34
 	} else 
dc8c34
 	{
dc8c34
 		/* No GUID found, try ntUserDomainId */
dc8c34
-		Slapi_Entry *remote_entry = NULL;
dc8c34
 		char *username = slapi_entry_attr_get_charptr(e,"ntUserDomainId");
dc8c34
 		slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
dc8c34
 				"%s: map_entry_dn_outbound: looking for AD entry for DS "
dc8c34
@@ -3562,16 +3617,22 @@ map_entry_dn_outbound(Slapi_Entry *e, Slapi_DN **dn, Private_Repl_Protocol *prp,
dc8c34
 			}
dc8c34
 			slapi_ch_free_string(&username);
dc8c34
 		}
dc8c34
-		if (remote_entry)
dc8c34
-		{
dc8c34
-			slapi_entry_free(remote_entry);
dc8c34
-		}
dc8c34
 	}
dc8c34
 done:
dc8c34
 	if (new_dn) 
dc8c34
 	{
dc8c34
 		*dn = new_dn;
dc8c34
 	}
dc8c34
+	if (remote_entry_to_return) {
dc8c34
+		if (retval) { /* failed */
dc8c34
+			slapi_entry_free(remote_entry);
dc8c34
+			*remote_entry_to_return = NULL;
dc8c34
+		} else {
dc8c34
+			*remote_entry_to_return = remote_entry;
dc8c34
+		}
dc8c34
+	} else {
dc8c34
+		slapi_entry_free(remote_entry);
dc8c34
+	}
dc8c34
 	slapi_ch_free_string(&guid);
dc8c34
 	return retval;
dc8c34
 }
dc8c34
@@ -4904,7 +4965,7 @@ int windows_process_total_entry(Private_Repl_Protocol *prp,Slapi_Entry *e)
dc8c34
 		agmt_get_long_name(prp->agmt), slapi_sdn_get_dn(slapi_entry_get_sdn_const(e)), is_ours ? "ours" : "not ours");
dc8c34
 	if (is_ours) 
dc8c34
 	{
dc8c34
-		retval = map_entry_dn_outbound(e,&remote_dn,prp,&missing_entry,0 /* we don't want the GUID */);
dc8c34
+		retval = map_entry_dn_outbound(e,&remote_dn,prp,&missing_entry,0 /* we don't want the GUID */, NULL);
dc8c34
 		if (retval || NULL == remote_dn) 
dc8c34
 		{
dc8c34
 			slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
dc8c34
-- 
dc8c34
1.8.1.4
dc8c34