From bb6b474bf90a54a74089459c397973efd9ed8b29 Mon Sep 17 00:00:00 2001
From: Quanah Gibson-Mount <quanah@openldap.org>
Date: Mon, 10 Nov 2008 20:15:11 +0000
Subject: [PATCH] ITS#5768

---
 CHANGES                            |   2 +
 clients/tools/common.c             |  76 ++++
 clients/tools/ldapsearch.c         |  81 +++++
 configure                          |  62 +++-
 configure.in                       |  15 +
 include/ldap.h                     |  53 +++
 include/portable.hin               |   3 +
 libraries/libldap/Makefile.in      |   4 +-
 libraries/libldap/deref.c          | 282 ++++++++++++++
 servers/slapd/overlays/Makefile.in |   4 +
 servers/slapd/overlays/deref.c     | 565 +++++++++++++++++++++++++++++
 11 files changed, 1135 insertions(+), 12 deletions(-)
 create mode 100644 libraries/libldap/deref.c
 create mode 100644 servers/slapd/overlays/deref.c

diff --git a/CHANGES b/CHANGES
index 58bb5cfece..8dcc2d1676 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,6 +1,7 @@
 OpenLDAP 2.4 Change Log
 
 OpenLDAP 2.4.13 Engineering
+	Added libldap dereference control support (ITS#5768)
 	Fixed liblutil hex conversion (ITS#5699)
 	Fixed liblutil returning undefined data (ITS#5748)
 	Fixed libldap error code return (ITS#5762)
@@ -36,6 +37,7 @@ OpenLDAP 2.4.13 Engineering
 	Build Environment
 		Added ldapurl command
 		Added slapd GSSAPI refactoring (ITS#5369)
+		Added slapo-deref overlay (ITS#5768)
 	Documentation
 		admin24 added olcLimits to example (ITS#5746)
 
diff --git a/clients/tools/common.c b/clients/tools/common.c
index 5222d6321d..4f38ede9fd 100644
--- a/clients/tools/common.c
+++ b/clients/tools/common.c
@@ -135,6 +135,9 @@ static int print_paged_results( LDAP *ld, LDAPControl *ctrl );
 static int print_ppolicy( LDAP *ld, LDAPControl *ctrl );
 #endif
 static int print_sss( LDAP *ld, LDAPControl *ctrl );
+#ifdef LDAP_CONTROL_X_DEREF
+static int print_deref( LDAP *ld, LDAPControl *ctrl );
+#endif
 
 static struct tool_ctrls_t {
 	const char	*oid;
@@ -148,6 +151,9 @@ static struct tool_ctrls_t {
 	{ LDAP_CONTROL_PASSWORDPOLICYRESPONSE,		TOOL_ALL,	print_ppolicy },
 #endif
 	{ LDAP_CONTROL_SORTRESPONSE,	TOOL_SEARCH,	print_sss },
+#ifdef LDAP_CONTROL_X_DEREF
+	{ LDAP_CONTROL_X_DEREF,				TOOL_SEARCH,	print_deref },
+#endif
 	{ NULL,						0,		NULL }
 };
 
@@ -1890,6 +1896,76 @@ print_sss( LDAP *ld, LDAPControl *ctrl )
 	return rc;
 }
 
+#ifdef LDAP_CONTROL_X_DEREF
+static int
+print_deref( LDAP *ld, LDAPControl *ctrl )
+{
+	LDAPDerefRes    *drhead = NULL, *dr;
+	int		rc;
+
+	rc = ldap_parse_derefresponse_control( ld, ctrl, &drhead );
+	if ( rc != LDAP_SUCCESS ) {
+		return rc;
+	}
+
+	for ( dr = drhead; dr != NULL; dr = dr->next ) {
+		LDAPDerefVal	*dv;
+		ber_len_t	len;
+		char		*buf, *ptr;
+
+		len = strlen( dr->derefAttr ) + STRLENOF(": ");
+
+		for ( dv = dr->attrVals; dv != NULL; dv = dv->next ) {
+			if ( dv->vals != NULL ) {
+				int j;
+				ber_len_t tlen = strlen(dv->type);
+
+				for ( j = 0; dv->vals[ j ].bv_val != NULL; j++ ) {
+					len += STRLENOF("<=>;") + tlen + dv->vals[ j ].bv_len;
+				}
+			}
+		}
+		len += dr->derefVal.bv_len;
+		buf = ldap_memalloc( len + 1 );
+		if ( buf == NULL ) {
+			rc = LDAP_NO_MEMORY;
+			goto done;
+		}
+
+		ptr = buf;
+		ptr = lutil_strcopy( ptr, dr->derefAttr );
+		*ptr++ = ':';
+		*ptr++ = ' ';
+		for ( dv = dr->attrVals; dv != NULL; dv = dv->next ) {
+			if ( dv->vals != NULL ) {
+				int j;
+				for ( j = 0; dv->vals[ j ].bv_val != NULL; j++ ) {
+					*ptr++ = '<';
+					ptr = lutil_strcopy( ptr, dv->type );
+					*ptr++ = '=';
+					ptr = lutil_strncopy( ptr, dv->vals[ j ].bv_val, dv->vals[ j ].bv_len );
+					*ptr++ = '>';
+					*ptr++ = ';';
+				}
+			}
+		}
+		ptr = lutil_strncopy( ptr, dr->derefVal.bv_val, dr->derefVal.bv_len );
+		*ptr++ = '\0';
+
+		tool_write_ldif( LDIF_PUT_COMMENT, NULL, buf, len );
+
+		ldap_memfree( buf );
+	}
+
+	rc = LDAP_SUCCESS;
+
+done:;
+	ldap_derefresponse_free( drhead );
+
+	return rc;
+}
+#endif
+
 #ifdef LDAP_CONTROL_PASSWORDPOLICYREQUEST
 static int
 print_ppolicy( LDAP *ld, LDAPControl *ctrl )
diff --git a/clients/tools/ldapsearch.c b/clients/tools/ldapsearch.c
index 7e69fa5992..8a60f7d804 100644
--- a/clients/tools/ldapsearch.c
+++ b/clients/tools/ldapsearch.c
@@ -133,6 +133,9 @@ usage( void )
 	fprintf( stderr, _("             [!]subentries[=true|false]  (RFC 3672 subentries)\n"));
 	fprintf( stderr, _("             [!]sync=ro[/<cookie>]       (RFC 4533 LDAP Sync refreshOnly)\n"));
 	fprintf( stderr, _("                     rp[/<cookie>][/<slimit>] (refreshAndPersist)\n"));
+#ifdef LDAP_CONTROL_X_DEREF
+	fprintf( stderr, _("             [!]deref=derefAttr:attr[,...][;derefAttr:attr[,...][;...]]\n"));
+#endif
 	fprintf( stderr, _("             [!]<oid>=:<value>           (generic control; no response handling)\n"));
 	fprintf( stderr, _("  -F prefix  URL prefix for files (default: %s)\n"), def_urlpre);
 	fprintf( stderr, _("  -l limit   time limit (in seconds, or \"none\" or \"max\") for search\n"));
@@ -223,6 +226,12 @@ static LDAPControl *c = NULL;
 static int nctrls = 0;
 static int save_nctrls = 0;
 
+#ifdef LDAP_CONTROL_X_DEREF
+static int derefcrit;
+static LDAPDerefSpec *ds;
+static struct berval derefval;
+#endif
+
 static int
 ctrl_add( void )
 {
@@ -491,6 +500,43 @@ handle_private_option( int i )
 			}
 			if ( crit ) ldapsync *= -1;
 
+#ifdef LDAP_CONTROL_X_DEREF
+		} else if ( strcasecmp( control, "deref" ) == 0 ) {
+			int ispecs;
+			char **specs;
+
+			/* cvalue is something like
+			 *
+			 * derefAttr:attr[,attr[...]][;derefAttr:attr[,attr[...]]]"
+			 */
+
+			specs = ldap_str2charray( cvalue, ";" );
+			for ( ispecs = 0; specs[ ispecs ] != NULL; ispecs++ )
+				/* count'em */
+
+			ds = ldap_memcalloc( ispecs + 1, sizeof( LDAPDerefSpec ) );
+			if ( ds == NULL ) {
+				/* error */
+			}
+
+			for ( ispecs = 0; specs[ ispecs ] != NULL; ispecs++ ) {
+				char *ptr;
+
+				ptr = strchr( specs[ ispecs ], ':' );
+				if ( ptr == NULL ) {
+					/* error */
+				}
+
+				ds[ ispecs ].derefAttr = specs[ ispecs ];
+				*ptr++ = '\0';
+				ds[ ispecs ].attributes = ldap_str2charray( ptr, "," );
+			}
+
+			derefcrit = 1 + crit;
+
+			ldap_memfree( specs );
+#endif /* LDAP_CONTROL_X_DEREF */
+
 		} else if ( tool_is_oid( control ) ) {
 			if ( ctrl_add() ) {
 				exit( EXIT_FAILURE );
@@ -780,6 +826,9 @@ getNextPage:
 	if ( nctrls > 0
 #ifdef LDAP_CONTROL_DONTUSECOPY
 		|| dontUseCopy
+#endif
+#ifdef LDAP_CONTROL_X_DEREF
+		|| derefcrit
 #endif
 		|| domainScope
 		|| pagedResults
@@ -933,6 +982,32 @@ getNextPage:
 			c[i].ldctl_iscritical = sss > 1;
 			i++;
 		}
+
+#ifdef LDAP_CONTROL_X_DEREF
+		if ( ds ) {
+			if ( derefval.bv_val == NULL ) {
+				int i;
+				if ( ldap_create_deref_control_value( ld, ds, &derefval ) != LDAP_SUCCESS ) {
+					return EXIT_FAILURE;
+				}
+
+				for ( i = 0; ds[ i ].derefAttr != NULL; i++ ) {
+					ldap_memfree( ds[ i ].derefAttr );
+					ldap_charray_free( ds[ i ].attributes );
+				}
+				ldap_memfree( ds );
+			}
+
+			if ( ctrl_add() ) {
+				exit( EXIT_FAILURE );
+			}
+
+			c[ i ].ldctl_iscritical = derefcrit > 1;
+			c[ i ].ldctl_oid = LDAP_CONTROL_X_DEREF;
+			c[ i ].ldctl_value = derefval;
+			i++;
+		}
+#endif /* LDAP_CONTROL_X_DEREF */
 	}
 
 	tool_server_controls( ld, c, i );
@@ -1019,6 +1094,12 @@ getNextPage:
 			printf(_("\n# with server side sorting %scontrol"),
 				sss > 1 ? _("critical ") : "" );
 		}
+#ifdef LDAP_CONTROL_X_DEREF
+		if ( sss ) {
+			printf(_("\n# with dereference %scontrol"),
+				sss > 1 ? _("critical ") : "" );
+		}
+#endif
 
 		printf( _("\n#\n\n") );
 
diff --git a/configure b/configure
index 6200468fe1..d04d8d64ac 100755
--- a/configure
+++ b/configure
@@ -1,5 +1,5 @@
 #! /bin/sh
-# From configure.in OpenLDAP: pkg/ldap/configure.in,v 1.631.2.14 2008/09/17 22:54:33 quanah Exp .
+# From configure.in OpenLDAP: pkg/ldap/configure.in,v 1.631.2.15 2008/11/08 00:14:44 quanah Exp .
 # Guess values for system-dependent variables and create Makefiles.
 # Generated by GNU Autoconf 2.59.
 #
@@ -1052,6 +1052,7 @@ SLAPD Overlay Options:
     --enable-collect	  Collect overlay no|yes|mod [no]
     --enable-constraint	  Attribute Constraint overlay no|yes|mod [no]
     --enable-dds  	  Dynamic Directory Services overlay no|yes|mod [no]
+    --enable-deref	  Dereference overlay no|yes|mod [no]
     --enable-dyngroup	  Dynamic Group overlay no|yes|mod [no]
     --enable-dynlist	  Dynamic List overlay no|yes|mod [no]
     --enable-memberof	  Reverse Group Membership overlay no|yes|mod [no]
@@ -3174,6 +3175,7 @@ Overlays="accesslog \
 	collect \
 	constraint \
 	dds \
+	deref \
 	dyngroup \
 	dynlist \
 	memberof \
@@ -3335,6 +3337,30 @@ else
 fi;
 # end --enable-dds
 
+# OpenLDAP --enable-deref
+
+	# Check whether --enable-deref or --disable-deref was given.
+if test "${enable_deref+set}" = set; then
+  enableval="$enable_deref"
+
+	ol_arg=invalid
+	for ol_val in no yes mod ; do
+		if test "$enableval" = "$ol_val" ; then
+			ol_arg="$ol_val"
+		fi
+	done
+	if test "$ol_arg" = "invalid" ; then
+		{ { echo "$as_me:$LINENO: error: bad value $enableval for --enable-deref" >&5
+echo "$as_me: error: bad value $enableval for --enable-deref" >&2;}
+   { (exit 1); exit 1; }; }
+	fi
+	ol_enable_deref="$ol_arg"
+
+else
+  	ol_enable_deref=${ol_enable_overlays:-no}
+fi;
+# end --enable-deref
+
 # OpenLDAP --enable-dyngroup
 
 	# Check whether --enable-dyngroup or --disable-dyngroup was given.
@@ -5701,7 +5727,7 @@ ia64-*-hpux*)
   ;;
 *-*-irix6*)
   # Find out which ABI we are using.
-  echo '#line 5704 "configure"' > conftest.$ac_ext
+  echo '#line 5730 "configure"' > conftest.$ac_ext
   if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
   (eval $ac_compile) 2>&5
   ac_status=$?
@@ -7681,11 +7707,11 @@ else
    -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
    -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
    -e 's:$: $lt_compiler_flag:'`
-   (eval echo "\"\$as_me:7684: $lt_compile\"" >&5)
+   (eval echo "\"\$as_me:7710: $lt_compile\"" >&5)
    (eval "$lt_compile" 2>conftest.err)
    ac_status=$?
    cat conftest.err >&5
-   echo "$as_me:7688: \$? = $ac_status" >&5
+   echo "$as_me:7714: \$? = $ac_status" >&5
    if (exit $ac_status) && test -s "$ac_outfile"; then
      # The compiler can only warn and ignore the option if not recognized
      # So say no if there are warnings other than the usual output.
@@ -7943,11 +7969,11 @@ else
    -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
    -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
    -e 's:$: $lt_compiler_flag:'`
-   (eval echo "\"\$as_me:7946: $lt_compile\"" >&5)
+   (eval echo "\"\$as_me:7972: $lt_compile\"" >&5)
    (eval "$lt_compile" 2>conftest.err)
    ac_status=$?
    cat conftest.err >&5
-   echo "$as_me:7950: \$? = $ac_status" >&5
+   echo "$as_me:7976: \$? = $ac_status" >&5
    if (exit $ac_status) && test -s "$ac_outfile"; then
      # The compiler can only warn and ignore the option if not recognized
      # So say no if there are warnings other than the usual output.
@@ -8005,11 +8031,11 @@ else
    -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
    -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
    -e 's:$: $lt_compiler_flag:'`
-   (eval echo "\"\$as_me:8008: $lt_compile\"" >&5)
+   (eval echo "\"\$as_me:8034: $lt_compile\"" >&5)
    (eval "$lt_compile" 2>out/conftest.err)
    ac_status=$?
    cat out/conftest.err >&5
-   echo "$as_me:8012: \$? = $ac_status" >&5
+   echo "$as_me:8038: \$? = $ac_status" >&5
    if (exit $ac_status) && test -s out/conftest2.$ac_objext
    then
      # The compiler can only warn and ignore the option if not recognized
@@ -10253,7 +10279,7 @@ else
   lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
   lt_status=$lt_dlunknown
   cat > conftest.$ac_ext <<EOF
-#line 10256 "configure"
+#line 10282 "configure"
 #include "confdefs.h"
 
 #if HAVE_DLFCN_H
@@ -10351,7 +10377,7 @@ else
   lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
   lt_status=$lt_dlunknown
   cat > conftest.$ac_ext <<EOF
-#line 10354 "configure"
+#line 10380 "configure"
 #include "confdefs.h"
 
 #if HAVE_DLFCN_H
@@ -38686,6 +38712,22 @@ _ACEOF
 
 fi
 
+if test "$ol_enable_deref" != no ; then
+	BUILD_DDS=$ol_enable_deref
+	if test "$ol_enable_deref" = mod ; then
+		MFLAG=SLAPD_MOD_DYNAMIC
+		SLAPD_DYNAMIC_OVERLAYS="$SLAPD_DYNAMIC_OVERLAYS deref.la"
+	else
+		MFLAG=SLAPD_MOD_STATIC
+		SLAPD_STATIC_OVERLAYS="$SLAPD_STATIC_OVERLAYS deref.o"
+	fi
+
+cat >>confdefs.h <<_ACEOF
+#define SLAPD_OVER_DEREF $MFLAG
+_ACEOF
+
+fi
+
 if test "$ol_enable_dyngroup" != no ; then
 	BUILD_DYNGROUP=$ol_enable_dyngroup
 	if test "$ol_enable_dyngroup" = mod ; then
diff --git a/configure.in b/configure.in
index 4fea72e517..56928631a5 100644
--- a/configure.in
+++ b/configure.in
@@ -338,6 +338,7 @@ Overlays="accesslog \
 	collect \
 	constraint \
 	dds \
+	deref \
 	dyngroup \
 	dynlist \
 	memberof \
@@ -367,6 +368,8 @@ OL_ARG_ENABLE(constraint,[    --enable-constraint	  Attribute Constraint overlay
 	no, [no yes mod], ol_enable_overlays)
 OL_ARG_ENABLE(dds,[    --enable-dds  	  Dynamic Directory Services overlay],
 	no, [no yes mod], ol_enable_overlays)
+OL_ARG_ENABLE(deref,[    --enable-deref	  Dereference overlay],
+	no, [no yes mod], ol_enable_overlays)
 OL_ARG_ENABLE(dyngroup,[    --enable-dyngroup	  Dynamic Group overlay],
 	no, [no yes mod], ol_enable_overlays)
 OL_ARG_ENABLE(dynlist,[    --enable-dynlist	  Dynamic List overlay],
@@ -2846,6 +2849,18 @@ if test "$ol_enable_dds" != no ; then
 	AC_DEFINE_UNQUOTED(SLAPD_OVER_DDS,$MFLAG,[define for Dynamic Directory Services overlay])
 fi
 
+if test "$ol_enable_deref" != no ; then
+	BUILD_DDS=$ol_enable_deref
+	if test "$ol_enable_deref" = mod ; then
+		MFLAG=SLAPD_MOD_DYNAMIC
+		SLAPD_DYNAMIC_OVERLAYS="$SLAPD_DYNAMIC_OVERLAYS deref.la"
+	else
+		MFLAG=SLAPD_MOD_STATIC
+		SLAPD_STATIC_OVERLAYS="$SLAPD_STATIC_OVERLAYS deref.o"
+	fi
+	AC_DEFINE_UNQUOTED(SLAPD_OVER_DEREF,$MFLAG,[define for Dynamic Directory Services overlay])
+fi
+
 if test "$ol_enable_dyngroup" != no ; then
 	BUILD_DYNGROUP=$ol_enable_dyngroup
 	if test "$ol_enable_dyngroup" = mod ; then
diff --git a/include/ldap.h b/include/ldap.h
index b86071aadb..99c2474723 100644
--- a/include/ldap.h
+++ b/include/ldap.h
@@ -321,6 +321,8 @@ typedef struct ldapcontrol {
 						LDAP_CONTROL_X_SESSION_TRACKING ".2"
 #define LDAP_CONTROL_X_SESSION_TRACKING_USERNAME \
 						LDAP_CONTROL_X_SESSION_TRACKING ".3"
+/* Dereference Control (work in progress) */
+#define	LDAP_CONTROL_X_DEREF			"1.3.6.1.4.1.4203.666.5.16"
 #endif /* LDAP_DEVEL */
 
 /* various expired works */
@@ -2400,5 +2402,56 @@ ldap_create_assertion_control LDAP_P((
 	int		iscritical,
 	LDAPControl	**ctrlp ));
 
+/*
+ * in deref.c
+ */
+
+typedef struct LDAPDerefSpec {
+	char *derefAttr;
+	char **attributes;
+} LDAPDerefSpec;
+
+typedef struct LDAPDerefVal {
+	char *type;
+	BerVarray vals;
+	struct LDAPDerefVal *next;
+} LDAPDerefVal;
+
+typedef struct LDAPDerefRes {
+	char *derefAttr;
+	struct berval derefVal;
+	LDAPDerefVal *attrVals;
+	struct LDAPDerefRes *next;
+} LDAPDerefRes;
+
+LDAP_F( int )
+ldap_create_deref_control_value LDAP_P((
+	LDAP *ld,
+	LDAPDerefSpec *ds,
+	struct berval *value ));
+
+LDAP_F( int )
+ldap_create_deref_control LDAP_P((
+	LDAP		*ld,
+	LDAPDerefSpec	*ds,
+	int		iscritical,
+	LDAPControl	**ctrlp ));
+
+LDAP_F( void )
+ldap_derefresponse_free LDAP_P((
+	LDAPDerefRes *dr ));
+
+LDAP_F( int )
+ldap_parse_derefresponse_control LDAP_P((
+	LDAP *ld,
+	LDAPControl *ctrl,
+	LDAPDerefRes **drp ));
+
+LDAP_F( int )
+ldap_parse_deref_control LDAP_P((
+	LDAP		*ld,
+	LDAPControl	**ctrls,
+	LDAPDerefRes	**drp ));
+
 LDAP_END_DECL
 #endif /* _LDAP_H */
diff --git a/include/portable.hin b/include/portable.hin
index eaaaa3aeb5..e5ff22e8ff 100644
--- a/include/portable.hin
+++ b/include/portable.hin
@@ -978,6 +978,9 @@
 /* define for Dynamic Directory Services overlay */
 #undef SLAPD_OVER_DDS
 
+/* define for Dynamic Directory Services overlay */
+#undef SLAPD_OVER_DEREF
+
 /* define for Dynamic Group overlay */
 #undef SLAPD_OVER_DYNGROUP
 
diff --git a/libraries/libldap/Makefile.in b/libraries/libldap/Makefile.in
index c62e56ffc0..ed8c2b17e6 100644
--- a/libraries/libldap/Makefile.in
+++ b/libraries/libldap/Makefile.in
@@ -27,7 +27,7 @@ SRCS	= bind.c open.c result.c error.c compare.c search.c \
 	init.c options.c print.c string.c util-int.c schema.c \
 	charray.c tls.c os-local.c dnssrv.c utf-8.c utf-8-conv.c \
 	turn.c ppolicy.c dds.c txn.c ldap_sync.c stctrl.c \
-	assertion.c
+	assertion.c deref.c
 
 OBJS	= bind.lo open.lo result.lo error.lo compare.lo search.lo \
 	controls.lo messages.lo references.lo extended.lo cyrus.lo \
@@ -39,7 +39,7 @@ OBJS	= bind.lo open.lo result.lo error.lo compare.lo search.lo \
 	init.lo options.lo print.lo string.lo util-int.lo schema.lo \
 	charray.lo tls.lo os-local.lo dnssrv.lo utf-8.lo utf-8-conv.lo \
 	turn.lo ppolicy.lo dds.lo txn.lo ldap_sync.lo stctrl.lo \
-	assertion.lo
+	assertion.lo deref.lo
 
 LDAP_INCDIR= ../../include       
 LDAP_LIBDIR= ../../libraries
diff --git a/libraries/libldap/deref.c b/libraries/libldap/deref.c
new file mode 100644
index 0000000000..003941d9a1
--- /dev/null
+++ b/libraries/libldap/deref.c
@@ -0,0 +1,282 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2008 The OpenLDAP Foundation.
+ * Portions Copyright 2008 Pierangelo Masarati.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Pierangelo Masarati
+ * for inclusion in OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/stdlib.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "ldap-int.h"
+
+int
+ldap_create_deref_control_value(
+	LDAP		*ld,
+	LDAPDerefSpec	*ds,
+	struct berval	*value )
+{
+	BerElement	*ber = NULL;
+	ber_tag_t	tag;
+	int		i;
+
+	if ( ld == NULL || value == NULL || ds == NULL )
+	{
+		if ( ld )
+			ld->ld_errno = LDAP_PARAM_ERROR;
+		return LDAP_PARAM_ERROR;
+	}
+
+	assert( LDAP_VALID( ld ) );
+
+	value->bv_val = NULL;
+	value->bv_len = 0;
+	ld->ld_errno = LDAP_SUCCESS;
+
+	ber = ldap_alloc_ber_with_options( ld );
+	if ( ber == NULL ) {
+		ld->ld_errno = LDAP_NO_MEMORY;
+		return ld->ld_errno;
+	}
+
+	tag = ber_printf( ber, "{" /*}*/ );
+	if ( tag == LBER_ERROR ) {
+		ld->ld_errno = LDAP_ENCODING_ERROR;
+		goto done;
+	}
+
+	for ( i = 0; ds[i].derefAttr != NULL; i++ ) {
+		int j;
+
+		tag = ber_printf( ber, "{s{" /*}}*/ , ds[i].derefAttr );
+		if ( tag == LBER_ERROR ) {
+			ld->ld_errno = LDAP_ENCODING_ERROR;
+			goto done;
+		}
+
+		for ( j = 0; ds[i].attributes[j] != NULL; j++ ) {
+			tag = ber_printf( ber, "s", ds[i].attributes[ j ] );
+			if ( tag == LBER_ERROR ) {
+				ld->ld_errno = LDAP_ENCODING_ERROR;
+				goto done;
+			}
+		}
+
+		tag = ber_printf( ber, /*{{*/ "}N}" );
+		if ( tag == LBER_ERROR ) {
+			ld->ld_errno = LDAP_ENCODING_ERROR;
+			goto done;
+		}
+	}
+
+	tag = ber_printf( ber, /*{*/ "}" );
+	if ( tag == LBER_ERROR ) {
+		ld->ld_errno = LDAP_ENCODING_ERROR;
+		goto done;
+	}
+
+	if ( ber_flatten2( ber, value, 1 ) == -1 ) {
+		ld->ld_errno = LDAP_NO_MEMORY;
+	}
+
+done:;
+	if ( ber != NULL ) {
+		ber_free( ber, 1 );
+	}
+
+	return ld->ld_errno;
+}
+
+int
+ldap_create_deref_control(
+	LDAP		*ld,
+	LDAPDerefSpec	*ds,
+	int		iscritical,
+	LDAPControl	**ctrlp )
+{
+	struct berval	value;
+
+	if ( ctrlp == NULL ) {
+		ld->ld_errno = LDAP_PARAM_ERROR;
+		return ld->ld_errno;
+	}
+
+	ld->ld_errno = ldap_create_deref_control_value( ld, ds, &value );
+	if ( ld->ld_errno == LDAP_SUCCESS ) {
+		ld->ld_errno = ldap_control_create( LDAP_CONTROL_PAGEDRESULTS,
+			iscritical, &value, 0, ctrlp );
+		if ( ld->ld_errno != LDAP_SUCCESS ) {
+			LDAP_FREE( value.bv_val );
+		}
+	}
+
+	return ld->ld_errno;
+}
+
+void
+ldap_derefresponse_free( LDAPDerefRes *dr )
+{
+	for ( ; dr; ) {
+		LDAPDerefRes *drnext = dr->next;
+		LDAPDerefVal *dv;
+
+		LDAP_FREE( dr->derefAttr );
+		LDAP_FREE( dr->derefVal.bv_val );
+
+		for ( dv = dr->attrVals; dv; ) {
+			LDAPDerefVal *dvnext = dv->next;
+			LDAP_FREE( dv->type );
+			ber_bvarray_free( dv->vals );
+			LDAP_FREE( dv );
+			dv = dvnext;
+		}
+
+		LDAP_FREE( dr );
+
+		dr = drnext;
+	}
+}
+
+int
+ldap_parse_derefresponse_control(
+	LDAP		*ld,
+	LDAPControl	*ctrl,
+	LDAPDerefRes	**drp2 )
+{
+	BerElement *ber;
+	ber_tag_t tag;
+	ber_len_t len;
+	char *last;
+	LDAPDerefRes *drhead = NULL, **drp;
+
+	if ( ld == NULL || ctrl == NULL || drp2 == NULL ) {
+		if ( ld )
+			ld->ld_errno = LDAP_PARAM_ERROR;
+		return LDAP_PARAM_ERROR;
+	}
+
+	/* Create a BerElement from the berval returned in the control. */
+	ber = ber_init( &ctrl->ldctl_value );
+
+	if ( ber == NULL ) {
+		ld->ld_errno = LDAP_NO_MEMORY;
+		return ld->ld_errno;
+	}
+
+	/* Extract the count and cookie from the control. */
+	drp = &drhead;
+	for ( tag = ber_first_element( ber, &len, &last );
+		tag != LBER_DEFAULT;
+		tag = ber_next_element( ber, &len, last ) )
+	{
+		LDAPDerefRes *dr;
+		LDAPDerefVal **dvp;
+		char *last2;
+
+		dr = LDAP_CALLOC( 1, sizeof(LDAPDerefRes) );
+		dvp = &dr->attrVals;
+
+		tag = ber_scanf( ber, "{ao", &dr->derefAttr, &dr->derefVal );
+		if ( tag == LBER_ERROR ) {
+			goto done;
+		}
+
+		tag = ber_peek_tag( ber, &len );
+		if ( tag == (LBER_CONSTRUCTED|LBER_CLASS_CONTEXT) ) {
+			for ( tag = ber_first_element( ber, &len, &last2 );
+				tag != LBER_DEFAULT;
+				tag = ber_next_element( ber, &len, last2 ) )
+			{
+				LDAPDerefVal *dv;
+
+				dv = LDAP_CALLOC( 1, sizeof(LDAPDerefVal) );
+
+				tag = ber_scanf( ber, "{a[W]}", &dv->type, &dv->vals );
+				if ( tag == LBER_ERROR ) {
+					goto done;
+				}
+
+				*dvp = dv;
+				dvp = &dv->next;
+			}
+		}
+
+		tag = ber_scanf( ber, "}" );
+		if ( tag == LBER_ERROR ) {
+			goto done;
+		}
+
+		*drp = dr;
+		drp = &dr->next;
+	}
+
+	tag = 0;
+
+done:;
+        ber_free( ber, 1 );
+
+	if ( tag == LBER_ERROR ) {
+		if ( drhead != NULL ) {
+			ldap_derefresponse_free( drhead );
+		}
+
+		*drp2 = NULL;
+		ld->ld_errno = LDAP_DECODING_ERROR;
+
+	} else {
+		*drp2 = drhead;
+		ld->ld_errno = LDAP_SUCCESS;
+	}
+
+	return ld->ld_errno;
+}
+
+int
+ldap_parse_deref_control(
+	LDAP		*ld,
+	LDAPControl	**ctrls,
+	LDAPDerefRes	**drp )
+{
+	LDAPControl *c;
+
+	if ( drp == NULL ) {
+		ld->ld_errno = LDAP_PARAM_ERROR;
+		return ld->ld_errno;
+	}
+
+	*drp = NULL;
+
+	if ( ctrls == NULL ) {
+		ld->ld_errno =  LDAP_CONTROL_NOT_FOUND;
+		return ld->ld_errno;
+	}
+
+	c = ldap_control_find( LDAP_CONTROL_X_DEREF, ctrls, NULL );
+	if ( c == NULL ) {
+		/* No deref control was found. */
+		ld->ld_errno = LDAP_CONTROL_NOT_FOUND;
+		return ld->ld_errno;
+	}
+
+	ld->ld_errno = ldap_parse_derefresponse_control( ld, c, drp );
+
+	return ld->ld_errno;
+}
+
diff --git a/servers/slapd/overlays/Makefile.in b/servers/slapd/overlays/Makefile.in
index 07b4b1ebb4..ce60327a4e 100644
--- a/servers/slapd/overlays/Makefile.in
+++ b/servers/slapd/overlays/Makefile.in
@@ -18,6 +18,7 @@ SRCS = overlays.c \
 	auditlog.c \
 	constraint.c \
 	dds.c \
+	deref.c \
 	dyngroup.c \
 	dynlist.c \
 	memberof.c \
@@ -72,6 +73,9 @@ constraint.la : constraint.lo
 dds.la : dds.lo
 	$(LTLINK_MOD) -module -o $@ dds.lo version.lo $(LINK_LIBS)
 
+deref.la : deref.lo
+	$(LTLINK_MOD) -module -o $@ deref.lo version.lo $(LINK_LIBS)
+
 dyngroup.la : dyngroup.lo
 	$(LTLINK_MOD) -module -o $@ dyngroup.lo version.lo $(LINK_LIBS)
 
diff --git a/servers/slapd/overlays/deref.c b/servers/slapd/overlays/deref.c
new file mode 100644
index 0000000000..c3d4e7d8ed
--- /dev/null
+++ b/servers/slapd/overlays/deref.c
@@ -0,0 +1,565 @@
+/* deref.c - dereference overlay */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2008 The OpenLDAP Foundation.
+ * Portions Copyright 2008 Pierangelo Masarati.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Pierangelo Masarati
+ * for inclusion in OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#ifdef SLAPD_OVER_DEREF
+
+#include <stdio.h>
+
+#include "ac/string.h"
+#include "ac/socket.h"
+
+#include "slap.h"
+#include "config.h"
+
+#include "lutil.h"
+
+/*
+ * 1. Specification
+ *
+ * 1.1. Request
+ *
+ *  controlValue ::= SEQUENCE OF derefSpec DerefSpec
+ *
+ *  DerefSpec ::= SEQUENCE {
+ *      derefAttr       attributeDescription,    ; DN-valued
+ *      attributes      AttributeList }
+ *
+ *  AttributeList ::= SEQUENCE OF attr AttributeDescription
+ *
+ *  derefAttr MUST be unique within controlValue
+ *
+ *
+ * 1.2. Response
+ *
+ *  controlValue ::= SEQUENCE OF DerefRes
+ *
+ * From RFC 4511:
+ *      PartialAttribute ::= SEQUENCE {
+ *           type       AttributeDescription,
+ *           vals       SET OF value AttributeValue }
+ *
+ *      PartialAttributeList ::= SEQUENCE OF
+ *                           partialAttribute PartialAttribute
+ *
+ *  DerefRes ::= SEQUENCE {
+ *      derefAttr       AttributeDescription,
+ *      derefVal        LDAPDN,
+ *      attrVals        [0] PartialAttributeList OPTIONAL }
+ *
+ *  If vals is empty, partialAttribute is omitted.
+ *  If all vals in attrVals are empty, attrVals is omitted.
+ *      
+ * 2. Examples
+ *
+ * 2.1. Example
+ *
+ * 2.1.1. Request
+ *
+ * { { member, { GUID, SID } }, { memberOf, { GUID, SID } } }
+ *
+ * 2.1.2. Response
+ *
+ * { { memberOf, "cn=abartlet,cn=users,dc=abartlet,dc=net",
+ *     { { GUID, [ "0bc11d00-e431-40a0-8767-344a320142fa" ] },
+ *       { SID, [ "S-1-2-3-2345" ] } } },
+ *   { memberOf, "cn=ando,cn=users,dc=sys-net,dc=it",
+ *     { { GUID, [ "0bc11d00-e431-40a0-8767-344a320142fb" ] },
+ *       { SID, [ "S-1-2-3-2346" ] } } } }
+ *
+ * 2.2. Example
+ *
+ * 2.2.1. Request
+ *
+ * { { member, { cn, uid, drink } } }
+ *
+ * 2.2.2. Response
+ *
+ * { { member, "cn=ando,cn=users,dc=sys-net,dc=it",
+ *     { { cn, [ "ando", "Pierangelo Masarati" ] },
+ *       { uid, [ "ando" ] } } },
+ *   { member, "dc=sys-net,dc=it" } }
+ *
+ *
+ * 3. Security considerations
+ *
+ * The control result must not disclose information the client's
+ * identity could not have accessed directly by performing the related
+ * search operations.  The presence of a derefVal in the control
+ * response does not imply neither the existence of nor any access
+ * privilege to the corresponding entry.  It is merely a consequence
+ * of the read access the client's identity has on the corresponding
+ * attribute's value.
+ */
+
+#define o_deref			o_ctrlflag[deref_cid]
+#define o_ctrlderef		o_controls[deref_cid]
+
+typedef struct DerefSpec {
+	AttributeDescription	*ds_derefAttr;
+	AttributeDescription	**ds_attributes;
+	int			ds_nattrs;
+	struct DerefSpec	*ds_next;
+} DerefSpec;
+
+typedef struct DerefVal {
+	struct berval	dv_derefSpecVal;
+	BerVarray	*dv_attrVals;
+} DerefVal;
+
+typedef struct DerefRes {
+	DerefSpec		dr_spec;
+	DerefVal		*dr_vals;
+	struct DerefRes		*dr_next;
+} DerefRes;
+
+typedef struct deref_cb_t {
+	slap_overinst *dc_on;
+	DerefSpec *dc_ds;
+} deref_cb_t;
+
+static int			deref_cid;
+static slap_overinst 		deref;
+
+static int
+deref_parseCtrl (
+	Operation *op,
+	SlapReply *rs,
+	LDAPControl *ctrl )
+{
+	ber_tag_t tag;
+	BerElementBuffer berbuf;
+	BerElement *ber = (BerElement *)&berbuf;
+	ber_len_t len;
+	char *last;
+	DerefSpec *dshead = NULL, **dsp = &dshead;
+	BerVarray attributes = NULL;
+
+	if ( op->o_deref != SLAP_CONTROL_NONE ) {
+		rs->sr_text = "Dereference control specified multiple times";
+		return LDAP_PROTOCOL_ERROR;
+	}
+
+	if ( BER_BVISNULL( &ctrl->ldctl_value ) ) {
+		rs->sr_text = "Dereference control value is absent";
+		return LDAP_PROTOCOL_ERROR;
+	}
+
+	if ( BER_BVISEMPTY( &ctrl->ldctl_value ) ) {
+		rs->sr_text = "Dereference control value is empty";
+		return LDAP_PROTOCOL_ERROR;
+	}
+
+	ber_init2( ber, &ctrl->ldctl_value, 0 );
+
+	for ( tag = ber_first_element( ber, &len, &last );
+		tag != LBER_DEFAULT;
+		tag = ber_next_element( ber, &len, last ) )
+	{
+		struct berval derefAttr;
+		DerefSpec *ds, *dstmp;
+		const char *text;
+		int rc;
+		ber_len_t cnt = sizeof(struct berval);
+		ber_len_t off = 0;
+
+		if ( ber_scanf( ber, "{m{M}}", &derefAttr, &attributes, &cnt, off ) == LBER_ERROR )
+		{
+			rs->sr_text = "Dereference control: derefSpec decoding error";
+			rs->sr_err = LDAP_PROTOCOL_ERROR;
+			goto done;
+		}
+
+		ds = (DerefSpec *)op->o_tmpcalloc( 1,
+			sizeof(DerefSpec) + sizeof(AttributeDescription *)*(cnt + 1),
+			op->o_tmpmemctx );
+		ds->ds_attributes = (AttributeDescription **)&ds[ 1 ];
+		ds->ds_nattrs = cnt;
+
+		rc = slap_bv2ad( &derefAttr, &ds->ds_derefAttr, &text );
+		if ( rc != LDAP_SUCCESS ) {
+			rs->sr_text = "Dereference control: derefAttr decoding error";
+			rs->sr_err = LDAP_PROTOCOL_ERROR;
+			goto done;
+		}
+
+		for ( dstmp = dshead; dstmp && dstmp != ds; dstmp = dstmp->ds_next ) {
+			if ( dstmp->ds_derefAttr == ds->ds_derefAttr ) {
+				rs->sr_text = "Dereference control: derefAttr must be unique within control";
+				rs->sr_err = LDAP_PROTOCOL_ERROR;
+				goto done;
+			}
+		}
+
+		if ( ds->ds_derefAttr->ad_type->sat_syntax != slap_schema.si_syn_distinguishedName ) {
+			if ( ctrl->ldctl_iscritical ) {
+				rs->sr_text = "Dereference control: derefAttr syntax not distinguishedName";
+				rs->sr_err = LDAP_PROTOCOL_ERROR;
+				goto done;
+			}
+
+			rs->sr_err = LDAP_SUCCESS;
+			goto justcleanup;
+		}
+
+		for ( cnt = 0; !BER_BVISNULL( &attributes[ cnt ] ); cnt++ ) {
+			rc = slap_bv2ad( &attributes[ cnt ], &ds->ds_attributes[ cnt ], &text );
+			if ( rc != LDAP_SUCCESS ) {
+				rs->sr_text = "Dereference control: attribute decoding error";
+				rs->sr_err = LDAP_PROTOCOL_ERROR;
+				goto done;
+			}
+		}
+
+		ber_memfree_x( attributes, op->o_tmpmemctx );
+		attributes = NULL;
+
+		*dsp = ds;
+		dsp = &ds->ds_next;
+	}
+
+	op->o_ctrlderef = (void *)dshead;
+
+	op->o_deref = ctrl->ldctl_iscritical
+		? SLAP_CONTROL_CRITICAL
+		: SLAP_CONTROL_NONCRITICAL;
+
+	rs->sr_err = LDAP_SUCCESS;
+
+done:;
+	if ( rs->sr_err != LDAP_SUCCESS ) {
+justcleanup:;
+		for ( ; dshead; ) {
+			DerefSpec *dsnext = dshead->ds_next;
+			op->o_tmpfree( dshead, op->o_tmpmemctx );
+			dshead = dsnext;
+		}
+	}
+
+	if ( attributes != NULL ) {
+		ber_memfree_x( attributes, op->o_tmpmemctx );
+	}
+
+	return rs->sr_err;
+}
+
+static int
+deref_cleanup( Operation *op, SlapReply *rs )
+{
+	if ( rs->sr_type == REP_RESULT || rs->sr_err == SLAPD_ABANDON ) {
+		op->o_tmpfree( op->o_callback, op->o_tmpmemctx );
+		op->o_callback = NULL;
+
+		op->o_tmpfree( op->o_ctrlderef, op->o_tmpmemctx );
+		op->o_ctrlderef = NULL;
+	}
+
+	return SLAP_CB_CONTINUE;
+}
+
+static int
+deref_response( Operation *op, SlapReply *rs )
+{
+	int rc = SLAP_CB_CONTINUE;
+
+	if ( rs->sr_type == REP_SEARCH ) {
+		BerElementBuffer berbuf;
+		BerElement *ber = (BerElement *) &berbuf;
+		deref_cb_t *dc = (deref_cb_t *)op->o_callback->sc_private;
+		DerefSpec *ds;
+		DerefRes *dr, *drhead = NULL, **drp = &drhead;
+		struct berval bv = BER_BVNULL;
+		int nDerefRes = 0, nDerefVals = 0, nAttrs = 0, nVals = 0;
+		struct berval ctrlval;
+		LDAPControl *ctrl, **ctrlsp;
+		AccessControlState acl_state = ACL_STATE_INIT;
+		static char dummy = '\0';
+		Entry *ebase;
+		int i;
+
+		rc = overlay_entry_get_ov( op, &rs->sr_entry->e_nname, NULL, NULL, 0, &ebase, dc->dc_on );
+		if ( rc != LDAP_SUCCESS || ebase == NULL ) {
+			return SLAP_CB_CONTINUE;
+		}
+
+		for ( ds = dc->dc_ds; ds; ds = ds->ds_next ) {
+			Attribute *a = attr_find( ebase->e_attrs, ds->ds_derefAttr );
+
+			if ( a != NULL ) {
+				DerefVal *dv;
+				BerVarray *bva;
+
+				if ( !access_allowed( op, rs->sr_entry, a->a_desc,
+						NULL, ACL_READ, &acl_state ) )
+				{
+					continue;
+				}
+
+				dr = op->o_tmpcalloc( 1,
+					sizeof( DerefRes ) + ( sizeof( DerefVal ) + sizeof( BerVarray * ) * ds->ds_nattrs ) * ( a->a_numvals + 1 ),
+					op->o_tmpmemctx );
+				dr->dr_spec = *ds;
+				dv = dr->dr_vals = (DerefVal *)&dr[ 1 ];
+				bva = (BerVarray *)&dv[ a->a_numvals + 1 ];
+
+				bv.bv_len += ds->ds_derefAttr->ad_cname.bv_len;
+				nAttrs++;
+				nDerefRes++;
+
+				for ( i = 0; !BER_BVISNULL( &a->a_nvals[ i ] ); i++ ) {
+					Entry *e = NULL;
+
+					dv[ i ].dv_attrVals = bva;
+					bva += ds->ds_nattrs;
+
+
+					if ( !access_allowed( op, rs->sr_entry, a->a_desc,
+							&a->a_nvals[ i ], ACL_READ, &acl_state ) )
+					{
+						dv[ i ].dv_derefSpecVal.bv_val = &dummy;
+						continue;
+					}
+
+					ber_dupbv_x( &dv[ i ].dv_derefSpecVal, &a->a_vals[ i ], op->o_tmpmemctx );
+					bv.bv_len += dv[ i ].dv_derefSpecVal.bv_len;
+					nVals++;
+					nDerefVals++;
+
+					rc = overlay_entry_get_ov( op, &a->a_nvals[ i ], NULL, NULL, 0, &e, dc->dc_on );
+					if ( rc == LDAP_SUCCESS && e != NULL ) {
+						int j;
+
+						if ( access_allowed( op, e, slap_schema.si_ad_entry,
+							NULL, ACL_READ, NULL ) )
+						{
+							for ( j = 0; j < ds->ds_nattrs; j++ ) {
+								Attribute *aa;
+
+								if ( !access_allowed( op, e, ds->ds_attributes[ j ], NULL,
+									ACL_READ, &acl_state ) )
+								{
+									continue;
+								}
+
+								aa = attr_find( e->e_attrs, ds->ds_attributes[ j ] );
+								if ( aa != NULL ) {
+									unsigned k, h, last = aa->a_numvals;
+
+									ber_bvarray_dup_x( &dv[ i ].dv_attrVals[ j ],
+										aa->a_vals, op->o_tmpmemctx );
+
+									bv.bv_len += ds->ds_attributes[ j ]->ad_cname.bv_len;
+
+									for ( k = 0, h = 0; k < aa->a_numvals; k++ ) {
+										if ( !access_allowed( op, e,
+											aa->a_desc,
+											&aa->a_nvals[ k ],
+											ACL_READ, &acl_state ) )
+										{
+											op->o_tmpfree( dv[ i ].dv_attrVals[ j ][ h ].bv_val,
+												op->o_tmpmemctx );
+											dv[ i ].dv_attrVals[ j ][ h ] = dv[ i ].dv_attrVals[ j ][ --last ];
+											BER_BVZERO( &dv[ i ].dv_attrVals[ j ][ last ] );
+											continue;
+										}
+										bv.bv_len += dv[ i ].dv_attrVals[ j ][ h ].bv_len;
+										nVals++;
+										h++;
+									}
+									nAttrs++;
+								}
+							}
+						}
+
+						overlay_entry_release_ov( op, e, 0, dc->dc_on );
+					}
+				}
+
+				*drp = dr;
+				drp = &dr->dr_next;
+			}
+		}
+		overlay_entry_release_ov( op, ebase, 0, dc->dc_on );
+
+		if ( drhead == NULL ) {
+			return SLAP_CB_CONTINUE;
+		}
+
+		/* cook the control value */
+		bv.bv_len += nVals * sizeof(struct berval)
+			+ nAttrs * sizeof(struct berval)
+			+ nDerefVals * sizeof(DerefVal)
+			+ nDerefRes * sizeof(DerefRes);
+		bv.bv_val = op->o_tmpalloc( bv.bv_len, op->o_tmpmemctx );
+
+		ber_init2( ber, &bv, LBER_USE_DER );
+		ber_set_option( ber, LBER_OPT_BER_MEMCTX, &op->o_tmpmemctx );
+
+		rc = ber_printf( ber, "{" /*}*/ );
+		for ( dr = drhead; dr != NULL; dr = dr->dr_next ) {
+			for ( i = 0; !BER_BVISNULL( &dr->dr_vals[ i ].dv_derefSpecVal ); i++ ) {
+				int j, first = 1;
+
+				if ( dr->dr_vals[ i ].dv_derefSpecVal.bv_val == &dummy ) {
+					continue;
+				}
+
+				rc = ber_printf( ber, "{OO" /*}*/,
+					&dr->dr_spec.ds_derefAttr->ad_cname,
+					&dr->dr_vals[ i ].dv_derefSpecVal );
+				op->o_tmpfree( dr->dr_vals[ i ].dv_derefSpecVal.bv_val, op->o_tmpmemctx );
+				for ( j = 0; j < dr->dr_spec.ds_nattrs; j++ ) {
+					if ( dr->dr_vals[ i ].dv_attrVals[ j ] != NULL ) {
+						if ( first ) {
+							rc = ber_printf( ber, "t{" /*}*/,
+								(LBER_CONSTRUCTED|LBER_CLASS_CONTEXT) );
+							first = 0;
+						}
+						rc = ber_printf( ber, "{O[W]}",
+							&dr->dr_spec.ds_attributes[ j ]->ad_cname,
+							dr->dr_vals[ i ].dv_attrVals[ j ] );
+						op->o_tmpfree( dr->dr_vals[ i ].dv_attrVals[ j ],
+							op->o_tmpmemctx );
+					}
+				}
+				if ( !first ) {
+					rc = ber_printf( ber, /*{{*/ "}N}" );
+				} else {
+					rc = ber_printf( ber, /*{*/ "}" );
+				}
+			}
+		}
+		rc = ber_printf( ber, /*{*/ "}" );
+		if ( ber_flatten2( ber, &ctrlval, 0 ) == -1 ) {
+			if ( op->o_deref == SLAP_CONTROL_CRITICAL ) {
+				rc = LDAP_CONSTRAINT_VIOLATION;
+
+			} else {
+				rc = SLAP_CB_CONTINUE;
+			}
+			goto cleanup;
+		}
+
+		ctrl = op->o_tmpcalloc( 1,
+			sizeof( LDAPControl ) + ctrlval.bv_len + 1,
+			op->o_tmpmemctx );
+		ctrl->ldctl_value.bv_val = (char *)&ctrl[ 1 ];
+		ctrl->ldctl_oid = LDAP_CONTROL_X_DEREF;
+		ctrl->ldctl_iscritical = 0;
+		ctrl->ldctl_value.bv_len = ctrlval.bv_len;
+		lutil_strncopy( ctrl->ldctl_value.bv_val, ctrlval.bv_val, ctrlval.bv_len );
+		ctrl->ldctl_value.bv_val[ ctrl->ldctl_value.bv_len ] = '\0';
+
+		ber_free_buf( ber );
+
+		i = 0;
+		if ( rs->sr_ctrls ) {
+			for ( ; rs->sr_ctrls[ i ] != NULL; i++ )
+				/* count'em */ ;
+		}
+		i += 2;
+		ctrlsp = op->o_tmpcalloc( i, sizeof(LDAPControl *), op->o_tmpmemctx );
+		i = 0;
+		if ( rs->sr_ctrls != NULL ) {
+			for ( ; rs->sr_ctrls[ i ] != NULL; i++ ) {
+				ctrlsp[ i ] = rs->sr_ctrls[ i ];
+			}
+		}
+		ctrlsp[ i++ ] = ctrl;
+		ctrlsp[ i++ ] = NULL;
+		if ( rs->sr_flags & REP_CTRLS_MUSTBEFREED ) {
+			op->o_tmpfree( rs->sr_ctrls, op->o_tmpmemctx );
+		}
+		rs->sr_ctrls = ctrlsp;
+		rs->sr_flags |= REP_CTRLS_MUSTBEFREED;
+
+		rc = SLAP_CB_CONTINUE;
+
+cleanup:;
+		/* release all */
+		for ( ; drhead != NULL; ) {
+			DerefRes *drnext = drhead->dr_next;
+			op->o_tmpfree( drhead, op->o_tmpmemctx );
+			drhead = drnext;
+		}
+
+	} else if ( rs->sr_type == REP_RESULT ) {
+		rc = deref_cleanup( op, rs );
+	}
+
+	return rc;
+}
+
+static int
+deref_op_search( Operation *op, SlapReply *rs )
+{
+	if ( op->o_deref ) {
+		slap_callback *sc;
+		deref_cb_t *dc;
+
+		sc = op->o_tmpcalloc( 1, sizeof( slap_callback ) + sizeof( deref_cb_t ), op->o_tmpmemctx );
+
+		dc = (deref_cb_t *)&sc[ 1 ];
+		dc->dc_on = (slap_overinst *)op->o_bd->bd_info;
+		dc->dc_ds = (DerefSpec *)op->o_ctrlderef;
+
+		sc->sc_response = deref_response;
+		sc->sc_cleanup = deref_cleanup;
+		sc->sc_private = (void *)dc;
+
+		sc->sc_next = op->o_callback->sc_next;
+                op->o_callback->sc_next = sc;
+	}
+
+	return SLAP_CB_CONTINUE;
+}
+
+int
+deref_initialize(void)
+{
+	int rc;
+
+	rc = register_supported_control( LDAP_CONTROL_X_DEREF,
+		SLAP_CTRL_SEARCH, NULL,
+		deref_parseCtrl, &deref_cid );
+	if ( rc != LDAP_SUCCESS ) {
+		Debug( LDAP_DEBUG_ANY,
+			"deref_init: Failed to register control (%d)\n",
+			rc, 0, 0 );
+		return -1;
+	}
+
+	deref.on_bi.bi_type = "deref";
+	deref.on_bi.bi_op_search = deref_op_search;
+
+	return overlay_register( &deref );
+}
+
+#if SLAPD_OVER_DEREF == SLAPD_MOD_DYNAMIC
+int
+init_module( int argc, char *argv[] )
+{
+	return deref_initialize();
+}
+#endif /* SLAPD_OVER_DEREF == SLAPD_MOD_DYNAMIC */
+
+#endif /* SLAPD_OVER_DEREF */
-- 
GitLab