From 8149863204f44539ec0d227f99a65490c970f661 Mon Sep 17 00:00:00 2001
From: Quanah Gibson-Mount <quanah@openldap.org>
Date: Sun, 2 Aug 2009 23:26:42 +0000
Subject: [PATCH] ITS#5942

---
 CHANGES                  |  1 +
 servers/slapd/bconfig.c  | 59 ++-----------------------------------
 servers/slapd/config.c   | 63 ++++++++++++++++++++++++++++++++++++++++
 servers/slapd/config.h   |  1 +
 servers/slapd/syncrepl.c | 32 ++++++++++++++------
 5 files changed, 91 insertions(+), 65 deletions(-)

diff --git a/CHANGES b/CHANGES
index 7dba74a5b2..c8cac37a97 100644
--- a/CHANGES
+++ b/CHANGES
@@ -5,6 +5,7 @@ OpenLDAP 2.4.18 Engineering
 	Fixed libldap tls_check_hostname for OpenSSL and MozNSS (ITS#6239)
 	Fixed slapd dncachesize behavior to unlimited by default (ITS#6222)
 	Fixed slapd incorrectly applying writetimeout when not set (ITS#6220)
+	Fixed slapd server URL matching (ITS#5942)
 	Fixed slapd subordinate needs a suffix (ITS#6216)
 	Fixed slapd tools to properly close database (ITS#6214)
 	Fixed slapd-ndb startup (ITS#6203)
diff --git a/servers/slapd/bconfig.c b/servers/slapd/bconfig.c
index 90f82e6769..6a32e49a45 100644
--- a/servers/slapd/bconfig.c
+++ b/servers/slapd/bconfig.c
@@ -1824,66 +1824,13 @@ sortval_reject:
 				*sip = si;
 
 				if (( slapMode & SLAP_SERVER_MODE ) && c->argc > 2 ) {
-					Listener **l = slapd_get_listeners();
-					int i, isMe = 0;
-
-					/* Try a straight compare with Listener strings */
-					for ( i=0; l && l[i]; i++ ) {
-						if ( !strcasecmp( c->argv[2], l[i]->sl_url.bv_val )) {
-							isMe = 1;
-							break;
-						}
-					}
-
-					/* If hostname is empty, or is localhost, or matches
-					 * our hostname, this serverID refers to this host.
-					 * Compare it against listeners and ports.
-					 */
-					if ( !isMe && ( !lud->lud_host || !lud->lud_host[0] ||
-						!strncasecmp("localhost", lud->lud_host,
-							STRLENOF("localhost")) ||
-						!strcasecmp( global_host, lud->lud_host ))) {
-
-						for ( i=0; l && l[i]; i++ ) {
-							LDAPURLDesc *lu2;
-							ldap_url_parse( l[i]->sl_url.bv_val, &lu2 );
-							do {
-								if ( strcasecmp( lud->lud_scheme,
-									lu2->lud_scheme ))
-									break;
-								if ( lud->lud_port != lu2->lud_port )
-									break;
-								/* Listener on ANY address */
-								if ( !lu2->lud_host || !lu2->lud_host[0] ) {
-									isMe = 1;
-									break;
-								}
-								/* URL on ANY address */
-								if ( !lud->lud_host || !lud->lud_host[0] ) {
-									isMe = 1;
-									break;
-								}
-								/* Listener has specific host, must
-								 * match it
-								 */
-								if ( !strcasecmp( lud->lud_host,
-									lu2->lud_host )) {
-									isMe = 1;
-									break;
-								}
-							} while(0);
-							ldap_free_urldesc( lu2 );
-							if ( isMe ) {
-								break;
-							}
-						}
-					}
-					if ( isMe ) {
+					Listener *l = config_check_my_url( c->argv[2], lud );
+					if ( l ) {
 						slap_serverID = si->si_num;
 						Debug( LDAP_DEBUG_CONFIG,
 							"%s: SID=%d (listener=%s)\n",
 							c->log, slap_serverID,
-							l[i]->sl_url.bv_val );
+							l->sl_url.bv_val );
 					}
 				}
 				if ( c->argc > 2 )
diff --git a/servers/slapd/config.c b/servers/slapd/config.c
index 9840780982..b90ae1ec75 100644
--- a/servers/slapd/config.c
+++ b/servers/slapd/config.c
@@ -2126,3 +2126,66 @@ int config_generic_wrapper( Backend *be, const char *fname, int lineno,
 	}
 	return rc;
 }
+
+/* See if the given URL (in plain and parsed form) matches
+ * any of the server's listener addresses. Return matching
+ * Listener or NULL for no match.
+ */
+Listener *config_check_my_url( const char *url, LDAPURLDesc *lud )
+{
+	Listener **l = slapd_get_listeners();
+	int i, isMe;
+
+	/* Try a straight compare with Listener strings */
+	for ( i=0; l && l[i]; i++ ) {
+		if ( !strcasecmp( url, l[i]->sl_url.bv_val )) {
+			return l[i];
+		}
+	}
+
+	isMe = 0;
+	/* If hostname is empty, or is localhost, or matches
+	 * our hostname, this url refers to this host.
+	 * Compare it against listeners and ports.
+	 */
+	if ( !lud->lud_host || !lud->lud_host[0] ||
+		!strncasecmp("localhost", lud->lud_host,
+			STRLENOF("localhost")) ||
+		!strcasecmp( global_host, lud->lud_host )) {
+
+		for ( i=0; l && l[i]; i++ ) {
+			LDAPURLDesc *lu2;
+			ldap_url_parse( l[i]->sl_url.bv_val, &lu2 );
+			do {
+				if ( strcasecmp( lud->lud_scheme,
+					lu2->lud_scheme ))
+					break;
+				if ( lud->lud_port != lu2->lud_port )
+					break;
+				/* Listener on ANY address */
+				if ( !lu2->lud_host || !lu2->lud_host[0] ) {
+					isMe = 1;
+					break;
+				}
+				/* URL on ANY address */
+				if ( !lud->lud_host || !lud->lud_host[0] ) {
+					isMe = 1;
+					break;
+				}
+				/* Listener has specific host, must
+				 * match it
+				 */
+				if ( !strcasecmp( lud->lud_host,
+					lu2->lud_host )) {
+					isMe = 1;
+					break;
+				}
+			} while(0);
+			ldap_free_urldesc( lu2 );
+			if ( isMe ) {
+				return l[i];
+			}
+		}
+	}
+	return NULL;
+}
diff --git a/servers/slapd/config.h b/servers/slapd/config.h
index 99eb6b2c8e..4c929dea6d 100644
--- a/servers/slapd/config.h
+++ b/servers/slapd/config.h
@@ -196,6 +196,7 @@ ConfigTable * config_find_keyword(ConfigTable *ct, ConfigArgs *c);
 Entry * config_build_entry( Operation *op, SlapReply *rs, CfEntryInfo *parent,
 	ConfigArgs *c, struct berval *rdn, ConfigOCs *main, ConfigOCs *extra );
 
+Listener *config_check_my_url(const char *url, LDAPURLDesc *lud);
 int config_shadow( ConfigArgs *c, int flag );
 #define	config_slurp_shadow(c)	config_shadow((c), SLAP_DBFLAG_SLURP_SHADOW)
 #define	config_sync_shadow(c)	config_shadow((c), SLAP_DBFLAG_SYNC_SHADOW)
diff --git a/servers/slapd/syncrepl.c b/servers/slapd/syncrepl.c
index 07e9dd7857..627e43a3d2 100644
--- a/servers/slapd/syncrepl.c
+++ b/servers/slapd/syncrepl.c
@@ -4332,6 +4332,8 @@ add_syncrepl(
 	rc = parse_syncrepl_line( c, si );
 
 	if ( rc == 0 ) {
+		LDAPURLDesc *lud;
+
 		/* Must be LDAPv3 because we need controls */
 		switch ( si->si_bindconf.sb_version ) {
 		case 0:
@@ -4349,24 +4351,35 @@ add_syncrepl(
 			return 1;
 		}
 
+		if ( ldap_url_parse( si->si_bindconf.sb_uri.bv_val, &lud )) {
+			snprintf( c->cr_msg, sizeof( c->cr_msg ),
+				"<%s> invalid URL", c->argv[0] );
+			Debug( LDAP_DEBUG_ANY, "%s: %s %s\n",
+				c->log, c->cr_msg, si->si_bindconf.sb_uri.bv_val );
+			return 1;
+		}
+
 		si->si_be = c->be;
 		if ( slapMode & SLAP_SERVER_MODE ) {
-			Listener **l = slapd_get_listeners();
 			int isMe = 0;
-
-			/* check if URL points to current server. If so, ignore
-			 * this configuration. We require an exact match. Just
-			 * in case they really want to do this, they can vary
-			 * the case of the URL to allow it.
+			/* check if consumer points to current server and database.
+			 * If so, ignore this configuration.
 			 */
-			if ( l && !SLAP_DBHIDDEN( c->be ) ) {
+			if ( !SLAP_DBHIDDEN( c->be ) ) {
 				int i;
-				for ( i=0; l[i]; i++ ) {
-					if ( bvmatch( &l[i]->sl_url, &si->si_bindconf.sb_uri ) ) {
+				/* if searchbase doesn't match current DB suffix,
+				 * assume it's different
+				 */
+				for ( i=0; !BER_BVISNULL( &c->be->be_nsuffix[i] ); i++ ) {
+					if ( bvmatch( &si->si_base, &c->be->be_nsuffix[i] )) {
 						isMe = 1;
 						break;
 					}
 				}
+				/* if searchbase matches, see if URLs match */
+				if ( isMe && config_check_my_url( si->si_bindconf.sb_uri.bv_val,
+						lud ) == NULL )
+					isMe = 0;
 			}
 
 			if ( !isMe ) {
@@ -4385,6 +4398,7 @@ add_syncrepl(
 			/* mirrormode still needs to see this flag in tool mode */
 			rc = config_sync_shadow( c ) ? -1 : 0;
 		}
+		ldap_free_urldesc( lud );
 	}
 
 #ifdef HAVE_TLS
-- 
GitLab