diff --git a/CHANGES b/CHANGES
index 7e3165f58e6fd09b4aea661266678c301ac4bb1f..b33b93024481ea1c051b545ca5dfb9ddbe40d789 100644
--- a/CHANGES
+++ b/CHANGES
@@ -78,6 +78,7 @@ OpenLDAP 2.4.24 Engineering
 	Fixed slapd-ldap chaining with ppolicy (ITS#6540)
 	Fixed slapd-ldap with SASL/EXTERNAL (ITS#6642)
 	Fixed slapd-ldap crasher on matchedDN (ITS#6793)
+	Fixed slapd-ldap with unknown objectClasses (ITS#6814)
 	Fixed slapd-ldif error strings (ITS#6731)
 	Fixed slapd-ndb to honor rootpw setting (ITS#6661)
 	Fixed slapd-ndb hasSubordinates generation (ITS#6712)
diff --git a/servers/slapd/back-ldap/search.c b/servers/slapd/back-ldap/search.c
index 0b569fe2f13064612277c8837dd45fd65057f260..1d213b927665ad8ec573feffc27ee5f11c0db14b 100644
--- a/servers/slapd/back-ldap/search.c
+++ b/servers/slapd/back-ldap/search.c
@@ -40,77 +40,57 @@ ldap_build_entry( Operation *op, LDAPMessage *e, Entry *ent,
 	 struct berval *bdn );
 
 /*
- * Quick'n'dirty rewrite of filter in case of error, to deal with
- * <draft-zeilenga-ldap-t-f>.
+ * replaces (&) with (objectClass=*) and (|) with (!(objectClass=*))
+ * as the best replacement for RFC 4526 absolute true/absolute false
+ * filters; the only difference (AFAIK) is that they require search
+ * access to objectClass.
+ *
+ * filter->bv_val may be alloc'd on the thread's slab, if equal to
+ * op->ors_filterstr.bv_val, or realloc'd on the thread's slab otherwise.
  */
 static int
 ldap_back_munge_filter(
 	Operation	*op,
-	struct berval	*filter,
-	int	*freeit )
+	struct berval	*filter )
 {
-	ldapinfo_t	*li = (ldapinfo_t *) op->o_bd->be_private;
-
-	char		*ptr;
-	int		gotit = 0;
+	char *ptr;
+	int gotit = 0;
 
 	Debug( LDAP_DEBUG_ARGS, "=> ldap_back_munge_filter \"%s\"\n",
 			filter->bv_val, 0, 0 );
 
-	for ( ptr = strstr( filter->bv_val, "(?=" ); 
+	for ( ptr = strchr( filter->bv_val, '(' ); 
 			ptr;
-			ptr = strstr( ptr, "(?=" ) )
+			ptr = strchr( ptr, '(' ) )
 	{
 		static struct berval
-			bv_true = BER_BVC( "(?=true)" ),
-			bv_false = BER_BVC( "(?=false)" ),
-			bv_undefined = BER_BVC( "(?=undefined)" ),
 			bv_t = BER_BVC( "(&)" ),
 			bv_f = BER_BVC( "(|)" ),
 			bv_T = BER_BVC( "(objectClass=*)" ),
 			bv_F = BER_BVC( "(!(objectClass=*))" );
-		struct berval	*oldbv = NULL,
-				*newbv = NULL,
-				oldfilter = BER_BVNULL;
-
-		if ( strncmp( ptr, bv_true.bv_val, bv_true.bv_len ) == 0 ) {
-			oldbv = &bv_true;
-			if ( LDAP_BACK_T_F( li ) ) {
-				newbv = &bv_t;
-
-			} else {
-				newbv = &bv_T;
-			}
+		struct berval *oldbv = NULL,
+			*newbv = NULL,
+			oldfilter = BER_BVNULL;
 
-		} else if ( strncmp( ptr, bv_false.bv_val, bv_false.bv_len ) == 0 )
-		{
-			oldbv = &bv_false;
-			if ( LDAP_BACK_T_F( li ) ) {
-				newbv = &bv_f;
-
-			} else {
-				newbv = &bv_F;
-			}
+		if ( ptr[2] != ')' ) {
+			ptr++;
+			continue;
+		}
 
-		} 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;
-			}
+		switch ( ptr[1] ) {
+		case '&':
+			oldbv = &bv_t;
+			newbv = &bv_T;
+			break;
 
-			oldbv = &bv_undefined;
+		case '|':
+			oldbv = &bv_f;
 			newbv = &bv_F;
+			break;
 
-		} else {
-			gotit = 0;
-			goto done;
+		default:
+			/* should be an error */
+			continue;
 		}
 
 		oldfilter = *filter;
@@ -120,9 +100,8 @@ ldap_back_munge_filter(
 					op->o_tmpmemctx );
 
 			AC_MEMCPY( filter->bv_val, op->ors_filterstr.bv_val,
-					op->ors_filterstr.bv_len + 1 );
+					ptr - oldfilter.bv_val );
 
-			*freeit = 1;
 		} else {
 			filter->bv_val = op->o_tmprealloc( filter->bv_val,
 					filter->bv_len + 1, op->o_tmpmemctx );
@@ -136,10 +115,10 @@ ldap_back_munge_filter(
 		AC_MEMCPY( ptr, newbv->bv_val, newbv->bv_len );
 
 		ptr += newbv->bv_len;
-		gotit = 1;
+
+		gotit++;
 	}
 
-done:;
 	Debug( LDAP_DEBUG_ARGS, "<= ldap_back_munge_filter \"%s\" (%d)\n",
 			filter->bv_val, gotit, 0 );
 
@@ -164,7 +143,7 @@ ldap_back_search(
 			filter = BER_BVNULL;
 	int		i;
 	char		**attrs = NULL;
-	int		freetext = 0, freefilter = 0;
+	int		freetext = 0, filter_undef = 0;
 	int		do_retry = 1, dont_retry = 0;
 	LDAPControl	**ctrls = NULL;
 	char		**references = NULL;
@@ -218,6 +197,14 @@ ldap_back_search(
 	/* deal with <draft-zeilenga-ldap-t-f> filters */
 	filter = op->ors_filterstr;
 retry:
+	/* this goes after retry because ldap_back_munge_filter()
+	 * optionally replaces RFC 4526 T-F filters (&) (|)
+	 * if already computed, they will be re-installed
+	 * by filter2bv_undef_x() later */
+	if ( !LDAP_BACK_T_F( li ) ) {
+		ldap_back_munge_filter( op, &filter );
+	}
+
 	rs->sr_err = ldap_pvt_search( lc->lc_ld, op->o_req_dn.bv_val,
 			op->ors_scope, filter.bv_val,
 			attrs, op->ors_attrsonly, ctrls, NULL,
@@ -245,7 +232,14 @@ retry:
 			goto finish;
 
 		case LDAP_FILTER_ERROR:
-			if (ldap_back_munge_filter( op, &filter, &freefilter ) > 0 ) {
+			/* first try? */
+			if ( !filter_undef &&
+				strstr( filter.bv_val, "(?" ) &&
+				!LDAP_BACK_NOUNDEFFILTER( li ) )
+			{
+				BER_BVZERO( &filter );
+				filter2bv_undef_x( op, op->ors_filter, 1, &filter );
+				filter_undef = 1;
 				goto retry;
 			}
 
@@ -566,7 +560,7 @@ finish:;
 		ldap_back_quarantine( op, rs );
 	}
 
-	if ( freefilter && filter.bv_val != op->ors_filterstr.bv_val ) {
+	if ( filter.bv_val != op->ors_filterstr.bv_val ) {
 		op->o_tmpfree( filter.bv_val, op->o_tmpmemctx );
 	}
 
diff --git a/servers/slapd/filter.c b/servers/slapd/filter.c
index 246b4ac6b13c0cd856f6d097bf281f6c0a064f3a..372f414d80cad0c0d8e7c3da561ea9fb9efde902 100644
--- a/servers/slapd/filter.c
+++ b/servers/slapd/filter.c
@@ -585,6 +585,12 @@ filter_free( Filter *f )
 
 void
 filter2bv_x( Operation *op, Filter *f, struct berval *fstr )
+{
+	return filter2bv_undef_x( op, f, 0, fstr );
+}
+
+void
+filter2bv_undef_x( Operation *op, Filter *f, int noundef, struct berval *fstr )
 {
 	int		i;
 	Filter		*p;
@@ -595,10 +601,12 @@ filter2bv_x( Operation *op, Filter *f, struct berval *fstr )
 			ber_bvundefined = BER_BVC( "(?=undefined)" ),
 			ber_bverror = BER_BVC( "(?=error)" ),
 			ber_bvunknown = BER_BVC( "(?=unknown)" ),
-			ber_bvnone = BER_BVC( "(?=none)" );
+			ber_bvnone = BER_BVC( "(?=none)" ),
+			ber_bvF = BER_BVC( "(|)" ),
+			ber_bvT = BER_BVC( "(&)" );
 	ber_len_t	len;
 	ber_tag_t	choice;
-	int undef;
+	int undef, undef2;
 	char *sign;
 
 	if ( f == NULL ) {
@@ -607,6 +615,7 @@ filter2bv_x( Operation *op, Filter *f, struct berval *fstr )
 	}
 
 	undef = f->f_choice & SLAPD_FILTER_UNDEFINED;
+	undef2 = (undef && !noundef);
 	choice = f->f_choice & SLAPD_FILTER_MASK;
 
 	switch ( choice ) {
@@ -644,12 +653,12 @@ simple:
 		 * is legal for that attribute's syntax */
 
 		fstr->bv_len += f->f_av_desc->ad_cname.bv_len + tmp.bv_len;
-		if ( undef )
+		if ( undef2 )
 			fstr->bv_len++;
 		fstr->bv_val = op->o_tmpalloc( fstr->bv_len + 1, op->o_tmpmemctx );
 
 		snprintf( fstr->bv_val, fstr->bv_len + 1, "(%s%s%s%s)",
-			undef ? "?" : "",
+			undef2 ? "?" : "",
 			f->f_av_desc->ad_cname.bv_val, sign,
 			tmp.bv_len ? tmp.bv_val : "" );
 
@@ -663,12 +672,12 @@ simple:
 	case LDAP_FILTER_SUBSTRINGS:
 		fstr->bv_len = f->f_sub_desc->ad_cname.bv_len +
 			STRLENOF("(=*)");
-		if ( undef )
+		if ( undef2 )
 			fstr->bv_len++;
 		fstr->bv_val = op->o_tmpalloc( fstr->bv_len + 128, op->o_tmpmemctx );
 
 		snprintf( fstr->bv_val, fstr->bv_len + 1, "(%s%s=*)",
-			undef ? "?" : "",
+			undef2 ? "?" : "",
 			f->f_sub_desc->ad_cname.bv_val );
 
 		if ( f->f_sub_initial.bv_val != NULL ) {
@@ -737,13 +746,13 @@ simple:
 	case LDAP_FILTER_PRESENT:
 		fstr->bv_len = f->f_desc->ad_cname.bv_len +
 			STRLENOF("(=*)");
-		if ( undef )
+		if ( undef2 )
 			fstr->bv_len++;
 
 		fstr->bv_val = op->o_tmpalloc( fstr->bv_len + 1, op->o_tmpmemctx );
 
 		snprintf( fstr->bv_val, fstr->bv_len + 1, "(%s%s=*)",
-			undef ? "?" : "",
+			undef2 ? "?" : "",
 			f->f_desc->ad_cname.bv_val );
 		break;
 
@@ -760,7 +769,7 @@ simple:
 		for ( p = f->f_list; p != NULL; p = p->f_next ) {
 			len = fstr->bv_len;
 
-			filter2bv_x( op, p, &tmp );
+			filter2bv_undef_x( op, p, noundef, &tmp );
 			
 			fstr->bv_len += tmp.bv_len;
 			fstr->bv_val = op->o_tmprealloc( fstr->bv_val, fstr->bv_len + 1,
@@ -792,13 +801,14 @@ simple:
 		}
 		
 		fstr->bv_len = ad.bv_len +
+			( undef2 ? 1 : 0 ) +
 			( f->f_mr_dnattrs ? STRLENOF(":dn") : 0 ) +
-			( f->f_mr_rule_text.bv_len ? f->f_mr_rule_text.bv_len+1 : 0 ) +
+			( f->f_mr_rule_text.bv_len ? f->f_mr_rule_text.bv_len + STRLENOF(":") : 0 ) +
 			tmp.bv_len + STRLENOF("(:=)");
 		fstr->bv_val = op->o_tmpalloc( fstr->bv_len + 1, op->o_tmpmemctx );
 
 		snprintf( fstr->bv_val, fstr->bv_len + 1, "(%s%s%s%s%s:=%s)",
-			undef ? "?" : "",
+			undef2 ? "?" : "",
 			ad.bv_val,
 			f->f_mr_dnattrs ? ":dn" : "",
 			f->f_mr_rule_text.bv_len ? ":" : "",
@@ -810,11 +820,11 @@ simple:
 	case SLAPD_FILTER_COMPUTED:
 		switch ( f->f_result ) {
 		case LDAP_COMPARE_FALSE:
-			tmp = ber_bvfalse;
+			tmp = ( noundef ? ber_bvF : ber_bvfalse );
 			break;
 
 		case LDAP_COMPARE_TRUE:
-			tmp = ber_bvtrue;
+			tmp = ( noundef ? ber_bvT : ber_bvtrue );
 			break;
 			
 		case SLAPD_COMPARE_UNDEFINED:
@@ -837,6 +847,12 @@ simple:
 
 void
 filter2bv( Filter *f, struct berval *fstr )
+{
+	return filter2bv_undef( f, 0, fstr );
+}
+
+void
+filter2bv_undef( Filter *f, int noundef, struct berval *fstr )
 {
 	Operation op;
 	Opheader ohdr;
@@ -845,7 +861,7 @@ filter2bv( Filter *f, struct berval *fstr )
 	op.o_tmpmemctx = NULL;
 	op.o_tmpmfuncs = &ch_mfuncs;
 
-	filter2bv_x( &op, f, fstr );
+	filter2bv_undef_x( &op, f, noundef, fstr );
 }
 
 Filter *
diff --git a/servers/slapd/proto-slap.h b/servers/slapd/proto-slap.h
index 6c0613062773e29ad627bd72e0e3f258badd7b82..c8331a0e104068cfa8a3da2e26e1c77938690b93 100644
--- a/servers/slapd/proto-slap.h
+++ b/servers/slapd/proto-slap.h
@@ -1078,7 +1078,8 @@ LDAP_SLAPD_F (int) get_filter LDAP_P((
 LDAP_SLAPD_F (void) filter_free LDAP_P(( Filter *f ));
 LDAP_SLAPD_F (void) filter_free_x LDAP_P(( Operation *op, Filter *f, int freeme ));
 LDAP_SLAPD_F (void) filter2bv LDAP_P(( Filter *f, struct berval *bv ));
-LDAP_SLAPD_F (void) filter2bv_x LDAP_P(( Operation *op, Filter *f, struct berval *bv ));
+LDAP_SLAPD_F (void) filter2bv_undef LDAP_P(( Filter *f, int noundef, struct berval *bv ));
+LDAP_SLAPD_F (void) filter2bv_undef_x LDAP_P(( Operation *op, Filter *f, int noundef, struct berval *bv ));
 LDAP_SLAPD_F (Filter *) filter_dup LDAP_P(( Filter *f, void *memctx ));
 
 LDAP_SLAPD_F (int) get_vrFilter LDAP_P(( Operation *op, BerElement *ber,