diff --git a/servers/slapd/Makefile.in b/servers/slapd/Makefile.in
index 2176cdcc35807368a119ead166775ff9091fac83..717b91dc7050bdc08269440676b9e01c0c938937 100644
--- a/servers/slapd/Makefile.in
+++ b/servers/slapd/Makefile.in
@@ -9,7 +9,7 @@ XSRCS=version.c
 NT_SRCS = nt_svc.c
 NT_OBJS = nt_svc.o ../../libraries/liblutil/slapdmsg.res
 
-SRCS	= main.c daemon.c connection.c search.c filter.c add.c \
+SRCS	= main.c daemon.c connection.c search.c filter.c add.c cr.c \
 		attr.c entry.c config.c backend.c result.c operation.c \
 		dn.c compare.c modify.c delete.c modrdn.c ch_malloc.c \
 		value.c ava.c bind.c unbind.c abandon.c filterentry.c \
@@ -22,7 +22,7 @@ SRCS	= main.c daemon.c connection.c search.c filter.c add.c \
 		limits.c backglue.c operational.c matchedValues.c \
 		$(@PLAT@_SRCS)
 
-OBJS	= main.o daemon.o connection.o search.o filter.o add.o \
+OBJS	= main.o daemon.o connection.o search.o filter.o add.o cr.o \
 		attr.o entry.o config.o backend.o result.o operation.o \
 		dn.o compare.o modify.o delete.o modrdn.o ch_malloc.o \
 		value.o ava.o bind.o unbind.o abandon.o filterentry.o \
diff --git a/servers/slapd/at.c b/servers/slapd/at.c
index 7d2f6e1892d674e54b36d56aef4cc4a88953664a..42c1b7a39653e708378f5ee157976c2a0abbfe21 100644
--- a/servers/slapd/at.c
+++ b/servers/slapd/at.c
@@ -591,12 +591,12 @@ at_schema_info( Entry *e )
 	vals[1].bv_val = NULL;
 
 	for ( at = attr_list; at; at = at->sat_next ) {
+		if( at->sat_flags & SLAP_AT_HIDE ) continue;
+
 		if ( ldap_attributetype2bv( &at->sat_atype, vals ) == NULL ) {
 			return -1;
 		}
 
-		if( at->sat_flags & SLAP_AT_HIDE ) continue;
-
 		attr_merge( e, ad_attributeTypes, vals );
 		ldap_memfree( vals[0].bv_val );
 	}
diff --git a/servers/slapd/compare.c b/servers/slapd/compare.c
index 7934821fd4f6e1c4f091df50c388950dce0e44a5..81e3e9f54e206783b54226bbe957f3b67fe4b4fa 100644
--- a/servers/slapd/compare.c
+++ b/servers/slapd/compare.c
@@ -36,6 +36,7 @@ do_compare(
 )
 {
 	Entry *entry = NULL;
+	Entry *fentry = NULL;
 	struct berval dn = { 0, NULL };
 	struct berval pdn = { 0, NULL };
 	struct berval ndn = { 0, NULL };
@@ -169,6 +170,8 @@ do_compare(
 			goto cleanup;
 		}
 
+		fentry = entry;
+
 	} else if ( bvmatch( &ndn, &global_schemandn ) ) {
 #ifdef NEW_LOGGING
 		LDAP_LOG( OPERATION, ARGS, 
@@ -198,11 +201,12 @@ do_compare(
 			rc = 0;
 			goto cleanup;
 		}
+		fentry = entry;
 	}
 
 	if( entry ) {
 		rc = compare_entry( conn, op, entry, &ava );
-		entry_free( entry );
+		if( fentry) entry_free( fentry );
 
 		send_ldap_result( conn, op, rc, NULL, text, NULL, NULL );
 
diff --git a/servers/slapd/config.c b/servers/slapd/config.c
index 3147c4ede93c31e5cd8f10d06c87f95ce213892e..44c410fac1f8eac7000e92b83a52d07804bacf66 100644
--- a/servers/slapd/config.c
+++ b/servers/slapd/config.c
@@ -1577,9 +1577,9 @@ read_config( const char *fname, int depth )
 
 		/* specify an objectclass */
 		} else if ( strcasecmp( cargv[0], "objectclass" ) == 0 ) {
-			if ( *cargv[1] == '(' ) {
+			if ( *cargv[1] == '('  /*')'*/) {
 				char * p;
-				p = strchr(saveline,'(');
+				p = strchr(saveline,'(' /*')'*/);
 				rc = parse_oc( fname, lineno, p, cargv );
 				if( rc ) return rc;
 
@@ -1595,13 +1595,21 @@ read_config( const char *fname, int depth )
 #endif
 			}
 
+#ifdef SLAP_EXTENDED_SCHEMA
+		} else if ( strcasecmp( cargv[0], "ditcontentrule" ) == 0 ) {
+			char * p;
+			p = strchr(saveline,'(' /*')'*/);
+			rc = parse_cr( fname, lineno, p, cargv );
+			if( rc ) return rc;
+#endif
+
 		/* specify an attribute type */
 		} else if (( strcasecmp( cargv[0], "attributetype" ) == 0 )
 			|| ( strcasecmp( cargv[0], "attribute" ) == 0 ))
 		{
-			if ( *cargv[1] == '(' ) {
+			if ( *cargv[1] == '(' /*')'*/) {
 				char * p;
-				p = strchr(saveline,'(');
+				p = strchr(saveline,'(' /*')'*/);
 				rc = parse_at( fname, lineno, p, cargv );
 				if( rc ) return rc;
 
diff --git a/servers/slapd/cr.c b/servers/slapd/cr.c
new file mode 100644
index 0000000000000000000000000000000000000000..f8f5216cb404956f2c4ede68afbb12c9342bac33
--- /dev/null
+++ b/servers/slapd/cr.c
@@ -0,0 +1,417 @@
+/* cr.c - content rule routines */
+/* $OpenLDAP$ */
+/*
+ * Copyright 1998-2002 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/ctype.h>
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#include "slap.h"
+#include "ldap_pvt.h"
+
+#ifdef SLAP_EXTENDED_SCHEMA
+
+struct cindexrec {
+	struct berval	cir_name;
+	ContentRule	*cir_cr;
+};
+
+static Avlnode	*cr_index = NULL;
+static ContentRule *cr_list = NULL;
+
+static int
+cr_index_cmp(
+    struct cindexrec	*cir1,
+    struct cindexrec	*cir2 )
+{
+	int i = cir1->cir_name.bv_len - cir2->cir_name.bv_len;
+	if (i)
+		return i;
+	return strcasecmp( cir1->cir_name.bv_val, cir2->cir_name.bv_val );
+}
+
+static int
+cr_index_name_cmp(
+    struct berval	*name,
+    struct cindexrec	*cir )
+{
+	int i = name->bv_len - cir->cir_name.bv_len;
+	if (i)
+		return i;
+	return strncasecmp( name->bv_val, cir->cir_name.bv_val, name->bv_len );
+}
+
+ContentRule *
+cr_find( const char *crname )
+{
+	struct berval bv;
+
+	bv.bv_val = (char *)crname;
+	bv.bv_len = strlen( crname );
+
+	return( cr_bvfind( &bv ) );
+}
+
+ContentRule *
+cr_bvfind( struct berval *crname )
+{
+	struct cindexrec	*cir;
+
+	cir = (struct cindexrec *) avl_find( cr_index, crname,
+            (AVL_CMP) cr_index_name_cmp );
+
+	if ( cir != NULL ) {
+		return( cir->cir_cr );
+	}
+
+	return( NULL );
+}
+
+void
+cr_destroy( void )
+{
+	ContentRule *c, *n;
+
+	avl_free(cr_index, ldap_memfree);
+	for (c=cr_list; c; c=n)
+	{
+		n = c->scr_next;
+		if (c->scr_auxiliaries) ldap_memfree(c->scr_auxiliaries);
+		if (c->scr_required) ldap_memfree(c->scr_required);
+		if (c->scr_allowed) ldap_memfree(c->scr_allowed);
+		if (c->scr_precluded) ldap_memfree(c->scr_precluded);
+		ldap_contentrule_free((LDAPContentRule *)c);
+	}
+}
+
+static int
+cr_insert(
+    ContentRule		*scr,
+    const char		**err
+)
+{
+	ContentRule	**crp;
+	struct cindexrec	*cir;
+	char			**names;
+
+	crp = &cr_list;
+	while ( *crp != NULL ) {
+		crp = &(*crp)->scr_next;
+	}
+	*crp = scr;
+
+	if ( scr->scr_oid ) {
+		cir = (struct cindexrec *)
+			ch_calloc( 1, sizeof(struct cindexrec) );
+		cir->cir_name.bv_val = scr->scr_oid;
+		cir->cir_name.bv_len = strlen( scr->scr_oid );
+		cir->cir_cr = scr;
+
+		assert( cir->cir_name.bv_val );
+		assert( cir->cir_cr );
+
+		if ( avl_insert( &cr_index, (caddr_t) cir,
+				 (AVL_CMP) cr_index_cmp,
+				 (AVL_DUP) avl_dup_error ) )
+		{
+			*err = scr->scr_oid;
+			ldap_memfree(cir);
+			return SLAP_SCHERR_CR_DUP;
+		}
+
+		/* FIX: temporal consistency check */
+		assert( cr_bvfind(&cir->cir_name) != NULL );
+	}
+
+	if ( (names = scr->scr_names) ) {
+		while ( *names ) {
+			cir = (struct cindexrec *)
+				ch_calloc( 1, sizeof(struct cindexrec) );
+			cir->cir_name.bv_val = *names;
+			cir->cir_name.bv_len = strlen( *names );
+			cir->cir_cr = scr;
+
+			assert( cir->cir_name.bv_val );
+			assert( cir->cir_cr );
+
+			if ( avl_insert( &cr_index, (caddr_t) cir,
+					 (AVL_CMP) cr_index_cmp,
+					 (AVL_DUP) avl_dup_error ) )
+			{
+				*err = *names;
+				ldap_memfree(cir);
+				return SLAP_SCHERR_CR_DUP;
+			}
+
+			/* FIX: temporal consistency check */
+			assert( cr_bvfind(&cir->cir_name) != NULL );
+
+			names++;
+		}
+	}
+
+	return 0;
+}
+
+static int
+cr_add_auxiliaries(
+    ContentRule		*scr,
+	int			*op,
+    const char		**err )
+{
+	int naux;
+
+	if( scr->scr_oc_oids_aux == NULL ) return 0;
+	
+	for( naux=0; scr->scr_oc_oids_aux[naux]; naux++ ) {
+		/* count them */ ;
+	}
+
+	scr->scr_auxiliaries = ch_calloc( naux+1, sizeof(ObjectClass *));
+
+	for( naux=0; scr->scr_oc_oids_aux[naux]; naux++ ) {
+		ObjectClass *soc = scr->scr_auxiliaries[naux]
+			= oc_find(scr->scr_oc_oids_aux[naux]);
+		if ( !soc ) {
+			*err = scr->scr_oc_oids_aux[naux];
+			return SLAP_SCHERR_CLASS_NOT_FOUND;
+		}
+
+		if( soc->soc_flags & SLAP_OC_OPERATIONAL ) (*op)++;
+
+		if( soc->soc_kind != LDAP_SCHEMA_AUXILIARY ) {
+			*err = scr->scr_oc_oids_aux[naux];
+			return SLAP_SCHERR_CR_BAD_AUX;
+		}
+	}
+
+	scr->scr_auxiliaries[naux] = NULL;
+
+	return 0;
+}
+
+static int
+cr_create_required(
+    ContentRule		*scr,
+	int			*op,
+    const char		**err )
+{
+    char		**attrs = scr->scr_at_oids_must;
+	char		**attrs1;
+	AttributeType	*sat;
+
+	if ( attrs ) {
+		attrs1 = attrs;
+		while ( *attrs1 ) {
+			sat = at_find(*attrs1);
+			if ( !sat ) {
+				*err = *attrs1;
+				return SLAP_SCHERR_ATTR_NOT_FOUND;
+			}
+
+			if( is_at_operational( sat )) (*op)++;
+
+			if ( at_find_in_list(sat, scr->scr_required) < 0) {
+				if ( at_append_to_list(sat, &scr->scr_required) ) {
+					*err = *attrs1;
+					return SLAP_SCHERR_OUTOFMEM;
+				}
+			} else {
+				*err = *attrs1;
+				return SLAP_SCHERR_CR_BAD_AT;
+			}
+			attrs1++;
+		}
+	}
+	return 0;
+}
+
+static int
+cr_create_allowed(
+    ContentRule		*scr,
+	int			*op,
+    const char		**err )
+{
+    char		**attrs = scr->scr_at_oids_may;
+	char		**attrs1;
+	AttributeType	*sat;
+
+	if ( attrs ) {
+		attrs1 = attrs;
+		while ( *attrs1 ) {
+			sat = at_find(*attrs1);
+			if ( !sat ) {
+				*err = *attrs1;
+				return SLAP_SCHERR_ATTR_NOT_FOUND;
+			}
+
+			if( is_at_operational( sat )) (*op)++;
+
+			if ( at_find_in_list(sat, scr->scr_required) < 0 &&
+				at_find_in_list(sat, scr->scr_allowed) < 0 )
+			{
+				if ( at_append_to_list(sat, &scr->scr_allowed) ) {
+					*err = *attrs1;
+					return SLAP_SCHERR_OUTOFMEM;
+				}
+			} else {
+				*err = *attrs1;
+				return SLAP_SCHERR_CR_BAD_AT;
+			}
+			attrs1++;
+		}
+	}
+	return 0;
+}
+
+static int
+cr_create_precluded(
+    ContentRule		*scr,
+	int			*op,
+    const char		**err )
+{
+    char		**attrs = scr->scr_at_oids_not;
+	char		**attrs1;
+	AttributeType	*sat;
+
+	if ( attrs ) {
+		attrs1 = attrs;
+		while ( *attrs1 ) {
+			sat = at_find(*attrs1);
+			if ( !sat ) {
+				*err = *attrs1;
+				return SLAP_SCHERR_ATTR_NOT_FOUND;
+			}
+
+			if( is_at_operational( sat )) (*op)++;
+
+			/* FIXME: should also make sure attribute type is not
+				a required attribute of the structural class or
+				any auxiliary class */
+			if ( at_find_in_list(sat, scr->scr_required) < 0 &&
+				at_find_in_list(sat, scr->scr_allowed) < 0 &&
+				at_find_in_list(sat, scr->scr_precluded) < 0 )
+			{
+				if ( at_append_to_list(sat, &scr->scr_precluded) ) {
+					*err = *attrs1;
+					return SLAP_SCHERR_OUTOFMEM;
+				}
+			} else {
+				*err = *attrs1;
+				return SLAP_SCHERR_CR_BAD_AT;
+			}
+			attrs1++;
+		}
+	}
+	return 0;
+}
+
+int
+cr_add(
+    LDAPContentRule	*cr,
+	int user,
+    const char		**err
+)
+{
+	ContentRule	*scr;
+	int		code;
+	int		op = 0;
+
+	if ( cr->cr_names != NULL ) {
+		int i;
+
+		for( i=0; cr->cr_names[i]; i++ ) {
+			if( !slap_valid_descr( cr->cr_names[i] ) ) {
+				return SLAP_SCHERR_BAD_DESCR;
+			}
+		}
+	}
+
+	if ( !OID_LEADCHAR( cr->cr_oid[0] )) {
+		/* Expand OID macros */
+		char *oid = oidm_find( cr->cr_oid );
+		if ( !oid ) {
+			*err = cr->cr_oid;
+			return SLAP_SCHERR_OIDM;
+		}
+		if ( oid != cr->cr_oid ) {
+			ldap_memfree( cr->cr_oid );
+			cr->cr_oid = oid;
+		}
+	}
+
+	scr = (ContentRule *) ch_calloc( 1, sizeof(ContentRule) );
+	AC_MEMCPY( &scr->scr_crule, cr, sizeof(LDAPContentRule) );
+
+	scr->scr_sclass = oc_find(cr->cr_oid);
+	if ( !scr->scr_sclass ) {
+		*err = cr->cr_oid;
+		return SLAP_SCHERR_CLASS_NOT_FOUND;
+	}
+
+	/* check object class usage */
+	if( scr->scr_sclass->soc_kind != LDAP_SCHEMA_STRUCTURAL )
+	{
+		*err = cr->cr_oid;
+		return SLAP_SCHERR_CR_BAD_STRUCT;
+	}
+
+	if( scr->scr_sclass->soc_flags & SLAP_OC_OPERATIONAL ) op++;
+
+	code = cr_add_auxiliaries( scr, &op, err );
+	if ( code != 0 ) return code;
+
+	code = cr_create_required( scr, &op, err );
+	if ( code != 0 ) return code;
+
+	code = cr_create_allowed( scr, &op, err );
+	if ( code != 0 ) return code;
+
+	code = cr_create_precluded( scr, &op, err );
+	if ( code != 0 ) return code;
+
+	if( user && op ) return SLAP_SCHERR_CR_BAD_AUX;
+
+	code = cr_insert(scr,err);
+	return code;
+}
+
+#endif
+
+int
+cr_schema_info( Entry *e )
+{
+#ifdef SLAP_EXTENDED_SCHEMA
+	struct berval	vals[2];
+	ContentRule	*cr;
+
+	AttributeDescription *ad_ditContentRules = slap_schema.si_ad_ditContentRules;
+
+	vals[1].bv_val = NULL;
+
+	for ( cr = cr_list; cr; cr = cr->scr_next ) {
+		if ( ldap_contentrule2bv( &cr->scr_crule, vals ) == NULL ) {
+			return -1;
+		}
+
+#if 0
+		if( cr->scr_flags & SLAP_CR_HIDE ) continue;
+#endif
+
+#if 0
+		Debug( LDAP_DEBUG_TRACE, "Merging cr [%ld] %s\n",
+	       (long) vals[0].bv_len, vals[0].bv_val, 0 );
+#endif
+		attr_merge( e, ad_ditContentRules, vals );
+		ldap_memfree( vals[0].bv_val );
+	}
+#endif
+	return 0;
+}
diff --git a/servers/slapd/libslapd.dsp b/servers/slapd/libslapd.dsp
index 41614b649119c930c80c6244fef22116756e1592..9920e800dfb6c0689dbd0e2f86c58580e27fb643 100644
--- a/servers/slapd/libslapd.dsp
+++ b/servers/slapd/libslapd.dsp
@@ -192,6 +192,10 @@ SOURCE=.\controls.c
 # End Source File
 # Begin Source File
 
+SOURCE=.\cr.c
+# End Source File
+# Begin Source File
+
 SOURCE=.\daemon.c
 # End Source File
 # Begin Source File
diff --git a/servers/slapd/oc.c b/servers/slapd/oc.c
index 16a64ebf514af8e002b31686b97608263676b61f..eeceeb2ac69bf3756cc19d64f447de0790203a3e 100644
--- a/servers/slapd/oc.c
+++ b/servers/slapd/oc.c
@@ -465,31 +465,6 @@ oc_add(
 	return code;
 }
 
-#ifdef LDAP_DEBUG
-static void
-oc_print( ObjectClass *oc )
-{
-	int	i;
-	const char *mid;
-
-	printf( "objectclass %s\n", ldap_objectclass2name( &oc->soc_oclass ) );
-	if ( oc->soc_required != NULL ) {
-		mid = "\trequires ";
-		for ( i = 0; oc->soc_required[i] != NULL; i++, mid = "," )
-			printf( "%s%s", mid,
-			        ldap_attributetype2name( &oc->soc_required[i]->sat_atype ) );
-		printf( "\n" );
-	}
-	if ( oc->soc_allowed != NULL ) {
-		mid = "\tallows ";
-		for ( i = 0; oc->soc_allowed[i] != NULL; i++, mid = "," )
-			printf( "%s%s", mid,
-			        ldap_attributetype2name( &oc->soc_allowed[i]->sat_atype ) );
-		printf( "\n" );
-	}
-}
-#endif
-
 int
 oc_schema_info( Entry *e )
 {
@@ -501,12 +476,12 @@ oc_schema_info( Entry *e )
 	vals[1].bv_val = NULL;
 
 	for ( oc = oc_list; oc; oc = oc->soc_next ) {
+		if( oc->soc_flags & SLAP_OC_HIDE ) continue;
+
 		if ( ldap_objectclass2bv( &oc->soc_oclass, vals ) == NULL ) {
 			return -1;
 		}
 
-		if( oc->soc_flags & SLAP_OC_HIDE ) continue;
-
 #if 0
 		Debug( LDAP_DEBUG_TRACE, "Merging oc [%ld] %s\n",
 	       (long) vals[0].bv_len, vals[0].bv_val, 0 );
diff --git a/servers/slapd/proto-slap.h b/servers/slapd/proto-slap.h
index 74f2b2655b452d6d5ca8668efd75d69b4f49fb42..d7a963ed1b5d193ab3aa98a58d810a1e1b5a9bf0 100644
--- a/servers/slapd/proto-slap.h
+++ b/servers/slapd/proto-slap.h
@@ -312,6 +312,22 @@ LDAP_SLAPD_F (void) connection_done LDAP_P((Connection *));
 
 LDAP_SLAPD_F (void) connection2anonymous LDAP_P((Connection *));
 
+/*
+ * cr.c
+ */
+LDAP_SLAPD_F (int) cr_schema_info( Entry *e );
+
+LDAP_SLAPD_F (int) cr_add LDAP_P((
+	LDAPContentRule *oc,
+	int user,
+	const char **err));
+LDAP_SLAPD_F (void) cr_destroy LDAP_P(( void ));
+
+LDAP_SLAPD_F (ContentRule *) cr_find LDAP_P((
+	const char *crname));
+LDAP_SLAPD_F (ContentRule *) cr_bvfind LDAP_P((
+	struct berval *crname));
+
 /*
  * daemon.c
  */
@@ -954,8 +970,8 @@ LDAP_SLAPD_F (int) slap_schema_check LDAP_P((void));
  */
 LDAP_SLAPD_F( int ) slap_valid_descr( const char * );
 
-LDAP_SLAPD_F (int) parse_oc_old LDAP_P((
-	Backend *be, const char *fname, int lineno, int argc, char **argv ));
+LDAP_SLAPD_F (int) parse_cr LDAP_P((
+	const char *fname, int lineno, char *line, char **argv ));
 LDAP_SLAPD_F (int) parse_oc LDAP_P((
 	const char *fname, int lineno, char *line, char **argv ));
 LDAP_SLAPD_F (int) parse_at LDAP_P((
diff --git a/servers/slapd/schema.c b/servers/slapd/schema.c
index bc218fcca8dc71afa47c3549d8fa4a6fba9607d7..c9d4436e8bc12f7da86f7cb4b8b8801cc9d2523e 100644
--- a/servers/slapd/schema.c
+++ b/servers/slapd/schema.c
@@ -115,7 +115,8 @@ schema_info( Entry **entry, const char **text )
 		|| mr_schema_info( e )
 		|| mru_schema_info( e )
 		|| at_schema_info( e )
-		|| oc_schema_info( e ) )
+		|| oc_schema_info( e )
+		|| cr_schema_info( e ) )
 	{
 		/* Out of memory, do something about it */
 		entry_free( e );
diff --git a/servers/slapd/schemaparse.c b/servers/slapd/schemaparse.c
index cb5fca8f82d05f7f792ed66f1593ad4f05594dfc..d0aca20d90b8d6f7d89035e1328f587714387d4e 100644
--- a/servers/slapd/schemaparse.c
+++ b/servers/slapd/schemaparse.c
@@ -42,7 +42,11 @@ static char *const err2text[] = {
 	"OID or name required",
 	"Qualifier not supported",
 	"Invalid NAME",
-	"OID could not be expanded"
+	"OID could not be expanded",
+	"Duplicate Content Rule",
+	"Content Rule not for STRUCTURAL object class",
+	"Content Rule AUX contains non-AUXILIARY object class"
+	"Content Rule attribute type list contains duplicate"
 };
 
 char *
@@ -92,6 +96,64 @@ dscompare(const char *s1, const char *s2, char delim)
 	return 0;
 }
 
+#ifdef SLAP_EXTENDED_SCHEMA
+
+static void
+cr_usage( void )
+{
+	fprintf( stderr,
+		"DITContentRuleDescription = \"(\" whsp\n"
+		"  numericoid whsp       ; StructuralObjectClass identifier\n"
+		"  [ \"NAME\" qdescrs ]\n"
+		"  [ \"DESC\" qdstring ]\n"
+		"  [ \"OBSOLETE\" whsp ]\n"
+		"  [ \"AUX\" oids ]      ; Auxiliary ObjectClasses\n"
+		"  [ \"MUST\" oids ]     ; AttributeTypes\n"
+		"  [ \"MAY\" oids ]      ; AttributeTypes\n"
+		"  [ \"NOT\" oids ]      ; AttributeTypes\n"
+		"  whsp \")\"\n" );
+}
+
+int
+parse_cr(
+    const char	*fname,
+    int		lineno,
+    char	*line,
+    char	**argv
+)
+{
+	LDAPContentRule *cr;
+	int		code;
+	const char	*err;
+
+	cr = ldap_str2contentrule(line, &code, &err, LDAP_SCHEMA_ALLOW_ALL );
+	if ( !cr ) {
+		fprintf( stderr, "%s: line %d: %s before %s\n",
+			 fname, lineno, ldap_scherr2str(code), err );
+		cr_usage();
+		return 1;
+	}
+
+	if ( cr->cr_oid == NULL ) {
+		fprintf( stderr,
+			"%s: line %d: Content rule has no OID\n",
+			fname, lineno );
+		cr_usage();
+		return 1;
+	}
+
+	code = cr_add(cr,1,&err);
+	if ( code ) {
+		fprintf( stderr, "%s: line %d: %s: \"%s\"\n",
+			 fname, lineno, scherr2str(code), err);
+		return 1;
+	}
+
+	ldap_memfree(cr);
+	return 0;
+}
+
+#endif
 
 int
 parse_oc(
@@ -149,7 +211,6 @@ oc_usage( void )
 		"  whsp \")\"\n" );
 }
 
-
 static void
 at_usage( void )
 {
diff --git a/servers/slapd/slap.h b/servers/slapd/slap.h
index 329a10868873d9ac6b86a0964bf2170a6145657a..ea0d985d3a6e1e7fcd5c3ff1f312bf488c5902ee 100644
--- a/servers/slapd/slap.h
+++ b/servers/slapd/slap.h
@@ -212,9 +212,11 @@ typedef struct slap_ssf_set {
 #define SLAP_INDEX_SUBSTR_FINAL_PREFIX '$'
 #define SLAP_INDEX_CONT_PREFIX		'.'		/* prefix for continuation keys */
 
-#define SLAP_SYNTAX_MATCHINGRULES_OID	"1.3.6.1.4.1.1466.115.121.1.30"
-#define SLAP_SYNTAX_ATTRIBUTETYPES_OID	"1.3.6.1.4.1.1466.115.121.1.3"
-#define SLAP_SYNTAX_OBJECTCLASSES_OID	"1.3.6.1.4.1.1466.115.121.1.37"
+#define SLAP_SYNTAX_MATCHINGRULES_OID	 "1.3.6.1.4.1.1466.115.121.1.30"
+#define SLAP_SYNTAX_ATTRIBUTETYPES_OID	 "1.3.6.1.4.1.1466.115.121.1.3"
+#define SLAP_SYNTAX_OBJECTCLASSES_OID	 "1.3.6.1.4.1.1466.115.121.1.37"
+#define SLAP_SYNTAX_MATCHINGRULEUSES_OID "1.3.6.1.4.1.1466.115.121.1.31"
+#define SLAP_SYNTAX_CONTENTRULE_OID		 "1.3.6.1.4.1.1466.115.121.1.16"
 
 #ifdef LDAP_CLIENT_UPDATE
 #define LCUP_COOKIE_OID "1.3.6.1.4.1.4203.666.10.1"
@@ -243,7 +245,11 @@ typedef struct slap_ssf_set {
 #define SLAP_SCHERR_NOT_SUPPORTED		18
 #define SLAP_SCHERR_BAD_DESCR			19
 #define SLAP_SCHERR_OIDM				20
-#define SLAP_SCHERR_LAST				SLAP_SCHERR_OIDM
+#define SLAP_SCHERR_CR_DUP				21
+#define SLAP_SCHERR_CR_BAD_STRUCT		22
+#define SLAP_SCHERR_CR_BAD_AUX			23
+#define SLAP_SCHERR_CR_BAD_AT			24
+#define SLAP_SCHERR_LAST				SLAP_SCHERR_CR_BAD_AT
 
 typedef union slap_sockaddr {
 	struct sockaddr sa_addr;
@@ -591,7 +597,6 @@ typedef struct slap_object_class {
 #define SLAP_OC_OPERATIONAL	0x4000
 #define SLAP_OC_HIDE		0x8000
 
-#ifdef LDAP_EXTENDED_SCHEMA
 /*
  * DIT content rule
  */
@@ -602,16 +607,17 @@ typedef struct slap_content_rule {
 	AttributeType		**scr_required;		/* optional */
 	AttributeType		**scr_allowed;		/* optional */
 	AttributeType		**scr_precluded;	/* optional */
-#define scr_oid			scr_crule.cr_oid
-#define scr_names		scr_crule.cr_names
-#define scr_desc		scr_crule.cr_desc
-#define scr_obsolete		soc_oclass.cr_obsolete
-#define scr_cr_oids_aux		soc_oclass.cr_oc_oids_aux
-#define scr_cr_oids_must	soc_oclass.cr_at_oids_must
-#define scr_cr_oids_may		soc_oclass.cr_at_oids_may
-#define scr_cr_oids_not		soc_oclass.cr_at_oids_not
+#define scr_oid				scr_crule.cr_oid
+#define scr_names			scr_crule.cr_names
+#define scr_desc			scr_crule.cr_desc
+#define scr_obsolete		scr_crule.cr_obsolete
+#define scr_oc_oids_aux		scr_crule.cr_oc_oids_aux
+#define scr_at_oids_must	scr_crule.cr_at_oids_must
+#define scr_at_oids_may		scr_crule.cr_at_oids_may
+#define scr_at_oids_not		scr_crule.cr_at_oids_not
+
+	struct slap_content_rule *scr_next;
 } ContentRule;
-#endif
 
 /*
  * represents a recognized attribute description ( type + options )