Browse Source

Bug 578863 - Password modify extop needs to send referrals on replicas

The password modify extended operation was modifying the local database
on a read-only replica instead of returning a referral.  The server is
designed to let the plugin ID used for updating password retry info make
local updates instead of returning a referral.  This plugin ID was being
used by the password extop code, which it should not be doing.

The second issue is that we need to check if a referral needs to be sent
as early as possible when processing the extop request.  We don't want
to reject the change if an entry does not exist before checking if a
referral is necessary since the server we refer to may have the target
entry present.  This required adding a new helper function that allows
one to see if a write operation to a particular DN would require a
referral to be sent.  The password modify extop code leverages this new
function to get the referrals and return them to the client if necessary.
Nathan Kinder 15 years ago
parent
commit
c53b8b31c8

+ 42 - 0
ldap/servers/slapd/mapping_tree.c

@@ -1925,6 +1925,48 @@ static int sdn_is_nulldn(const Slapi_DN *sdn){
     return 0;
 }
 
+/* Checks if a write operation for a particular DN would
+ * require a referral to be sent. */
+int slapi_dn_write_needs_referral(Slapi_DN *target_sdn, Slapi_Entry **referral)
+{
+    mapping_tree_node *target_node = NULL;
+    int ret = 0;
+
+    if(mapping_tree_freed){
+        /* shutdown detected */
+        goto done;
+    }
+
+    if(!mapping_tree_inited) {
+        mapping_tree_init();
+    }
+
+    if (target_sdn) {
+        mtn_lock();
+
+        /* Get the mapping tree node that is the best match for the target dn. */
+        target_node = slapi_get_mapping_tree_node_by_dn(target_sdn);
+        if (target_node == NULL) {
+            target_node = mapping_tree_root;
+        }
+
+        /* See if we need to return a referral. */
+        if ((target_node->mtn_state == MTN_REFERRAL) ||
+            (target_node->mtn_state == MTN_REFERRAL_ON_UPDATE)) {
+            *referral = (target_node->mtn_referral_entry ?
+                         slapi_entry_dup(target_node->mtn_referral_entry) :
+                         NULL);
+            if (*referral) {
+                ret = 1;
+            }
+        }
+
+        mtn_unlock();
+    }
+
+  done:
+    return ret;
+}
 /* 
  * Description:
  * The reason we have a mapping tree.  This function selects a backend or

+ 37 - 15
ldap/servers/slapd/passwd_extop.c

@@ -167,7 +167,7 @@ static int passwd_apply_mods(Slapi_PBlock *pb_orig, const char *dn, Slapi_Mods *
 		slapi_modify_internal_set_pb (&pb, dn, 
 			slapi_mods_get_ldapmods_byref(mods),
 			req_controls_copy, NULL, /* UniqueID */
-			pw_get_componentID(), /* PluginID */
+			plugin_get_default_component_id(), /* PluginID */
 			0); /* Flags */ 
 
 		/* We copy the connection from the original pblock into the
@@ -472,6 +472,8 @@ passwd_modify_extop( Slapi_PBlock *pb )
 	LDAPControl	**req_controls = NULL;
 	LDAPControl	**resp_controls = NULL;
 	passwdPolicy	*pwpolicy = NULL;
+	Slapi_DN	*target_sdn = NULL;
+	Slapi_Entry	*referrals = NULL;
 	/* Slapi_DN sdn; */
 
     	LDAPDebug( LDAP_DEBUG_TRACE, "=> passwd_modify_extop\n", 0, 0, 0 );
@@ -607,19 +609,33 @@ parse_req_done:
 	/* LDAPDebug( LDAP_DEBUG_ARGS, "passwd: dn (%s), oldPasswd (%s) ,newPasswd (%s)\n",
 					 dn, oldPasswd, newPasswd); */
 
-	 
-	 /* Get Bind DN */
-	 slapi_pblock_get( pb, SLAPI_CONN_DN, &bindDN );
+	/* Get Bind DN */
+	slapi_pblock_get( pb, SLAPI_CONN_DN, &bindDN );
+
+	/* Find and set the target DN. */
+	if (dn && *dn != '\0') {
+		target_sdn = slapi_sdn_new_dn_byref(dn);
+		slapi_pblock_set(pb, SLAPI_TARGET_DN, dn);
+	} else if (bindDN && *bindDN != '\0') {
+		target_sdn = slapi_sdn_new_dn_byref(bindDN);
+		slapi_pblock_set(pb, SLAPI_TARGET_DN, bindDN);
+	}
 
-	 /* If the connection is bound anonymously, we must refuse to process this operation. */
-	 if (bindDN == NULL || *bindDN == '\0') {
+	/* Check if we need to send any referrals. */
+	if (slapi_dn_write_needs_referral(target_sdn, &referrals)) {
+		rc = LDAP_REFERRAL;
+		goto free_and_return;
+	}
+
+	/* If the connection is bound anonymously, we must refuse to process this operation. */
+	if (bindDN == NULL || *bindDN == '\0') {
 	 	/* Refuse the operation because they're bound anonymously */
 		errMesg = "Anonymous Binds are not allowed.\n";
 		rc = LDAP_INSUFFICIENT_ACCESS;
 		goto free_and_return;
-	 }
+	}
 
-	 if (oldPasswd == NULL || *oldPasswd == '\0') {
+	if (oldPasswd == NULL || *oldPasswd == '\0') {
 		/* If user is authenticated, they already gave their password during
 		 * the bind operation (or used sasl or client cert auth or OS creds) */
 		slapi_pblock_get(pb, SLAPI_CONN_AUTHMETHOD, &authmethod);
@@ -628,7 +644,7 @@ parse_req_done:
 			rc = LDAP_INSUFFICIENT_ACCESS;
 			goto free_and_return;
 		}
-	 }
+	}
 
 	/* Fetch the password policy.  We need this in case we need to
 	 * generate a password as well as for some policy checks. */
@@ -694,7 +710,7 @@ parse_req_done:
     		    "Missing userIdentity in request, using the bind DN instead.\n",
 		     0, 0, 0 );
 	 }
-	 
+
 	 slapi_pblock_set( pb, SLAPI_ORIGINAL_TARGET, dn ); 
 
 	 /* Now we have the DN, look for the entry */
@@ -807,6 +823,16 @@ parse_req_done:
 	
 	/* Free anything that we allocated above */
 free_and_return:
+	slapi_log_error( SLAPI_LOG_PLUGIN, "passwd_modify_extop",
+		errMesg ? errMesg : "success" );
+
+	if ((rc == LDAP_REFERRAL) && (referrals)) {
+		send_referrals_from_entry(pb, referrals);
+	} else {
+		send_ldap_result( pb, rc, NULL, errMesg, 0, NULL );
+	}
+
+	slapi_sdn_free(&target_sdn);
 	slapi_ch_free_string(&bindDN); /* slapi_pblock_get SLAPI_CONN_DN does strdup */
 	slapi_ch_free_string(&oldPasswd);
 	slapi_ch_free_string(&newPasswd);
@@ -821,6 +847,7 @@ free_and_return:
 	slapi_pblock_set( pb, SLAPI_ORIGINAL_TARGET, NULL );
 	slapi_ch_free_string(&authmethod);
 	delete_passwdPolicy(&pwpolicy);
+	slapi_entry_free(referrals);
 
 	if ( targetEntry != NULL ){
 		slapi_entry_free (targetEntry); 
@@ -831,11 +858,6 @@ free_and_return:
 		ber = NULL;
 	}
 	
-	slapi_log_error( SLAPI_LOG_PLUGIN, "passwd_modify_extop", 
-                     errMesg ? errMesg : "success" );
-	send_ldap_result( pb, rc, NULL, errMesg, 0, NULL );
-	
-
 	/* We can free the generated password bval now */
 	ber_bvfree(gen_passwd);
 

+ 13 - 0
ldap/servers/slapd/slapi-plugin.h

@@ -3139,6 +3139,19 @@ int slapi_dn_isroot( const char *dn );
  */
 int slapi_dn_isbesuffix( Slapi_PBlock *pb, const char *dn );
 
+/**
+ * Checks if writes to a particular DN need to send a referral.
+ *
+ * \param target_sdn The target DN that you want to check.
+ * \param referral The address of a pointer to receive a referral
+ *        if one is needed.
+ * \return \c 1 if a referral needs to be sent.
+ * \return \c 0 if no referral is needed.
+ * \warning The referral entry must be freed when it is no longer
+ *          being used.
+ */
+int slapi_dn_write_needs_referral(Slapi_DN *target_sdn, Slapi_Entry **referral);
+
 /**
  * Converts the second RDN type value to the berval value.
  *