diff --git a/servers/slapd/aclparse.c b/servers/slapd/aclparse.c
index b7b0d9507ab8b5684e577cb2e860fbf4995a504f..61a31f1c577cab5d790e49b480f8337f50e4f9bc 100644
--- a/servers/slapd/aclparse.c
+++ b/servers/slapd/aclparse.c
@@ -108,7 +108,7 @@ parse_acl(
 					break;
 				}
 
-				if ( strcasecmp( argv[i], "*" ) == 0 ) {
+				if ( strcmp( argv[i], "*" ) == 0 ) {
 					int e;
 					if ((e = regcomp( &a->acl_dnre, ".*",
 						REG_EXTENDED|REG_ICASE)))
@@ -152,6 +152,7 @@ parse_acl(
 						acl_usage();
 
 					} else {
+						/* ### Should be normalized, but how? */
 						a->acl_dnpat = dn_upcase(ch_strdup( right ));
 					}
 				} else if ( strncasecmp( left, "attr", 4 )
@@ -192,12 +193,13 @@ parse_acl(
 
 			/* get <who> */
 			split( argv[i], '=', &left, &right );
-			if ( strcasecmp( argv[i], "*" ) == 0 ) {
+			if ( strcmp( argv[i], "*" ) == 0 ) {
 				b->a_dnpat = ch_strdup( ".*" );
 			} else if ( strcasecmp( argv[i], "self" ) == 0 ) {
 				b->a_dnpat = ch_strdup( "self" );
 			} else if ( strcasecmp( left, "dn" ) == 0 ) {
 				regtest(fname, lineno, right);
+				/* ### Should be normalized, but how? */
 				b->a_dnpat = dn_upcase( ch_strdup( right ) );
 			} else if ( strcasecmp( left, "dnattr" ) == 0 ) {
 				b->a_dnattr = ch_strdup( right );
@@ -216,6 +218,7 @@ parse_acl(
                                             *name++ = '\0';
                                 }
 
+				/* ### Should it be normalized? */
 				b->a_group = dn_upcase(ch_strdup( right ));
 
                                 if (value && *value) {
diff --git a/servers/slapd/attr.c b/servers/slapd/attr.c
index c1178c94e919005772ca5bb535f616829a8ba29d..581f4570e720af40da687b8b56d5bbfad81200c6 100644
--- a/servers/slapd/attr.c
+++ b/servers/slapd/attr.c
@@ -279,7 +279,7 @@ attr_syntax_config(
 	    strcasecmp( argv[lasti], "tel" ) == 0 ) {
 		a->asi_syntax = (SYNTAX_CIS | SYNTAX_TEL);
 	} else if ( strcasecmp( argv[lasti], "dn" ) == 0 ) {
-		a->asi_syntax = (SYNTAX_CIS | SYNTAX_DN);
+		a->asi_syntax = SYNTAX_DN;
 	} else if ( strcasecmp( argv[lasti], "caseexactstring" ) == 0 ||
 	    strcasecmp( argv[lasti], "ces" ) == 0 ) {
 		a->asi_syntax = SYNTAX_CES;
diff --git a/servers/slapd/back-bdb2/alias.c b/servers/slapd/back-bdb2/alias.c
index 5b3588f7ee385af36e11b4bc7dae90bd51295763..4bec355fa64b276f9c2fa406fb8b95134182fd46 100644
--- a/servers/slapd/back-bdb2/alias.c
+++ b/servers/slapd/back-bdb2/alias.c
@@ -222,7 +222,7 @@ char *bdb2i_derefDN ( BackendDB     *be,
 
 	  Debug( LDAP_DEBUG_TRACE, "<= l&g we have %s vs %s \n", matched, eNew->e_dn, 0 );
 
-	  i = strcasecmp (matched, eNew->e_dn);
+	  i = dn_casecmp (matched, eNew->e_dn);
           /* free reader lock */
           bdb2i_cache_return_entry_r(&li->li_cache, eNew);
 
diff --git a/servers/slapd/back-bdb2/bind.c b/servers/slapd/back-bdb2/bind.c
index b51d9ea1360ce31e1559a7a43db5954da42732ad..29e5304a640c6b66fd03d4741c32dba67aea33f0 100644
--- a/servers/slapd/back-bdb2/bind.c
+++ b/servers/slapd/back-bdb2/bind.c
@@ -163,6 +163,7 @@ bdb2i_back_bind_internal(
 			/*
 			 * no krbName values present:  check against DN
 			 */
+			/*###??? Should this be some variant of dn_casecmp? */
 			if ( strcasecmp( dn, krbname ) == 0 ) {
 				rc = 0; /* XXX wild ass guess */
 				break;
diff --git a/servers/slapd/back-bdb2/group.c b/servers/slapd/back-bdb2/group.c
index 4634107ba435e2edd44b600ab553422a2406d926..6865cf2ac261de02ba10924383f47bd1dc05aa53 100644
--- a/servers/slapd/back-bdb2/group.c
+++ b/servers/slapd/back-bdb2/group.c
@@ -101,7 +101,7 @@ bdb2i_back_group_internal(
 					"<= bdb2i_back_group: failed to find %s in objectClass\n", 
                         objectclassValue, 0, 0 ); 
             }
-            else if (value_find(member->a_vals, &bvMembers, SYNTAX_CIS, 1) != 0) {
+            else if (value_find(member->a_vals, &bvMembers, member->a_syntax, 1) != 0) {
                 Debug( LDAP_DEBUG_ACL,
 					"<= bdb2i_back_group: \"%s\" not in \"%s\": %s\n", 
 					op_ndn, gr_ndn, groupattrName ); 
diff --git a/servers/slapd/back-bdb2/search.c b/servers/slapd/back-bdb2/search.c
index 5080a5faadb3aceb2ba99a25587709926e152c8e..8e6b7950d9ec88a1562d91ff9cb2da3deb1fb3e1 100644
--- a/servers/slapd/back-bdb2/search.c
+++ b/servers/slapd/back-bdb2/search.c
@@ -525,7 +525,7 @@ subtree_candidates(
 			f->f_and->f_sub_initial = NULL;
 			f->f_and->f_sub_any = NULL;
 			f->f_and->f_sub_final = ch_strdup( base );
-			value_normalize( f->f_and->f_sub_final, SYNTAX_CIS );
+			value_normalize( f->f_and->f_sub_final, SYNTAX_DN );
 			f->f_and->f_next = filter;
 			filter = f;
 		}
diff --git a/servers/slapd/back-ldbm/alias.c b/servers/slapd/back-ldbm/alias.c
index 9c4f1adeacb014b5ce9855cd6964f824ab9eaeb8..5224839c5b58cc9da8fa9ca700974a9a0303bcfc 100644
--- a/servers/slapd/back-ldbm/alias.c
+++ b/servers/slapd/back-ldbm/alias.c
@@ -221,7 +221,7 @@ char *derefDN ( Backend     *be,
 
 	  Debug( LDAP_DEBUG_TRACE, "<= l&g we have %s vs %s \n", matched, eNew->e_dn, 0 );
 
-	  i = strcasecmp (matched, eNew->e_dn);
+	  i = dn_casecmp (matched, eNew->e_dn);
           /* free reader lock */
           cache_return_entry_r(&li->li_cache, eNew);
 
diff --git a/servers/slapd/back-ldbm/bind.c b/servers/slapd/back-ldbm/bind.c
index 70d3d06d4a79e8e7596c510e30fd43feedc32857..8c4fb3fcc0ca5720b7b96aa6bee9d40854625f69 100644
--- a/servers/slapd/back-ldbm/bind.c
+++ b/servers/slapd/back-ldbm/bind.c
@@ -164,6 +164,7 @@ ldbm_back_bind(
 			/*
 			 * no krbName values present:  check against DN
 			 */
+			/*###??? Should this be some variant of dn_casecmp? */
 			if ( strcasecmp( dn, krbname ) == 0 ) {
 				rc = 0; /* XXX wild ass guess */
 				break;
diff --git a/servers/slapd/back-ldbm/group.c b/servers/slapd/back-ldbm/group.c
index eb35dc3d7e31715a296e1dfa7e6b2d08c0142d02..beecb2d1fa01dd010b8c7dc5312002ed71255c00 100644
--- a/servers/slapd/back-ldbm/group.c
+++ b/servers/slapd/back-ldbm/group.c
@@ -101,7 +101,7 @@ ldbm_back_group(
 					"<= ldbm_back_group: failed to find %s in objectClass\n", 
                         objectclassValue, 0, 0 ); 
             }
-            else if (value_find(member->a_vals, &bvMembers, SYNTAX_CIS, 1) != 0) {
+            else if (value_find(member->a_vals, &bvMembers, member->a_syntax, 1) != 0) {
                 Debug( LDAP_DEBUG_ACL,
 					"<= ldbm_back_group: \"%s\" not in \"%s\": %s\n", 
 					op_ndn, gr_ndn, groupattrName ); 
diff --git a/servers/slapd/back-ldbm/search.c b/servers/slapd/back-ldbm/search.c
index 04e05931930dca8e1a949e167071657ea815e565..5434966e5d8ab1b33e9de354beef9e005d1fb3b9 100644
--- a/servers/slapd/back-ldbm/search.c
+++ b/servers/slapd/back-ldbm/search.c
@@ -483,7 +483,7 @@ subtree_candidates(
 			f->f_and->f_sub_initial = NULL;
 			f->f_and->f_sub_any = NULL;
 			f->f_and->f_sub_final = ch_strdup( base );
-			value_normalize( f->f_and->f_sub_final, SYNTAX_CIS );
+			value_normalize( f->f_and->f_sub_final, SYNTAX_DN );
 			f->f_and->f_next = filter;
 			filter = f;
 		}
diff --git a/servers/slapd/config.c b/servers/slapd/config.c
index b02313b5cea80a278d19228788c011051459f7c5..94ff13b46825d61097fdbec4b0b303a06a990045 100644
--- a/servers/slapd/config.c
+++ b/servers/slapd/config.c
@@ -208,19 +208,17 @@ read_config( char *fname )
                                 char *alias, *aliased_dn;
 
 								alias = ch_strdup( cargv[1] );
-                                (void) dn_normalize( alias );
+                                (void) dn_normalize_case( alias );
 
                                 aliased_dn = ch_strdup( cargv[2] );
-                                (void) dn_normalize( aliased_dn );
+                                (void) dn_normalize_case( aliased_dn );
 
 
-								if ( strcasecmp( alias, aliased_dn) == 0 ) {
+				if ( strcmp( alias, aliased_dn) == 0 ) {
                                 	Debug( LDAP_DEBUG_ANY,
 "%s: line %d: suffixAlias %s is not different from aliased dn (ignored)\n",
                                     fname, lineno, alias );
 								} else {
-                                	(void) dn_normalize_case( alias );
-                                	(void) dn_normalize_case( aliased_dn );
                                 	charray_add( &be->be_suffixAlias, alias );
                                 	charray_add( &be->be_suffixAlias, aliased_dn );
 								}
diff --git a/servers/slapd/dn.c b/servers/slapd/dn.c
index e7bb61d5845c1ee2653fb57f05ac235ac4d6386a..38c4bdab89921976f6b9e6226be4e387d8db8c06 100644
--- a/servers/slapd/dn.c
+++ b/servers/slapd/dn.c
@@ -11,24 +11,32 @@
 
 #include "slap.h"
 
-#define B4TYPE		0
-#define INTYPE		1
-#define B4EQUAL		2
-#define B4VALUE		3
-#define INVALUE		4
-#define INQUOTEDVALUE	5
-#define B4SEPARATOR	6
+typedef enum DnState {
+	B4TYPE,			/* before attribute type */
+	INTYPE,			/* in     attribute type */
+	B4EQUAL,		/* before '=' */
+	B4VALUE,		/* before attribute value */
+	INVALUE,		/* in     attribute value */
+	INQUOTEDVALUE,		/* in "" in attribute value */
+	B4SEPARATOR,		/* before separator ('+', ',' or ';') */
+} DnState;
 
 /*
- * dn_normalize - put dn into a canonical format.  the dn is
- * normalized in place, as well as returned.
+ * dn_normalize_internal - put dn into a canonical form suitable for storing
+ * in a hash database.  If correct_case == 1, this involves normalizing the case
+ * as well as the format.  The dn is normalized in place as well as returned.
+ *
+ * The dn_normalize() and dn_normalize_case() macros use this function.
  */
 
 char *
-dn_normalize( char *dn )
+dn_normalize_internal( char *dn, int correct_case )
 {
-	char	*d, *s;
-	int	state, gotesc;
+	char	*s, *d;		/* source and destination pointers */
+	char	*type;		/* start of attr.type when state==INTYPE */
+	int	gotesc;		/* last char was '\\' */
+	int	ic;		/* ignore case  */
+	DnState	state;
 
 	/* Debug( LDAP_DEBUG_TRACE, "=> dn_normalize \"%s\"\n", dn, 0, 0 ); */
 
@@ -39,18 +47,28 @@ dn_normalize( char *dn )
 		case B4TYPE:
 			if ( ! SPACE( *s ) ) {
 				state = INTYPE;
-				*d++ = *s;
+				ic = 1;
+				type = d;
+				*d++ = TOUPPER( (unsigned char) *s );
 			}
 			break;
 		case INTYPE:
 			if ( *s == '=' ) {
 				state = B4VALUE;
-				*d++ = *s;
 			} else if ( SPACE( *s ) ) {
 				state = B4EQUAL;
 			} else {
-				*d++ = *s;
+				*d++ = TOUPPER( (unsigned char) *s );
+				break;
+			}
+			/* Check if case is ignored in this type */
+			if ( correct_case ) {
+				*d = '\0';
+				if ( ! (attr_syntax( type ) | SYNTAX_CIS) )
+					ic = 0;
 			}
+			if (state == B4VALUE)
+				*d++ = '=';
 			break;
 		case B4EQUAL:
 			if ( *s == '=' ) {
@@ -58,7 +76,7 @@ dn_normalize( char *dn )
 				*d++ = *s;
 			} else if ( ! SPACE( *s ) ) {
 				/* not a valid dn - but what can we do here? */
-				*d++ = *s;
+				*d++ = TOUPPER( (unsigned char) *s );
 			}
 			break;
 		case B4VALUE:
@@ -67,7 +85,7 @@ dn_normalize( char *dn )
 				*d++ = *s;
 			} else if ( ! SPACE( *s ) ) { 
 				state = INVALUE;
-				*d++ = *s;
+				*d++ = (ic ? TOUPPER((unsigned char) *s) : *s);
 			}
 			break;
 		case INVALUE:
@@ -82,10 +100,10 @@ dn_normalize( char *dn )
 				}
 			} else if ( gotesc && !NEEDSESCAPE( *s ) &&
 			    !SEPARATOR( *s ) ) {
-				*--d = *s;
+				*--d = (ic ? TOUPPER((unsigned char) *s) : *s);
 				d++;
 			} else {
-				*d++ = *s;
+				*d++ = (ic ? TOUPPER((unsigned char) *s) : *s);
 			}
 			break;
 		case INQUOTEDVALUE:
@@ -93,10 +111,10 @@ dn_normalize( char *dn )
 				state = B4SEPARATOR;
 				*d++ = *s;
 			} else if ( gotesc && !NEEDSESCAPE( *s ) ) {
-				*--d = *s;
+				*--d = (ic ? TOUPPER((unsigned char) *s) : *s);
 				d++;
 			} else {
-				*d++ = *s;
+				*d++ = (ic ? TOUPPER((unsigned char) *s) : *s);
 			}
 			break;
 		case B4SEPARATOR:
@@ -122,28 +140,23 @@ dn_normalize( char *dn )
 	return( dn );
 }
 
+
 /*
- * dn_normalize_case - put dn into a canonical form suitable for storing
- * in a hash database.  this involves normalizing the case as well as
- * the format.  the dn is normalized in place as well as returned.
+ * dn_casecmp - compare two DNs after normalizing (private copies of) them
  */
 
-char *
-dn_normalize_case( char *dn )
+int
+dn_casecmp( const char *dn1, const char *dn2 )
 {
-	char	*s;
-
-	/* normalize format */
-	dn_normalize( dn );
-
-	/* normalize case */
-	for ( s = dn; *s; s++ ) {
-		*s = TOUPPER( (unsigned char) *s );
-	}
-
-	return( dn );
+	char *ndn1 = dn_normalize_case( ch_strdup( dn1 ) );
+	char *ndn2 = dn_normalize_case( ch_strdup( dn2 ) );
+	int i = strcmp( ndn1, ndn2 );
+	free( ndn1 );
+	free( ndn2 );
+	return i;
 }
 
+
 /*
  * dn_parent - return a copy of the dn of dn's parent
  */
diff --git a/servers/slapd/main.c b/servers/slapd/main.c
index 6c06c02799e950f3beaea6b943026b6eadfd3d02..c37b0031b7461d15e4fe03951cc50705dd5d09e9 100644
--- a/servers/slapd/main.c
+++ b/servers/slapd/main.c
@@ -312,23 +312,19 @@ static RETSIGTYPE
 wait4child( int sig )
 {
     int save_errno = errno;
+
+#ifdef WNOHANG
     errno = 0;
-    /*
-     * ### The wait3 vs. waitpid choice needs improvement.
-     * ### There are apparently systems where waitpid(-1, ...) fails, and
-     * ### others where waitpid should preferred over wait3 for some reason.
-     * ### Now wait3 is only here for reference, configure does not detect it.
-     */
-#if defined(HAVE_WAITPID) && defined(WNOHANG)
+#ifdef HAVE_WAITPID
     while ( waitpid( (pid_t)-1, NULL, WNOHANG ) >= 0 || errno == EINTR )
 	;	/* NULL */
-#elif defined(HAVE_WAIT3) && defined(WNOHANG)
+#else
     while ( wait3( NULL, WNOHANG, NULL ) >= 0 || errno == EINTR )
 	;	/* NULL */
-#else
+#endif
     (void) wait( NULL );
 #endif
-    (void) signal( sig, wait4child );
+    (void) SIGNAL( sig, wait4child );
     errno = save_errno;
 }
 
diff --git a/servers/slapd/proto-slap.h b/servers/slapd/proto-slap.h
index 7907d6a623cddbf1ccd05ab2a5c822d15771e70f..108ca114b0d0454ec4fd7deb8d50426b2d206eee 100644
--- a/servers/slapd/proto-slap.h
+++ b/servers/slapd/proto-slap.h
@@ -130,8 +130,8 @@ void connection_done LDAP_P((Connection *));
  * dn.c
  */
 
-char * dn_normalize LDAP_P(( char *dn ));
-char * dn_normalize_case LDAP_P(( char *dn ));
+char * dn_normalize_internal LDAP_P(( char *dn, int correct_case ));
+int dn_casecmp LDAP_P(( const char *dn1, const char *dn2 ));
 char * dn_parent LDAP_P(( Backend *be, char *dn ));
 char * dn_rdn LDAP_P(( Backend *be, char *dn ));
 int dn_issuffix LDAP_P(( char *dn, char *suffix ));
diff --git a/servers/slapd/result.c b/servers/slapd/result.c
index 17f454ac83ee47a92238716e8221486b1e02551f..f03423a0b6087f5171bd436cd92781e1134ca933 100644
--- a/servers/slapd/result.c
+++ b/servers/slapd/result.c
@@ -273,6 +273,10 @@ send_search_entry(
 					! acl_access_allowed( acl, be, conn, e, a->a_vals[i], op,
 						ACL_READ, edn, matches) )
 				{
+				/* ### What the hell? Attributes with DN-syntax
+				 * ### are only returned if we have access to
+				 * ### the entry with that DN?  Or...?
+				 */
 					continue;
 				}
 
diff --git a/servers/slapd/shell-backends/passwd-shell.c b/servers/slapd/shell-backends/passwd-shell.c
index 89d2fd73d66ad91f0a66a8b0fccb8aa209d5371b..f546d2de5b8fc41b1d2330af298a7fdd04afa013 100644
--- a/servers/slapd/shell-backends/passwd-shell.c
+++ b/servers/slapd/shell-backends/passwd-shell.c
@@ -106,7 +106,7 @@ pwdfile_search( struct ldop *op, FILE *ofp )
     for ( pw = getpwent(); pw != NULL; pw = getpwent()) {
 	if (( entry = pw2entry( op, pw )) != NULL ) {
 	    if ( oneentry ) {
-		if ( strcasecmp( op->ldop_dn, entry->lde_dn ) == 0 ) {
+		if ( dn_casecmp( op->ldop_dn, entry->lde_dn ) == 0 ) {
 		    write_entry( op, entry, ofp );
 		    break;
 		}
diff --git a/servers/slapd/slap.h b/servers/slapd/slap.h
index a7d99114ddd2e40a1a9a59c82a8971c10a0d2378..0f86b9001e282769845fb5edf7f68cbf1bad7668 100644
--- a/servers/slapd/slap.h
+++ b/servers/slapd/slap.h
@@ -460,6 +460,9 @@ typedef struct slap_conn {
 #define Statslog( level, fmt, connid, opid, arg1, arg2, arg3 )
 #endif
 
+#define dn_normalize(dn)	dn_normalize_internal( dn, 0 )
+#define dn_normalize_case(dn)	dn_normalize_internal( dn, 1 )
+
 #include "proto-slap.h"
 
 LDAP_END_DECL
diff --git a/servers/slapd/tools/centipede.c b/servers/slapd/tools/centipede.c
index 536417b43f7b8ccbce36aff62452662ebf18af57..f01b44a72fcd0c1696c5ef7a37def37f0f1819e6 100644
--- a/servers/slapd/tools/centipede.c
+++ b/servers/slapd/tools/centipede.c
@@ -502,6 +502,10 @@ generate_new_centroids(
 
 				/* normalize the value */
 				for ( s = val[j]; *s; s++ ) {
+					/* May need other normalization here,
+					 * unless that breaks compatibility
+					 * with other centipedes
+					 */
 					*s = TOLOWER( (unsigned char) *s );
 					last = *s;
 				}
diff --git a/servers/slapd/value.c b/servers/slapd/value.c
index ccabdf06a42ae84c2e6250a0591da93e163f6d84..97fd1f4a39d590f2049d1279237eb65529c77b0a 100644
--- a/servers/slapd/value.c
+++ b/servers/slapd/value.c
@@ -82,18 +82,16 @@ value_normalize(
     int		syntax
 )
 {
-	char	*d, *save;
-
-	if ( ! (syntax & SYNTAX_CIS) ) {
-		return;
-	}
+	char	*d;
 
 	if ( syntax & SYNTAX_DN ) {
 		(void) dn_normalize_case( s );
 		return;
 	}
+	if ( ! (syntax & SYNTAX_CIS) ) {
+		return;
+	}
 
-	save = s;
 	for ( d = s; *s; s++ ) {
 		if ( (syntax & SYNTAX_TEL) && (*s == ' ' || *s == '-') ) {
 			continue;
@@ -112,7 +110,16 @@ value_cmp(
 )
 {
 	int		rc;
+	unsigned char	*s1, *s2;
+
+	if ( syntax & SYNTAX_DN )	/* #### TEST ### */
+		normalize = 3;
 
+    if ( normalize ) {
+	if ( ! (syntax & ~(SYNTAX_CIS | SYNTAX_CES | SYNTAX_BIN)) )
+		/* Normalization not needed,
+		 * in SYNTAX_CIS's case because it's handled by strcascmp */
+		normalize = 0;
 	if ( normalize & 1 ) {
 		v1 = ber_bvdup( v1 );
 		value_normalize( v1->bv_val, syntax );
@@ -121,18 +128,22 @@ value_cmp(
 		v2 = ber_bvdup( v2 );
 		value_normalize( v2->bv_val, syntax );
 	}
+    }
 
 	switch ( syntax ) {
 	case SYNTAX_CIS:
 	case (SYNTAX_CIS | SYNTAX_TEL):
-	case (SYNTAX_CIS | SYNTAX_DN):
 		rc = strcasecmp( v1->bv_val, v2->bv_val );
 		break;
 
+	case SYNTAX_DN:
 	case SYNTAX_CES:
 		rc = strcmp( v1->bv_val, v2->bv_val );
 		break;
 
+	default:
+		Debug( LDAP_DEBUG_ANY, "value_cmp: unknown syntax %d.\n", syntax, 0, 0);
+		/* Fall through */
 	case SYNTAX_BIN:
 		rc = (v1->bv_len == v2->bv_len
 		      ? memcmp( v1->bv_val, v2->bv_val, v1->bv_len )