From e9c2895472d41da41fee1ffb049195b190f6adbc Mon Sep 17 00:00:00 2001
From: Kurt Zeilenga <kurt@openldap.org>
Date: Wed, 7 Jul 1999 18:51:39 +0000
Subject: [PATCH] Add support for unsolicited notifications.

---
 servers/slapd/Makefile.in          |   4 +-
 servers/slapd/abandon.c            |   4 +-
 servers/slapd/acl.c                |  17 ++--
 servers/slapd/add.c                |  26 +++---
 servers/slapd/ava.c                |   4 +-
 servers/slapd/back-bdb2/modify.c   |   4 +-
 servers/slapd/back-bdb2/search.c   |   2 +-
 servers/slapd/back-ldap/search.c   |   2 +-
 servers/slapd/back-ldbm/modify.c   |   4 +-
 servers/slapd/back-ldbm/search.c   |   2 +-
 servers/slapd/back-passwd/search.c |   4 +-
 servers/slapd/back-perl/search.c   |   3 +-
 servers/slapd/back-shell/result.c  |   2 +-
 servers/slapd/back-tcl/tcl_util.c  |   4 +-
 servers/slapd/bind.c               |  19 +++--
 servers/slapd/compare.c            |   5 +-
 servers/slapd/configinfo.c         |   2 +-
 servers/slapd/connection.c         |  13 ++-
 servers/slapd/controls.c           |  19 ++++-
 servers/slapd/delete.c             |   5 +-
 servers/slapd/filter.c             |  53 +++++++-----
 servers/slapd/libslapd.dsp         |   4 +
 servers/slapd/modify.c             |  23 +++---
 servers/slapd/modrdn.c             |  25 +++---
 servers/slapd/monitor.c            |   6 +-
 servers/slapd/proto-slap.h         |  26 ++++--
 servers/slapd/result.c             | 124 +++++++++++++++++++++++------
 servers/slapd/root_dse.c           |  42 ++++------
 servers/slapd/schema.c             |  81 ++++++++++++++++---
 servers/slapd/search.c             |  21 +++--
 servers/slapd/tools/mimic.c        |   3 +-
 31 files changed, 372 insertions(+), 181 deletions(-)

diff --git a/servers/slapd/Makefile.in b/servers/slapd/Makefile.in
index 2c251e85a0..f3c87f6da7 100644
--- a/servers/slapd/Makefile.in
+++ b/servers/slapd/Makefile.in
@@ -9,7 +9,7 @@ SRCS	= main.c daemon.c connection.c search.c filter.c add.c charray.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 \
 		phonetic.c acl.c str2filter.c aclparse.c init.c user.c \
-		repl.c lock.c controls.c \
+		repl.c lock.c controls.c extended.c \
 		suffixalias.c schema.c schemaparse.c monitor.c configinfo.c \
 		root_dse.c module.c
 OBJS	= main.o daemon.o connection.o search.o filter.o add.o charray.o \
@@ -17,7 +17,7 @@ OBJS	= main.o daemon.o connection.o search.o filter.o add.o charray.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 \
 		phonetic.o acl.o str2filter.o aclparse.o init.o user.o \
-		repl.o lock.o controls.o \
+		repl.o lock.o controls.o extended.o \
 		suffixalias.o schema.o schemaparse.o monitor.o configinfo.o \
 		root_dse.o module.o
 
diff --git a/servers/slapd/abandon.c b/servers/slapd/abandon.c
index d0eb0244e8..bac282e2b3 100644
--- a/servers/slapd/abandon.c
+++ b/servers/slapd/abandon.c
@@ -40,7 +40,9 @@ do_abandon(
 
 	if ( ber_scanf( op->o_ber, "i", &id ) == LBER_ERROR ) {
 		Debug( LDAP_DEBUG_ANY, "do_abandon: ber_scanf failed\n", 0, 0 ,0 );
-		return LDAP_PROTOCOL_ERROR;
+		send_ldap_disconnect( conn, op,
+			LDAP_PROTOCOL_ERROR, "decoding error" );
+		return -1;
 	}
 
 	if( (rc = get_ctrls( conn, op, 0 )) != LDAP_SUCCESS ) {
diff --git a/servers/slapd/acl.c b/servers/slapd/acl.c
index cfc8e8923c..6dcdcd5087 100644
--- a/servers/slapd/acl.c
+++ b/servers/slapd/acl.c
@@ -55,7 +55,7 @@ access_allowed(
 		e->e_dn, attr, 0 );
 
 	/* the lastmod attributes are ignored by ACL checking */
-	if ( oc_check_operational( attr ) ) {
+	if ( oc_check_no_usermod_attr( attr ) ) {
  		Debug( LDAP_DEBUG_ACL, "Operational attribute: %s access allowed\n",
 			attr, 0, 0 );
 		return(1);
@@ -212,7 +212,6 @@ acl_access_allowed(
 )
 {
 	int		i;
-	char		*odn;
 	struct access	*b;
 	Attribute	*at;
 	struct berval	bv;
@@ -244,11 +243,9 @@ acl_access_allowed(
 		return( default_access >= access );
 	}
 
-	odn = op->o_ndn;
-
-	if ( odn != NULL ) {
-		bv.bv_val = odn;
-		bv.bv_len = strlen( odn );
+	if ( op->o_ndn != NULL ) {
+		bv.bv_val = op->o_ndn;
+		bv.bv_len = strlen( bv.bv_val );
 	}
 
 	for ( i = 1, b = a->acl_access; b != NULL; b = b->a_next, i++ ) {
@@ -282,7 +279,7 @@ acl_access_allowed(
 					return ACL_GRANT(b->a_access, access );
 				}
 			} else {
-				if ( regex_matches( b->a_dnpat, odn, edn, matches ) ) {
+				if ( regex_matches( b->a_dnpat, op->o_ndn, edn, matches ) ) {
 					Debug( LDAP_DEBUG_ACL,
 				    "<= acl_access_allowed: matched by clause #%d access %s\n",
 				    i, ACL_GRANT(b->a_access, access)
@@ -365,7 +362,7 @@ acl_access_allowed(
 			string_expand(buf, sizeof(buf), b->a_group, edn, matches);
 			(void) dn_normalize_case(buf);
 
-			if (backend_group(be, e, buf, odn,
+			if (backend_group(be, e, buf, op->o_ndn,
 				b->a_group_oc, b->a_group_at) == 0)
 			{
 				Debug( LDAP_DEBUG_ACL,
@@ -408,7 +405,7 @@ acl_check_modlist(
 		regmatch_t       matches[MAXREMATCHES];
 
 		/* the lastmod attributes are ignored by ACL checking */
-		if ( oc_check_operational( mlist->ml_type ) ) {
+		if ( oc_check_no_usermod_attr( mlist->ml_type ) ) {
 			Debug( LDAP_DEBUG_ACL, "Operational attribute: %s access allowed\n",
 				mlist->ml_type, 0, 0 );
 			continue;
diff --git a/servers/slapd/add.c b/servers/slapd/add.c
index 5e85be3aa7..e2438a5d3b 100644
--- a/servers/slapd/add.c
+++ b/servers/slapd/add.c
@@ -57,9 +57,9 @@ do_add( Connection *conn, Operation *op )
 	/* get the name */
 	if ( ber_scanf( ber, "{a", /*}*/ &dn ) == LBER_ERROR ) {
 		Debug( LDAP_DEBUG_ANY, "do_add: ber_scanf failed\n", 0, 0, 0 );
-		send_ldap_result( conn, op, LDAP_PROTOCOL_ERROR, NULL,
-		    "decoding error" );
-		return LDAP_PROTOCOL_ERROR;
+		send_ldap_disconnect( conn, op,
+			LDAP_PROTOCOL_ERROR, "decoding error" );
+		return -1;
 	}
 
 	e = (Entry *) ch_calloc( 1, sizeof(Entry) );
@@ -80,17 +80,17 @@ do_add( Connection *conn, Operation *op )
 		struct berval	**vals;
 
 		if ( ber_scanf( ber, "{a{V}}", &type, &vals ) == LBER_ERROR ) {
-			send_ldap_result( conn, op, LDAP_PROTOCOL_ERROR,
-			    NULL, "decoding error" );
+			send_ldap_disconnect( conn, op,
+				LDAP_PROTOCOL_ERROR, "decoding error" );
 			entry_free( e );
-			return LDAP_PROTOCOL_ERROR;
+			return -1;
 		}
 
 		if ( vals == NULL ) {
 			Debug( LDAP_DEBUG_ANY, "no values for type %s\n", type,
 			    0, 0 );
-			send_ldap_result( conn, op, LDAP_PROTOCOL_ERROR, NULL,
-			    NULL );
+			send_ldap_result( conn, op,
+				LDAP_PROTOCOL_ERROR, NULL, "no values for type" );
 			free( type );
 			entry_free( e );
 			return LDAP_PROTOCOL_ERROR;
@@ -105,9 +105,9 @@ do_add( Connection *conn, Operation *op )
 	if ( ber_scanf( ber, /*{*/ "}") == LBER_ERROR ) {
 		entry_free( e );
 		Debug( LDAP_DEBUG_ANY, "do_add: ber_scanf failed\n", 0, 0, 0 );
-		send_ldap_result( conn, op, LDAP_PROTOCOL_ERROR, NULL,
-		    "decoding error" );
-		return LDAP_PROTOCOL_ERROR;
+		send_ldap_disconnect( conn, op,
+			LDAP_PROTOCOL_ERROR, "decoding error" );
+		return -1;
 	}
 
 	if( (rc = get_ctrls( conn, op, 1 )) != LDAP_SUCCESS ) {
@@ -186,7 +186,7 @@ add_created_attrs( Operation *op, Entry *e )
 
 	/* remove any attempts by the user to add these attrs */
 	for ( a = &e->e_attrs; *a != NULL; a = next ) {
-		if ( oc_check_operational( (*a)->a_type ) ) {
+		if ( oc_check_no_usermod_attr( (*a)->a_type ) ) {
 			tmp = *a;
 			*a = (*a)->a_next;
 			attr_free( tmp );
@@ -197,7 +197,7 @@ add_created_attrs( Operation *op, Entry *e )
 	}
 
 	if ( op->o_dn == NULL || op->o_dn[0] == '\0' ) {
-		bv.bv_val = "NULLDN";
+		bv.bv_val = "<anonymous>";
 		bv.bv_len = strlen( bv.bv_val );
 	} else {
 		bv.bv_val = op->o_dn;
diff --git a/servers/slapd/ava.c b/servers/slapd/ava.c
index 1e10339cb2..33a70723dd 100644
--- a/servers/slapd/ava.c
+++ b/servers/slapd/ava.c
@@ -18,12 +18,12 @@ get_ava(
 	if ( ber_scanf( ber, "{ao}", &ava->ava_type, &ava->ava_value )
 	    == LBER_ERROR ) {
 		Debug( LDAP_DEBUG_ANY, "  get_ava ber_scanf\n", 0, 0, 0 );
-		return( LDAP_PROTOCOL_ERROR );
+		return( -1 );
 	}
 	attr_normalize( ava->ava_type );
 	value_normalize( ava->ava_value.bv_val, attr_syntax( ava->ava_type ) );
 
-	return( 0 );
+	return( LDAP_SUCCESS );
 }
 
 void
diff --git a/servers/slapd/back-bdb2/modify.c b/servers/slapd/back-bdb2/modify.c
index 8f137608f3..9e891e6a78 100644
--- a/servers/slapd/back-bdb2/modify.c
+++ b/servers/slapd/back-bdb2/modify.c
@@ -33,9 +33,9 @@ add_lastmods( Operation *op, LDAPModList **modlist )
 
 	/* remove any attempts by the user to modify these attrs */
 	for ( m = modlist; *m != NULL; m = &(*m)->ml_next ) {
-            if ( oc_check_operational( (*m)->ml_type ) ) {
+            if ( oc_check_no_usermod_attr( (*m)->ml_type ) ) {
                 Debug( LDAP_DEBUG_TRACE,
-					"add_lastmods: found operational attr: %s\n",
+					"add_lastmods: found no user mod attr: %s\n",
 					(*m)->ml_type, 0, 0 );
                 tmp = *m;
                 *m = (*m)->ml_next;
diff --git a/servers/slapd/back-bdb2/search.c b/servers/slapd/back-bdb2/search.c
index 3090e89cca..28fed4863a 100644
--- a/servers/slapd/back-bdb2/search.c
+++ b/servers/slapd/back-bdb2/search.c
@@ -265,7 +265,7 @@ bdb2i_back_search_internal(
 
 					if (e) {
 						switch ( send_search_entry( be, conn, op, e,
-							attrs, attrsonly ) ) {
+							attrs, attrsonly, 0 ) ) {
 						case 0:		/* entry sent ok */
 							nentries++;
 							break;
diff --git a/servers/slapd/back-ldap/search.c b/servers/slapd/back-ldap/search.c
index e89cec69ae..6145a0704d 100644
--- a/servers/slapd/back-ldap/search.c
+++ b/servers/slapd/back-ldap/search.c
@@ -158,7 +158,7 @@ ldap_send_entry(
 		if (!attr->a_vals)
 			attr->a_vals = &dummy;
 	}
-	send_search_entry( be, lc->conn, op, &ent, attrs, attrsonly );
+	send_search_entry( be, lc->conn, op, &ent, attrs, attrsonly, 0 );
 	for (;ent.e_attrs;) {
 		attr=ent.e_attrs;
 		ent.e_attrs = attr->a_next;
diff --git a/servers/slapd/back-ldbm/modify.c b/servers/slapd/back-ldbm/modify.c
index 15c2c72896..9ae8220543 100644
--- a/servers/slapd/back-ldbm/modify.c
+++ b/servers/slapd/back-ldbm/modify.c
@@ -33,9 +33,9 @@ add_lastmods( Operation *op, LDAPModList **modlist )
 
 	/* remove any attempts by the user to modify these attrs */
 	for ( m = modlist; *m != NULL; m = &(*m)->ml_next ) {
-            if ( oc_check_operational( (*m)->ml_type ) ) {
+            if ( oc_check_no_usermod_attr( (*m)->ml_type ) ) {
                 Debug( LDAP_DEBUG_TRACE,
-					"add_lastmods: found operational attr: %s\n",
+					"add_lastmods: found no user mod attr: %s\n",
 					(*m)->ml_type, 0, 0 );
                 tmp = *m;
                 *m = (*m)->ml_next;
diff --git a/servers/slapd/back-ldbm/search.c b/servers/slapd/back-ldbm/search.c
index 80b184893c..7ab4f5f761 100644
--- a/servers/slapd/back-ldbm/search.c
+++ b/servers/slapd/back-ldbm/search.c
@@ -264,7 +264,7 @@ ldbm_back_search(
 					}
 					if (e) {
 						switch ( send_search_entry( be, conn, op, e,
-							attrs, attrsonly ) ) {
+							attrs, attrsonly, 0 ) ) {
 						case 0:		/* entry sent ok */
 							nentries++;
 							break;
diff --git a/servers/slapd/back-passwd/search.c b/servers/slapd/back-passwd/search.c
index 859648e9ed..bd61a8070c 100644
--- a/servers/slapd/back-passwd/search.c
+++ b/servers/slapd/back-passwd/search.c
@@ -139,7 +139,7 @@ passwd_back_search(
 						return( 0 );
 					}
 
-					send_search_entry( be, conn, op, e, attrs, attrsonly );
+					send_search_entry( be, conn, op, e, attrs, attrsonly, 0 );
 					sent++;
 				}
 
@@ -176,7 +176,7 @@ passwd_back_search(
 		e = pw2entry( be, pw, rdn );
 
 		if ( test_filter( be, conn, op, e, filter ) == 0 ) {
-			send_search_entry( be, conn, op, e, attrs, attrsonly );
+			send_search_entry( be, conn, op, e, attrs, attrsonly, 0 );
 			sent++;
 		}
 
diff --git a/servers/slapd/back-perl/search.c b/servers/slapd/back-perl/search.c
index 693fcd89fc..7117f4b985 100644
--- a/servers/slapd/back-perl/search.c
+++ b/servers/slapd/back-perl/search.c
@@ -91,7 +91,8 @@ perl_back_search(
 							   op,
 							   e,
 							   attrs,
-							   attrsonly );
+							   attrsonly,
+							   0 );
 							 
 					entry_free( e );
 				}
diff --git a/servers/slapd/back-shell/result.c b/servers/slapd/back-shell/result.c
index daa826775d..dfb479e36c 100644
--- a/servers/slapd/back-shell/result.c
+++ b/servers/slapd/back-shell/result.c
@@ -59,7 +59,7 @@ read_and_send_results(
 				    buf, 0, 0 );
 			} else {
 				send_search_entry( be, conn, op, e, attrs,
-				    attrsonly );
+				    attrsonly, 0 );
 				entry_free( e );
 			}
 
diff --git a/servers/slapd/back-tcl/tcl_util.c b/servers/slapd/back-tcl/tcl_util.c
index 3163abcdbc..cdff332fc3 100644
--- a/servers/slapd/back-tcl/tcl_util.c
+++ b/servers/slapd/back-tcl/tcl_util.c
@@ -1,6 +1,6 @@
 /* result.c - tcl backend utility functions
  *
- * $Id: tcl_util.c,v 1.4 1999/02/19 06:55:20 bcollins Exp $
+ * $Id: tcl_util.c,v 1.5 1999/02/28 04:55:49 bcollins Exp $
  *
  * Copyright 1999, Ben Collins <bcollins@debian.org>, All rights reserved.
  *
@@ -80,7 +80,7 @@ interp_send_results (
 					buf, 0, 0);
 			} else {
 				send_search_entry (be, conn, op, e, attrs,
-					attrsonly);
+					attrsonly, 0 );
 				entry_free (e);
 			}
 
diff --git a/servers/slapd/bind.c b/servers/slapd/bind.c
index 9586812e3e..4ab5de594a 100644
--- a/servers/slapd/bind.c
+++ b/servers/slapd/bind.c
@@ -22,7 +22,6 @@
 #include "slap.h"
 
 char *supportedSASLMechanisms[] = {
-	"X-CRAM-MD5",
 	"X-DIGEST-MD5",
 	NULL
 };
@@ -103,8 +102,9 @@ do_bind(
 
 	if ( tag == LBER_ERROR ) {
 		Debug( LDAP_DEBUG_ANY, "bind: ber_scanf failed\n", 0, 0, 0 );
-		send_ldap_result( conn, op, rc = LDAP_PROTOCOL_ERROR, NULL,
-		    "decoding error" );
+		send_ldap_disconnect( conn, op,
+			LDAP_PROTOCOL_ERROR, "decoding error" );
+		rc = -1;
 		goto cleanup;
 	}
 
@@ -131,8 +131,10 @@ do_bind(
 	}
 
 	if ( tag == LBER_ERROR ) {
-		send_ldap_result( conn, op, rc = LDAP_PROTOCOL_ERROR, NULL,
+		send_ldap_disconnect( conn, op,
+			LDAP_PROTOCOL_ERROR,
     		"decoding error" );
+		rc = -1;
 		goto cleanup;
 	}
 
@@ -156,8 +158,8 @@ do_bind(
 
 	if ( version < LDAP_VERSION_MIN || version > LDAP_VERSION_MAX ) {
 		Debug( LDAP_DEBUG_ANY, "unknown version %d\n", version, 0, 0 );
-		send_ldap_result( conn, op, rc = LDAP_PROTOCOL_ERROR, NULL,
-		    "version not supported" );
+		send_ldap_result( conn, op,
+			rc = LDAP_PROTOCOL_ERROR, NULL, "version not supported" );
 		goto cleanup;
 	}
 
@@ -165,8 +167,9 @@ do_bind(
 		if ( version < LDAP_VERSION3 ) {
 			Debug( LDAP_DEBUG_ANY, "do_bind: sasl with LDAPv%d\n",
 				version, 0, 0 );
-			send_ldap_result( conn, op, rc = LDAP_PROTOCOL_ERROR, NULL,
-				"sasl bind requires LDAPv3" );
+			send_ldap_disconnect( conn, op,
+				LDAP_PROTOCOL_ERROR, "sasl bind requires LDAPv3" );
+			rc = -1;
 			goto cleanup;
 		}
 
diff --git a/servers/slapd/compare.c b/servers/slapd/compare.c
index c07c8dabcb..028a4f44d0 100644
--- a/servers/slapd/compare.c
+++ b/servers/slapd/compare.c
@@ -54,8 +54,9 @@ do_compare(
 	if ( ber_scanf( op->o_ber, "{a{ao}}", &ndn, &ava.ava_type,
 	    &ava.ava_value ) == LBER_ERROR ) {
 		Debug( LDAP_DEBUG_ANY, "ber_scanf failed\n", 0, 0, 0 );
-		send_ldap_result( conn, op, rc = LDAP_PROTOCOL_ERROR, NULL, "" );
-		return rc;
+		send_ldap_disconnect( conn, op,
+			LDAP_PROTOCOL_ERROR, "decoding error" );
+		return -1;
 	}
 
 	if( ( rc = get_ctrls( conn, op, 1 )) != LDAP_SUCCESS ) {
diff --git a/servers/slapd/configinfo.c b/servers/slapd/configinfo.c
index 332826ceb0..ee6d63fd50 100644
--- a/servers/slapd/configinfo.c
+++ b/servers/slapd/configinfo.c
@@ -63,7 +63,7 @@ config_info( Connection *conn, Operation *op )
 		attr_merge( e, "database", vals );
 	}
 
-	send_search_entry( &backends[0], conn, op, e, NULL, 0 );
+	send_search_entry( &backends[0], conn, op, e, NULL, 0, 1 );
 	send_ldap_search_result( conn, op, LDAP_SUCCESS, NULL, NULL, 1 );
 
 	entry_free( e );
diff --git a/servers/slapd/connection.c b/servers/slapd/connection.c
index dbae86bd30..e8defc9674 100644
--- a/servers/slapd/connection.c
+++ b/servers/slapd/connection.c
@@ -681,18 +681,22 @@ connection_operation( void *arg_v )
 		rc = do_abandon( conn, arg->co_op );
 		break;
 
-#if 0
 	case LDAP_REQ_EXTENDED:
 		rc = do_extended( conn, arg->co_op );
 		break;
-#endif
 
 	default:
-		Debug( LDAP_DEBUG_ANY, "unknown request 0x%lx\n",
-		    arg->co_op->o_tag, 0, 0 );
+		Debug( LDAP_DEBUG_ANY, "unknown LDAP request 0x%lx\n",
+		    tag, 0, 0 );
+		arg->co_op->o_tag = LBER_ERROR;
+		send_ldap_disconnect( conn, arg->co_op,
+			LDAP_PROTOCOL_ERROR, "unknown LDAP request" );
+		rc = -1;
 		break;
 	}
 
+	if( rc == -1 ) tag = LBER_ERROR;
+
 	ldap_pvt_thread_mutex_lock( &num_ops_mutex );
 	num_ops_completed++;
 	ldap_pvt_thread_mutex_unlock( &num_ops_mutex );
@@ -710,6 +714,7 @@ connection_operation( void *arg_v )
 	arg = NULL;
 
 	switch( tag ) {
+	case LBER_ERROR:
 	case LDAP_REQ_UNBIND:
 		/* c_mutex is locked */
 		connection_closing( conn );
diff --git a/servers/slapd/controls.c b/servers/slapd/controls.c
index a1c70d7b3d..1afb52038a 100644
--- a/servers/slapd/controls.c
+++ b/servers/slapd/controls.c
@@ -32,6 +32,7 @@ int get_ctrls(
 	BerElement *ber = op->o_ber;
 	LDAPControl ***ctrls = &op->o_ctrls;
 	int rc = LDAP_SUCCESS;
+	char *errmsg = NULL;
 
 	len = ber_pvt_ber_remaining(ber);
 
@@ -43,14 +44,16 @@ int get_ctrls(
 
 	if(( tag = ber_peek_tag( ber, &len )) != LDAP_TAG_CONTROLS ) {
 		if( tag == LBER_ERROR ) {
-			rc = LDAP_PROTOCOL_ERROR;
+			rc = -1;
+			errmsg = "unexpected data in PDU";
 		}
 
 		goto return_results;
 	}
 
 	if( op->o_protocol < LDAP_VERSION3 ) {
-		rc = LDAP_PROTOCOL_ERROR;
+		rc = -1;
+		errmsg = "controls require LDAPv3";
 		goto return_results;
 	}
 
@@ -61,6 +64,7 @@ int get_ctrls(
 #if 0
 	if( *ctrls == NULL ) {
 		rc = LDAP_NO_MEMORY;
+		errmsg = "no memory";
 		goto return_results;
 	}
 #endif
@@ -94,6 +98,7 @@ int get_ctrls(
 			*ctrls = NULL;
 
 			rc = LDAP_NO_MEMORY;
+			errmsg = "no memory";
 			goto return_results;
 		}
 #endif
@@ -128,7 +133,8 @@ int get_ctrls(
 		if( tag == LBER_ERROR ) {
 			*ctrls = NULL;
 			ldap_controls_free( tctrls );
-			rc = LDAP_DECODING_ERROR;
+			rc = -1;
+			errmsg = "decoding controls error";
 			goto return_results;
 		}
 
@@ -136,6 +142,7 @@ int get_ctrls(
 			!charray_inlist( supportedControls, tctrl->ldctl_oid ) )
 		{
 			rc = LDAP_UNAVAILABLE_CRITICAL_EXTENSION;
+			errmsg = "critical extension is unavailable ";
 			goto return_results;
 		}
 
@@ -144,7 +151,11 @@ int get_ctrls(
 
 return_results:
 	if( sendres && rc != LDAP_SUCCESS ) {
-		send_ldap_result( conn, op, rc, NULL, NULL );
+		if( rc == -1 ) {
+			send_ldap_disconnect( conn, op, rc, errmsg );
+		} else {
+			send_ldap_result( conn, op, rc, NULL, errmsg );
+		}
 	}
 
 	return rc;
diff --git a/servers/slapd/delete.c b/servers/slapd/delete.c
index 87b3207601..4d0d7fb076 100644
--- a/servers/slapd/delete.c
+++ b/servers/slapd/delete.c
@@ -47,8 +47,9 @@ do_delete(
 
 	if ( ber_scanf( op->o_ber, "a", &ndn ) == LBER_ERROR ) {
 		Debug( LDAP_DEBUG_ANY, "ber_scanf failed\n", 0, 0, 0 );
-		send_ldap_result( conn, op, rc = LDAP_PROTOCOL_ERROR, NULL, "" );
-		return rc;
+		send_ldap_disconnect( conn, op,
+			LDAP_PROTOCOL_ERROR, "decoding error" );
+		return -1;
 	}
 
 	if( ( rc = get_ctrls( conn, op, 1 ) ) != LDAP_SUCCESS ) {
diff --git a/servers/slapd/filter.c b/servers/slapd/filter.c
index 7ea1850916..c1bed43cce 100644
--- a/servers/slapd/filter.c
+++ b/servers/slapd/filter.c
@@ -34,6 +34,7 @@ get_filter( Connection *conn, BerElement *ber, Filter **filt, char **fstr )
 	 *		lessOrEqual	[6]	AttributeValueAssertion,
 	 *		present		[7]	AttributeType,,
 	 *		approxMatch	[8]	AttributeValueAssertion
+	 *		extensibleMatch [9] MatchingRuleAssertion
 	 *	}
 	 *
 	 *	SubstringFilter ::= SEQUENCE {
@@ -44,19 +45,27 @@ get_filter( Connection *conn, BerElement *ber, Filter **filt, char **fstr )
 	 *			final            [2] IA5String
 	 *		}
 	 *	}
+	 *
+     *  MatchingRuleAssertion ::= SEQUENCE {
+     *          matchingRule    [1] MatchingRuleId OPTIONAL,
+     *          type            [2] AttributeDescription OPTIONAL,
+     *          matchValue      [3] AssertionValue,
+     *          dnAttributes    [4] BOOLEAN DEFAULT FALSE
+	 *	}
+	 *
 	 */
 
 	f = (Filter *) ch_malloc( sizeof(Filter) );
 	f->f_next = NULL;
 
-	err = 0;
+	err = LDAP_SUCCESS;
 	*fstr = NULL;
 	f->f_choice = ber_peek_tag( ber, &len );
 
 	switch ( f->f_choice ) {
 	case LDAP_FILTER_EQUALITY:
 		Debug( LDAP_DEBUG_FILTER, "EQUALITY\n", 0, 0, 0 );
-		if ( (err = get_ava( ber, &f->f_ava )) == 0 ) {
+		if ( (err = get_ava( ber, &f->f_ava )) == LDAP_SUCCESS ) {
 			*fstr = ch_malloc(4 + strlen( f->f_avtype ) +
 			    f->f_avvalue.bv_len);
 			sprintf( *fstr, "(%s=%s)", f->f_avtype,
@@ -71,7 +80,7 @@ get_filter( Connection *conn, BerElement *ber, Filter **filt, char **fstr )
 
 	case LDAP_FILTER_GE:
 		Debug( LDAP_DEBUG_FILTER, "GE\n", 0, 0, 0 );
-		if ( (err = get_ava( ber, &f->f_ava )) == 0 ) {
+		if ( (err = get_ava( ber, &f->f_ava )) == LDAP_SUCCESS ) {
 			*fstr = ch_malloc(5 + strlen( f->f_avtype ) +
 			    f->f_avvalue.bv_len);
 			sprintf( *fstr, "(%s>=%s)", f->f_avtype,
@@ -81,7 +90,7 @@ get_filter( Connection *conn, BerElement *ber, Filter **filt, char **fstr )
 
 	case LDAP_FILTER_LE:
 		Debug( LDAP_DEBUG_FILTER, "LE\n", 0, 0, 0 );
-		if ( (err = get_ava( ber, &f->f_ava )) == 0 ) {
+		if ( (err = get_ava( ber, &f->f_ava )) == LDAP_SUCCESS ) {
 			*fstr = ch_malloc(5 + strlen( f->f_avtype ) +
 			    f->f_avvalue.bv_len);
 			sprintf( *fstr, "(%s<=%s)", f->f_avtype,
@@ -92,7 +101,7 @@ get_filter( Connection *conn, BerElement *ber, Filter **filt, char **fstr )
 	case LDAP_FILTER_PRESENT:
 		Debug( LDAP_DEBUG_FILTER, "PRESENT\n", 0, 0, 0 );
 		if ( ber_scanf( ber, "a", &f->f_type ) == LBER_ERROR ) {
-			err = LDAP_PROTOCOL_ERROR;
+			err = -1;
 		} else {
 			err = LDAP_SUCCESS;
 			attr_normalize( f->f_type );
@@ -103,7 +112,7 @@ get_filter( Connection *conn, BerElement *ber, Filter **filt, char **fstr )
 
 	case LDAP_FILTER_APPROX:
 		Debug( LDAP_DEBUG_FILTER, "APPROX\n", 0, 0, 0 );
-		if ( (err = get_ava( ber, &f->f_ava )) == 0 ) {
+		if ( (err = get_ava( ber, &f->f_ava )) == LDAP_SUCCESS ) {
 			*fstr = ch_malloc(5 + strlen( f->f_avtype ) +
 			    f->f_avvalue.bv_len);
 			sprintf( *fstr, "(%s~=%s)", f->f_avtype,
@@ -114,7 +123,7 @@ get_filter( Connection *conn, BerElement *ber, Filter **filt, char **fstr )
 	case LDAP_FILTER_AND:
 		Debug( LDAP_DEBUG_FILTER, "AND\n", 0, 0, 0 );
 		if ( (err = get_filter_list( conn, ber, &f->f_and, &ftmp ))
-		    == 0 ) {
+		    == LDAP_SUCCESS ) {
 			if (ftmp == NULL) ftmp = ch_strdup("");
 			*fstr = ch_malloc( 4 + strlen( ftmp ) );
 			sprintf( *fstr, "(&%s)", ftmp );
@@ -125,7 +134,7 @@ get_filter( Connection *conn, BerElement *ber, Filter **filt, char **fstr )
 	case LDAP_FILTER_OR:
 		Debug( LDAP_DEBUG_FILTER, "OR\n", 0, 0, 0 );
 		if ( (err = get_filter_list( conn, ber, &f->f_or, &ftmp ))
-		    == 0 ) {
+		    == LDAP_SUCCESS ) {
 			if (ftmp == NULL) ftmp = ch_strdup("");
 			*fstr = ch_malloc( 4 + strlen( ftmp ) );
 			sprintf( *fstr, "(|%s)", ftmp );
@@ -136,7 +145,7 @@ get_filter( Connection *conn, BerElement *ber, Filter **filt, char **fstr )
 	case LDAP_FILTER_NOT:
 		Debug( LDAP_DEBUG_FILTER, "NOT\n", 0, 0, 0 );
 		(void) ber_skip_tag( ber, &len );
-		if ( (err = get_filter( conn, ber, &f->f_not, &ftmp )) == 0 ) {
+		if ( (err = get_filter( conn, ber, &f->f_not, &ftmp )) == LDAP_SUCCESS ) {
 			if (ftmp == NULL) ftmp = ch_strdup("");
 			*fstr = ch_malloc( 4 + strlen( ftmp ) );
 			sprintf( *fstr, "(!%s)", ftmp );
@@ -144,6 +153,12 @@ get_filter( Connection *conn, BerElement *ber, Filter **filt, char **fstr )
 		}
 		break;
 
+	case LBER_DEFAULT:
+		Debug( LDAP_DEBUG_ANY, "decoding filter error\n",
+		       0, 0, 0 );
+		err = -1;
+		break;
+
 	default:
 		Debug( LDAP_DEBUG_ANY, "unknown filter type %lu\n",
 		       f->f_choice, 0, 0 );
@@ -151,7 +166,7 @@ get_filter( Connection *conn, BerElement *ber, Filter **filt, char **fstr )
 		break;
 	}
 
-	if ( err != 0 ) {
+	if ( err != LDAP_SUCCESS ) {
 		free( (char *) f );
 		if ( *fstr != NULL ) {
 			free( *fstr );
@@ -178,8 +193,9 @@ get_filter_list( Connection *conn, BerElement *ber, Filter **f, char **fstr )
 	*fstr = NULL;
 	new = f;
 	for ( tag = ber_first_element( ber, &len, &last ); tag != LBER_DEFAULT;
-	    tag = ber_next_element( ber, &len, last ) ) {
-		if ( (err = get_filter( conn, ber, new, &ftmp )) != 0 )
+	    tag = ber_next_element( ber, &len, last ) )
+	{
+		if ( (err = get_filter( conn, ber, new, &ftmp )) != LDAP_SUCCESS )
 			return( err );
 		if ( *fstr == NULL ) {
 			*fstr = ftmp;
@@ -194,7 +210,7 @@ get_filter_list( Connection *conn, BerElement *ber, Filter **f, char **fstr )
 	*new = NULL;
 
 	Debug( LDAP_DEBUG_FILTER, "end get_filter_list\n", 0, 0, 0 );
-	return( 0 );
+	return( LDAP_SUCCESS );
 }
 
 static int
@@ -214,7 +230,7 @@ get_substring_filter(
 	Debug( LDAP_DEBUG_FILTER, "begin get_substring_filter\n", 0, 0, 0 );
 
 	if ( ber_scanf( ber, "{a" /*}*/, &f->f_sub_type ) == LBER_ERROR ) {
-		return( LDAP_PROTOCOL_ERROR );
+		return( -1 );
 	}
 	attr_normalize( f->f_sub_type );
 	syntax = attr_syntax( f->f_sub_type );
@@ -225,10 +241,11 @@ get_substring_filter(
 	*fstr = ch_malloc( strlen( f->f_sub_type ) + 3 );
 	sprintf( *fstr, "(%s=", f->f_sub_type );
 	for ( tag = ber_first_element( ber, &len, &last ); tag != LBER_DEFAULT;
-	    tag = ber_next_element( ber, &len, last ) ) {
-			rc = ber_scanf( ber, "a", &val );
+	    tag = ber_next_element( ber, &len, last ) )
+	{
+		rc = ber_scanf( ber, "a", &val );
 		if ( rc == LBER_ERROR ) {
-			return( LDAP_PROTOCOL_ERROR );
+			return( -1 );
 		}
 		if ( val == NULL || *val == '\0' ) {
 			if ( val != NULL ) {
@@ -284,7 +301,7 @@ get_substring_filter(
 	strcat( *fstr, ")" );
 
 	Debug( LDAP_DEBUG_FILTER, "end get_substring_filter\n", 0, 0, 0 );
-	return( 0 );
+	return( LDAP_SUCCESS );
 }
 
 void
diff --git a/servers/slapd/libslapd.dsp b/servers/slapd/libslapd.dsp
index 6800343d72..898d368c6a 100644
--- a/servers/slapd/libslapd.dsp
+++ b/servers/slapd/libslapd.dsp
@@ -200,6 +200,10 @@ SOURCE=.\entry.c
 # End Source File
 # Begin Source File
 
+SOURCE=.\extended.c
+# End Source File
+# Begin Source File
+
 SOURCE=.\filter.c
 # End Source File
 # Begin Source File
diff --git a/servers/slapd/modify.c b/servers/slapd/modify.c
index 6be8038bdf..ccae24d41f 100644
--- a/servers/slapd/modify.c
+++ b/servers/slapd/modify.c
@@ -71,8 +71,9 @@ do_modify(
 
 	if ( ber_scanf( op->o_ber, "{a" /*}*/, &ndn ) == LBER_ERROR ) {
 		Debug( LDAP_DEBUG_ANY, "ber_scanf failed\n", 0, 0, 0 );
-		send_ldap_result( conn, op, rc = LDAP_PROTOCOL_ERROR, NULL, "" );
-		return rc;
+		send_ldap_disconnect( conn, op,
+			LDAP_PROTOCOL_ERROR, "decoding error" );
+		return -1;
 	}
 
 	Debug( LDAP_DEBUG_ARGS, "do_modify: dn (%s)\n", ndn, 0, 0 );
@@ -95,13 +96,13 @@ do_modify(
 		    &(*modtail)->ml_type, &(*modtail)->ml_bvalues )
 		    == LBER_ERROR )
 		{
-			send_ldap_result( conn, op, rc = LDAP_PROTOCOL_ERROR, NULL,
-			    "decoding error" );
+			send_ldap_disconnect( conn, op,
+				LDAP_PROTOCOL_ERROR, "decoding modlist error" );
 			free( ndn );
 			free( *modtail );
 			*modtail = NULL;
 			modlist_free( modlist );
-			return rc;
+			return -1;
 		}
 
 		(*modtail)->ml_op = mop;
@@ -110,21 +111,21 @@ do_modify(
 		    (*modtail)->ml_op != LDAP_MOD_DELETE &&
 		    (*modtail)->ml_op != LDAP_MOD_REPLACE )
 		{
-			send_ldap_result( conn, op, rc = LDAP_PROTOCOL_ERROR, NULL,
-			    "unrecognized modify operation" );
+			send_ldap_result( conn, op, LDAP_PROTOCOL_ERROR,
+			    NULL, "unrecognized modify operation" );
 			free( ndn );
 			modlist_free( modlist );
-			return rc;
+			return LDAP_PROTOCOL_ERROR;
 		}
 
 		if ( (*modtail)->ml_bvalues == NULL
 			&& (*modtail)->ml_op != LDAP_MOD_DELETE )
 		{
-			send_ldap_result( conn, op, rc = LDAP_PROTOCOL_ERROR, NULL,
-			    "no values given" );
+			send_ldap_result( conn, op, LDAP_PROTOCOL_ERROR,
+			    NULL, "unrecognized modify operation" );
 			free( ndn );
 			modlist_free( modlist );
-			return rc;
+			return LDAP_PROTOCOL_ERROR;
 		}
 		attr_normalize( (*modtail)->ml_type );
 
diff --git a/servers/slapd/modrdn.c b/servers/slapd/modrdn.c
index d3f04f8ff5..c80a605fbd 100644
--- a/servers/slapd/modrdn.c
+++ b/servers/slapd/modrdn.c
@@ -72,8 +72,9 @@ do_modrdn(
 	if ( ber_scanf( op->o_ber, "{aab", &ndn, &newrdn, &deloldrdn )
 	    == LBER_ERROR ) {
 		Debug( LDAP_DEBUG_ANY, "ber_scanf failed\n", 0, 0, 0 );
-		send_ldap_result( conn, op, rc = LDAP_PROTOCOL_ERROR, NULL, "" );
-		return rc;
+		send_ldap_disconnect( conn, op,
+			LDAP_PROTOCOL_ERROR, "decoding error" );
+		return -1;
 	}
 
 	/* Check for newSuperior parameter, if present scan it */
@@ -96,9 +97,9 @@ do_modrdn(
 			Debug( LDAP_DEBUG_ANY,
 			       "modrdn(v2): invalid field newSuperior!\n",
 			       0, 0, 0 );
-			send_ldap_result( conn, op, rc = LDAP_PROTOCOL_ERROR,
-					  NULL, "" );
-			return rc;
+			send_ldap_disconnect( conn, op,
+				LDAP_PROTOCOL_ERROR, "newSuperior requires LDAPv3" );
+			return -1;
 		}
 
 		if ( ber_scanf( op->o_ber, "a", &newSuperior ) 
@@ -106,9 +107,9 @@ do_modrdn(
 
 		    Debug( LDAP_DEBUG_ANY, "ber_scanf(\"a\"}) failed\n",
 			   0, 0, 0 );
-		    send_ldap_result( conn, op, rc = LDAP_PROTOCOL_ERROR, NULL,
-				      "" );
-		    return rc;
+			send_ldap_disconnect( conn, op,
+				LDAP_PROTOCOL_ERROR, "decoding error" );
+		    return -1;
 
 		}
 
@@ -124,12 +125,11 @@ do_modrdn(
 		free( newrdn );	
 		free( newSuperior );
 		Debug( LDAP_DEBUG_ANY, "do_modrdn: ber_scanf failed\n", 0, 0, 0 );
-		send_ldap_result( conn, op, rc = LDAP_PROTOCOL_ERROR, NULL,
-		    "decoding error" );
-		return rc;
+		send_ldap_disconnect( conn, op,
+				LDAP_PROTOCOL_ERROR, "decoding error" );
+		return -1;
 	}
 
-#ifdef  GET_CTRLS
 	if( (rc = get_ctrls( conn, op, 1 )) != LDAP_SUCCESS ) {
 		free( ndn );
 		free( newrdn );	
@@ -137,7 +137,6 @@ do_modrdn(
 		Debug( LDAP_DEBUG_ANY, "do_modrdn: get_ctrls failed\n", 0, 0, 0 );
 		return rc;
 	} 
-#endif
 
 	if( newSuperior != NULL ) {
 		/* GET BACKEND FOR NEW SUPERIOR */
diff --git a/servers/slapd/monitor.c b/servers/slapd/monitor.c
index 9dcf13d9e9..a5a5274f12 100644
--- a/servers/slapd/monitor.c
+++ b/servers/slapd/monitor.c
@@ -21,10 +21,6 @@
 #include "ldap_defaults.h"
 #include "slap.h"
 
-char *supportedExtensions[] = {
-	NULL
-};
-
 #if defined( SLAPD_MONITOR_DN )
 
 void
@@ -232,7 +228,7 @@ monitor_info( Connection *conn, Operation *op )
 	attr_merge( e, "concurrency", vals );
 #endif
 
-	send_search_entry( &backends[0], conn, op, e, NULL, 0 );
+	send_search_entry( &backends[0], conn, op, e, NULL, 0, 1 );
 	send_ldap_search_result( conn, op, LDAP_SUCCESS, NULL, NULL, 1 );
 
 	entry_free( e );
diff --git a/servers/slapd/proto-slap.h b/servers/slapd/proto-slap.h
index d8c33673a6..23cd1297bd 100644
--- a/servers/slapd/proto-slap.h
+++ b/servers/slapd/proto-slap.h
@@ -257,17 +257,26 @@ void replog LDAP_P(( Backend *be, int optype, char *dn, void *change, int flag )
  * result.c
  */
 
-void send_ldap_result LDAP_P(( Connection *conn, Operation *op, int err, char *matched,
-	char *text ));
-void send_ldap_search_result LDAP_P(( Connection *conn, Operation *op, int err,
-	char *matched, char *text, int nentries ));
+void send_ldap_result LDAP_P((
+	Connection *conn, Operation *op,
+	int err, char *matched, char *text ));
+
+void send_ldap_disconnect LDAP_P((
+	Connection *conn, Operation *op,
+	int err, char *text ));
+
+void send_ldap_search_result LDAP_P((
+	Connection *conn, Operation *op,
+	int err, char *matched, char *text, int nentries ));
 
 /*
  * schema.c
  */
 
 int oc_schema_check LDAP_P(( Entry *e ));
-int oc_check_operational LDAP_P(( char *type ));
+int oc_check_operational_attr LDAP_P(( char *type ));
+int oc_check_usermod_attr LDAP_P(( char *type ));
+int oc_check_no_usermod_attr LDAP_P(( char *type ));
 ObjectClass *oc_find LDAP_P((const char *ocname));
 int oc_add LDAP_P((LDAP_OBJECT_CLASS *oc, const char **err));
 Syntax *syn_find LDAP_P((const char *synname));
@@ -401,9 +410,12 @@ extern int	do_modify LDAP_P((Connection *conn, Operation *op));
 extern int	do_modrdn LDAP_P((Connection *conn, Operation *op));
 extern int	do_search LDAP_P((Connection *conn, Operation *op));
 extern int	do_unbind LDAP_P((Connection *conn, Operation *op));
-extern int	do_exop LDAP_P((Connection *conn, Operation *op));
+extern int	do_extended LDAP_P((Connection *conn, Operation *op));
+
+extern int send_search_entry LDAP_P((
+	Backend *be, Connection *conn, Operation *op,
+	Entry *e, char **attrs, int attrsonly, int opattrs ));
 
-extern int send_search_entry LDAP_P((Backend *be, Connection *conn, Operation *op, Entry *e, char **attrs, int attrsonly));
 extern int str2result LDAP_P(( char *s, int *code, char **matched, char **info ));
 
 extern ber_socket_t dtblsize;
diff --git a/servers/slapd/result.c b/servers/slapd/result.c
index e9c05de439..4fbe02e21c 100644
--- a/servers/slapd/result.c
+++ b/servers/slapd/result.c
@@ -20,6 +20,7 @@ static void
 send_ldap_result2(
     Connection	*conn,
     Operation	*op,
+	int		disconnect,
     ber_int_t	err,
     char	*matched,
     char	*text,
@@ -30,32 +31,50 @@ send_ldap_result2(
 	int		rc;
 	ber_tag_t	tag;
 	ber_len_t	bytes;
+	int	msgid;
 
 	assert( !LDAP_API_ERROR( err ) );
 
 	if ( err == LDAP_PARTIAL_RESULTS && (text == NULL || *text == '\0') )
 		err = LDAP_NO_SUCH_OBJECT;
 
-	Debug( LDAP_DEBUG_TRACE, "send_ldap_result %d:%s:%s\n", err,
-		matched ?  matched : "",
-		text ? text : "" );
+	if( disconnect ) {
+		msgid = 0;	/* unsolicited */
+		if ( op->o_protocol > LDAP_VERSION3 ) {
+			tag = LDAP_RES_EXTENDED;
+		}
+
+#define LDAP_UNSOLICITED_ERROR(e) \
+	(  (e) == LDAP_PROTOCOL_ERROR \
+	|| (e) == LDAP_STRONG_AUTH_REQUIRED \
+	|| (e) == LDAP_UNAVAILABLE )
+
+		assert( LDAP_UNSOLICITED_ERROR( err ) );
 
-	switch ( op->o_tag ) {
-	case LBER_DEFAULT:
-		tag = LBER_SEQUENCE;
-		break;
+	} else {
+		msgid = op->o_msgid;
+
+		switch ( op->o_tag ) {
+		case LDAP_REQ_ABANDON:
+		case LDAP_REQ_UNBIND:
+		case LBER_ERROR:
+			tag = LBER_SEQUENCE;
+			msgid = 0;
+			assert( LDAP_UNSOLICITED_ERROR( err ) );
+			break;
 
-	case LDAP_REQ_SEARCH:
-		tag = LDAP_RES_SEARCH_RESULT;
-		break;
+		case LDAP_REQ_SEARCH:
+			tag = LDAP_RES_SEARCH_RESULT;
+			break;
 
-	case LDAP_REQ_DELETE:
-		tag = LDAP_RES_DELETE;
-		break;
+		case LDAP_REQ_DELETE:
+			tag = LDAP_RES_DELETE;
+			break;
 
-	default:
-		tag = op->o_tag + 1;
-		break;
+		default:
+			tag = op->o_tag + 1;
+			break;
+		}
 	}
 
 
@@ -68,13 +87,20 @@ send_ldap_result2(
 
 #ifdef LDAP_CONNECTIONLESS
 	if ( op->o_cldap ) {
-		rc = ber_printf( ber, "{is{t{ess}}}", op->o_msgid, "", tag,
+		rc = ber_printf( ber, "{is{t{ess}}}", msgid, "", tag,
 		    err, matched ? matched : "", text ? text : "" );
 	} else
 #endif
-	{
-		rc = ber_printf( ber, "{it{ess}}", op->o_msgid, tag, err,
-		    matched ? matched : "", text ? text : "" );
+	if( tag == LDAP_RES_EXTENDED ) {
+		rc = ber_printf( ber, "{it{esss}}",
+			msgid, tag, err,
+			"", text ? text : "",
+			LDAP_NOTICE_DISCONNECT );
+
+	} else {
+		rc = ber_printf( ber, "{it{ess}}",
+			msgid, tag, err,
+			matched ? matched : "", text ? text : "" );
 	}
 
 	if ( rc == -1 ) {
@@ -158,6 +184,9 @@ send_ldap_result(
 {
 	assert( !LDAP_API_ERROR( err ) );
 
+	Debug( LDAP_DEBUG_TRACE, "send_ldap_result %d:%s:%s\n",
+		err, matched ?  matched : "", text ? text : "" );
+
 #ifdef LDAP_CONNECTIONLESS
 	if ( op->o_cldap ) {
 		ber_pvt_sb_udp_set_dst( &conn->c_sb, &op->o_clientaddr );
@@ -168,7 +197,34 @@ send_ldap_result(
 		    0 );
 	}
 #endif
-	send_ldap_result2( conn, op, err, matched, text, 0 );
+	send_ldap_result2( conn, op, 0, err, matched, text, 0 );
+}
+
+void
+send_ldap_disconnect(
+    Connection	*conn,
+    Operation	*op,
+    ber_int_t	err,
+    char	*text
+)
+{
+	assert( !LDAP_API_ERROR( err ) );
+
+	Debug( LDAP_DEBUG_TRACE,
+		"send_ldap_disconnect %d:%s\n",
+		err, text ? text : "", NULL );
+
+#ifdef LDAP_CONNECTIONLESS
+	if ( op->o_cldap ) {
+		ber_pvt_sb_udp_set_dst( &conn->c_sb, &op->o_clientaddr );
+		Debug( LDAP_DEBUG_TRACE, "UDP response to %s port %d\n", 
+		    inet_ntoa(((struct sockaddr_in *)
+		    &op->o_clientaddr)->sin_addr ),
+		    ((struct sockaddr_in *) &op->o_clientaddr)->sin_port,
+		    0 );
+	}
+#endif
+	send_ldap_result2( conn, op, 0, err, NULL, text, 0 );
 }
 
 void
@@ -181,7 +237,10 @@ send_ldap_search_result(
     int		nentries
 )
 {
-	send_ldap_result2( conn, op, err, matched, text, nentries );
+	Debug( LDAP_DEBUG_TRACE, "send_ldap_search_result %d:%s:%s\n",
+		err, matched ?  matched : "", text ? text : "" );
+
+	send_ldap_result2( conn, op, 0, err, matched, text, nentries );
 }
 
 int
@@ -191,7 +250,8 @@ send_search_entry(
     Operation	*op,
     Entry	*e,
     char	**attrs,
-    int		attrsonly
+    int		attrsonly,
+	int		opattrs
 )
 {
 	BerElement	*ber;
@@ -202,6 +262,17 @@ send_search_entry(
 
 	Debug( LDAP_DEBUG_TRACE, "=> send_search_entry (%s)\n", e->e_dn, 0, 0 );
 
+#if defined( SLAPD_SCHEMA_DN )
+	{
+		/* this could be backend specific */
+		struct	berval	val;
+		val.bv_val = SLAPD_SCHEMA_DN;
+		val.bv_len = strlen( val.bv_val );
+		attr_merge( e, "subschemaSubentry", vals );
+		ldap_memfree( val.bv_val );
+	}
+#endif
+
 	if ( ! access_allowed( be, conn, op, e,
 		"entry", NULL, ACL_READ ) )
 	{
@@ -237,12 +308,15 @@ send_search_entry(
 
 		if ( attrs == NULL ) {
 			/* all addrs request, skip operational attributes */
-			if( oc_check_operational( a->a_type )) {
+			if( !opattrs && oc_check_operational_attr( a->a_type )) {
 				continue;
 			}
+
 		} else {
 			/* specific addrs requested */
-			if ( !charray_inlist( attrs, a->a_type )) {
+			if ( !charray_inlist( attrs, a->a_type )
+				&& !charray_inlist( attrs, LDAP_ALL_USER_ATTRIBUTES ) )
+			{
 				continue;
 			}
 		}
diff --git a/servers/slapd/root_dse.c b/servers/slapd/root_dse.c
index f609e71142..540d218d13 100644
--- a/servers/slapd/root_dse.c
+++ b/servers/slapd/root_dse.c
@@ -19,8 +19,8 @@
 void
 root_dse_info( Connection *conn, Operation *op, char **attrs, int attrsonly )
 {
+	char buf[BUFSIZ];
 	Entry		*e;
-	char		buf[BUFSIZ];
 	struct berval	val;
 	struct berval	*vals[2];
 	int		i, j;
@@ -37,51 +37,44 @@ root_dse_info( Connection *conn, Operation *op, char **attrs, int attrsonly )
 
 	for ( i = 0; i < nbackends; i++ ) {
 		for ( j = 0; backends[i].be_suffix[j] != NULL; j++ ) {
-			strcpy( buf, backends[i].be_suffix[j] );
-			val.bv_val = buf;
-			val.bv_len = strlen( buf );
+			val.bv_val = backends[i].be_suffix[j];
+			val.bv_len = strlen( val.bv_val );
 			attr_merge( e, "namingContexts", vals );
 		}
 	}
 
 #if defined( SLAPD_MONITOR_DN )
-	strcpy( buf, SLAPD_MONITOR_DN );
-	val.bv_val = buf;
-	val.bv_len = strlen( buf );
+	val.bv_val = SLAPD_MONITOR_DN;
+	val.bv_len = strlen( val.bv_val );
 	attr_merge( e, "namingContexts", vals );
+	/* subschemasubentry is added by send_search_entry() */
 #endif
 
 #if defined( SLAPD_CONFIG_DN )
-	strcpy( buf, SLAPD_CONFIG_DN );
-	val.bv_val = buf;
-	val.bv_len = strlen( buf );
+	val.bv_val = SLAPD_CONFIG_DN;
+	val.bv_len = strlen( val.bv_val );
 	attr_merge( e, "namingContexts", vals );
 #endif
 
 #if defined( SLAPD_SCHEMA_DN )
-	strcpy( buf, SLAPD_SCHEMA_DN );
-	val.bv_val = buf;
+	val.bv_val = SLAPD_SCHEMA_DN;
 	val.bv_len = strlen( val.bv_val );
 	attr_merge( e, "namingContexts", vals );
-	attr_merge( e, "subschemaSubentry", vals );
-	ldap_memfree( val.bv_val );
 #endif
 
 	/* altServer unsupported */
 
 	/* supportedControl */
 	for ( i=0; supportedControls[i] != NULL; i++ ) {
-		strcpy( buf, supportedControls[i] );
-		val.bv_val = buf;
-		val.bv_len = strlen( buf );
+		val.bv_val = supportedControls[i];
+		val.bv_len = strlen( val.bv_val );
 		attr_merge( e, "supportedControl", vals );
 	}
 
 	/* supportedExtension */
 	for ( i=0; supportedExtensions[i] != NULL; i++ ) {
-		strcpy( buf, supportedExtensions[i] );
-		val.bv_val = buf;
-		val.bv_len = strlen( buf );
+		val.bv_val = supportedExtensions[i];
+		val.bv_len = strlen( val.bv_val );
 		attr_merge( e, "supportedExtension", vals );
 	}
 
@@ -89,20 +82,19 @@ root_dse_info( Connection *conn, Operation *op, char **attrs, int attrsonly )
 	for ( i=LDAP_VERSION_MIN; i<=LDAP_VERSION_MAX; i++ ) {
 		sprintf(buf,"%d",i);
 		val.bv_val = buf;
-		val.bv_len = strlen( buf );
+		val.bv_len = strlen( val.bv_val );
 		attr_merge( e, "supportedLDAPVersion", vals );
 	}
 
 	/* supportedSASLMechanism */
 	for ( i=0; supportedSASLMechanisms[i] != NULL; i++ ) {
-		strcpy( buf, supportedSASLMechanisms[i] );
-		val.bv_val = buf;
-		val.bv_len = strlen( buf );
+		val.bv_val = supportedSASLMechanisms[i];
+		val.bv_len = strlen( val.bv_val );
 		attr_merge( e, "supportedSASLMechanism", vals );
 	}
 
 
-	send_search_entry( &backends[0], conn, op, e, attrs, attrsonly );
+	send_search_entry( &backends[0], conn, op, e, attrs, attrsonly, 1 );
 	send_ldap_search_result( conn, op, LDAP_SUCCESS, NULL, NULL, 1 );
 
 	entry_free( e );
diff --git a/servers/slapd/schema.c b/servers/slapd/schema.c
index 746833f796..e71913e0e6 100644
--- a/servers/slapd/schema.c
+++ b/servers/slapd/schema.c
@@ -123,20 +123,78 @@ oc_check_required( Entry *e, char *ocname )
 	return( NULL );
 }
 
+static char *oc_usermod_attrs[] = {
+	/*
+	 * OpenLDAP doesn't support any user modification of
+	 * operational attributes.
+	 */
+	NULL
+};
+
+static char *oc_operational_attrs[] = {
+	/*
+	 * these are operational attributes that *could* be
+	 * modified by users if we supported such.
+	 */
+	"objectClasses",
+	"attributeTypes",
+	"matchingRules",
+	"matchingRuleUse",
+	"dITStructureRules",
+	"dITContentRules",
+	"nameForms",
+	"ldapSyntaxes",
+	NULL
+
+};
+
+/* this list should be extensible  */
+static char *oc_no_usermod_attrs[] = {
+	/*
+	 * Operational and 'no user modification' attributes
+	 */
+
+	/* RFC2252, 3.2.1 */
+	"creatorsName",
+	"createTimestamp",
+	"modifiersName",
+	"modifyTimestamp",
+	"subschemaSubentry",
+
+	NULL
+};
+
+
 /*
  * check to see if attribute is 'operational' or not.
- * this list should be extensible...
  */
 int
-oc_check_operational( char *type )
+oc_check_operational_attr( char *type )
 {
-	return ( strcasecmp( type, "modifiersname" ) == 0 ||
-		strcasecmp( type, "modifytimestamp" ) == 0 ||
-		strcasecmp( type, "creatorsname" ) == 0 ||
-		strcasecmp( type, "createtimestamp" ) == 0 )
-		? 1 : 0;
+	return charray_inlist( oc_operational_attrs, type )
+		|| charray_inlist( oc_usermod_attrs, type )
+		|| charray_inlist( oc_no_usermod_attrs, type );
 }
 
+/*
+ * check to see if attribute can be user modified or not.
+ */
+int
+oc_check_usermod_attr( char *type )
+{
+	return charray_inlist( oc_usermod_attrs, type );
+}
+
+/*
+ * check to see if attribute is 'no user modification' or not.
+ */
+int
+oc_check_no_usermod_attr( char *type )
+{
+	return charray_inlist( oc_no_usermod_attrs, type );
+}
+
+
 static int
 oc_check_allowed( char *type, struct berval **ocl )
 {
@@ -153,7 +211,12 @@ oc_check_allowed( char *type, struct berval **ocl )
 		return( 0 );
 	}
 
-	if ( oc_check_operational( type ) ) {
+	/*
+	 * All operational attributions are allowed by schema rules.
+	 * However, we only check attributions which are stored in the
+	 * the directory regardless if they are user or non-user modified.
+	 */
+	if ( oc_check_usermod_attr( type ) || oc_check_no_usermod_attr( type ) ) {
 		return( 0 );
 	}
 
@@ -1080,7 +1143,7 @@ schema_info( Connection *conn, Operation *op, char **attrs, int attrsonly )
 		return;
 	}
 	
-	send_search_entry( &backends[0], conn, op, e, attrs, attrsonly );
+	send_search_entry( &backends[0], conn, op, e, attrs, attrsonly, 0 );
 	send_ldap_search_result( conn, op, LDAP_SUCCESS, NULL, NULL, 1 );
 
 	entry_free( e );
diff --git a/servers/slapd/search.c b/servers/slapd/search.c
index fece855dc7..38f44be01c 100644
--- a/servers/slapd/search.c
+++ b/servers/slapd/search.c
@@ -74,14 +74,17 @@ do_search(
 	if ( ber_scanf( op->o_ber, "{aiiiib",
 		&base, &scope, &deref, &sizelimit,
 	    &timelimit, &attrsonly ) == LBER_ERROR ) {
-		send_ldap_result( conn, op, rc = LDAP_PROTOCOL_ERROR, NULL, "" );
+		send_ldap_disconnect( conn, op,
+			LDAP_PROTOCOL_ERROR, "decoding error" );
+		rc = -1;
 		goto return_results;
 	}
 
 	if ( scope != LDAP_SCOPE_BASE && scope != LDAP_SCOPE_ONELEVEL
 	    && scope != LDAP_SCOPE_SUBTREE ) {
-		send_ldap_result( conn, op, rc = LDAP_PROTOCOL_ERROR, NULL,
-		    "Unknown search scope" );
+		send_ldap_disconnect( conn, op,
+			LDAP_PROTOCOL_ERROR, "decoding error" );
+		rc = -1;
 		goto return_results;
 	}
 
@@ -93,14 +96,22 @@ do_search(
 
 	/* filter - returns a "normalized" version */
 	if ( (err = get_filter( conn, op->o_ber, &filter, &fstr )) != 0 ) {
-		send_ldap_result( conn, op, err, NULL, "Bad search filter" );
+		if( err == -1 ) {
+			send_ldap_disconnect( conn, op,
+				LDAP_PROTOCOL_ERROR, "decode error" );
+		} else {
+			send_ldap_result( conn, op, err, NULL, "Bad search filter" );
+		}
 		goto return_results;
 	}
+
 	Debug( LDAP_DEBUG_ARGS, "    filter: %s\n", fstr, 0, 0 );
 
 	/* attributes */
 	if ( ber_scanf( op->o_ber, /*{*/ "{v}}", &attrs ) == LBER_ERROR ) {
-		send_ldap_result( conn, op, rc = LDAP_PROTOCOL_ERROR, NULL, "" );
+		send_ldap_disconnect( conn, op,
+			LDAP_PROTOCOL_ERROR, "decoding error" );
+		rc = -1;
 		goto return_results;
 	}
 
diff --git a/servers/slapd/tools/mimic.c b/servers/slapd/tools/mimic.c
index d84e8e5bd5..be5a177ab7 100644
--- a/servers/slapd/tools/mimic.c
+++ b/servers/slapd/tools/mimic.c
@@ -55,7 +55,8 @@ send_search_entry(
 	Operation   *op,
 	Entry	*e,
 	char	**attrs,
-	int		attrsonly
+	int		attrsonly,
+	int		opattrs
 )        
 {
 	assert(0);
-- 
GitLab