diff --git a/CHANGES b/CHANGES
index 09cf77992932088d34d2c46aac610c9e12ea17c1..5756c73862f42fda72a32be7fa28da320b3b7cd9 100644
--- a/CHANGES
+++ b/CHANGES
@@ -2,12 +2,15 @@ OpenLDAP 2.4 Change Log
 
 OpenLDAP 2.4.12 Engineering
 	Fixed slapd socket closing on Windows (ITS#5606)
+	Fixed slapd-ldap,slapd-meta invalid filter behavior (ITS#5614)
 	Fixed slapd-meta quarantine behavior (ITS#5592)
 	Fixed slapd-sql freeing of connection (ITS#5607)
 	Fixed slapo-constraint string termination (ITS#5609)
 	Fixed slapo-rwm callback cleanup (ITS#5601)
 	Build Environment
 		Fixed ODBC library detection (ITS#5602)
+	Documentation
+		Added slapd-ldap(5), slapd-meta(5) noundeffilter (ITS#5614)
 
 OpenLDAP 2.4.11 Release (2008/07/16)
 	Fixed liblber ber_get_next length decoding (ITS#5580)
diff --git a/doc/man/man5/slapd-ldap.5 b/doc/man/man5/slapd-ldap.5
index 4ce6d217951339071ea3b7f0bb18865a1dead58c..ddadfb493514aa0d279c5c1d9d7c91d1ba9edab6 100644
--- a/doc/man/man5/slapd-ldap.5
+++ b/doc/man/man5/slapd-ldap.5
@@ -391,6 +391,17 @@ If
 do not return search reference responses.
 By default, they are returned unless request is LDAPv2.
 
+.TP
+.B noundeffilter <NO|yes>
+If
+.BR yes ,
+return success instead of searching if a filter is undefined or contains
+undefined portions.
+By default, the search is propagated after replacing undefined portions
+with
+.BR (!(objectClass=*)) ,
+which corresponds to the empty result set.
+
 .TP
 .B protocol\-version {0,2,3}
 This directive indicates what protocol version must be used to contact
diff --git a/doc/man/man5/slapd-meta.5 b/doc/man/man5/slapd-meta.5
index 897578c22f84873a27ce87f641a24ff90273c500..e44dcd6bff8cafac65745461e1ede7af10427cbc 100644
--- a/doc/man/man5/slapd-meta.5
+++ b/doc/man/man5/slapd-meta.5
@@ -136,6 +136,19 @@ By default, they are returned unless request is LDAPv2.
 If set before any target specification, it affects all targets, unless
 overridden by any per-target directive.
 
+.TP
+.B noundeffilter <NO|yes>
+If
+.BR yes ,
+return success instead of searching if a filter is undefined or contains
+undefined portions.
+By default, the search is propagated after replacing undefined portions
+with
+.BR (!(objectClass=*)) ,
+which corresponds to the empty result set.
+If set before any target specification, it affects all targets, unless
+overridden by any per-target directive.
+
 .TP
 .B protocol\-version {0,2,3}
 This directive indicates what protocol version must be used to contact
diff --git a/servers/slapd/back-ldap/back-ldap.h b/servers/slapd/back-ldap/back-ldap.h
index dedbe9da92ea8c5654ad27168fae7d4847c9bf4a..a5242e0a88ebf40615cacd6a210d44c9928b03e3 100644
--- a/servers/slapd/back-ldap/back-ldap.h
+++ b/servers/slapd/back-ldap/back-ldap.h
@@ -316,6 +316,7 @@ typedef struct ldapinfo_t {
 #endif /* SLAP_CONTROL_X_SESSION_TRACKING */
 
 #define LDAP_BACK_F_NOREFS		(0x00080000U)
+#define LDAP_BACK_F_NOUNDEFFILTER	(0x00100000U)
 
 #define	LDAP_BACK_ISSET_F(ff,f)		( ( (ff) & (f) ) == (f) )
 #define	LDAP_BACK_ISMASK_F(ff,m,f)	( ( (ff) & (m) ) == (f) )
@@ -356,6 +357,7 @@ typedef struct ldapinfo_t {
 #endif /* SLAP_CONTROL_X_SESSION_TRACKING */
 
 #define	LDAP_BACK_NOREFS(li)		LDAP_BACK_ISSET( (li), LDAP_BACK_F_NOREFS)
+#define	LDAP_BACK_NOUNDEFFILTER(li)	LDAP_BACK_ISSET( (li), LDAP_BACK_F_NOUNDEFFILTER)
 
 	int			li_version;
 
diff --git a/servers/slapd/back-ldap/config.c b/servers/slapd/back-ldap/config.c
index bf8a8ff253c83fdae44d909ff6245490e6ae2485..ca89cf97f082a46f48b48f01f1148c2bb5a05384 100644
--- a/servers/slapd/back-ldap/config.c
+++ b/servers/slapd/back-ldap/config.c
@@ -71,6 +71,7 @@ enum {
 	LDAP_BACK_CFG_QUARANTINE,
 	LDAP_BACK_CFG_ST_REQUEST,
 	LDAP_BACK_CFG_NOREFS,
+	LDAP_BACK_CFG_NOUNDEFFILTER,
 
 	LDAP_BACK_CFG_REWRITE,
 
@@ -311,11 +312,19 @@ static ConfigTable ldapcfg[] = {
 	{ "norefs", "true|FALSE", 2, 2, 0,
 		ARG_MAGIC|ARG_ON_OFF|LDAP_BACK_CFG_NOREFS,
 		ldap_back_cf_gen, "( OLcfgDbAt:3.25 "
-			"NAME 'olcDbNorefs' "
+			"NAME 'olcDbNoRefs' "
 			"DESC 'Do not return search reference responses' "
 			"SYNTAX OMsBoolean "
 			"SINGLE-VALUE )",
 		NULL, NULL },
+	{ "noundeffilter", "true|FALSE", 2, 2, 0,
+		ARG_MAGIC|ARG_ON_OFF|LDAP_BACK_CFG_NOUNDEFFILTER,
+		ldap_back_cf_gen, "( OLcfgDbAt:3.26 "
+			"NAME 'olcDbNoUndefFilter' "
+			"DESC 'Do not propagate undefined search filters' "
+			"SYNTAX OMsBoolean "
+			"SINGLE-VALUE )",
+		NULL, NULL },
 	{ "suffixmassage", "[virtual]> <real", 2, 3, 0,
 		ARG_STRING|ARG_MAGIC|LDAP_BACK_CFG_REWRITE,
 		ldap_back_cf_gen, NULL, NULL, NULL },
@@ -358,7 +367,8 @@ static ConfigOCs ldapocs[] = {
 #ifdef SLAP_CONTROL_X_SESSION_TRACKING
 			"$ olcDbSessionTrackingRequest "
 #endif /* SLAP_CONTROL_X_SESSION_TRACKING */
-			"$ olcDbNorefs "
+			"$ olcDbNoRefs "
+			"$ olcDbNoUndefFilter "
 		") )",
 		 	Cft_Database, ldapcfg},
 	{ NULL, 0, NULL }
@@ -1152,6 +1162,10 @@ ldap_back_cf_gen( ConfigArgs *c )
 			c->value_int = LDAP_BACK_NOREFS( li );
 			break;
 
+		case LDAP_BACK_CFG_NOUNDEFFILTER:
+			c->value_int = LDAP_BACK_NOUNDEFFILTER( li );
+			break;
+
 		default:
 			/* FIXME: we need to handle all... */
 			assert( 0 );
@@ -1278,6 +1292,10 @@ ldap_back_cf_gen( ConfigArgs *c )
 			li->li_flags &= ~LDAP_BACK_F_NOREFS;
 			break;
 
+		case LDAP_BACK_CFG_NOUNDEFFILTER:
+			li->li_flags &= ~LDAP_BACK_F_NOUNDEFFILTER;
+			break;
+
 		default:
 			/* FIXME: we need to handle all... */
 			assert( 0 );
@@ -1931,6 +1949,15 @@ done_url:;
 		}
 		break;
 
+	case LDAP_BACK_CFG_NOUNDEFFILTER:
+		if ( c->value_int ) {
+			li->li_flags |= LDAP_BACK_F_NOUNDEFFILTER;
+
+		} else {
+			li->li_flags &= ~LDAP_BACK_F_NOUNDEFFILTER;
+		}
+		break;
+
 	case LDAP_BACK_CFG_REWRITE:
 		snprintf( c->cr_msg, sizeof( c->cr_msg ),
 			"rewrite/remap capabilities have been moved "
diff --git a/servers/slapd/back-ldap/search.c b/servers/slapd/back-ldap/search.c
index 79549b22aa71fa466ff6346ee2f2c8588f3de35e..fb32cb0373acb404bee976057f47d0cc12152cf7 100644
--- a/servers/slapd/back-ldap/search.c
+++ b/servers/slapd/back-ldap/search.c
@@ -94,6 +94,17 @@ ldap_back_munge_filter(
 
 		} else if ( strncmp( ptr, bv_undefined.bv_val, bv_undefined.bv_len ) == 0 )
 		{
+			/* if undef or invalid filter is not allowed,
+			 * don't rewrite filter */
+			if ( LDAP_BACK_NOUNDEFFILTER( li ) ) {
+				if ( filter->bv_val != op->ors_filterstr.bv_val ) {
+					op->o_tmpfree( filter->bv_val, op->o_tmpmemctx );
+				}
+				BER_BVZERO( filter );
+				gotit = -1;
+				goto done;
+			}
+
 			oldbv = &bv_undefined;
 			newbv = &bv_F;
 
@@ -103,23 +114,21 @@ ldap_back_munge_filter(
 		}
 
 		oldfilter = *filter;
-		if ( newbv->bv_len > oldbv->bv_len ) {
-			filter->bv_len += newbv->bv_len - oldbv->bv_len;
-			if ( filter->bv_val == op->ors_filterstr.bv_val ) {
-				filter->bv_val = op->o_tmpalloc( filter->bv_len + 1,
-						op->o_tmpmemctx );
-
-				AC_MEMCPY( filter->bv_val, op->ors_filterstr.bv_val,
-						op->ors_filterstr.bv_len + 1 );
+		filter->bv_len += newbv->bv_len - oldbv->bv_len;
+		if ( filter->bv_val == op->ors_filterstr.bv_val ) {
+			filter->bv_val = op->o_tmpalloc( filter->bv_len + 1,
+					op->o_tmpmemctx );
 
-			} else {
-				filter->bv_val = op->o_tmprealloc( filter->bv_val,
-						filter->bv_len + 1, op->o_tmpmemctx );
-			}
+			AC_MEMCPY( filter->bv_val, op->ors_filterstr.bv_val,
+					op->ors_filterstr.bv_len + 1 );
 
-			ptr = filter->bv_val + ( ptr - oldfilter.bv_val );
+		} else {
+			filter->bv_val = op->o_tmprealloc( filter->bv_val,
+					filter->bv_len + 1, op->o_tmpmemctx );
 		}
 
+		ptr = filter->bv_val + ( ptr - oldfilter.bv_val );
+
 		AC_MEMCPY( &ptr[ newbv->bv_len ],
 				&ptr[ oldbv->bv_len ], 
 				oldfilter.bv_len - ( ptr - filter->bv_val ) - oldbv->bv_len + 1 );
@@ -152,7 +161,6 @@ ldap_back_search(
 			msgid; 
 	struct berval	match = BER_BVNULL,
 			filter = BER_BVNULL;
-	int		free_filter = 0;
 	int		i;
 	char		**attrs = NULL;
 	int		freetext = 0;
@@ -240,8 +248,7 @@ retry:
 			goto finish;
 
 		case LDAP_FILTER_ERROR:
-			if ( ldap_back_munge_filter( op, &filter ) ) {
-				free_filter = 1;
+			if (ldap_back_munge_filter( op, &filter ) > 0 ) {
 				goto retry;
 			}
 
@@ -525,6 +532,10 @@ finish:;
 		ldap_back_quarantine( op, rs );
 	}
 
+	if ( filter.bv_val != op->ors_filterstr.bv_val ) {
+		op->o_tmpfree( filter.bv_val, op->o_tmpmemctx );
+	}
+
 #if 0
 	/* let send_ldap_result play cleanup handlers (ITS#4645) */
 	if ( rc != SLAPD_ABANDON )
@@ -550,10 +561,6 @@ finish:;
 		rs->sr_matched = save_matched;
 	}
 
-	if ( free_filter ) {
-		op->o_tmpfree( filter.bv_val, op->o_tmpmemctx );
-	}
-
 	if ( rs->sr_text ) {
 		if ( freetext ) {
 			LDAP_FREE( (char *)rs->sr_text );
diff --git a/servers/slapd/back-meta/back-meta.h b/servers/slapd/back-meta/back-meta.h
index 8e73c1f272d8f67c9dba6349d95a0b3c19bca391..62339568a6fdf317ba6ef8c90ee00097091c818b 100644
--- a/servers/slapd/back-meta/back-meta.h
+++ b/servers/slapd/back-meta/back-meta.h
@@ -316,6 +316,7 @@ typedef struct metatarget_t {
 #endif /* SLAP_CONTROL_X_SESSION_TRACKING */
 
 #define	META_BACK_TGT_NOREFS(mt)		META_BACK_TGT_ISSET( (mt), LDAP_BACK_F_NOREFS )
+#define	META_BACK_TGT_NOUNDEFFILTER(mt)		META_BACK_TGT_ISSET( (mt), LDAP_BACK_F_NOUNDEFFILTER )
 
 	int			mt_version;
 	time_t			mt_network_timeout;
@@ -374,13 +375,13 @@ typedef struct metainfo_t {
 	unsigned		mi_flags;
 #define	li_flags		mi_flags
 /* uses flags as defined in <back-ldap/back-ldap.h> */
-#define	META_BACK_F_ONERR_STOP		(0x00100000U)
-#define	META_BACK_F_ONERR_REPORT	(0x00200000U)
+#define	META_BACK_F_ONERR_STOP		(0x01000000U)
+#define	META_BACK_F_ONERR_REPORT	(0x02000000U)
 #define	META_BACK_F_ONERR_MASK		(META_BACK_F_ONERR_STOP|META_BACK_F_ONERR_REPORT)
-#define	META_BACK_F_DEFER_ROOTDN_BIND	(0x00400000U)
-#define	META_BACK_F_PROXYAUTHZ_ALWAYS	(0x00800000U)	/* users always proxyauthz */
-#define	META_BACK_F_PROXYAUTHZ_ANON	(0x01000000U)	/* anonymous always proxyauthz */
-#define	META_BACK_F_PROXYAUTHZ_NOANON	(0x02000000U)	/* anonymous remains anonymous */
+#define	META_BACK_F_DEFER_ROOTDN_BIND	(0x04000000U)
+#define	META_BACK_F_PROXYAUTHZ_ALWAYS	(0x08000000U)	/* users always proxyauthz */
+#define	META_BACK_F_PROXYAUTHZ_ANON	(0x10000000U)	/* anonymous always proxyauthz */
+#define	META_BACK_F_PROXYAUTHZ_NOANON	(0x20000000U)	/* anonymous remains anonymous */
 
 #define	META_BACK_ONERR_STOP(mi)	LDAP_BACK_ISSET( (mi), META_BACK_F_ONERR_STOP )
 #define	META_BACK_ONERR_REPORT(mi)	LDAP_BACK_ISSET( (mi), META_BACK_F_ONERR_REPORT )
diff --git a/servers/slapd/back-meta/config.c b/servers/slapd/back-meta/config.c
index 52795400efa26435b19aff9b677bcaf4c244607f..c3424438e6fd5715ae7042f059cc418ab532ae6a 100644
--- a/servers/slapd/back-meta/config.c
+++ b/servers/slapd/back-meta/config.c
@@ -1483,6 +1483,36 @@ idassert-authzFrom	"dn:<rootdn>"
 			return( 1 );
 		}
 
+	/* do not propagate undefined search filters */
+	} else if ( strcasecmp( argv[ 0 ], "noundeffilter" ) == 0 ) {
+		unsigned	*flagsp = mi->mi_ntargets ?
+				&mi->mi_targets[ mi->mi_ntargets - 1 ]->mt_flags
+				: &mi->mi_flags;
+
+		if ( argc != 2 ) {
+			Debug( LDAP_DEBUG_ANY,
+	"%s: line %d: \"noundeffilter {TRUE|false}\" needs 1 argument.\n",
+				fname, lineno, 0 );
+			return( 1 );
+		}
+
+		/* this is the default; we add it because the default might change... */
+		switch ( check_true_false( argv[ 1 ] ) ) {
+		case 1:
+			*flagsp |= LDAP_BACK_F_NOUNDEFFILTER;
+			break;
+
+		case 0:
+			*flagsp &= ~LDAP_BACK_F_NOUNDEFFILTER;
+			break;
+
+		default:
+			Debug( LDAP_DEBUG_ANY,
+		"%s: line %d: \"noundeffilter {TRUE|false}\": unknown argument \"%s\".\n",
+				fname, lineno, argv[ 1 ] );
+			return( 1 );
+		}
+
 	/* anything else */
 	} else {
 		return SLAP_CONF_UNKNOWN;
diff --git a/servers/slapd/back-meta/map.c b/servers/slapd/back-meta/map.c
index b4df6759f530af1a6f83a5ef7f4d79b84852e63a..962b4f05e51d3fa58e47891f8ae9e485cd208625 100644
--- a/servers/slapd/back-meta/map.c
+++ b/servers/slapd/back-meta/map.c
@@ -530,10 +530,15 @@ ldap_back_int_filter_map_rewrite(
 
 	case SLAPD_FILTER_COMPUTED:
 		switch ( f->f_result ) {
-		case LDAP_COMPARE_FALSE:
 		/* FIXME: treat UNDEFINED as FALSE */
 		case SLAPD_COMPARE_UNDEFINED:
 computed:;
+			if ( META_BACK_TGT_NOUNDEFFILTER( dc->target ) ) {
+				return LDAP_COMPARE_FALSE;
+			}
+			/* fallthru */
+
+		case LDAP_COMPARE_FALSE:
 			if ( META_BACK_TGT_T_F( dc->target ) ) {
 				tmp = &ber_bvtf_false;
 				break;