summaryrefslogtreecommitdiffstats
path: root/servers/slapd/back-ldap
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 17:54:12 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 17:54:12 +0000
commitb527294153be3b79563c82c66102adc0004736c0 (patch)
tree9b423a224848441885190b5ea7cf0feb23510c9d /servers/slapd/back-ldap
parentInitial commit. (diff)
downloadopenldap-b527294153be3b79563c82c66102adc0004736c0.tar.xz
openldap-b527294153be3b79563c82c66102adc0004736c0.zip
Adding upstream version 2.6.7+dfsg.upstream/2.6.7+dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'servers/slapd/back-ldap')
-rw-r--r--servers/slapd/back-ldap/Makefile.in45
-rw-r--r--servers/slapd/back-ldap/TODO.proxy101
-rw-r--r--servers/slapd/back-ldap/add.c139
-rw-r--r--servers/slapd/back-ldap/back-ldap.h479
-rw-r--r--servers/slapd/back-ldap/bind.c3230
-rw-r--r--servers/slapd/back-ldap/chain.c2356
-rw-r--r--servers/slapd/back-ldap/compare.c88
-rw-r--r--servers/slapd/back-ldap/config.c2214
-rw-r--r--servers/slapd/back-ldap/delete.c85
-rw-r--r--servers/slapd/back-ldap/distproc.c998
-rw-r--r--servers/slapd/back-ldap/extended.c386
-rw-r--r--servers/slapd/back-ldap/init.c374
-rw-r--r--servers/slapd/back-ldap/modify.c136
-rw-r--r--servers/slapd/back-ldap/modrdn.c123
-rw-r--r--servers/slapd/back-ldap/monitor.c1074
-rw-r--r--servers/slapd/back-ldap/pbind.c173
-rw-r--r--servers/slapd/back-ldap/proto-ldap.h124
-rw-r--r--servers/slapd/back-ldap/search.c1042
-rw-r--r--servers/slapd/back-ldap/unbind.c78
19 files changed, 13245 insertions, 0 deletions
diff --git a/servers/slapd/back-ldap/Makefile.in b/servers/slapd/back-ldap/Makefile.in
new file mode 100644
index 0000000..4b10d77
--- /dev/null
+++ b/servers/slapd/back-ldap/Makefile.in
@@ -0,0 +1,45 @@
+# Makefile.in for back-ldap
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## 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>.
+
+SRCS = init.c config.c search.c bind.c unbind.c add.c compare.c \
+ delete.c modify.c modrdn.c extended.c chain.c \
+ distproc.c monitor.c pbind.c
+OBJS = init.lo config.lo search.lo bind.lo unbind.lo add.lo compare.lo \
+ delete.lo modify.lo modrdn.lo extended.lo chain.lo \
+ distproc.lo monitor.lo pbind.lo
+
+LDAP_INCDIR= ../../../include
+LDAP_LIBDIR= ../../../libraries
+
+BUILD_OPT = "--enable-ldap"
+BUILD_MOD = @BUILD_LDAP@
+
+mod_DEFS = -DSLAPD_IMPORT
+MOD_DEFS = $(@BUILD_LDAP@_DEFS)
+
+shared_LDAP_LIBS = $(LDAP_LIBLDAP_LA) $(LDAP_LIBLBER_LA)
+NT_LINK_LIBS = -L.. -lslapd $(@BUILD_LIBS_DYNAMIC@_LDAP_LIBS)
+UNIX_LINK_LIBS = $(@BUILD_LIBS_DYNAMIC@_LDAP_LIBS)
+
+LIBBASE = back_ldap
+
+XINCPATH = -I.. -I$(srcdir)/..
+XDEFS = $(MODULES_CPPFLAGS)
+
+all-local-lib: ../.backend
+
+../.backend: lib$(LIBBASE).a
+ @touch $@
+
diff --git a/servers/slapd/back-ldap/TODO.proxy b/servers/slapd/back-ldap/TODO.proxy
new file mode 100644
index 0000000..01406c5
--- /dev/null
+++ b/servers/slapd/back-ldap/TODO.proxy
@@ -0,0 +1,101 @@
+back-proxy
+
+A proxy that handles a pool of URI associated to a unique suffix.
+Each request is spread over the different URIs and results are
+masqueraded to appear as coming from a unique server.
+
+Suppose a company has two branches, whose existing DS have URIs
+
+"ldap://ldap.branch1.com/o=Branch 1, c=US"
+"ldap://ldap.branch2.it/o=Branch 2, c=IT"
+
+and it wants to propose to the outer world as a unique URI
+
+"ldap://ldap.company.net/dc=company, dc=net"
+
+It could do some rewriting to map everything that comes in with a base DN
+of "o=Branch 1, dc=company, dc=net" as the URI of the Branch 1, and
+everything that comes in with a base DN of "o=Branch 2, dc=company, dc=net"
+as the URI of Branch 2, and by rewriting all the DNs back to the new, uniform
+base. Everything that comes in with a base DN of "dc=company, dc=net" should
+be handled locally and propagated to the two branch URIs if a subtree
+(or at least onelevel) search is required.
+
+Operations:
+
+- bind
+- unbind
+- search
+- compare
+- add
+- modify
+- modrdn
+- delete
+- abandon
+
+The input of each operation may be related to:
+
+ exact DN exact parent ancestor
+-------------------------------------------------------------
+bind x
+unbind
+search x x x
+compare x
+add x
+modify x
+modrdn x
+delete x
+abandon
+
+The backend must rely on a DN fetching mechanism. Each operation requires
+to determine as early as possible which URI will be able to satisfy it.
+Apart from searches, which by definition are usually allowed to return
+multiple results, and apart from unbind and abandon, which do not return any
+result, all the remaining operations require the related entry to be unique.
+
+A major problem isposed by the uniqueness of the DNs. As far as the suffixes
+are masqueraded by a common suffix, the DNs are no longer guaranteed to be
+unique. This backend relies on the assumption that the uniqueness of the
+DNs is guaranteed.
+
+Two layers of depth in DN fetching are envisaged.
+The first layer is provided by a backend-side cache made of previously
+retrieved entries. The cache relates each RDN (i.e. the DN apart from the
+common suffix) to the pool of URIs that are expected to contain a subset
+of its children.
+
+The second layer is provided by a fetching function that spawns a search for
+each URI in the pool determined by the cache if the correct URI has not been
+directly determined.
+
+Note that, as the remote servers may have been updated by some direct
+operation, this mechanism does not guarantee the uniqueness of the result.
+So write operations will require to skip the cache search and to perform
+the exhaustive search of all the URIs unless some hint mechanism is provided
+to the backend (e.g. a server is read-only).
+
+Again, the lag between the fetching of the required DN and the actual
+read/write may result in a failure; however, this applies to any LDAP
+operation AFAIK.
+
+- bind
+if updates are to be strictly honored, a bind operation is performed against
+each URI; otherwise, it is performed against the URIs resulting from a
+cache-level DN fetch.
+
+- unbind
+nothing to say; all the open handles related to the connection are reset.
+
+- search
+if updates are to be strictly honored, a search operation is performed against
+each URI. Note that this needs be performed also when the backend suffix
+is used as base. In case the base is stricter, the URI pool may be restricted
+by performing a cache DN fetch of the base first.
+
+- compare
+the same applies to the compare DN.
+
+- add
+this operation is delicate. Unless the DN up to the top-level part excluded
+can be uniquely associated to a URI, and unless its uniqueness can be trusted,
+no add operation should be allowed.
diff --git a/servers/slapd/back-ldap/add.c b/servers/slapd/back-ldap/add.c
new file mode 100644
index 0000000..32ceda2
--- /dev/null
+++ b/servers/slapd/back-ldap/add.c
@@ -0,0 +1,139 @@
+/* add.c - ldap backend add function */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1999-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2000-2003 Pierangelo Masarati.
+ * Portions Copyright 1999-2003 Howard Chu.
+ * 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 the Howard Chu for inclusion
+ * in OpenLDAP Software and subsequently enhanced by Pierangelo
+ * Masarati.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#include "slap.h"
+#include "back-ldap.h"
+
+int
+ldap_back_add(
+ Operation *op,
+ SlapReply *rs )
+{
+ ldapinfo_t *li = (ldapinfo_t *)op->o_bd->be_private;
+
+ ldapconn_t *lc = NULL;
+ int i = 0,
+ j = 0;
+ Attribute *a;
+ LDAPMod **attrs = NULL,
+ *attrs2 = NULL;
+ ber_int_t msgid;
+ int isupdate;
+ ldap_back_send_t retrying = LDAP_BACK_RETRYING;
+ LDAPControl **ctrls = NULL;
+
+ rs->sr_err = LDAP_SUCCESS;
+
+ Debug( LDAP_DEBUG_ARGS, "==> ldap_back_add(\"%s\")\n",
+ op->o_req_dn.bv_val );
+
+ if ( !ldap_back_dobind( &lc, op, rs, LDAP_BACK_SENDERR ) ) {
+ lc = NULL;
+ goto cleanup;
+ }
+
+ /* Count number of attributes in entry */
+ for ( i = 1, a = op->oq_add.rs_e->e_attrs; a; i++, a = a->a_next )
+ /* just count attrs */ ;
+
+ /* Create array of LDAPMods for ldap_add() */
+ attrs = (LDAPMod **)ch_malloc( sizeof( LDAPMod * )*i
+ + sizeof( LDAPMod )*( i - 1 ) );
+ attrs2 = ( LDAPMod * )&attrs[ i ];
+
+ isupdate = be_shadow_update( op );
+ for ( i = 0, a = op->oq_add.rs_e->e_attrs; a; a = a->a_next ) {
+ if ( !isupdate && !get_relax( op ) && a->a_desc->ad_type->sat_no_user_mod )
+ {
+ continue;
+ }
+
+ attrs[ i ] = &attrs2[ i ];
+ attrs[ i ]->mod_op = LDAP_MOD_BVALUES;
+ attrs[ i ]->mod_type = a->a_desc->ad_cname.bv_val;
+
+ for ( j = 0; a->a_vals[ j ].bv_val; j++ )
+ /* just count vals */ ;
+ attrs[i]->mod_vals.modv_bvals =
+ ch_malloc( ( j + 1 )*sizeof( struct berval * ) );
+ for ( j = 0; a->a_vals[ j ].bv_val; j++ ) {
+ attrs[ i ]->mod_vals.modv_bvals[ j ] = &a->a_vals[ j ];
+ }
+ attrs[ i ]->mod_vals.modv_bvals[ j ] = NULL;
+ i++;
+ }
+ attrs[ i ] = NULL;
+
+retry:
+ ctrls = op->o_ctrls;
+ rs->sr_err = ldap_back_controls_add( op, rs, lc, &ctrls );
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ send_ldap_result( op, rs );
+ goto cleanup;
+ }
+
+ rs->sr_err = ldap_add_ext( lc->lc_ld, op->o_req_dn.bv_val, attrs,
+ ctrls, NULL, &msgid );
+ rs->sr_err = ldap_back_op_result( lc, op, rs, msgid,
+ li->li_timeout[ SLAP_OP_ADD ],
+ ( LDAP_BACK_SENDRESULT | retrying ) );
+ if ( rs->sr_err == LDAP_UNAVAILABLE && retrying ) {
+ retrying &= ~LDAP_BACK_RETRYING;
+ if ( ldap_back_retry( &lc, op, rs, LDAP_BACK_SENDERR ) ) {
+ /* if the identity changed, there might be need to re-authz */
+ (void)ldap_back_controls_free( op, rs, &ctrls );
+ goto retry;
+ }
+ }
+
+ ldap_pvt_thread_mutex_lock( &li->li_counter_mutex );
+ ldap_pvt_mp_add( li->li_ops_completed[ SLAP_OP_ADD ], 1 );
+ ldap_pvt_thread_mutex_unlock( &li->li_counter_mutex );
+
+cleanup:
+ (void)ldap_back_controls_free( op, rs, &ctrls );
+
+ if ( attrs ) {
+ for ( --i; i >= 0; --i ) {
+ ch_free( attrs[ i ]->mod_vals.modv_bvals );
+ }
+ ch_free( attrs );
+ }
+
+ if ( lc ) {
+ ldap_back_release_conn( li, lc );
+ }
+
+ Debug( LDAP_DEBUG_ARGS, "<== ldap_back_add(\"%s\"): %d\n",
+ op->o_req_dn.bv_val, rs->sr_err );
+
+ return rs->sr_err;
+}
+
diff --git a/servers/slapd/back-ldap/back-ldap.h b/servers/slapd/back-ldap/back-ldap.h
new file mode 100644
index 0000000..96bc6f3
--- /dev/null
+++ b/servers/slapd/back-ldap/back-ldap.h
@@ -0,0 +1,479 @@
+/* back-ldap.h - ldap backend header file */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1999-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2000-2003 Pierangelo Masarati.
+ * Portions Copyright 1999-2003 Howard Chu.
+ * 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 the Howard Chu for inclusion
+ * in OpenLDAP Software and subsequently enhanced by Pierangelo
+ * Masarati.
+ */
+
+#ifndef SLAPD_LDAP_H
+#define SLAPD_LDAP_H
+
+#include "../back-monitor/back-monitor.h"
+
+LDAP_BEGIN_DECL
+
+struct ldapinfo_t;
+
+/* stuff required for monitoring */
+typedef struct ldap_monitor_info_t {
+ monitor_subsys_t lmi_mss[2];
+
+ struct berval lmi_ndn;
+ struct berval lmi_conn_rdn;
+ struct berval lmi_ops_rdn;
+} ldap_monitor_info_t;
+
+enum {
+ /* even numbers are connection types */
+ LDAP_BACK_PCONN_FIRST = 0,
+ LDAP_BACK_PCONN_ROOTDN = LDAP_BACK_PCONN_FIRST,
+ LDAP_BACK_PCONN_ANON = 2,
+ LDAP_BACK_PCONN_BIND = 4,
+
+ /* add the TLS bit */
+ LDAP_BACK_PCONN_TLS = 0x1U,
+
+ LDAP_BACK_PCONN_ROOTDN_TLS = (LDAP_BACK_PCONN_ROOTDN|LDAP_BACK_PCONN_TLS),
+ LDAP_BACK_PCONN_ANON_TLS = (LDAP_BACK_PCONN_ANON|LDAP_BACK_PCONN_TLS),
+ LDAP_BACK_PCONN_BIND_TLS = (LDAP_BACK_PCONN_BIND|LDAP_BACK_PCONN_TLS),
+
+ LDAP_BACK_PCONN_LAST
+};
+
+typedef struct ldapconn_base_t {
+ Connection *lcb_conn;
+#define LDAP_BACK_CONN2PRIV(lc) ((unsigned long)(lc)->lc_conn)
+#define LDAP_BACK_PCONN_ISPRIV(lc) (((void *)(lc)->lc_conn) >= ((void *)LDAP_BACK_PCONN_FIRST) \
+ && ((void *)(lc)->lc_conn) < ((void *)LDAP_BACK_PCONN_LAST))
+#define LDAP_BACK_PCONN_ISROOTDN(lc) (LDAP_BACK_PCONN_ISPRIV((lc)) \
+ && (LDAP_BACK_CONN2PRIV((lc)) < LDAP_BACK_PCONN_ANON))
+#define LDAP_BACK_PCONN_ISANON(lc) (LDAP_BACK_PCONN_ISPRIV((lc)) \
+ && (LDAP_BACK_CONN2PRIV((lc)) < LDAP_BACK_PCONN_BIND) \
+ && (LDAP_BACK_CONN2PRIV((lc)) >= LDAP_BACK_PCONN_ANON))
+#define LDAP_BACK_PCONN_ISBIND(lc) (LDAP_BACK_PCONN_ISPRIV((lc)) \
+ && (LDAP_BACK_CONN2PRIV((lc)) >= LDAP_BACK_PCONN_BIND))
+#define LDAP_BACK_PCONN_ISTLS(lc) (LDAP_BACK_PCONN_ISPRIV((lc)) \
+ && (LDAP_BACK_CONN2PRIV((lc)) & LDAP_BACK_PCONN_TLS))
+#ifdef HAVE_TLS
+#define LDAP_BACK_PCONN_ROOTDN_SET(lc, op) \
+ ((lc)->lc_conn = (void *)((op)->o_conn->c_is_tls ? (void *) LDAP_BACK_PCONN_ROOTDN_TLS : (void *) LDAP_BACK_PCONN_ROOTDN))
+#define LDAP_BACK_PCONN_ANON_SET(lc, op) \
+ ((lc)->lc_conn = (void *)((op)->o_conn->c_is_tls ? (void *) LDAP_BACK_PCONN_ANON_TLS : (void *) LDAP_BACK_PCONN_ANON))
+#define LDAP_BACK_PCONN_BIND_SET(lc, op) \
+ ((lc)->lc_conn = (void *)((op)->o_conn->c_is_tls ? (void *) LDAP_BACK_PCONN_BIND_TLS : (void *) LDAP_BACK_PCONN_BIND))
+#else /* ! HAVE_TLS */
+#define LDAP_BACK_PCONN_ROOTDN_SET(lc, op) \
+ ((lc)->lc_conn = (void *)LDAP_BACK_PCONN_ROOTDN)
+#define LDAP_BACK_PCONN_ANON_SET(lc, op) \
+ ((lc)->lc_conn = (void *)LDAP_BACK_PCONN_ANON)
+#define LDAP_BACK_PCONN_BIND_SET(lc, op) \
+ ((lc)->lc_conn = (void *)LDAP_BACK_PCONN_BIND)
+#endif /* ! HAVE_TLS */
+#define LDAP_BACK_PCONN_SET(lc, op) \
+ (BER_BVISEMPTY(&(op)->o_ndn) ? \
+ LDAP_BACK_PCONN_ANON_SET((lc), (op)) : LDAP_BACK_PCONN_ROOTDN_SET((lc), (op)))
+
+ struct ldapinfo_t *lcb_ldapinfo;
+ struct berval lcb_local_ndn;
+ unsigned lcb_refcnt;
+ time_t lcb_create_time;
+ time_t lcb_time;
+} ldapconn_base_t;
+
+typedef struct ldapconn_t {
+ ldapconn_base_t lc_base;
+#define lc_conn lc_base.lcb_conn
+#define lc_ldapinfo lc_base.lcb_ldapinfo
+#define lc_local_ndn lc_base.lcb_local_ndn
+#define lc_refcnt lc_base.lcb_refcnt
+#define lc_create_time lc_base.lcb_create_time
+#define lc_time lc_base.lcb_time
+
+ LDAP_TAILQ_ENTRY(ldapconn_t) lc_q;
+
+ unsigned lc_lcflags;
+#define LDAP_BACK_CONN_ISSET_F(fp,f) (*(fp) & (f))
+#define LDAP_BACK_CONN_SET_F(fp,f) (*(fp) |= (f))
+#define LDAP_BACK_CONN_CLEAR_F(fp,f) (*(fp) &= ~(f))
+#define LDAP_BACK_CONN_CPY_F(fp,f,mfp) \
+ do { \
+ if ( ((f) & *(mfp)) == (f) ) { \
+ *(fp) |= (f); \
+ } else { \
+ *(fp) &= ~(f); \
+ } \
+ } while ( 0 )
+
+#define LDAP_BACK_CONN_ISSET(lc,f) LDAP_BACK_CONN_ISSET_F(&(lc)->lc_lcflags, (f))
+#define LDAP_BACK_CONN_SET(lc,f) LDAP_BACK_CONN_SET_F(&(lc)->lc_lcflags, (f))
+#define LDAP_BACK_CONN_CLEAR(lc,f) LDAP_BACK_CONN_CLEAR_F(&(lc)->lc_lcflags, (f))
+#define LDAP_BACK_CONN_CPY(lc,f,mlc) LDAP_BACK_CONN_CPY_F(&(lc)->lc_lcflags, (f), &(mlc)->lc_lcflags)
+
+/* 0xFFF00000U are reserved for back-meta */
+
+#define LDAP_BACK_FCONN_ISBOUND (0x00000001U)
+#define LDAP_BACK_FCONN_ISANON (0x00000002U)
+#define LDAP_BACK_FCONN_ISBMASK (LDAP_BACK_FCONN_ISBOUND|LDAP_BACK_FCONN_ISANON)
+#define LDAP_BACK_FCONN_ISPRIV (0x00000004U)
+#define LDAP_BACK_FCONN_ISTLS (0x00000008U)
+#define LDAP_BACK_FCONN_BINDING (0x00000010U)
+#define LDAP_BACK_FCONN_TAINTED (0x00000020U)
+#define LDAP_BACK_FCONN_ABANDON (0x00000040U)
+#define LDAP_BACK_FCONN_ISIDASR (0x00000080U)
+#define LDAP_BACK_FCONN_CACHED (0x00000100U)
+
+#define LDAP_BACK_CONN_ISBOUND(lc) LDAP_BACK_CONN_ISSET((lc), LDAP_BACK_FCONN_ISBOUND)
+#define LDAP_BACK_CONN_ISBOUND_SET(lc) LDAP_BACK_CONN_SET((lc), LDAP_BACK_FCONN_ISBOUND)
+#define LDAP_BACK_CONN_ISBOUND_CLEAR(lc) LDAP_BACK_CONN_CLEAR((lc), LDAP_BACK_FCONN_ISBMASK)
+#define LDAP_BACK_CONN_ISBOUND_CPY(lc, mlc) LDAP_BACK_CONN_CPY((lc), LDAP_BACK_FCONN_ISBOUND, (mlc))
+#define LDAP_BACK_CONN_ISANON(lc) LDAP_BACK_CONN_ISSET((lc), LDAP_BACK_FCONN_ISANON)
+#define LDAP_BACK_CONN_ISANON_SET(lc) LDAP_BACK_CONN_SET((lc), LDAP_BACK_FCONN_ISANON)
+#define LDAP_BACK_CONN_ISANON_CLEAR(lc) LDAP_BACK_CONN_ISBOUND_CLEAR((lc))
+#define LDAP_BACK_CONN_ISANON_CPY(lc, mlc) LDAP_BACK_CONN_CPY((lc), LDAP_BACK_FCONN_ISANON, (mlc))
+#define LDAP_BACK_CONN_ISPRIV(lc) LDAP_BACK_CONN_ISSET((lc), LDAP_BACK_FCONN_ISPRIV)
+#define LDAP_BACK_CONN_ISPRIV_SET(lc) LDAP_BACK_CONN_SET((lc), LDAP_BACK_FCONN_ISPRIV)
+#define LDAP_BACK_CONN_ISPRIV_CLEAR(lc) LDAP_BACK_CONN_CLEAR((lc), LDAP_BACK_FCONN_ISPRIV)
+#define LDAP_BACK_CONN_ISPRIV_CPY(lc, mlc) LDAP_BACK_CONN_CPY((lc), LDAP_BACK_FCONN_ISPRIV, (mlc))
+#define LDAP_BACK_CONN_ISTLS(lc) LDAP_BACK_CONN_ISSET((lc), LDAP_BACK_FCONN_ISTLS)
+#define LDAP_BACK_CONN_ISTLS_SET(lc) LDAP_BACK_CONN_SET((lc), LDAP_BACK_FCONN_ISTLS)
+#define LDAP_BACK_CONN_ISTLS_CLEAR(lc) LDAP_BACK_CONN_CLEAR((lc), LDAP_BACK_FCONN_ISTLS)
+#define LDAP_BACK_CONN_ISTLS_CPY(lc, mlc) LDAP_BACK_CONN_CPY((lc), LDAP_BACK_FCONN_ISTLS, (mlc))
+#define LDAP_BACK_CONN_BINDING(lc) LDAP_BACK_CONN_ISSET((lc), LDAP_BACK_FCONN_BINDING)
+#define LDAP_BACK_CONN_BINDING_SET(lc) LDAP_BACK_CONN_SET((lc), LDAP_BACK_FCONN_BINDING)
+#define LDAP_BACK_CONN_BINDING_CLEAR(lc) LDAP_BACK_CONN_CLEAR((lc), LDAP_BACK_FCONN_BINDING)
+#define LDAP_BACK_CONN_TAINTED(lc) LDAP_BACK_CONN_ISSET((lc), LDAP_BACK_FCONN_TAINTED)
+#define LDAP_BACK_CONN_TAINTED_SET(lc) LDAP_BACK_CONN_SET((lc), LDAP_BACK_FCONN_TAINTED)
+#define LDAP_BACK_CONN_TAINTED_CLEAR(lc) LDAP_BACK_CONN_CLEAR((lc), LDAP_BACK_FCONN_TAINTED)
+#define LDAP_BACK_CONN_ABANDON(lc) LDAP_BACK_CONN_ISSET((lc), LDAP_BACK_FCONN_ABANDON)
+#define LDAP_BACK_CONN_ABANDON_SET(lc) LDAP_BACK_CONN_SET((lc), LDAP_BACK_FCONN_ABANDON)
+#define LDAP_BACK_CONN_ABANDON_CLEAR(lc) LDAP_BACK_CONN_CLEAR((lc), LDAP_BACK_FCONN_ABANDON)
+#define LDAP_BACK_CONN_ISIDASSERT(lc) LDAP_BACK_CONN_ISSET((lc), LDAP_BACK_FCONN_ISIDASR)
+#define LDAP_BACK_CONN_ISIDASSERT_SET(lc) LDAP_BACK_CONN_SET((lc), LDAP_BACK_FCONN_ISIDASR)
+#define LDAP_BACK_CONN_ISIDASSERT_CLEAR(lc) LDAP_BACK_CONN_CLEAR((lc), LDAP_BACK_FCONN_ISIDASR)
+#define LDAP_BACK_CONN_ISIDASSERT_CPY(lc, mlc) LDAP_BACK_CONN_CPY((lc), LDAP_BACK_FCONN_ISIDASR, (mlc))
+#define LDAP_BACK_CONN_CACHED(lc) LDAP_BACK_CONN_ISSET((lc), LDAP_BACK_FCONN_CACHED)
+#define LDAP_BACK_CONN_CACHED_SET(lc) LDAP_BACK_CONN_SET((lc), LDAP_BACK_FCONN_CACHED)
+#define LDAP_BACK_CONN_CACHED_CLEAR(lc) LDAP_BACK_CONN_CLEAR((lc), LDAP_BACK_FCONN_CACHED)
+
+ LDAP *lc_ld;
+ unsigned long lc_connid;
+ struct berval lc_cred;
+ struct berval lc_bound_ndn;
+ unsigned lc_flags;
+} ldapconn_t;
+
+typedef struct ldap_avl_info_t {
+ ldap_pvt_thread_mutex_t lai_mutex;
+ TAvlnode *lai_tree;
+} ldap_avl_info_t;
+
+typedef struct slap_retry_info_t {
+ time_t *ri_interval;
+ int *ri_num;
+ int ri_idx;
+ int ri_count;
+ time_t ri_last;
+
+#define SLAP_RETRYNUM_FOREVER (-1) /* retry forever */
+#define SLAP_RETRYNUM_TAIL (-2) /* end of retrynum array */
+#define SLAP_RETRYNUM_VALID(n) ((n) >= SLAP_RETRYNUM_FOREVER) /* valid retrynum */
+#define SLAP_RETRYNUM_FINITE(n) ((n) > SLAP_RETRYNUM_FOREVER) /* not forever */
+} slap_retry_info_t;
+
+/*
+ * identity assertion modes
+ */
+typedef enum {
+ LDAP_BACK_IDASSERT_LEGACY = 1,
+ LDAP_BACK_IDASSERT_NOASSERT,
+ LDAP_BACK_IDASSERT_ANONYMOUS,
+ LDAP_BACK_IDASSERT_SELF,
+ LDAP_BACK_IDASSERT_OTHERDN,
+ LDAP_BACK_IDASSERT_OTHERID
+} slap_idassert_mode_t;
+
+/* ID assert stuff */
+typedef struct slap_idassert_t {
+ slap_idassert_mode_t si_mode;
+#define li_idassert_mode li_idassert.si_mode
+
+ slap_bindconf si_bc;
+#define li_idassert_authcID li_idassert.si_bc.sb_authcId
+#define li_idassert_authcDN li_idassert.si_bc.sb_binddn
+#define li_idassert_passwd li_idassert.si_bc.sb_cred
+#define li_idassert_authzID li_idassert.si_bc.sb_authzId
+#define li_idassert_authmethod li_idassert.si_bc.sb_method
+#define li_idassert_sasl_mech li_idassert.si_bc.sb_saslmech
+#define li_idassert_sasl_realm li_idassert.si_bc.sb_realm
+#define li_idassert_secprops li_idassert.si_bc.sb_secprops
+#define li_idassert_tls li_idassert.si_bc.sb_tls
+
+ unsigned si_flags;
+#define LDAP_BACK_AUTH_NONE (0x00U)
+#define LDAP_BACK_AUTH_NATIVE_AUTHZ (0x01U)
+#define LDAP_BACK_AUTH_OVERRIDE (0x02U)
+#define LDAP_BACK_AUTH_PRESCRIPTIVE (0x04U)
+#define LDAP_BACK_AUTH_OBSOLETE_PROXY_AUTHZ (0x08U)
+#define LDAP_BACK_AUTH_OBSOLETE_ENCODING_WORKAROUND (0x10U)
+#define LDAP_BACK_AUTH_AUTHZ_ALL (0x20U)
+#define LDAP_BACK_AUTH_PROXYAUTHZ_CRITICAL (0x40U)
+#define LDAP_BACK_AUTH_DN_AUTHZID (0x100U)
+#define LDAP_BACK_AUTH_DN_WHOAMI (0x200U)
+#define LDAP_BACK_AUTH_DN_MASK (LDAP_BACK_AUTH_DN_AUTHZID|LDAP_BACK_AUTH_DN_WHOAMI)
+#define li_idassert_flags li_idassert.si_flags
+
+ BerVarray si_authz;
+#define li_idassert_authz li_idassert.si_authz
+
+ BerVarray si_passthru;
+#define li_idassert_passthru li_idassert.si_passthru
+} slap_idassert_t;
+
+/*
+ * Hook to allow mucking with ldapinfo_t when quarantine is over
+ */
+typedef int (*ldap_back_quarantine_f)( struct ldapinfo_t *, void * );
+
+typedef struct ldapinfo_t {
+ /* li_uri: the string that goes into ldap_initialize()
+ * TODO: use li_acl.sb_uri instead */
+ char *li_uri;
+ /* li_bvuri: an array of each single URI that is equivalent;
+ * to be checked for the presence of a certain item */
+ BerVarray li_bvuri;
+ ldap_pvt_thread_mutex_t li_uri_mutex;
+ /* hack because when TLS is used we need to lock and let
+ * the li_urllist_f function to know it's locked */
+ int li_uri_mutex_do_not_lock;
+
+ LDAP_REBIND_PROC *li_rebind_f;
+ LDAP_URLLIST_PROC *li_urllist_f;
+ void *li_urllist_p;
+
+ /* we only care about the TLS options here */
+ slap_bindconf li_tls;
+
+ slap_bindconf li_acl;
+#define li_acl_authcID li_acl.sb_authcId
+#define li_acl_authcDN li_acl.sb_binddn
+#define li_acl_passwd li_acl.sb_cred
+#define li_acl_authzID li_acl.sb_authzId
+#define li_acl_authmethod li_acl.sb_method
+#define li_acl_sasl_mech li_acl.sb_saslmech
+#define li_acl_sasl_realm li_acl.sb_realm
+#define li_acl_secprops li_acl.sb_secprops
+
+ /* ID assert stuff */
+ slap_idassert_t li_idassert;
+ /* end of ID assert stuff */
+
+ int li_nretries;
+#define LDAP_BACK_RETRY_UNDEFINED (-2)
+#define LDAP_BACK_RETRY_FOREVER (-1)
+#define LDAP_BACK_RETRY_NEVER (0)
+#define LDAP_BACK_RETRY_DEFAULT (3)
+
+ unsigned li_flags;
+
+/* 0xFF000000U are reserved for back-meta */
+
+#define LDAP_BACK_F_NONE (0x00000000U)
+#define LDAP_BACK_F_SAVECRED (0x00000001U)
+#define LDAP_BACK_F_USE_TLS (0x00000002U)
+#define LDAP_BACK_F_PROPAGATE_TLS (0x00000004U)
+#define LDAP_BACK_F_TLS_CRITICAL (0x00000008U)
+#define LDAP_BACK_F_TLS_LDAPS (0x00000010U)
+
+#define LDAP_BACK_F_TLS_USE_MASK (LDAP_BACK_F_USE_TLS|LDAP_BACK_F_TLS_CRITICAL)
+#define LDAP_BACK_F_TLS_PROPAGATE_MASK (LDAP_BACK_F_PROPAGATE_TLS|LDAP_BACK_F_TLS_CRITICAL)
+#define LDAP_BACK_F_TLS_MASK (LDAP_BACK_F_TLS_USE_MASK|LDAP_BACK_F_TLS_PROPAGATE_MASK|LDAP_BACK_F_TLS_LDAPS)
+#define LDAP_BACK_F_CHASE_REFERRALS (0x00000020U)
+#define LDAP_BACK_F_PROXY_WHOAMI (0x00000040U)
+
+#define LDAP_BACK_F_T_F (0x00000080U)
+#define LDAP_BACK_F_T_F_DISCOVER (0x00000100U)
+#define LDAP_BACK_F_T_F_MASK (LDAP_BACK_F_T_F)
+#define LDAP_BACK_F_T_F_MASK2 (LDAP_BACK_F_T_F_MASK|LDAP_BACK_F_T_F_DISCOVER)
+
+#define LDAP_BACK_F_MONITOR (0x00000200U)
+#define LDAP_BACK_F_SINGLECONN (0x00000400U)
+#define LDAP_BACK_F_USE_TEMPORARIES (0x00000800U)
+
+#define LDAP_BACK_F_ISOPEN (0x00001000U)
+
+#define LDAP_BACK_F_CANCEL_ABANDON (0x00000000U)
+#define LDAP_BACK_F_CANCEL_IGNORE (0x00002000U)
+#define LDAP_BACK_F_CANCEL_EXOP (0x00004000U)
+#define LDAP_BACK_F_CANCEL_EXOP_DISCOVER (0x00008000U)
+#define LDAP_BACK_F_CANCEL_MASK (LDAP_BACK_F_CANCEL_IGNORE|LDAP_BACK_F_CANCEL_EXOP)
+#define LDAP_BACK_F_CANCEL_MASK2 (LDAP_BACK_F_CANCEL_MASK|LDAP_BACK_F_CANCEL_EXOP_DISCOVER)
+
+#define LDAP_BACK_F_QUARANTINE (0x00010000U)
+
+#ifdef SLAP_CONTROL_X_SESSION_TRACKING
+#define LDAP_BACK_F_ST_REQUEST (0x00020000U)
+#define LDAP_BACK_F_ST_RESPONSE (0x00040000U)
+#endif /* SLAP_CONTROL_X_SESSION_TRACKING */
+
+#define LDAP_BACK_F_NOREFS (0x00080000U)
+#define LDAP_BACK_F_NOUNDEFFILTER (0x00100000U)
+#define LDAP_BACK_F_OMIT_UNKNOWN_SCHEMA (0x00200000U)
+
+#define LDAP_BACK_F_ONERR_STOP (0x00400000U)
+
+#define LDAP_BACK_ISSET_F(ff,f) ( ( (ff) & (f) ) == (f) )
+#define LDAP_BACK_ISMASK_F(ff,m,f) ( ( (ff) & (m) ) == (f) )
+
+#define LDAP_BACK_ISSET(li,f) LDAP_BACK_ISSET_F( (li)->li_flags, (f) )
+#define LDAP_BACK_ISMASK(li,m,f) LDAP_BACK_ISMASK_F( (li)->li_flags, (m), (f) )
+
+#define LDAP_BACK_SAVECRED(li) LDAP_BACK_ISSET( (li), LDAP_BACK_F_SAVECRED )
+#define LDAP_BACK_USE_TLS(li) LDAP_BACK_ISSET( (li), LDAP_BACK_F_USE_TLS )
+#define LDAP_BACK_PROPAGATE_TLS(li) LDAP_BACK_ISSET( (li), LDAP_BACK_F_PROPAGATE_TLS )
+#define LDAP_BACK_TLS_CRITICAL(li) LDAP_BACK_ISSET( (li), LDAP_BACK_F_TLS_CRITICAL )
+#define LDAP_BACK_CHASE_REFERRALS(li) LDAP_BACK_ISSET( (li), LDAP_BACK_F_CHASE_REFERRALS )
+#define LDAP_BACK_PROXY_WHOAMI(li) LDAP_BACK_ISSET( (li), LDAP_BACK_F_PROXY_WHOAMI )
+
+#define LDAP_BACK_USE_TLS_F(ff) LDAP_BACK_ISSET_F( (ff), LDAP_BACK_F_USE_TLS )
+#define LDAP_BACK_PROPAGATE_TLS_F(ff) LDAP_BACK_ISSET_F( (ff), LDAP_BACK_F_PROPAGATE_TLS )
+#define LDAP_BACK_TLS_CRITICAL_F(ff) LDAP_BACK_ISSET_F( (ff), LDAP_BACK_F_TLS_CRITICAL )
+
+#define LDAP_BACK_T_F(li) LDAP_BACK_ISMASK( (li), LDAP_BACK_F_T_F_MASK, LDAP_BACK_F_T_F )
+#define LDAP_BACK_T_F_DISCOVER(li) LDAP_BACK_ISMASK( (li), LDAP_BACK_F_T_F_MASK2, LDAP_BACK_F_T_F_DISCOVER )
+
+#define LDAP_BACK_MONITOR(li) LDAP_BACK_ISSET( (li), LDAP_BACK_F_MONITOR )
+#define LDAP_BACK_SINGLECONN(li) LDAP_BACK_ISSET( (li), LDAP_BACK_F_SINGLECONN )
+#define LDAP_BACK_USE_TEMPORARIES(li) LDAP_BACK_ISSET( (li), LDAP_BACK_F_USE_TEMPORARIES)
+
+#define LDAP_BACK_ISOPEN(li) LDAP_BACK_ISSET( (li), LDAP_BACK_F_ISOPEN )
+
+#define LDAP_BACK_ABANDON(li) LDAP_BACK_ISMASK( (li), LDAP_BACK_F_CANCEL_MASK, LDAP_BACK_F_CANCEL_ABANDON )
+#define LDAP_BACK_IGNORE(li) LDAP_BACK_ISMASK( (li), LDAP_BACK_F_CANCEL_MASK, LDAP_BACK_F_CANCEL_IGNORE )
+#define LDAP_BACK_CANCEL(li) LDAP_BACK_ISMASK( (li), LDAP_BACK_F_CANCEL_MASK, LDAP_BACK_F_CANCEL_EXOP )
+#define LDAP_BACK_CANCEL_DISCOVER(li) LDAP_BACK_ISMASK( (li), LDAP_BACK_F_CANCEL_MASK2, LDAP_BACK_F_CANCEL_EXOP_DISCOVER )
+
+#define LDAP_BACK_QUARANTINE(li) LDAP_BACK_ISSET( (li), LDAP_BACK_F_QUARANTINE )
+
+#ifdef SLAP_CONTROL_X_SESSION_TRACKING
+#define LDAP_BACK_ST_REQUEST(li) LDAP_BACK_ISSET( (li), LDAP_BACK_F_ST_REQUEST)
+#define LDAP_BACK_ST_RESPONSE(li) LDAP_BACK_ISSET( (li), LDAP_BACK_F_ST_RESPONSE)
+#endif /* SLAP_CONTROL_X_SESSION_TRACKING */
+
+#define LDAP_BACK_NOREFS(li) LDAP_BACK_ISSET( (li), LDAP_BACK_F_NOREFS)
+#define LDAP_BACK_NOUNDEFFILTER(li) LDAP_BACK_ISSET( (li), LDAP_BACK_F_NOUNDEFFILTER)
+#define LDAP_BACK_OMIT_UNKNOWN_SCHEMA(li) LDAP_BACK_ISSET( (li), LDAP_BACK_F_OMIT_UNKNOWN_SCHEMA)
+#define LDAP_BACK_ONERR_STOP(li) LDAP_BACK_ISSET( (li), LDAP_BACK_F_ONERR_STOP)
+
+ int li_version;
+
+ unsigned long li_conn_nextid;
+
+ /* cached connections;
+ * special conns are in tailq rather than in tree */
+ ldap_avl_info_t li_conninfo;
+ struct {
+ int lic_num;
+ LDAP_TAILQ_HEAD(lc_conn_priv_q, ldapconn_t) lic_priv;
+ } li_conn_priv[ LDAP_BACK_PCONN_LAST ];
+ int li_conn_priv_max;
+#define LDAP_BACK_CONN_PRIV_MIN (1)
+#define LDAP_BACK_CONN_PRIV_MAX (256)
+ /* must be between LDAP_BACK_CONN_PRIV_MIN
+ * and LDAP_BACK_CONN_PRIV_MAX ! */
+#define LDAP_BACK_CONN_PRIV_DEFAULT (16)
+
+ ldap_monitor_info_t li_monitor_info;
+
+ sig_atomic_t li_isquarantined;
+#define LDAP_BACK_FQ_NO (0)
+#define LDAP_BACK_FQ_YES (1)
+#define LDAP_BACK_FQ_RETRYING (2)
+
+ slap_retry_info_t li_quarantine;
+ ldap_pvt_thread_mutex_t li_quarantine_mutex;
+ ldap_back_quarantine_f li_quarantine_f;
+ void *li_quarantine_p;
+
+ time_t li_network_timeout;
+ time_t li_conn_ttl;
+ time_t li_idle_timeout;
+ time_t li_timeout[ SLAP_OP_LAST ];
+
+ ldap_pvt_thread_mutex_t li_counter_mutex;
+ ldap_pvt_mp_t li_ops_completed[SLAP_OP_LAST];
+ struct re_s* li_conn_expire_task;
+} ldapinfo_t;
+
+#define LDAP_ERR_OK(err) ((err) == LDAP_SUCCESS || (err) == LDAP_COMPARE_FALSE || (err) == LDAP_COMPARE_TRUE)
+
+typedef enum ldap_back_send_t {
+ LDAP_BACK_DONTSEND = 0x00,
+ LDAP_BACK_SENDOK = 0x01,
+ LDAP_BACK_SENDERR = 0x02,
+ LDAP_BACK_SENDRESULT = (LDAP_BACK_SENDOK|LDAP_BACK_SENDERR),
+ LDAP_BACK_BINDING = 0x04,
+
+ LDAP_BACK_BIND_DONTSEND = (LDAP_BACK_BINDING),
+ LDAP_BACK_BIND_SOK = (LDAP_BACK_BINDING|LDAP_BACK_SENDOK),
+ LDAP_BACK_BIND_SERR = (LDAP_BACK_BINDING|LDAP_BACK_SENDERR),
+ LDAP_BACK_BIND_SRES = (LDAP_BACK_BINDING|LDAP_BACK_SENDRESULT),
+
+ LDAP_BACK_RETRYING = 0x08,
+ LDAP_BACK_RETRY_DONTSEND = (LDAP_BACK_RETRYING),
+ LDAP_BACK_RETRY_SOK = (LDAP_BACK_RETRYING|LDAP_BACK_SENDOK),
+ LDAP_BACK_RETRY_SERR = (LDAP_BACK_RETRYING|LDAP_BACK_SENDERR),
+ LDAP_BACK_RETRY_SRES = (LDAP_BACK_RETRYING|LDAP_BACK_SENDRESULT),
+
+ LDAP_BACK_GETCONN = 0x10
+} ldap_back_send_t;
+
+/* define to use asynchronous StartTLS */
+#define SLAP_STARTTLS_ASYNCHRONOUS
+
+/* timeout to use when calling ldap_result() */
+#define LDAP_BACK_RESULT_TIMEOUT (0)
+#define LDAP_BACK_RESULT_UTIMEOUT (100000)
+#define LDAP_BACK_TV_SET(tv) \
+ do { \
+ (tv)->tv_sec = LDAP_BACK_RESULT_TIMEOUT; \
+ (tv)->tv_usec = LDAP_BACK_RESULT_UTIMEOUT; \
+ } while ( 0 )
+
+#ifndef LDAP_BACK_PRINT_CONNTREE
+#define LDAP_BACK_PRINT_CONNTREE 0
+#endif /* !LDAP_BACK_PRINT_CONNTREE */
+
+typedef struct ldap_extra_t {
+ int (*proxy_authz_ctrl)( Operation *op, SlapReply *rs, struct berval *bound_ndn,
+ int version, slap_idassert_t *si, LDAPControl *ctrl );
+ int (*controls_free)( Operation *op, SlapReply *rs, LDAPControl ***pctrls );
+ int (*idassert_authzfrom_parse)( struct config_args_s *ca, slap_idassert_t *si );
+ int (*idassert_passthru_parse_cf)( const char *fname, int lineno, const char *arg, slap_idassert_t *si );
+ int (*idassert_parse)( struct config_args_s *ca, slap_idassert_t *si );
+ void (*retry_info_destroy)( slap_retry_info_t *ri );
+ int (*retry_info_parse)( char *in, slap_retry_info_t *ri, char *buf, ber_len_t buflen );
+ int (*retry_info_unparse)( slap_retry_info_t *ri, struct berval *bvout );
+ int (*connid2str)( const ldapconn_base_t *lc, char *buf, ber_len_t buflen );
+} ldap_extra_t;
+
+LDAP_END_DECL
+
+#include "proto-ldap.h"
+
+#endif /* SLAPD_LDAP_H */
diff --git a/servers/slapd/back-ldap/bind.c b/servers/slapd/back-ldap/bind.c
new file mode 100644
index 0000000..16a09bf
--- /dev/null
+++ b/servers/slapd/back-ldap/bind.c
@@ -0,0 +1,3230 @@
+/* bind.c - ldap backend bind function */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1999-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2000-2003 Pierangelo Masarati.
+ * Portions Copyright 1999-2003 Howard Chu.
+ * 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 Howard Chu for inclusion
+ * in OpenLDAP Software and subsequently enhanced by Pierangelo
+ * Masarati.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/errno.h>
+#include <ac/socket.h>
+#include <ac/string.h>
+
+#define AVL_INTERNAL
+#include "slap.h"
+#include "back-ldap.h"
+#include "lutil.h"
+#include "lutil_ldap.h"
+#include "ldap_rq.h"
+
+#define LDAP_CONTROL_OBSOLETE_PROXY_AUTHZ "2.16.840.1.113730.3.4.12"
+
+#ifdef LDAP_DEVEL
+#define SLAP_AUTH_DN 1
+#endif
+
+#if LDAP_BACK_PRINT_CONNTREE > 0
+
+static const struct {
+ slap_mask_t f;
+ char c;
+} flagsmap[] = {
+ { LDAP_BACK_FCONN_ISBOUND, 'B' },
+ { LDAP_BACK_FCONN_ISANON, 'A' },
+ { LDAP_BACK_FCONN_ISPRIV, 'P' },
+ { LDAP_BACK_FCONN_ISTLS, 'T' },
+ { LDAP_BACK_FCONN_BINDING, 'X' },
+ { LDAP_BACK_FCONN_TAINTED, 'E' },
+ { LDAP_BACK_FCONN_ABANDON, 'N' },
+ { LDAP_BACK_FCONN_ISIDASR, 'S' },
+ { LDAP_BACK_FCONN_CACHED, 'C' },
+ { 0, '\0' }
+};
+
+static void
+ldap_back_conn_print( ldapconn_t *lc )
+{
+ char buf[ SLAP_TEXT_BUFLEN ];
+ char fbuf[ sizeof("BAPTIENSC") ];
+ int i;
+
+ ldap_back_conn2str( &lc->lc_base, buf, sizeof( buf ) );
+ for ( i = 0; flagsmap[ i ].c != '\0'; i++ ) {
+ if ( lc->lc_lcflags & flagsmap[i].f ) {
+ fbuf[i] = flagsmap[i].c;
+
+ } else {
+ fbuf[i] = '.';
+ }
+ }
+ fbuf[i] = '\0';
+
+ fprintf( stderr, "lc=%p %s flags=0x%08x (%s)\n",
+ (void *)lc, buf, lc->lc_lcflags, fbuf );
+}
+
+
+static char* priv2str[] = {
+ "privileged",
+ "privileged/TLS",
+ "anonymous",
+ "anonymous/TLS",
+ "bind",
+ "bind/TLS",
+ NULL
+};
+
+void
+ldap_back_print_conntree( ldapinfo_t *li, char *msg )
+{
+ int c;
+
+ fprintf( stderr, "========> %s\n", msg );
+
+ for ( c = LDAP_BACK_PCONN_FIRST; c < LDAP_BACK_PCONN_LAST; c++ ) {
+ int i = 0;
+ ldapconn_t *lc;
+
+ fprintf( stderr, " %s[%d]\n", priv2str[ c ], li->li_conn_priv[ c ].lic_num );
+
+ LDAP_TAILQ_FOREACH( lc, &li->li_conn_priv[ c ].lic_priv, lc_q )
+ {
+ fprintf( stderr, " [%d] ", i );
+ ldap_back_conn_print( lc );
+ i++;
+ }
+ }
+
+ if ( li->li_conninfo.lai_tree == 0 ) {
+ fprintf( stderr, "\t(empty)\n" );
+
+ } else {
+ TAvlnode *edge = ldap_tavl_end( li->li_conninfo.lai_tree, TAVL_DIR_LEFT );
+ while ( edge ) {
+ ldap_back_conn_print( (ldapconn_t *)edge->avl_data );
+ edge = ldap_tavl_next( edge, TAVL_DIR_RIGHT );
+ }
+ }
+
+ fprintf( stderr, "<======== %s\n", msg );
+}
+#endif /* LDAP_BACK_PRINT_CONNTREE */
+
+static int
+ldap_back_freeconn( ldapinfo_t *li, ldapconn_t *lc, int dolock );
+
+static ldapconn_t *
+ldap_back_getconn( Operation *op, SlapReply *rs, ldap_back_send_t sendok,
+ struct berval *binddn, struct berval *bindcred );
+
+static int
+ldap_back_is_proxy_authz( Operation *op, SlapReply *rs, ldap_back_send_t sendok,
+ struct berval *binddn, struct berval *bindcred );
+
+static int
+ldap_back_proxy_authz_bind( ldapconn_t *lc, Operation *op, SlapReply *rs,
+ ldap_back_send_t sendok, struct berval *binddn, struct berval *bindcred );
+
+static int
+ldap_back_prepare_conn( ldapconn_t *lc, Operation *op, SlapReply *rs,
+ ldap_back_send_t sendok );
+
+static int
+ldap_back_conndnlc_cmp( const void *c1, const void *c2 );
+
+static void
+ldap_back_conn_prune( ldapinfo_t *li );
+
+static void
+ldap_back_schedule_conn_expiry( ldapinfo_t *li, ldapconn_t *lc );
+
+ldapconn_t *
+ldap_back_conn_delete( ldapinfo_t *li, ldapconn_t *lc )
+{
+ if ( LDAP_BACK_PCONN_ISPRIV( lc ) ) {
+ if ( LDAP_BACK_CONN_CACHED( lc ) ) {
+ assert( lc->lc_q.tqe_prev != NULL );
+ assert( li->li_conn_priv[ LDAP_BACK_CONN2PRIV( lc ) ].lic_num > 0 );
+ li->li_conn_priv[ LDAP_BACK_CONN2PRIV( lc ) ].lic_num--;
+ LDAP_TAILQ_REMOVE( &li->li_conn_priv[ LDAP_BACK_CONN2PRIV( lc ) ].lic_priv, lc, lc_q );
+ LDAP_TAILQ_ENTRY_INIT( lc, lc_q );
+ LDAP_BACK_CONN_CACHED_CLEAR( lc );
+
+ } else {
+ assert( LDAP_BACK_CONN_TAINTED( lc ) );
+ assert( lc->lc_q.tqe_prev == NULL );
+ }
+
+ } else {
+ ldapconn_t *tmplc = NULL;
+
+ if ( LDAP_BACK_CONN_CACHED( lc ) ) {
+ assert( !LDAP_BACK_CONN_TAINTED( lc ) );
+ tmplc = ldap_tavl_delete( &li->li_conninfo.lai_tree, (caddr_t)lc,
+ ldap_back_conndnlc_cmp );
+ assert( tmplc == lc );
+ LDAP_BACK_CONN_CACHED_CLEAR( lc );
+ }
+
+ assert( LDAP_BACK_CONN_TAINTED( lc ) || tmplc == lc );
+ }
+
+ return lc;
+}
+
+int
+ldap_back_bind( Operation *op, SlapReply *rs )
+{
+ ldapinfo_t *li = (ldapinfo_t *) op->o_bd->be_private;
+ ldapconn_t *lc;
+
+ LDAPControl **ctrls = NULL;
+ struct berval save_o_dn;
+ int save_o_do_not_cache,
+ rc = 0;
+ ber_int_t msgid;
+ ldap_back_send_t retrying = LDAP_BACK_RETRYING;
+
+ /* allow rootdn as a means to auth without the need to actually
+ * contact the proxied DSA */
+ switch ( be_rootdn_bind( op, rs ) ) {
+ case SLAP_CB_CONTINUE:
+ break;
+
+ default:
+ return rs->sr_err;
+ }
+
+ lc = ldap_back_getconn( op, rs, LDAP_BACK_BIND_SERR, NULL, NULL );
+ if ( !lc ) {
+ return rs->sr_err;
+ }
+
+ /* we can do (almost) whatever we want with this conn,
+ * because either it's temporary, or it's marked as binding */
+ if ( !BER_BVISNULL( &lc->lc_bound_ndn ) ) {
+ ch_free( lc->lc_bound_ndn.bv_val );
+ BER_BVZERO( &lc->lc_bound_ndn );
+ }
+ if ( !BER_BVISNULL( &lc->lc_cred ) ) {
+ memset( lc->lc_cred.bv_val, 0, lc->lc_cred.bv_len );
+ ch_free( lc->lc_cred.bv_val );
+ BER_BVZERO( &lc->lc_cred );
+ }
+ LDAP_BACK_CONN_ISBOUND_CLEAR( lc );
+
+ /* don't add proxyAuthz; set the bindDN */
+ save_o_dn = op->o_dn;
+ save_o_do_not_cache = op->o_do_not_cache;
+ op->o_dn = op->o_req_dn;
+ op->o_do_not_cache = 1;
+
+ ctrls = op->o_ctrls;
+ rc = ldap_back_controls_add( op, rs, lc, &ctrls );
+ op->o_dn = save_o_dn;
+ op->o_do_not_cache = save_o_do_not_cache;
+ if ( rc != LDAP_SUCCESS ) {
+ send_ldap_result( op, rs );
+ ldap_back_release_conn( li, lc );
+ return( rc );
+ }
+
+retry:;
+ /* method is always LDAP_AUTH_SIMPLE if we got here */
+ rs->sr_err = ldap_sasl_bind( lc->lc_ld, op->o_req_dn.bv_val,
+ LDAP_SASL_SIMPLE,
+ &op->orb_cred, ctrls, NULL, &msgid );
+ /* FIXME: should we always retry, or only when piping the bind
+ * in the "override" connection pool? */
+ rc = ldap_back_op_result( lc, op, rs, msgid,
+ li->li_timeout[ SLAP_OP_BIND ],
+ LDAP_BACK_BIND_SERR | retrying );
+ if ( rc == LDAP_UNAVAILABLE && retrying ) {
+ retrying &= ~LDAP_BACK_RETRYING;
+ if ( ldap_back_retry( &lc, op, rs, LDAP_BACK_BIND_SERR ) ) {
+ goto retry;
+ }
+ if ( !lc )
+ return( rc );
+ }
+
+ ldap_pvt_thread_mutex_lock( &li->li_counter_mutex );
+ ldap_pvt_mp_add( li->li_ops_completed[ SLAP_OP_BIND ], 1 );
+ ldap_pvt_thread_mutex_unlock( &li->li_counter_mutex );
+
+ ldap_back_controls_free( op, rs, &ctrls );
+
+ if ( rc == LDAP_SUCCESS ) {
+ op->o_conn->c_authz_cookie = op->o_bd->be_private;
+
+ /* If defined, proxyAuthz will be used also when
+ * back-ldap is the authorizing backend; for this
+ * purpose, after a successful bind the connection
+ * is left for further binds, and further operations
+ * on this client connection will use a default
+ * connection with identity assertion */
+ /* NOTE: use with care */
+ if ( li->li_idassert_flags & LDAP_BACK_AUTH_OVERRIDE ) {
+ ldap_back_release_conn( li, lc );
+ return( rc );
+ }
+
+ /* rebind is now done inside ldap_back_proxy_authz_bind()
+ * in case of success */
+ LDAP_BACK_CONN_ISBOUND_SET( lc );
+ ber_dupbv( &lc->lc_bound_ndn, &op->o_req_ndn );
+
+ if ( !BER_BVISNULL( &lc->lc_cred ) ) {
+ memset( lc->lc_cred.bv_val, 0,
+ lc->lc_cred.bv_len );
+ }
+
+ if ( LDAP_BACK_SAVECRED( li ) ) {
+ ber_bvreplace( &lc->lc_cred, &op->orb_cred );
+ ldap_set_rebind_proc( lc->lc_ld, li->li_rebind_f, lc );
+
+ } else {
+ lc->lc_cred.bv_len = 0;
+ }
+ }
+
+ /* must re-insert if local DN changed as result of bind */
+ if ( !LDAP_BACK_CONN_ISBOUND( lc )
+ || ( !dn_match( &op->o_req_ndn, &lc->lc_local_ndn )
+ && !LDAP_BACK_PCONN_ISPRIV( lc ) ) )
+ {
+ int lerr = -1;
+ ldapconn_t *tmplc;
+
+ /* wait for all other ops to release the connection */
+retry_lock:;
+ ldap_pvt_thread_mutex_lock( &li->li_conninfo.lai_mutex );
+ if ( lc->lc_refcnt > 1 ) {
+ ldap_pvt_thread_mutex_unlock( &li->li_conninfo.lai_mutex );
+ ldap_pvt_thread_yield();
+ goto retry_lock;
+ }
+
+#if LDAP_BACK_PRINT_CONNTREE > 0
+ ldap_back_print_conntree( li, ">>> ldap_back_bind" );
+#endif /* LDAP_BACK_PRINT_CONNTREE */
+
+ assert( lc->lc_refcnt == 1 );
+ ldap_back_conn_delete( li, lc );
+
+ /* delete all cached connections with the current connection */
+ if ( LDAP_BACK_SINGLECONN( li ) ) {
+ while ( ( tmplc = ldap_tavl_delete( &li->li_conninfo.lai_tree, (caddr_t)lc, ldap_back_conn_cmp ) ) != NULL )
+ {
+ assert( !LDAP_BACK_PCONN_ISPRIV( lc ) );
+ Debug( LDAP_DEBUG_TRACE,
+ "=>ldap_back_bind: destroying conn %lu (refcnt=%u)\n",
+ lc->lc_conn->c_connid, lc->lc_refcnt );
+
+ if ( tmplc->lc_refcnt != 0 ) {
+ /* taint it */
+ LDAP_BACK_CONN_TAINTED_SET( tmplc );
+ LDAP_BACK_CONN_CACHED_CLEAR( tmplc );
+
+ } else {
+ /*
+ * Needs a test because the handler may be corrupted,
+ * and calling ldap_unbind on a corrupted header results
+ * in a segmentation fault
+ */
+ ldap_back_conn_free( tmplc );
+ }
+ }
+ }
+
+ if ( LDAP_BACK_CONN_ISBOUND( lc ) ) {
+ ber_bvreplace( &lc->lc_local_ndn, &op->o_req_ndn );
+ if ( be_isroot_dn( op->o_bd, &op->o_req_ndn ) ) {
+ LDAP_BACK_PCONN_ROOTDN_SET( lc, op );
+ }
+ lerr = ldap_tavl_insert( &li->li_conninfo.lai_tree, (caddr_t)lc,
+ ldap_back_conndn_cmp, ldap_back_conndn_dup );
+ }
+
+#if LDAP_BACK_PRINT_CONNTREE > 0
+ ldap_back_print_conntree( li, "<<< ldap_back_bind" );
+#endif /* LDAP_BACK_PRINT_CONNTREE */
+
+ ldap_pvt_thread_mutex_unlock( &li->li_conninfo.lai_mutex );
+ switch ( lerr ) {
+ case 0:
+ LDAP_BACK_CONN_CACHED_SET( lc );
+ break;
+
+ case -1:
+ /* duplicate; someone else successfully bound
+ * on the same connection with the same identity;
+ * we can do this because lc_refcnt == 1 */
+ ldap_back_conn_free( lc );
+ lc = NULL;
+ }
+ }
+
+ if ( lc != NULL ) {
+ ldap_back_release_conn( li, lc );
+ }
+
+ return( rc );
+}
+
+/*
+ * ldap_back_conndn_cmp
+ *
+ * compares two ldapconn_t based on the value of the conn pointer
+ * and of the local DN; used by avl stuff for insert, lookup
+ * and direct delete
+ */
+int
+ldap_back_conndn_cmp( const void *c1, const void *c2 )
+{
+ const ldapconn_t *lc1 = (const ldapconn_t *)c1;
+ const ldapconn_t *lc2 = (const ldapconn_t *)c2;
+ int rc;
+
+ /* If local DNs don't match, it is definitely not a match */
+ /* For shared sessions, conn is NULL. Only explicitly
+ * bound sessions will have non-NULL conn.
+ */
+ rc = SLAP_PTRCMP( lc1->lc_conn, lc2->lc_conn );
+ if ( rc == 0 ) {
+ rc = ber_bvcmp( &lc1->lc_local_ndn, &lc2->lc_local_ndn );
+ }
+
+ return rc;
+}
+
+/*
+ * ldap_back_conndnlc_cmp
+ *
+ * compares two ldapconn_t based on the value of the conn pointer,
+ * the local DN and the lc pointer; used by avl stuff for insert, lookup
+ * and direct delete
+ */
+static int
+ldap_back_conndnlc_cmp( const void *c1, const void *c2 )
+{
+ const ldapconn_t *lc1 = (const ldapconn_t *)c1;
+ const ldapconn_t *lc2 = (const ldapconn_t *)c2;
+ int rc;
+
+ /* If local DNs don't match, it is definitely not a match */
+ /* For shared sessions, conn is NULL. Only explicitly
+ * bound sessions will have non-NULL conn.
+ */
+ rc = SLAP_PTRCMP( lc1->lc_conn, lc2->lc_conn );
+ if ( rc == 0 ) {
+ rc = ber_bvcmp( &lc1->lc_local_ndn, &lc2->lc_local_ndn );
+ if ( rc == 0 ) {
+ rc = SLAP_PTRCMP( lc1, lc2 );
+ }
+ }
+
+ return rc;
+}
+
+/*
+ * ldap_back_conn_cmp
+ *
+ * compares two ldapconn_t based on the value of the conn pointer;
+ * used by avl stuff for delete of all conns with the same connid
+ */
+int
+ldap_back_conn_cmp( const void *c1, const void *c2 )
+{
+ const ldapconn_t *lc1 = (const ldapconn_t *)c1;
+ const ldapconn_t *lc2 = (const ldapconn_t *)c2;
+
+ /* For shared sessions, conn is NULL. Only explicitly
+ * bound sessions will have non-NULL conn.
+ */
+ return SLAP_PTRCMP( lc1->lc_conn, lc2->lc_conn );
+}
+
+/*
+ * ldap_back_conndn_dup
+ *
+ * returns -1 in case a duplicate ldapconn_t has been inserted;
+ * used by avl stuff
+ */
+int
+ldap_back_conndn_dup( void *c1, void *c2 )
+{
+ ldapconn_t *lc1 = (ldapconn_t *)c1;
+ ldapconn_t *lc2 = (ldapconn_t *)c2;
+
+ /* Cannot have more than one shared session with same DN */
+ if ( lc1->lc_conn == lc2->lc_conn &&
+ dn_match( &lc1->lc_local_ndn, &lc2->lc_local_ndn ) )
+ {
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+ldap_back_freeconn( ldapinfo_t *li, ldapconn_t *lc, int dolock )
+{
+ if ( dolock ) {
+ ldap_pvt_thread_mutex_lock( &li->li_conninfo.lai_mutex );
+ }
+
+#if LDAP_BACK_PRINT_CONNTREE > 0
+ ldap_back_print_conntree( li, ">>> ldap_back_freeconn" );
+#endif /* LDAP_BACK_PRINT_CONNTREE */
+
+ (void)ldap_back_conn_delete( li, lc );
+
+ if ( lc->lc_refcnt == 0 ) {
+ ldap_back_conn_free( (void *)lc );
+ }
+
+#if LDAP_BACK_PRINT_CONNTREE > 0
+ ldap_back_print_conntree( li, "<<< ldap_back_freeconn" );
+#endif /* LDAP_BACK_PRINT_CONNTREE */
+
+ if ( dolock ) {
+ ldap_pvt_thread_mutex_unlock( &li->li_conninfo.lai_mutex );
+ }
+
+ return 0;
+}
+
+#ifdef HAVE_TLS
+static int
+ldap_back_start_tls(
+ LDAP *ld,
+ int protocol,
+ int *is_tls,
+ const char *url,
+ unsigned flags,
+ int timeout,
+ const char **text )
+{
+ int rc = LDAP_SUCCESS;
+
+ /* start TLS ("tls-[try-]{start,propagate}" statements) */
+ if ( ( LDAP_BACK_USE_TLS_F( flags ) || ( *is_tls && LDAP_BACK_PROPAGATE_TLS_F( flags ) ) )
+ && !ldap_is_ldaps_url( url ) )
+ {
+#ifdef SLAP_STARTTLS_ASYNCHRONOUS
+ /*
+ * use asynchronous StartTLS
+ * in case, chase referral (not implemented yet)
+ */
+ int msgid;
+
+ if ( protocol == 0 ) {
+ ldap_get_option( ld, LDAP_OPT_PROTOCOL_VERSION,
+ (void *)&protocol );
+ }
+
+ if ( protocol < LDAP_VERSION3 ) {
+ /* we should rather bail out... */
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ *text = "invalid protocol version";
+ }
+
+ if ( rc == LDAP_SUCCESS ) {
+ rc = ldap_start_tls( ld, NULL, NULL, &msgid );
+ }
+
+ if ( rc == LDAP_SUCCESS ) {
+ LDAPMessage *res = NULL;
+ struct timeval tv;
+
+ if ( timeout ) {
+ tv.tv_sec = timeout;
+ tv.tv_usec = 0;
+ } else {
+ LDAP_BACK_TV_SET( &tv );
+ }
+ rc = ldap_result( ld, msgid, LDAP_MSG_ALL, &tv, &res );
+ if ( rc <= 0 ) {
+ rc = LDAP_UNAVAILABLE;
+
+ } else if ( rc == LDAP_RES_EXTENDED ) {
+ struct berval *data = NULL;
+
+ rc = ldap_parse_extended_result( ld, res,
+ NULL, &data, 0 );
+ if ( rc == LDAP_SUCCESS ) {
+ SlapReply rs;
+ rc = ldap_parse_result( ld, res, &rs.sr_err,
+ NULL, NULL, NULL, NULL, 1 );
+ if ( rc != LDAP_SUCCESS ) {
+ rs.sr_err = rc;
+ }
+ rc = slap_map_api2result( &rs );
+ res = NULL;
+
+ /* FIXME: in case a referral
+ * is returned, should we try
+ * using it instead of the
+ * configured URI? */
+ if ( rc == LDAP_SUCCESS ) {
+ rc = ldap_install_tls( ld );
+
+ } else if ( rc == LDAP_REFERRAL ) {
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ *text = "unwilling to chase referral returned by Start TLS exop";
+ }
+
+ if ( data ) {
+ if ( data->bv_val ) {
+ ber_memfree( data->bv_val );
+ }
+ ber_memfree( data );
+ }
+ }
+
+ } else {
+ rc = LDAP_OTHER;
+ }
+
+ if ( res != NULL ) {
+ ldap_msgfree( res );
+ }
+ }
+#else /* ! SLAP_STARTTLS_ASYNCHRONOUS */
+ /*
+ * use synchronous StartTLS
+ */
+ rc = ldap_start_tls_s( ld, NULL, NULL );
+#endif /* ! SLAP_STARTTLS_ASYNCHRONOUS */
+
+ /* if StartTLS is requested, only attempt it if the URL
+ * is not "ldaps://"; this may occur not only in case
+ * of misconfiguration, but also when used in the chain
+ * overlay, where the "uri" can be parsed out of a referral */
+ switch ( rc ) {
+ case LDAP_SUCCESS:
+ *is_tls = 1;
+ break;
+
+ case LDAP_SERVER_DOWN:
+ break;
+
+ default:
+ if ( LDAP_BACK_TLS_CRITICAL_F( flags ) ) {
+ *text = "could not start TLS";
+ break;
+ }
+
+ /* in case Start TLS is not critical */
+ *is_tls = 0;
+ rc = LDAP_SUCCESS;
+ break;
+ }
+
+ } else {
+ *is_tls = 0;
+ }
+
+ return rc;
+}
+#endif /* HAVE_TLS */
+
+static int
+ldap_back_prepare_conn( ldapconn_t *lc, Operation *op, SlapReply *rs, ldap_back_send_t sendok )
+{
+ ldapinfo_t *li = (ldapinfo_t *)op->o_bd->be_private;
+ int version;
+ LDAP *ld = NULL;
+#ifdef HAVE_TLS
+ int is_tls = op->o_conn->c_is_tls;
+ int flags = li->li_flags;
+ slap_bindconf *sb;
+#endif /* HAVE_TLS */
+
+ ldap_pvt_thread_mutex_lock( &li->li_uri_mutex );
+ rs->sr_err = ldap_initialize( &ld, li->li_uri );
+ ldap_pvt_thread_mutex_unlock( &li->li_uri_mutex );
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ goto error_return;
+ }
+
+ if ( li->li_urllist_f ) {
+ ldap_set_urllist_proc( ld, li->li_urllist_f, li->li_urllist_p );
+ }
+
+ /* Set LDAP version. This will always succeed: If the client
+ * bound with a particular version, then so can we.
+ */
+ if ( li->li_version != 0 ) {
+ version = li->li_version;
+
+ } else if ( op->o_protocol != 0 ) {
+ version = op->o_protocol;
+
+ } else {
+ /* assume it's an internal op; set to LDAPv3 */
+ version = LDAP_VERSION3;
+ }
+ ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION, (const void *)&version );
+
+ /* automatically chase referrals ("chase-referrals [{yes|no}]" statement) */
+ ldap_set_option( ld, LDAP_OPT_REFERRALS,
+ LDAP_BACK_CHASE_REFERRALS( li ) ? LDAP_OPT_ON : LDAP_OPT_OFF );
+
+ if ( li->li_network_timeout > 0 ) {
+ struct timeval tv;
+
+ tv.tv_sec = li->li_network_timeout;
+ tv.tv_usec = 0;
+ ldap_set_option( ld, LDAP_OPT_NETWORK_TIMEOUT, (const void *)&tv );
+ }
+
+ /* turn on network keepalive, if configured so */
+ slap_client_keepalive(ld, &li->li_tls.sb_keepalive);
+
+ if ( li->li_tls.sb_tcp_user_timeout > 0 ) {
+ ldap_set_option( ld, LDAP_OPT_TCP_USER_TIMEOUT,
+ &li->li_tls.sb_tcp_user_timeout );
+ }
+
+#ifdef HAVE_TLS
+ if ( LDAP_BACK_CONN_ISPRIV( lc ) ) {
+ /* See "rationale" comment in ldap_back_getconn() */
+ if ( li->li_acl_authmethod == LDAP_AUTH_NONE &&
+ li->li_idassert_authmethod != LDAP_AUTH_NONE )
+ sb = &li->li_idassert.si_bc;
+ else
+ sb = &li->li_acl;
+
+ } else if ( LDAP_BACK_CONN_ISIDASSERT( lc ) ) {
+ sb = &li->li_idassert.si_bc;
+
+ } else {
+ sb = &li->li_tls;
+ }
+
+ bindconf_tls_set( sb, ld );
+
+ /* if required by the bindconf configuration, force TLS */
+ if ( ( sb == &li->li_acl || sb == &li->li_idassert.si_bc ) &&
+ sb->sb_tls_ctx )
+ {
+ flags |= LDAP_BACK_F_USE_TLS;
+ }
+
+ ldap_pvt_thread_mutex_lock( &li->li_uri_mutex );
+ assert( li->li_uri_mutex_do_not_lock == 0 );
+ li->li_uri_mutex_do_not_lock = 1;
+ rs->sr_err = ldap_back_start_tls( ld, op->o_protocol, &is_tls,
+ li->li_uri, flags, li->li_timeout[ SLAP_OP_BIND ], &rs->sr_text );
+ li->li_uri_mutex_do_not_lock = 0;
+ ldap_pvt_thread_mutex_unlock( &li->li_uri_mutex );
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ ldap_unbind_ext( ld, NULL, NULL );
+ rs->sr_text = "Start TLS failed";
+ goto error_return;
+ }
+#endif /* HAVE_TLS */
+
+ lc->lc_ld = ld;
+ lc->lc_refcnt = 1;
+#ifdef HAVE_TLS
+ if ( is_tls ) {
+ LDAP_BACK_CONN_ISTLS_SET( lc );
+ } else {
+ LDAP_BACK_CONN_ISTLS_CLEAR( lc );
+ }
+#endif /* HAVE_TLS */
+
+error_return:;
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ rs->sr_err = slap_map_api2result( rs );
+ if ( sendok & LDAP_BACK_SENDERR ) {
+ if ( rs->sr_text == NULL ) {
+ rs->sr_text = "Proxy connection initialization failed";
+ }
+ send_ldap_result( op, rs );
+ }
+
+ } else {
+ lc->lc_create_time = op->o_time;
+ lc->lc_time = op->o_time;
+ }
+
+ return rs->sr_err;
+}
+
+static ldapconn_t *
+ldap_back_getconn(
+ Operation *op,
+ SlapReply *rs,
+ ldap_back_send_t sendok,
+ struct berval *binddn,
+ struct berval *bindcred )
+{
+ ldapinfo_t *li = (ldapinfo_t *)op->o_bd->be_private;
+ ldapconn_t *lc = NULL,
+ lc_curr = {{ 0 }};
+ int refcnt = 1,
+ lookupconn = !( sendok & LDAP_BACK_BINDING );
+
+ /* if the server is quarantined, and
+ * - the current interval did not expire yet, or
+ * - no more retries should occur,
+ * don't return the connection */
+ if ( li->li_isquarantined ) {
+ slap_retry_info_t *ri = &li->li_quarantine;
+ int dont_retry = 1;
+
+ if ( li->li_quarantine.ri_interval ) {
+ ldap_pvt_thread_mutex_lock( &li->li_quarantine_mutex );
+ if ( li->li_isquarantined == LDAP_BACK_FQ_YES ) {
+ dont_retry = ( ri->ri_num[ ri->ri_idx ] == SLAP_RETRYNUM_TAIL
+ || slap_get_time() < ri->ri_last + ri->ri_interval[ ri->ri_idx ] );
+ if ( !dont_retry ) {
+ Debug( LDAP_DEBUG_ANY,
+ "%s: ldap_back_getconn quarantine "
+ "retry block #%d try #%d.\n",
+ op->o_log_prefix, ri->ri_idx, ri->ri_count );
+ li->li_isquarantined = LDAP_BACK_FQ_RETRYING;
+ }
+ }
+ ldap_pvt_thread_mutex_unlock( &li->li_quarantine_mutex );
+ }
+
+ if ( dont_retry ) {
+ rs->sr_err = LDAP_UNAVAILABLE;
+ if ( op->o_conn && ( sendok & LDAP_BACK_SENDERR ) ) {
+ rs->sr_text = "Target is quarantined";
+ send_ldap_result( op, rs );
+ }
+ return NULL;
+ }
+ }
+
+ /* Internal searches are privileged and shared. So is root. */
+ if ( op->o_do_not_cache || be_isroot( op ) ) {
+ LDAP_BACK_CONN_ISPRIV_SET( &lc_curr );
+ lc_curr.lc_local_ndn = op->o_bd->be_rootndn;
+ LDAP_BACK_PCONN_ROOTDN_SET( &lc_curr, op );
+
+ } else {
+ struct berval tmpbinddn,
+ tmpbindcred,
+ save_o_dn,
+ save_o_ndn;
+ int isproxyauthz;
+
+ /* need cleanup */
+ if ( binddn == NULL ) {
+ binddn = &tmpbinddn;
+ }
+ if ( bindcred == NULL ) {
+ bindcred = &tmpbindcred;
+ }
+ if ( op->o_tag == LDAP_REQ_BIND ) {
+ save_o_dn = op->o_dn;
+ save_o_ndn = op->o_ndn;
+ op->o_dn = op->o_req_dn;
+ op->o_ndn = op->o_req_ndn;
+ }
+ isproxyauthz = ldap_back_is_proxy_authz( op, rs, sendok, binddn, bindcred );
+ if ( op->o_tag == LDAP_REQ_BIND ) {
+ op->o_dn = save_o_dn;
+ op->o_ndn = save_o_ndn;
+ }
+ if ( isproxyauthz == -1 ) {
+ return NULL;
+ }
+
+ lc_curr.lc_local_ndn = op->o_ndn;
+ /* Explicit binds must not be shared;
+ * however, explicit binds are piped in a special connection
+ * when idassert is to occur with "override" set */
+ if ( op->o_tag == LDAP_REQ_BIND && !isproxyauthz ) {
+ lc_curr.lc_conn = op->o_conn;
+
+ } else {
+ if ( isproxyauthz && !( sendok & LDAP_BACK_BINDING ) ) {
+ lc_curr.lc_local_ndn = *binddn;
+ LDAP_BACK_PCONN_ROOTDN_SET( &lc_curr, op );
+ LDAP_BACK_CONN_ISIDASSERT_SET( &lc_curr );
+
+ } else if ( isproxyauthz && ( li->li_idassert_flags & LDAP_BACK_AUTH_OVERRIDE ) ) {
+ lc_curr.lc_local_ndn = slap_empty_bv;
+ LDAP_BACK_PCONN_BIND_SET( &lc_curr, op );
+ LDAP_BACK_CONN_ISIDASSERT_SET( &lc_curr );
+ lookupconn = 1;
+
+ } else if ( SLAP_IS_AUTHZ_BACKEND( op ) ) {
+ lc_curr.lc_conn = op->o_conn;
+
+ } else {
+ LDAP_BACK_PCONN_ANON_SET( &lc_curr, op );
+ }
+ }
+ }
+
+ /* Explicit Bind requests always get their own conn */
+ if ( lookupconn ) {
+retry_lock:
+ ldap_pvt_thread_mutex_lock( &li->li_conninfo.lai_mutex );
+ if ( LDAP_BACK_PCONN_ISPRIV( &lc_curr ) ) {
+ /* lookup a conn that's not binding */
+ LDAP_TAILQ_FOREACH( lc,
+ &li->li_conn_priv[ LDAP_BACK_CONN2PRIV( &lc_curr ) ].lic_priv,
+ lc_q )
+ {
+ if ( !LDAP_BACK_CONN_BINDING( lc ) && lc->lc_refcnt == 0 ) {
+ break;
+ }
+ }
+
+ if ( lc != NULL ) {
+ if ( lc != LDAP_TAILQ_LAST( &li->li_conn_priv[ LDAP_BACK_CONN2PRIV( lc ) ].lic_priv,
+ lc_conn_priv_q ) )
+ {
+ LDAP_TAILQ_REMOVE( &li->li_conn_priv[ LDAP_BACK_CONN2PRIV( lc ) ].lic_priv,
+ lc, lc_q );
+ LDAP_TAILQ_ENTRY_INIT( lc, lc_q );
+ LDAP_TAILQ_INSERT_TAIL( &li->li_conn_priv[ LDAP_BACK_CONN2PRIV( lc ) ].lic_priv,
+ lc, lc_q );
+ }
+
+ } else if ( !LDAP_BACK_USE_TEMPORARIES( li )
+ && li->li_conn_priv[ LDAP_BACK_CONN2PRIV( &lc_curr ) ].lic_num == li->li_conn_priv_max )
+ {
+ lc = LDAP_TAILQ_FIRST( &li->li_conn_priv[ LDAP_BACK_CONN2PRIV( &lc_curr ) ].lic_priv );
+ }
+
+ } else {
+
+ /* Searches for a ldapconn in the avl tree */
+ lc = (ldapconn_t *)ldap_tavl_find( li->li_conninfo.lai_tree,
+ (caddr_t)&lc_curr, ldap_back_conndn_cmp );
+ }
+
+ if ( lc != NULL ) {
+ /* Don't reuse connections while they're still binding */
+ if ( LDAP_BACK_CONN_BINDING( lc ) ) {
+ if ( !LDAP_BACK_USE_TEMPORARIES( li ) ) {
+ ldap_pvt_thread_mutex_unlock( &li->li_conninfo.lai_mutex );
+
+ ldap_pvt_thread_yield();
+ goto retry_lock;
+ }
+ lc = NULL;
+ }
+
+ if ( lc != NULL ) {
+ if ( op->o_tag == LDAP_REQ_BIND ) {
+ /* right now, this is the only possible case */
+ assert( ( li->li_idassert_flags & LDAP_BACK_AUTH_OVERRIDE ) );
+ LDAP_BACK_CONN_BINDING_SET( lc );
+ }
+
+ refcnt = ++lc->lc_refcnt;
+ }
+ }
+ ldap_pvt_thread_mutex_unlock( &li->li_conninfo.lai_mutex );
+ }
+
+ /* Looks like we didn't get a bind. Open a new session... */
+ if ( lc == NULL ) {
+ lc = (ldapconn_t *)ch_calloc( 1, sizeof( ldapconn_t ) );
+ lc->lc_flags = li->li_flags;
+ lc->lc_lcflags = lc_curr.lc_lcflags;
+ lc->lc_ldapinfo = li;
+ if ( ldap_back_prepare_conn( lc, op, rs, sendok ) != LDAP_SUCCESS ) {
+ ch_free( lc );
+ return NULL;
+ }
+
+ if ( sendok & LDAP_BACK_BINDING ) {
+ LDAP_BACK_CONN_BINDING_SET( lc );
+ }
+
+ lc->lc_conn = lc_curr.lc_conn;
+ ber_dupbv( &lc->lc_local_ndn, &lc_curr.lc_local_ndn );
+
+ /*
+ * the rationale is: connections as the rootdn are privileged,
+ * so li_acl is to be used; however, in some cases
+ * one already configured identity assertion with a highly
+ * privileged idassert_authcDN, so if li_acl is not configured
+ * and idassert is, use idassert instead.
+ *
+ * might change in the future, because it's preferable
+ * to make clear what identity is being used, since
+ * the only drawback is that one risks to configure
+ * the same identity twice...
+ */
+ if ( LDAP_BACK_CONN_ISPRIV( &lc_curr ) ) {
+ if ( li->li_acl_authmethod == LDAP_AUTH_NONE &&
+ li->li_idassert_authmethod != LDAP_AUTH_NONE ) {
+ ber_dupbv( &lc->lc_bound_ndn, &li->li_idassert_authcDN );
+ ber_dupbv( &lc->lc_cred, &li->li_idassert_passwd );
+
+ } else {
+ ber_dupbv( &lc->lc_bound_ndn, &li->li_acl_authcDN );
+ ber_dupbv( &lc->lc_cred, &li->li_acl_passwd );
+ }
+ LDAP_BACK_CONN_ISPRIV_SET( lc );
+
+ } else if ( LDAP_BACK_CONN_ISIDASSERT( &lc_curr ) ) {
+ if ( !LDAP_BACK_PCONN_ISBIND( &lc_curr ) ) {
+ ber_dupbv( &lc->lc_bound_ndn, &li->li_idassert_authcDN );
+ ber_dupbv( &lc->lc_cred, &li->li_idassert_passwd );
+ }
+ LDAP_BACK_CONN_ISIDASSERT_SET( lc );
+
+ } else {
+ BER_BVZERO( &lc->lc_cred );
+ BER_BVZERO( &lc->lc_bound_ndn );
+ if ( !BER_BVISEMPTY( &op->o_ndn )
+ && SLAP_IS_AUTHZ_BACKEND( op ) )
+ {
+ ber_dupbv( &lc->lc_bound_ndn, &op->o_ndn );
+ }
+ }
+
+#ifdef HAVE_TLS
+ /* if start TLS failed but it was not mandatory,
+ * check if the non-TLS connection was already
+ * in cache; in case, destroy the newly created
+ * connection and use the existing one */
+ if ( LDAP_BACK_PCONN_ISTLS( lc )
+ && !ldap_tls_inplace( lc->lc_ld ) )
+ {
+ ldapconn_t *tmplc = NULL;
+ int idx = LDAP_BACK_CONN2PRIV( &lc_curr ) - 1;
+
+ ldap_pvt_thread_mutex_lock( &li->li_conninfo.lai_mutex );
+ LDAP_TAILQ_FOREACH( tmplc,
+ &li->li_conn_priv[ idx ].lic_priv,
+ lc_q )
+ {
+ if ( !LDAP_BACK_CONN_BINDING( tmplc ) ) {
+ break;
+ }
+ }
+
+ if ( tmplc != NULL ) {
+ refcnt = ++tmplc->lc_refcnt;
+ ldap_back_conn_free( lc );
+ lc = tmplc;
+ }
+ ldap_pvt_thread_mutex_unlock( &li->li_conninfo.lai_mutex );
+
+ if ( tmplc != NULL ) {
+ goto done;
+ }
+ }
+#endif /* HAVE_TLS */
+
+ /* Inserts the newly created ldapconn in the avl tree */
+ ldap_pvt_thread_mutex_lock( &li->li_conninfo.lai_mutex );
+
+ LDAP_BACK_CONN_ISBOUND_CLEAR( lc );
+ lc->lc_connid = li->li_conn_nextid++;
+
+ assert( lc->lc_refcnt == 1 );
+
+#if LDAP_BACK_PRINT_CONNTREE > 0
+ ldap_back_print_conntree( li, ">>> ldap_back_getconn(insert)" );
+#endif /* LDAP_BACK_PRINT_CONNTREE */
+
+ if ( LDAP_BACK_PCONN_ISPRIV( lc ) ) {
+ if ( li->li_conn_priv[ LDAP_BACK_CONN2PRIV( lc ) ].lic_num < li->li_conn_priv_max ) {
+ LDAP_TAILQ_INSERT_TAIL( &li->li_conn_priv[ LDAP_BACK_CONN2PRIV( lc ) ].lic_priv, lc, lc_q );
+ li->li_conn_priv[ LDAP_BACK_CONN2PRIV( lc ) ].lic_num++;
+ LDAP_BACK_CONN_CACHED_SET( lc );
+
+ } else {
+ LDAP_BACK_CONN_TAINTED_SET( lc );
+ }
+ rs->sr_err = 0;
+
+ } else {
+ rs->sr_err = ldap_tavl_insert( &li->li_conninfo.lai_tree, (caddr_t)lc,
+ ldap_back_conndn_cmp, ldap_back_conndn_dup );
+ LDAP_BACK_CONN_CACHED_SET( lc );
+ }
+
+#if LDAP_BACK_PRINT_CONNTREE > 0
+ ldap_back_print_conntree( li, "<<< ldap_back_getconn(insert)" );
+#endif /* LDAP_BACK_PRINT_CONNTREE */
+
+ ldap_pvt_thread_mutex_unlock( &li->li_conninfo.lai_mutex );
+
+ Debug(LDAP_DEBUG_TRACE,
+ "=>ldap_back_getconn: %s: lc=%p inserted refcnt=%u rc=%d\n",
+ op->o_log_prefix, (void *)lc, refcnt,
+ rs->sr_err );
+
+ if ( !LDAP_BACK_PCONN_ISPRIV( lc ) ) {
+ /* Err could be -1 in case a duplicate ldapconn is inserted */
+ switch ( rs->sr_err ) {
+ case 0:
+ break;
+
+ case -1:
+ LDAP_BACK_CONN_CACHED_CLEAR( lc );
+ if ( !( sendok & LDAP_BACK_BINDING ) && !LDAP_BACK_USE_TEMPORARIES( li ) ) {
+ /* duplicate: free and try to get the newly created one */
+ ldap_back_conn_free( lc );
+ lc = NULL;
+ goto retry_lock;
+ }
+
+ /* taint connection, so that it'll be freed when released */
+ LDAP_BACK_CONN_TAINTED_SET( lc );
+ break;
+
+ default:
+ LDAP_BACK_CONN_CACHED_CLEAR( lc );
+ ldap_back_conn_free( lc );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "Proxy bind collision";
+ if ( op->o_conn && ( sendok & LDAP_BACK_SENDERR ) ) {
+ send_ldap_result( op, rs );
+ }
+ return NULL;
+ }
+ }
+ ldap_back_schedule_conn_expiry( li, lc );
+
+ } else {
+ int expiring = 0;
+
+ if ( ( li->li_idle_timeout != 0 && op->o_time > lc->lc_time + li->li_idle_timeout )
+ || ( li->li_conn_ttl != 0 && op->o_time > lc->lc_create_time + li->li_conn_ttl ) )
+ {
+ expiring = 1;
+
+ /* let it be used, but taint/delete it so that
+ * no-one else can look it up any further */
+ ldap_pvt_thread_mutex_lock( &li->li_conninfo.lai_mutex );
+
+#if LDAP_BACK_PRINT_CONNTREE > 0
+ ldap_back_print_conntree( li, ">>> ldap_back_getconn(timeout)" );
+#endif /* LDAP_BACK_PRINT_CONNTREE */
+
+ (void)ldap_back_conn_delete( li, lc );
+ LDAP_BACK_CONN_TAINTED_SET( lc );
+
+#if LDAP_BACK_PRINT_CONNTREE > 0
+ ldap_back_print_conntree( li, "<<< ldap_back_getconn(timeout)" );
+#endif /* LDAP_BACK_PRINT_CONNTREE */
+
+ ldap_pvt_thread_mutex_unlock( &li->li_conninfo.lai_mutex );
+ }
+
+ Debug(LDAP_DEBUG_TRACE,
+ "=>ldap_back_getconn: conn %p fetched refcnt=%u%s.\n",
+ (void *)lc, refcnt, expiring ? " expiring" : "" );
+ }
+
+#ifdef HAVE_TLS
+done:;
+#endif /* HAVE_TLS */
+
+ return lc;
+}
+
+void
+ldap_back_release_conn_lock(
+ ldapinfo_t *li,
+ ldapconn_t **lcp,
+ int dolock )
+{
+
+ ldapconn_t *lc = *lcp;
+
+ if ( dolock ) {
+ ldap_pvt_thread_mutex_lock( &li->li_conninfo.lai_mutex );
+ }
+ assert( lc->lc_refcnt > 0 );
+ LDAP_BACK_CONN_BINDING_CLEAR( lc );
+ lc->lc_refcnt--;
+ if ( LDAP_BACK_CONN_TAINTED( lc ) ) {
+ ldap_back_freeconn( li, lc, 0 );
+ *lcp = NULL;
+ }
+ if ( dolock ) {
+ ldap_pvt_thread_mutex_unlock( &li->li_conninfo.lai_mutex );
+ }
+}
+
+void
+ldap_back_quarantine(
+ Operation *op,
+ SlapReply *rs )
+{
+ ldapinfo_t *li = (ldapinfo_t *)op->o_bd->be_private;
+
+ slap_retry_info_t *ri = &li->li_quarantine;
+
+ ldap_pvt_thread_mutex_lock( &li->li_quarantine_mutex );
+
+ if ( rs->sr_err == LDAP_UNAVAILABLE ) {
+ time_t new_last = slap_get_time();
+
+ switch ( li->li_isquarantined ) {
+ case LDAP_BACK_FQ_NO:
+ if ( ri->ri_last == new_last ) {
+ goto done;
+ }
+
+ Debug( LDAP_DEBUG_ANY,
+ "%s: ldap_back_quarantine enter.\n",
+ op->o_log_prefix );
+
+ ri->ri_idx = 0;
+ ri->ri_count = 0;
+ break;
+
+ case LDAP_BACK_FQ_RETRYING:
+ Debug( LDAP_DEBUG_ANY,
+ "%s: ldap_back_quarantine block #%d try #%d failed.\n",
+ op->o_log_prefix, ri->ri_idx, ri->ri_count );
+
+ ++ri->ri_count;
+ if ( ri->ri_num[ ri->ri_idx ] != SLAP_RETRYNUM_FOREVER
+ && ri->ri_count == ri->ri_num[ ri->ri_idx ] )
+ {
+ ri->ri_count = 0;
+ ++ri->ri_idx;
+ }
+ break;
+
+ default:
+ goto done;
+ }
+
+ li->li_isquarantined = LDAP_BACK_FQ_YES;
+ ri->ri_last = new_last;
+
+ } else if ( li->li_isquarantined != LDAP_BACK_FQ_NO ) {
+ if ( ri->ri_last == slap_get_time() ) {
+ goto done;
+ }
+
+ Debug( LDAP_DEBUG_ANY,
+ "%s: ldap_back_quarantine exit (%d) err=%d.\n",
+ op->o_log_prefix, li->li_isquarantined, rs->sr_err );
+
+ if ( li->li_quarantine_f ) {
+ (void)li->li_quarantine_f( li, li->li_quarantine_p );
+ }
+
+ ri->ri_count = 0;
+ ri->ri_idx = 0;
+ li->li_isquarantined = LDAP_BACK_FQ_NO;
+ }
+
+done:;
+ ldap_pvt_thread_mutex_unlock( &li->li_quarantine_mutex );
+}
+
+static int
+ldap_back_dobind_cb(
+ Operation *op,
+ SlapReply *rs
+)
+{
+ ber_tag_t *tptr = op->o_callback->sc_private;
+ op->o_tag = *tptr;
+ rs->sr_tag = slap_req2res( op->o_tag );
+
+ return SLAP_CB_CONTINUE;
+}
+
+/*
+ * ldap_back_dobind_int
+ *
+ * Note: dolock indicates whether li->li_conninfo.lai_mutex must be locked or not
+ */
+static int
+ldap_back_dobind_int(
+ ldapconn_t **lcp,
+ Operation *op,
+ SlapReply *rs,
+ ldap_back_send_t sendok,
+ int retries,
+ int dolock )
+{
+ ldapinfo_t *li = (ldapinfo_t *)op->o_bd->be_private;
+
+ ldapconn_t *lc;
+ struct berval binddn = slap_empty_bv,
+ bindcred = slap_empty_bv;
+
+ int rc = 0,
+ isbound,
+ binding = 0;
+ ber_int_t msgid;
+ ber_tag_t o_tag = op->o_tag;
+ slap_callback cb = {0};
+ char *tmp_dn;
+
+ assert( lcp != NULL );
+ assert( retries >= 0 );
+
+ if ( sendok & LDAP_BACK_GETCONN ) {
+ assert( *lcp == NULL );
+
+ lc = ldap_back_getconn( op, rs, sendok, &binddn, &bindcred );
+ if ( lc == NULL ) {
+ return 0;
+ }
+ *lcp = lc;
+
+ } else {
+ lc = *lcp;
+ }
+
+ assert( lc != NULL );
+
+retry_lock:;
+ if ( dolock ) {
+ ldap_pvt_thread_mutex_lock( &li->li_conninfo.lai_mutex );
+ }
+
+ if ( binding == 0 ) {
+ /* check if already bound */
+ rc = isbound = LDAP_BACK_CONN_ISBOUND( lc );
+ if ( isbound ) {
+ if ( dolock ) {
+ ldap_pvt_thread_mutex_unlock( &li->li_conninfo.lai_mutex );
+ }
+ return rc;
+ }
+
+ if ( LDAP_BACK_CONN_BINDING( lc ) ) {
+ /* if someone else is about to bind it, give up and retry */
+ if ( dolock ) {
+ ldap_pvt_thread_mutex_unlock( &li->li_conninfo.lai_mutex );
+ }
+ ldap_pvt_thread_yield();
+ goto retry_lock;
+
+ } else {
+ /* otherwise this thread will bind it */
+ LDAP_BACK_CONN_BINDING_SET( lc );
+ binding = 1;
+ }
+ }
+
+ if ( dolock ) {
+ ldap_pvt_thread_mutex_unlock( &li->li_conninfo.lai_mutex );
+ }
+
+ /*
+ * FIXME: we need to let clients use proxyAuthz
+ * otherwise we cannot do symmetric pools of servers;
+ * we have to live with the fact that a user can
+ * authorize itself as any ID that is allowed
+ * by the authzTo directive of the "proxyauthzdn".
+ */
+ /*
+ * NOTE: current Proxy Authorization specification
+ * and implementation do not allow proxy authorization
+ * control to be provided with Bind requests
+ */
+ /*
+ * if no bind took place yet, but the connection is bound
+ * and the "idassert-authcDN" (or other ID) is set,
+ * then bind as the asserting identity and explicitly
+ * add the proxyAuthz control to every operation with the
+ * dn bound to the connection as control value.
+ * This is done also if this is the authorizing backend,
+ * but the "override" flag is given to idassert.
+ * It allows to use SASL bind and yet proxyAuthz users
+ */
+ op->o_tag = LDAP_REQ_BIND;
+ cb.sc_next = op->o_callback;
+ cb.sc_private = &o_tag;
+ cb.sc_response = ldap_back_dobind_cb;
+ op->o_callback = &cb;
+
+ if ( LDAP_BACK_CONN_ISIDASSERT( lc ) ) {
+ if ( BER_BVISEMPTY( &binddn ) && BER_BVISEMPTY( &bindcred ) ) {
+ /* if we got here, it shouldn't return result */
+ rc = ldap_back_is_proxy_authz( op, rs,
+ LDAP_BACK_DONTSEND, &binddn, &bindcred );
+ if ( rc != 1 ) {
+ Debug( LDAP_DEBUG_ANY, "Error: ldap_back_is_proxy_authz "
+ "returned %d, misconfigured URI?\n", rc );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "misconfigured URI?";
+ LDAP_BACK_CONN_ISBOUND_CLEAR( lc );
+ if ( sendok & LDAP_BACK_SENDERR ) {
+ send_ldap_result( op, rs );
+ }
+ goto done;
+ }
+ }
+ rc = ldap_back_proxy_authz_bind( lc, op, rs, sendok, &binddn, &bindcred );
+ goto done;
+ }
+
+#ifdef HAVE_CYRUS_SASL
+ if ( LDAP_BACK_CONN_ISPRIV( lc )) {
+ slap_bindconf *sb;
+ if ( li->li_acl_authmethod != LDAP_AUTH_NONE )
+ sb = &li->li_acl;
+ else
+ sb = &li->li_idassert.si_bc;
+
+ if ( sb->sb_method == LDAP_AUTH_SASL ) {
+ void *defaults = NULL;
+
+ if ( sb->sb_secprops != NULL ) {
+ rc = ldap_set_option( lc->lc_ld,
+ LDAP_OPT_X_SASL_SECPROPS, sb->sb_secprops );
+
+ if ( rc != LDAP_OPT_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "Error: ldap_set_option "
+ "(SECPROPS,\"%s\") failed!\n",
+ sb->sb_secprops );
+ goto done;
+ }
+ }
+
+ defaults = lutil_sasl_defaults( lc->lc_ld,
+ sb->sb_saslmech.bv_val,
+ sb->sb_realm.bv_val,
+ sb->sb_authcId.bv_val,
+ sb->sb_cred.bv_val,
+ sb->sb_authzId.bv_val );
+ if ( defaults == NULL ) {
+ rs->sr_err = LDAP_OTHER;
+ LDAP_BACK_CONN_ISBOUND_CLEAR( lc );
+ if ( sendok & LDAP_BACK_SENDERR ) {
+ send_ldap_result( op, rs );
+ }
+ goto done;
+ }
+
+ rs->sr_err = ldap_sasl_interactive_bind_s( lc->lc_ld,
+ sb->sb_binddn.bv_val,
+ sb->sb_saslmech.bv_val, NULL, NULL,
+ LDAP_SASL_QUIET, lutil_sasl_interact,
+ defaults );
+
+ ldap_pvt_thread_mutex_lock( &li->li_counter_mutex );
+ ldap_pvt_mp_add( li->li_ops_completed[ SLAP_OP_BIND ], 1 );
+ ldap_pvt_thread_mutex_unlock( &li->li_counter_mutex );
+
+ lutil_sasl_freedefs( defaults );
+
+ switch ( rs->sr_err ) {
+ case LDAP_SUCCESS:
+ LDAP_BACK_CONN_ISBOUND_SET( lc );
+ break;
+
+ case LDAP_LOCAL_ERROR:
+ /* list client API error codes that require
+ * to taint the connection */
+ /* FIXME: should actually retry? */
+ LDAP_BACK_CONN_TAINTED_SET( lc );
+
+ /* fallthru */
+
+ default:
+ LDAP_BACK_CONN_ISBOUND_CLEAR( lc );
+ rs->sr_err = slap_map_api2result( rs );
+ if ( sendok & LDAP_BACK_SENDERR ) {
+ send_ldap_result( op, rs );
+ }
+ break;
+ }
+
+ if ( LDAP_BACK_QUARANTINE( li ) ) {
+ ldap_back_quarantine( op, rs );
+ }
+
+ goto done;
+ }
+ }
+#endif /* HAVE_CYRUS_SASL */
+
+retry:;
+ if ( BER_BVISNULL( &lc->lc_cred ) ) {
+ tmp_dn = "";
+ /*
+ * Bind is requested with DN but without credentials.
+ * This can happen when connection to remote server has been
+ * lost either due to remote server disconnecting it or due to
+ * proxy disconnecting it by itself (idle-timeout, conn-ttl).
+ * See comment in ldap_back_conn_prune().
+ */
+ if ( !BER_BVISNULL( &lc->lc_bound_ndn ) && !BER_BVISEMPTY( &lc->lc_bound_ndn ) ) {
+ Debug( LDAP_DEBUG_ANY,
+ "%s ldap_back_dobind_int: DN=\"%s\" connection "
+ "was re-established but cannot rebind without creds\n",
+ op->o_log_prefix, lc->lc_bound_ndn.bv_val );
+ rs->sr_text = "Proxy lost connection to remote server";
+ rs->sr_err = LDAP_UNAVAILABLE;
+ if ( sendok & LDAP_BACK_SENDERR ) {
+ send_ldap_result( op, rs );
+ }
+ rs->sr_err = SLAPD_DISCONNECT;
+ rc = 0;
+ goto done;
+ }
+
+ } else {
+ tmp_dn = lc->lc_bound_ndn.bv_val;
+ }
+ rs->sr_err = ldap_sasl_bind( lc->lc_ld,
+ tmp_dn,
+ LDAP_SASL_SIMPLE, &lc->lc_cred,
+ NULL, NULL, &msgid );
+
+ ldap_pvt_thread_mutex_lock( &li->li_counter_mutex );
+ ldap_pvt_mp_add( li->li_ops_completed[ SLAP_OP_BIND ], 1 );
+ ldap_pvt_thread_mutex_unlock( &li->li_counter_mutex );
+
+ if ( rs->sr_err == LDAP_SERVER_DOWN ) {
+ if ( retries != LDAP_BACK_RETRY_NEVER ) {
+ if ( dolock ) {
+ ldap_pvt_thread_mutex_lock( &li->li_conninfo.lai_mutex );
+ }
+
+ assert( lc->lc_refcnt > 0 );
+ if ( lc->lc_refcnt == 1 ) {
+ ldap_unbind_ext( lc->lc_ld, NULL, NULL );
+ lc->lc_ld = NULL;
+
+ /* lc here must be the regular lc, reset and ready for init */
+ rs->sr_err = ldap_back_prepare_conn( lc, op, rs, sendok );
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ sendok &= ~LDAP_BACK_SENDERR;
+ lc->lc_refcnt = 0;
+ }
+ }
+
+ if ( dolock ) {
+ ldap_pvt_thread_mutex_unlock( &li->li_conninfo.lai_mutex );
+ }
+
+ if ( rs->sr_err == LDAP_SUCCESS ) {
+ if ( retries > 0 ) {
+ retries--;
+ }
+ goto retry;
+ }
+ }
+
+ assert( lc->lc_refcnt == 1 );
+ lc->lc_refcnt = 0;
+ ldap_back_freeconn( li, lc, dolock );
+ *lcp = NULL;
+ rs->sr_err = slap_map_api2result( rs );
+
+ if ( LDAP_BACK_QUARANTINE( li ) ) {
+ ldap_back_quarantine( op, rs );
+ }
+
+ if ( rs->sr_err != LDAP_SUCCESS &&
+ ( sendok & LDAP_BACK_SENDERR ) )
+ {
+ if ( op->o_callback == &cb )
+ op->o_callback = cb.sc_next;
+ op->o_tag = o_tag;
+ rs->sr_text = "Proxy can't contact remote server";
+ send_ldap_result( op, rs );
+ /* if we originally bound and wanted rebind-as-user, must drop
+ * the connection now because we just discarded the credentials.
+ * ITS#7464, #8142
+ */
+ if ( LDAP_BACK_SAVECRED( li ) && SLAP_IS_AUTHZ_BACKEND( op ) )
+ rs->sr_err = SLAPD_DISCONNECT;
+ }
+
+ rc = 0;
+ goto func_leave;
+ }
+
+ rc = ldap_back_op_result( lc, op, rs, msgid,
+ -1, ( sendok | LDAP_BACK_BINDING ) );
+ if ( rc == LDAP_SUCCESS ) {
+ LDAP_BACK_CONN_ISBOUND_SET( lc );
+ }
+
+done:;
+ LDAP_BACK_CONN_BINDING_CLEAR( lc );
+ rc = LDAP_BACK_CONN_ISBOUND( lc );
+ if ( !rc ) {
+ ldap_back_release_conn_lock( li, lcp, dolock );
+
+ } else if ( LDAP_BACK_SAVECRED( li ) ) {
+ ldap_set_rebind_proc( lc->lc_ld, li->li_rebind_f, lc );
+ }
+
+func_leave:;
+ if ( op->o_callback == &cb )
+ op->o_callback = cb.sc_next;
+ op->o_tag = o_tag;
+
+ return rc;
+}
+
+/*
+ * ldap_back_dobind
+ *
+ * Note: dolock indicates whether li->li_conninfo.lai_mutex must be locked or not
+ */
+int
+ldap_back_dobind( ldapconn_t **lcp, Operation *op, SlapReply *rs, ldap_back_send_t sendok )
+{
+ ldapinfo_t *li = (ldapinfo_t *)op->o_bd->be_private;
+
+ return ldap_back_dobind_int( lcp, op, rs,
+ ( sendok | LDAP_BACK_GETCONN ), li->li_nretries, 1 );
+}
+
+/*
+ * ldap_back_default_rebind
+ *
+ * This is a callback used for chasing referrals using the same
+ * credentials as the original user on this session.
+ */
+int
+ldap_back_default_rebind( LDAP *ld, LDAP_CONST char *url, ber_tag_t request,
+ ber_int_t msgid, void *params )
+{
+ ldapconn_t *lc = (ldapconn_t *)params;
+
+#ifdef HAVE_TLS
+ /* ... otherwise we couldn't get here */
+ assert( lc != NULL );
+
+ if ( !ldap_tls_inplace( ld ) ) {
+ int is_tls = LDAP_BACK_CONN_ISTLS( lc ),
+ rc;
+ const char *text = NULL;
+
+ rc = ldap_back_start_tls( ld, 0, &is_tls, url, lc->lc_flags,
+ lc->lc_ldapinfo->li_timeout[ SLAP_OP_BIND ], &text );
+ if ( rc != LDAP_SUCCESS ) {
+ return rc;
+ }
+ }
+#endif /* HAVE_TLS */
+
+ /* FIXME: add checks on the URL/identity? */
+ /* TODO: would like to count this bind operation for monitoring
+ * too, but where do we get the ldapinfo_t? */
+
+ return ldap_sasl_bind_s( ld,
+ BER_BVISNULL( &lc->lc_cred ) ? "" : lc->lc_bound_ndn.bv_val,
+ LDAP_SASL_SIMPLE, &lc->lc_cred, NULL, NULL, NULL );
+}
+
+/*
+ * ldap_back_default_urllist
+ */
+int
+ldap_back_default_urllist(
+ LDAP *ld,
+ LDAPURLDesc **urllist,
+ LDAPURLDesc **url,
+ void *params )
+{
+ ldapinfo_t *li = (ldapinfo_t *)params;
+ LDAPURLDesc **urltail;
+
+ if ( urllist == url ) {
+ return LDAP_SUCCESS;
+ }
+
+ for ( urltail = &(*url)->lud_next; *urltail; urltail = &(*urltail)->lud_next )
+ /* count */ ;
+
+ *urltail = *urllist;
+ *urllist = *url;
+ *url = NULL;
+
+ if ( !li->li_uri_mutex_do_not_lock ) {
+ ldap_pvt_thread_mutex_lock( &li->li_uri_mutex );
+ }
+
+ if ( li->li_uri ) {
+ ch_free( li->li_uri );
+ }
+
+ ldap_get_option( ld, LDAP_OPT_URI, (void *)&li->li_uri );
+
+ if ( !li->li_uri_mutex_do_not_lock ) {
+ ldap_pvt_thread_mutex_unlock( &li->li_uri_mutex );
+ }
+
+ return LDAP_SUCCESS;
+}
+
+int
+ldap_back_cancel(
+ ldapconn_t *lc,
+ Operation *op,
+ SlapReply *rs,
+ ber_int_t msgid,
+ ldap_back_send_t sendok )
+{
+ ldapinfo_t *li = (ldapinfo_t *)op->o_bd->be_private;
+
+ /* default behavior */
+ if ( LDAP_BACK_ABANDON( li ) ) {
+ return ldap_abandon_ext( lc->lc_ld, msgid, NULL, NULL );
+ }
+
+ if ( LDAP_BACK_IGNORE( li ) ) {
+ return ldap_pvt_discard( lc->lc_ld, msgid );
+ }
+
+ if ( LDAP_BACK_CANCEL( li ) ) {
+ /* FIXME: asynchronous? */
+ return ldap_cancel_s( lc->lc_ld, msgid, NULL, NULL );
+ }
+
+ assert( 0 );
+
+ return LDAP_OTHER;
+}
+
+int
+ldap_back_op_result(
+ ldapconn_t *lc,
+ Operation *op,
+ SlapReply *rs,
+ ber_int_t msgid,
+ time_t timeout,
+ ldap_back_send_t sendok )
+{
+ ldapinfo_t *li = (ldapinfo_t *)op->o_bd->be_private;
+
+ char *match = NULL;
+ char *text = NULL;
+ char **refs = NULL;
+ LDAPControl **ctrls = NULL;
+
+ rs->sr_text = NULL;
+ rs->sr_matched = NULL;
+ rs->sr_ref = NULL;
+ rs->sr_ctrls = NULL;
+
+ /* if the error recorded in the reply corresponds
+ * to a successful state, get the error from the
+ * remote server response */
+ if ( LDAP_ERR_OK( rs->sr_err ) ) {
+ int rc;
+ struct timeval tv;
+ LDAPMessage *res = NULL;
+ time_t stoptime = (time_t)(-1);
+ int timeout_err = op->o_protocol >= LDAP_VERSION3 ?
+ LDAP_ADMINLIMIT_EXCEEDED : LDAP_OTHER;
+ const char *timeout_text = "Operation timed out";
+
+ /* if timeout is not specified, compute and use
+ * the one specific to the ongoing operation */
+ if ( timeout == (time_t)(-1) ) {
+ slap_op_t opidx = slap_req2op( op->o_tag );
+
+ if ( opidx == SLAP_OP_SEARCH ) {
+ if ( op->ors_tlimit <= 0 ) {
+ timeout = 0;
+
+ } else {
+ timeout = op->ors_tlimit;
+ timeout_err = LDAP_TIMELIMIT_EXCEEDED;
+ timeout_text = NULL;
+ }
+
+ } else {
+ timeout = li->li_timeout[ opidx ];
+ }
+ }
+
+ /* better than nothing :) */
+ if ( timeout == 0 ) {
+ if ( li->li_idle_timeout ) {
+ timeout = li->li_idle_timeout;
+
+ } else if ( li->li_conn_ttl ) {
+ timeout = li->li_conn_ttl;
+ }
+ }
+
+ if ( timeout ) {
+ stoptime = op->o_time + timeout;
+ }
+
+ LDAP_BACK_TV_SET( &tv );
+
+retry:;
+ /* if result parsing fails, note the failure reason */
+ rc = ldap_result( lc->lc_ld, msgid, LDAP_MSG_ALL, &tv, &res );
+ switch ( rc ) {
+ case 0:
+ if ( timeout && slap_get_time() > stoptime ) {
+ if ( sendok & LDAP_BACK_BINDING ) {
+ ldap_unbind_ext( lc->lc_ld, NULL, NULL );
+ lc->lc_ld = NULL;
+
+ /* let it be used, but taint/delete it so that
+ * no-one else can look it up any further */
+ ldap_pvt_thread_mutex_lock( &li->li_conninfo.lai_mutex );
+
+#if LDAP_BACK_PRINT_CONNTREE > 0
+ ldap_back_print_conntree( li, ">>> ldap_back_getconn(timeout)" );
+#endif /* LDAP_BACK_PRINT_CONNTREE */
+
+ (void)ldap_back_conn_delete( li, lc );
+ LDAP_BACK_CONN_TAINTED_SET( lc );
+
+#if LDAP_BACK_PRINT_CONNTREE > 0
+ ldap_back_print_conntree( li, "<<< ldap_back_getconn(timeout)" );
+#endif /* LDAP_BACK_PRINT_CONNTREE */
+ ldap_pvt_thread_mutex_unlock( &li->li_conninfo.lai_mutex );
+
+ } else {
+ (void)ldap_back_cancel( lc, op, rs, msgid, sendok );
+ }
+ rs->sr_err = timeout_err;
+ rs->sr_text = timeout_text;
+ break;
+ }
+
+ /* timeout == 0 */
+ LDAP_BACK_TV_SET( &tv );
+ ldap_pvt_thread_yield();
+ goto retry;
+
+ case -1:
+ ldap_get_option( lc->lc_ld, LDAP_OPT_ERROR_NUMBER,
+ &rs->sr_err );
+ break;
+
+
+ /* otherwise get the result; if it is not
+ * LDAP_SUCCESS, record it in the reply
+ * structure (this includes
+ * LDAP_COMPARE_{TRUE|FALSE}) */
+ default:
+ /* only touch when activity actually took place... */
+ if ( li->li_idle_timeout ) {
+ lc->lc_time = op->o_time;
+ }
+
+ rc = ldap_parse_result( lc->lc_ld, res, &rs->sr_err,
+ &match, &text, &refs, &ctrls, 1 );
+ if ( rc == LDAP_SUCCESS ) {
+ rs->sr_text = text;
+ } else {
+ rs->sr_err = rc;
+ }
+ rs->sr_err = slap_map_api2result( rs );
+
+ /* RFC 4511: referrals can only appear
+ * if result code is LDAP_REFERRAL */
+ if ( refs != NULL
+ && refs[ 0 ] != NULL
+ && refs[ 0 ][ 0 ] != '\0' )
+ {
+ if ( rs->sr_err != LDAP_REFERRAL ) {
+ Debug( LDAP_DEBUG_ANY,
+ "%s ldap_back_op_result: "
+ "got referrals with err=%d\n",
+ op->o_log_prefix,
+ rs->sr_err );
+
+ } else {
+ int i;
+
+ for ( i = 0; refs[ i ] != NULL; i++ )
+ /* count */ ;
+ rs->sr_ref = op->o_tmpalloc( sizeof( struct berval ) * ( i + 1 ),
+ op->o_tmpmemctx );
+ for ( i = 0; refs[ i ] != NULL; i++ ) {
+ ber_str2bv( refs[ i ], 0, 0, &rs->sr_ref[ i ] );
+ }
+ BER_BVZERO( &rs->sr_ref[ i ] );
+ }
+
+ } else if ( rs->sr_err == LDAP_REFERRAL ) {
+ Debug( LDAP_DEBUG_ANY,
+ "%s ldap_back_op_result: "
+ "got err=%d with null "
+ "or empty referrals\n",
+ op->o_log_prefix,
+ rs->sr_err );
+
+ rs->sr_err = LDAP_NO_SUCH_OBJECT;
+ }
+
+ if ( ctrls != NULL ) {
+ rs->sr_ctrls = ctrls;
+ }
+ }
+ }
+
+ /* if the error in the reply structure is not
+ * LDAP_SUCCESS, try to map it from client
+ * to server error */
+ if ( !LDAP_ERR_OK( rs->sr_err ) ) {
+ rs->sr_err = slap_map_api2result( rs );
+
+ /* internal ops ( op->o_conn == NULL )
+ * must not reply to client */
+ if ( op->o_conn && !op->o_do_not_cache && match ) {
+
+ /* record the (massaged) matched
+ * DN into the reply structure */
+ rs->sr_matched = match;
+ }
+ }
+
+ if ( rs->sr_err == LDAP_UNAVAILABLE ) {
+ if ( !( sendok & LDAP_BACK_RETRYING ) ) {
+ if ( LDAP_BACK_QUARANTINE( li ) ) {
+ ldap_back_quarantine( op, rs );
+ }
+ if ( op->o_conn && ( sendok & LDAP_BACK_SENDERR ) ) {
+ if ( rs->sr_text == NULL ) rs->sr_text = "Proxy operation retry failed";
+ send_ldap_result( op, rs );
+ }
+ }
+
+ } else if ( op->o_conn &&
+ ( ( ( sendok & LDAP_BACK_SENDOK ) && LDAP_ERR_OK( rs->sr_err ) )
+ || ( ( sendok & LDAP_BACK_SENDERR ) && !LDAP_ERR_OK( rs->sr_err ) ) ) )
+ {
+ send_ldap_result( op, rs );
+ }
+
+ if ( text ) {
+ ldap_memfree( text );
+ }
+ rs->sr_text = NULL;
+
+ /* there can't be refs with a (successful) bind */
+ if ( rs->sr_ref ) {
+ op->o_tmpfree( rs->sr_ref, op->o_tmpmemctx );
+ rs->sr_ref = NULL;
+ }
+
+ if ( refs ) {
+ ber_memvfree( (void **)refs );
+ }
+
+ /* match should not be possible with a successful bind */
+ if ( match ) {
+ if ( rs->sr_matched != match ) {
+ free( (char *)rs->sr_matched );
+ }
+ rs->sr_matched = NULL;
+ ldap_memfree( match );
+ }
+
+ if ( ctrls != NULL ) {
+ if ( op->o_tag == LDAP_REQ_BIND && rs->sr_err == LDAP_SUCCESS ) {
+ int i;
+
+ for ( i = 0; ctrls[i] != NULL; i++ );
+
+ rs->sr_ctrls = op->o_tmpalloc( sizeof( LDAPControl * )*( i + 1 ),
+ op->o_tmpmemctx );
+ for ( i = 0; ctrls[ i ] != NULL; i++ ) {
+ char *ptr;
+ ber_len_t oidlen = strlen( ctrls[i]->ldctl_oid );
+ ber_len_t size = sizeof( LDAPControl )
+ + oidlen + 1
+ + ctrls[i]->ldctl_value.bv_len + 1;
+
+ rs->sr_ctrls[ i ] = op->o_tmpalloc( size, op->o_tmpmemctx );
+ rs->sr_ctrls[ i ]->ldctl_oid = (char *)&rs->sr_ctrls[ i ][ 1 ];
+ lutil_strcopy( rs->sr_ctrls[ i ]->ldctl_oid, ctrls[i]->ldctl_oid );
+ rs->sr_ctrls[ i ]->ldctl_value.bv_val
+ = (char *)&rs->sr_ctrls[ i ]->ldctl_oid[oidlen + 1];
+ rs->sr_ctrls[ i ]->ldctl_value.bv_len
+ = ctrls[i]->ldctl_value.bv_len;
+ ptr = lutil_memcopy( rs->sr_ctrls[ i ]->ldctl_value.bv_val,
+ ctrls[i]->ldctl_value.bv_val, ctrls[i]->ldctl_value.bv_len );
+ *ptr = '\0';
+ }
+ rs->sr_ctrls[ i ] = NULL;
+ rs->sr_flags |= REP_CTRLS_MUSTBEFREED;
+
+ } else {
+ assert( rs->sr_ctrls != NULL );
+ rs->sr_ctrls = NULL;
+ }
+
+ ldap_controls_free( ctrls );
+ }
+
+ return( LDAP_ERR_OK( rs->sr_err ) ? LDAP_SUCCESS : rs->sr_err );
+}
+
+/* return true if bound, false if failed */
+int
+ldap_back_retry( ldapconn_t **lcp, Operation *op, SlapReply *rs, ldap_back_send_t sendok )
+{
+ ldapinfo_t *li = (ldapinfo_t *)op->o_bd->be_private;
+ int rc = 0;
+
+ assert( lcp != NULL );
+ assert( *lcp != NULL );
+
+ ldap_pvt_thread_mutex_lock( &li->li_conninfo.lai_mutex );
+
+ if ( (*lcp)->lc_refcnt == 1 ) {
+ int binding = LDAP_BACK_CONN_BINDING( *lcp );
+
+ ldap_pvt_thread_mutex_lock( &li->li_uri_mutex );
+ Debug( LDAP_DEBUG_ANY,
+ "%s ldap_back_retry: retrying URI=\"%s\" DN=\"%s\"\n",
+ op->o_log_prefix, li->li_uri,
+ BER_BVISNULL( &(*lcp)->lc_bound_ndn ) ?
+ "" : (*lcp)->lc_bound_ndn.bv_val );
+ ldap_pvt_thread_mutex_unlock( &li->li_uri_mutex );
+
+ ldap_unbind_ext( (*lcp)->lc_ld, NULL, NULL );
+ (*lcp)->lc_ld = NULL;
+ LDAP_BACK_CONN_ISBOUND_CLEAR( (*lcp) );
+
+ /* lc here must be the regular lc, reset and ready for init */
+ rc = ldap_back_prepare_conn( *lcp, op, rs, sendok );
+ if ( rc != LDAP_SUCCESS ) {
+ /* freeit, because lc_refcnt == 1 */
+ (*lcp)->lc_refcnt = 0;
+ (void)ldap_back_freeconn( li, *lcp, 0 );
+ *lcp = NULL;
+ rc = 0;
+
+ } else if ( ( sendok & LDAP_BACK_BINDING ) ) {
+ if ( binding ) {
+ LDAP_BACK_CONN_BINDING_SET( *lcp );
+ }
+ rc = 1;
+
+ } else {
+ rc = ldap_back_dobind_int( lcp, op, rs, sendok, 0, 0 );
+ if ( rc == 0 && *lcp != NULL ) {
+ /* freeit, because lc_refcnt == 1 */
+ (*lcp)->lc_refcnt = 0;
+ (void)ldap_back_freeconn( li, *lcp, 0 );
+ *lcp = NULL;
+ }
+ }
+
+ } else {
+ Debug( LDAP_DEBUG_TRACE,
+ "ldap_back_retry: conn %p refcnt=%u unable to retry.\n",
+ (void *)(*lcp), (*lcp)->lc_refcnt );
+
+ LDAP_BACK_CONN_TAINTED_SET( *lcp );
+ ldap_back_release_conn_lock( li, lcp, 0 );
+ assert( *lcp == NULL );
+
+ if ( sendok & LDAP_BACK_SENDERR ) {
+ rs->sr_err = LDAP_UNAVAILABLE;
+ rs->sr_text = "Unable to retry";
+ send_ldap_result( op, rs );
+ }
+ }
+
+ ldap_pvt_thread_mutex_unlock( &li->li_conninfo.lai_mutex );
+
+ return rc;
+}
+
+static int
+ldap_back_is_proxy_authz( Operation *op, SlapReply *rs, ldap_back_send_t sendok,
+ struct berval *binddn, struct berval *bindcred )
+{
+ ldapinfo_t *li = (ldapinfo_t *)op->o_bd->be_private;
+ struct berval ndn;
+ int dobind = 0;
+
+ if ( op->o_conn == NULL || op->o_do_not_cache ) {
+ goto done;
+ }
+
+ /* don't proxyAuthz if protocol is not LDAPv3 */
+ switch ( li->li_version ) {
+ case LDAP_VERSION3:
+ break;
+
+ case 0:
+ if ( op->o_protocol == 0 || op->o_protocol == LDAP_VERSION3 ) {
+ break;
+ }
+ /* fall thru */
+
+ default:
+ rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+ if ( sendok & LDAP_BACK_SENDERR ) {
+ send_ldap_result( op, rs );
+ dobind = -1;
+ }
+ goto done;
+ }
+
+ /* safe default */
+ *binddn = slap_empty_bv;
+ *bindcred = slap_empty_bv;
+
+ if ( !BER_BVISNULL( &op->o_conn->c_ndn ) ) {
+ ndn = op->o_conn->c_ndn;
+
+ } else {
+ ndn = op->o_ndn;
+ }
+
+ if ( !( li->li_idassert_flags & LDAP_BACK_AUTH_OVERRIDE )) {
+ if ( op->o_tag == LDAP_REQ_BIND && ( sendok & LDAP_BACK_SENDERR )) {
+ if ( !BER_BVISEMPTY( &ndn )) {
+ dobind = 0;
+ goto done;
+ }
+ } else if ( SLAP_IS_AUTHZ_BACKEND( op )) {
+ dobind = 0;
+ goto done;
+ }
+ }
+
+ switch ( li->li_idassert_mode ) {
+ case LDAP_BACK_IDASSERT_LEGACY:
+ if ( !BER_BVISNULL( &ndn ) && !BER_BVISEMPTY( &ndn ) ) {
+ if ( !BER_BVISNULL( &li->li_idassert_authcDN ) && !BER_BVISEMPTY( &li->li_idassert_authcDN ) )
+ {
+ *binddn = li->li_idassert_authcDN;
+ *bindcred = li->li_idassert_passwd;
+ dobind = 1;
+ }
+ }
+ break;
+
+ default:
+ /* NOTE: rootdn can always idassert */
+ if ( BER_BVISNULL( &ndn )
+ && li->li_idassert_authz == NULL
+ && !( li->li_idassert_flags & LDAP_BACK_AUTH_AUTHZ_ALL ) )
+ {
+ if ( li->li_idassert_flags & LDAP_BACK_AUTH_PRESCRIPTIVE ) {
+ rs->sr_err = LDAP_INAPPROPRIATE_AUTH;
+ if ( sendok & LDAP_BACK_SENDERR ) {
+ send_ldap_result( op, rs );
+ dobind = -1;
+ }
+
+ } else {
+ rs->sr_err = LDAP_SUCCESS;
+ *binddn = slap_empty_bv;
+ *bindcred = slap_empty_bv;
+ break;
+ }
+
+ goto done;
+
+ } else if ( !be_isroot( op ) ) {
+ if ( li->li_idassert_passthru ) {
+ struct berval authcDN;
+
+ if ( BER_BVISNULL( &ndn ) ) {
+ authcDN = slap_empty_bv;
+
+ } else {
+ authcDN = ndn;
+ }
+ rs->sr_err = slap_sasl_matches( op, li->li_idassert_passthru,
+ &authcDN, &authcDN );
+ if ( rs->sr_err == LDAP_SUCCESS ) {
+ dobind = 0;
+ break;
+ }
+ }
+
+ if ( li->li_idassert_authz ) {
+ struct berval authcDN;
+
+ if ( BER_BVISNULL( &ndn ) ) {
+ authcDN = slap_empty_bv;
+
+ } else {
+ authcDN = ndn;
+ }
+ rs->sr_err = slap_sasl_matches( op, li->li_idassert_authz,
+ &authcDN, &authcDN );
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ if ( li->li_idassert_flags & LDAP_BACK_AUTH_PRESCRIPTIVE ) {
+ if ( sendok & LDAP_BACK_SENDERR ) {
+ send_ldap_result( op, rs );
+ dobind = -1;
+ }
+
+ } else {
+ rs->sr_err = LDAP_SUCCESS;
+ *binddn = slap_empty_bv;
+ *bindcred = slap_empty_bv;
+ break;
+ }
+
+ goto done;
+ }
+ }
+ }
+
+ *binddn = li->li_idassert_authcDN;
+ *bindcred = li->li_idassert_passwd;
+ dobind = 1;
+ break;
+ }
+
+done:;
+ return dobind;
+}
+
+static int
+ldap_back_proxy_authz_bind(
+ ldapconn_t *lc,
+ Operation *op,
+ SlapReply *rs,
+ ldap_back_send_t sendok,
+ struct berval *binddn,
+ struct berval *bindcred )
+{
+ ldapinfo_t *li = (ldapinfo_t *)op->o_bd->be_private;
+ struct berval ndn;
+ int msgid;
+ int rc;
+
+ if ( !BER_BVISNULL( &op->o_conn->c_ndn ) ) {
+ ndn = op->o_conn->c_ndn;
+
+ } else {
+ ndn = op->o_ndn;
+ }
+
+ if ( li->li_idassert_authmethod == LDAP_AUTH_SASL ) {
+#ifdef HAVE_CYRUS_SASL
+ void *defaults = NULL;
+ struct berval authzID = BER_BVNULL;
+ int freeauthz = 0;
+ LDAPControl **ctrlsp = NULL;
+ LDAPMessage *result = NULL;
+ const char *rmech = NULL;
+ const char *save_text = rs->sr_text;
+
+#ifdef SLAP_AUTH_DN
+ LDAPControl ctrl, *ctrls[2];
+ int msgid;
+#endif /* SLAP_AUTH_DN */
+
+ /* if SASL supports native authz, prepare for it */
+ if ( ( !op->o_do_not_cache || !op->o_is_auth_check ) &&
+ ( li->li_idassert_flags & LDAP_BACK_AUTH_NATIVE_AUTHZ ) )
+ {
+ switch ( li->li_idassert_mode ) {
+ case LDAP_BACK_IDASSERT_OTHERID:
+ case LDAP_BACK_IDASSERT_OTHERDN:
+ authzID = li->li_idassert_authzID;
+ break;
+
+ case LDAP_BACK_IDASSERT_ANONYMOUS:
+ BER_BVSTR( &authzID, "dn:" );
+ break;
+
+ case LDAP_BACK_IDASSERT_SELF:
+ if ( BER_BVISNULL( &ndn ) ) {
+ /* connection is not authc'd, so don't idassert */
+ BER_BVSTR( &authzID, "dn:" );
+ break;
+ }
+ authzID.bv_len = STRLENOF( "dn:" ) + ndn.bv_len;
+ authzID.bv_val = slap_sl_malloc( authzID.bv_len + 1, op->o_tmpmemctx );
+ AC_MEMCPY( authzID.bv_val, "dn:", STRLENOF( "dn:" ) );
+ AC_MEMCPY( authzID.bv_val + STRLENOF( "dn:" ),
+ ndn.bv_val, ndn.bv_len + 1 );
+ freeauthz = 1;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if ( li->li_idassert_secprops != NULL ) {
+ rs->sr_err = ldap_set_option( lc->lc_ld,
+ LDAP_OPT_X_SASL_SECPROPS,
+ (void *)li->li_idassert_secprops );
+
+ if ( rs->sr_err != LDAP_OPT_SUCCESS ) {
+ rs->sr_err = LDAP_OTHER;
+ if ( sendok & LDAP_BACK_SENDERR ) {
+ send_ldap_result( op, rs );
+ }
+ LDAP_BACK_CONN_ISBOUND_CLEAR( lc );
+ goto done;
+ }
+ }
+
+ defaults = lutil_sasl_defaults( lc->lc_ld,
+ li->li_idassert_sasl_mech.bv_val,
+ li->li_idassert_sasl_realm.bv_val,
+ li->li_idassert_authcID.bv_val,
+ li->li_idassert_passwd.bv_val,
+ authzID.bv_val );
+ if ( defaults == NULL ) {
+ rs->sr_err = LDAP_OTHER;
+ LDAP_BACK_CONN_ISBOUND_CLEAR( lc );
+ if ( sendok & LDAP_BACK_SENDERR ) {
+ send_ldap_result( op, rs );
+ }
+ goto done;
+ }
+
+#ifdef SLAP_AUTH_DN
+ if ( li->li_idassert_flags & LDAP_BACK_AUTH_DN_AUTHZID ) {
+ assert( BER_BVISNULL( binddn ) );
+
+ ctrl.ldctl_oid = LDAP_CONTROL_AUTHZID_REQUEST;
+ ctrl.ldctl_iscritical = 0;
+ BER_BVZERO( &ctrl.ldctl_value );
+ ctrls[0] = &ctrl;
+ ctrls[1] = NULL;
+ ctrlsp = ctrls;
+ }
+#endif /* SLAP_AUTH_DN */
+
+ do {
+ rs->sr_err = ldap_sasl_interactive_bind( lc->lc_ld, binddn->bv_val,
+ li->li_idassert_sasl_mech.bv_val,
+ ctrlsp, NULL, LDAP_SASL_QUIET, lutil_sasl_interact, defaults,
+ result, &rmech, &msgid );
+
+ if ( rs->sr_err != LDAP_SASL_BIND_IN_PROGRESS )
+ break;
+
+ ldap_msgfree( result );
+
+ if ( ldap_result( lc->lc_ld, msgid, LDAP_MSG_ALL, NULL, &result ) == -1 || !result ) {
+ ldap_get_option( lc->lc_ld, LDAP_OPT_RESULT_CODE, (void*)&rs->sr_err );
+ ldap_get_option( lc->lc_ld, LDAP_OPT_DIAGNOSTIC_MESSAGE, (void*)&rs->sr_text );
+ break;
+ }
+ } while ( rs->sr_err == LDAP_SASL_BIND_IN_PROGRESS );
+
+ ldap_pvt_thread_mutex_lock( &li->li_counter_mutex );
+ ldap_pvt_mp_add( li->li_ops_completed[ SLAP_OP_BIND ], 1 );
+ ldap_pvt_thread_mutex_unlock( &li->li_counter_mutex );
+
+ switch ( rs->sr_err ) {
+ case LDAP_SUCCESS:
+#ifdef SLAP_AUTH_DN
+ /* FIXME: right now, the only reason to check
+ * response controls is RFC 3829 authzid */
+ if ( li->li_idassert_flags & LDAP_BACK_AUTH_DN_AUTHZID ) {
+ ctrlsp = NULL;
+ rc = ldap_parse_result( lc->lc_ld, result, NULL, NULL, NULL, NULL,
+ &ctrlsp, 0 );
+ if ( rc == LDAP_SUCCESS && ctrlsp ) {
+ LDAPControl *ctrl;
+
+ ctrl = ldap_control_find( LDAP_CONTROL_AUTHZID_RESPONSE,
+ ctrlsp, NULL );
+ if ( ctrl ) {
+ Debug( LDAP_DEBUG_TRACE, "%s: ldap_back_proxy_authz_bind: authzID=\"%s\" (authzid)\n",
+ op->o_log_prefix, ctrl->ldctl_value.bv_val );
+ if ( ctrl->ldctl_value.bv_len > STRLENOF("dn:") &&
+ strncasecmp( ctrl->ldctl_value.bv_val, "dn:", STRLENOF("dn:") ) == 0 )
+ {
+ struct berval bv;
+ bv.bv_val = &ctrl->ldctl_value.bv_val[STRLENOF("dn:")];
+ bv.bv_len = ctrl->ldctl_value.bv_len - STRLENOF("dn:");
+ ber_bvreplace( &lc->lc_bound_ndn, &bv );
+ }
+ }
+ }
+
+ ldap_controls_free( ctrlsp );
+
+ } else if ( li->li_idassert_flags & LDAP_BACK_AUTH_DN_WHOAMI ) {
+ struct berval *val = NULL;
+ rc = ldap_whoami_s( lc->lc_ld, &val, NULL, NULL );
+ if ( rc == LDAP_SUCCESS && val != NULL ) {
+ Debug( LDAP_DEBUG_TRACE, "%s: ldap_back_proxy_authz_bind: authzID=\"%s\" (whoami)\n",
+ op->o_log_prefix, val->bv_val );
+ if ( val->bv_len > STRLENOF("dn:") &&
+ strncasecmp( val->bv_val, "dn:", STRLENOF("dn:") ) == 0 )
+ {
+ struct berval bv;
+ bv.bv_val = &val->bv_val[STRLENOF("dn:")];
+ bv.bv_len = val->bv_len - STRLENOF("dn:");
+ ber_bvreplace( &lc->lc_bound_ndn, &bv );
+ }
+ ber_bvfree( val );
+ }
+ }
+
+ if ( ( li->li_idassert_flags & LDAP_BACK_AUTH_DN_MASK ) &&
+ BER_BVISNULL( &lc->lc_bound_ndn ) )
+ {
+ /* all in all, we only need it to be non-null */
+ /* FIXME: should this be configurable? */
+ static struct berval bv = BER_BVC("cn=authzdn");
+ ber_bvreplace( &lc->lc_bound_ndn, &bv );
+ }
+#endif /* SLAP_AUTH_DN */
+ LDAP_BACK_CONN_ISBOUND_SET( lc );
+ break;
+
+ case LDAP_LOCAL_ERROR:
+ /* list client API error codes that require
+ * to taint the connection */
+ /* FIXME: should actually retry? */
+ LDAP_BACK_CONN_TAINTED_SET( lc );
+
+ /* fallthru */
+
+ default:
+ LDAP_BACK_CONN_ISBOUND_CLEAR( lc );
+ rs->sr_err = slap_map_api2result( rs );
+ if ( sendok & LDAP_BACK_SENDERR ) {
+ send_ldap_result( op, rs );
+ }
+ break;
+ }
+
+ if ( save_text != rs->sr_text ) {
+ ldap_memfree( (char *)rs->sr_text );
+ rs->sr_text = save_text;
+ }
+
+ ldap_msgfree( result );
+
+ lutil_sasl_freedefs( defaults );
+ if ( freeauthz ) {
+ slap_sl_free( authzID.bv_val, op->o_tmpmemctx );
+ }
+
+ goto done;
+#endif /* HAVE_CYRUS_SASL */
+ }
+
+ switch ( li->li_idassert_authmethod ) {
+ case LDAP_AUTH_NONE:
+ /* FIXME: do we really need this? */
+ BER_BVSTR( binddn, "" );
+ BER_BVSTR( bindcred, "" );
+ /* fallthru */
+
+ case LDAP_AUTH_SIMPLE:
+ rs->sr_err = ldap_sasl_bind( lc->lc_ld,
+ binddn->bv_val, LDAP_SASL_SIMPLE,
+ bindcred, NULL, NULL, &msgid );
+ rc = ldap_back_op_result( lc, op, rs, msgid,
+ -1, ( sendok | LDAP_BACK_BINDING ) );
+
+ ldap_pvt_thread_mutex_lock( &li->li_counter_mutex );
+ ldap_pvt_mp_add( li->li_ops_completed[ SLAP_OP_BIND ], 1 );
+ ldap_pvt_thread_mutex_unlock( &li->li_counter_mutex );
+ break;
+
+ default:
+ /* unsupported! */
+ LDAP_BACK_CONN_ISBOUND_CLEAR( lc );
+ rs->sr_err = LDAP_AUTH_METHOD_NOT_SUPPORTED;
+ if ( sendok & LDAP_BACK_SENDERR ) {
+ send_ldap_result( op, rs );
+ }
+ goto done;
+ }
+
+ if ( rc == LDAP_SUCCESS ) {
+ /* set rebind stuff in case of successful proxyAuthz bind,
+ * so that referral chasing is attempted using the right
+ * identity */
+ LDAP_BACK_CONN_ISBOUND_SET( lc );
+ if ( !BER_BVISNULL( binddn ) ) {
+ ber_bvreplace( &lc->lc_bound_ndn, binddn );
+ }
+
+ if ( !BER_BVISNULL( &lc->lc_cred ) ) {
+ memset( lc->lc_cred.bv_val, 0,
+ lc->lc_cred.bv_len );
+ }
+
+ if ( LDAP_BACK_SAVECRED( li ) ) {
+ if ( !BER_BVISNULL( bindcred ) ) {
+ ber_bvreplace( &lc->lc_cred, bindcred );
+ ldap_set_rebind_proc( lc->lc_ld, li->li_rebind_f, lc );
+ }
+
+ } else {
+ lc->lc_cred.bv_len = 0;
+ }
+ }
+
+done:;
+ return LDAP_BACK_CONN_ISBOUND( lc );
+}
+
+/*
+ * ldap_back_proxy_authz_ctrl() prepends a proxyAuthz control
+ * to existing server-side controls if required; if not,
+ * the existing server-side controls are placed in *pctrls.
+ * The caller, after using the controls in client API
+ * operations, if ( *pctrls != op->o_ctrls ), should
+ * free( (*pctrls)[ 0 ] ) and free( *pctrls ).
+ * The function returns success if the control could
+ * be added if required, or if it did nothing; in the future,
+ * it might return some error if it failed.
+ *
+ * if no bind took place yet, but the connection is bound
+ * and the "proxyauthzdn" is set, then bind as "proxyauthzdn"
+ * and explicitly add proxyAuthz the control to every operation
+ * with the dn bound to the connection as control value.
+ *
+ * If no server-side controls are defined for the operation,
+ * simply add the proxyAuthz control; otherwise, if the
+ * proxyAuthz control is not already set, add it as
+ * the first one
+ *
+ * FIXME: is controls order significant for security?
+ * ANSWER: controls ordering and interoperability
+ * must be indicated by the specs of each control; if none
+ * is specified, the order is irrelevant.
+ */
+int
+ldap_back_proxy_authz_ctrl(
+ Operation *op,
+ SlapReply *rs,
+ struct berval *bound_ndn,
+ int version,
+ slap_idassert_t *si,
+ LDAPControl *ctrl )
+{
+ slap_idassert_mode_t mode;
+ struct berval assertedID,
+ ndn;
+ int isroot = 0;
+
+ rs->sr_err = SLAP_CB_CONTINUE;
+
+ /* FIXME: SASL/EXTERNAL over ldapi:// doesn't honor the authcID,
+ * but if it is not set this test fails. We need a different
+ * means to detect if idassert is enabled */
+ if ( ( BER_BVISNULL( &si->si_bc.sb_authcId ) || BER_BVISEMPTY( &si->si_bc.sb_authcId ) )
+ && ( BER_BVISNULL( &si->si_bc.sb_binddn ) || BER_BVISEMPTY( &si->si_bc.sb_binddn ) )
+ && BER_BVISNULL( &si->si_bc.sb_saslmech ) )
+ {
+ goto done;
+ }
+
+ if ( !op->o_conn || op->o_do_not_cache || ( isroot = be_isroot( op ) ) ) {
+ goto done;
+ }
+
+ if ( op->o_tag == LDAP_REQ_BIND ) {
+ ndn = op->o_req_ndn;
+ } else {
+ ndn = op->o_ndn;
+ }
+
+ if ( si->si_mode == LDAP_BACK_IDASSERT_LEGACY ) {
+ if ( op->o_proxy_authz ) {
+ /*
+ * FIXME: we do not want to perform proxyAuthz
+ * on behalf of the client, because this would
+ * be performed with "proxyauthzdn" privileges.
+ *
+ * This might actually be too strict, since
+ * the "proxyauthzdn" authzTo, and each entry's
+ * authzFrom attributes may be crafted
+ * to avoid unwanted proxyAuthz to take place.
+ */
+#if 0
+ rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+ rs->sr_text = "proxyAuthz not allowed within namingContext";
+#endif
+ goto done;
+ }
+
+ if ( !BER_BVISNULL( bound_ndn ) ) {
+ goto done;
+ }
+
+ if ( BER_BVISNULL( &ndn ) ) {
+ goto done;
+ }
+
+ if ( BER_BVISNULL( &si->si_bc.sb_binddn ) ) {
+ goto done;
+ }
+
+ } else if ( si->si_bc.sb_method == LDAP_AUTH_SASL ) {
+ if ( ( si->si_flags & LDAP_BACK_AUTH_NATIVE_AUTHZ ) )
+ {
+ /* already asserted in SASL via native authz */
+ goto done;
+ }
+
+ } else if ( si->si_authz && !isroot ) {
+ int rc;
+ struct berval authcDN;
+
+ if ( BER_BVISNULL( &ndn ) ) {
+ authcDN = slap_empty_bv;
+ } else {
+ authcDN = ndn;
+ }
+ rc = slap_sasl_matches( op, si->si_authz,
+ &authcDN, &authcDN );
+ if ( rc != LDAP_SUCCESS ) {
+ if ( si->si_flags & LDAP_BACK_AUTH_PRESCRIPTIVE ) {
+ /* ndn is not authorized
+ * to use idassert */
+ rs->sr_err = rc;
+ }
+ goto done;
+ }
+ }
+
+ if ( op->o_proxy_authz ) {
+ /*
+ * FIXME: we can:
+ * 1) ignore the already set proxyAuthz control
+ * 2) leave it in place, and don't set ours
+ * 3) add both
+ * 4) reject the operation
+ *
+ * option (4) is very drastic
+ * option (3) will make the remote server reject
+ * the operation, thus being equivalent to (4)
+ * option (2) will likely break the idassert
+ * assumptions, so we cannot accept it;
+ * option (1) means that we are contradicting
+ * the client's request.
+ *
+ * I think (4) is the only correct choice.
+ */
+ rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+ rs->sr_text = "proxyAuthz not allowed within namingContext";
+ }
+
+ if ( op->o_is_auth_check ) {
+ mode = LDAP_BACK_IDASSERT_NOASSERT;
+
+ } else {
+ mode = si->si_mode;
+ }
+
+ switch ( mode ) {
+ case LDAP_BACK_IDASSERT_LEGACY:
+ /* original behavior:
+ * assert the client's identity */
+ case LDAP_BACK_IDASSERT_SELF:
+ assertedID = ndn;
+ break;
+
+ case LDAP_BACK_IDASSERT_ANONYMOUS:
+ /* assert "anonymous" */
+ assertedID = slap_empty_bv;
+ break;
+
+ case LDAP_BACK_IDASSERT_NOASSERT:
+ /* don't assert; bind as proxyauthzdn */
+ goto done;
+
+ case LDAP_BACK_IDASSERT_OTHERID:
+ case LDAP_BACK_IDASSERT_OTHERDN:
+ /* assert idassert DN */
+ assertedID = si->si_bc.sb_authzId;
+ break;
+
+ default:
+ assert( 0 );
+ }
+
+ /* if we got here, "" is allowed to proxyAuthz */
+ if ( BER_BVISNULL( &assertedID ) ) {
+ assertedID = slap_empty_bv;
+ }
+
+ /* don't idassert the bound DN (ITS#4497) */
+ if ( dn_match( &assertedID, bound_ndn ) ) {
+ goto done;
+ }
+
+ ctrl->ldctl_oid = LDAP_CONTROL_PROXY_AUTHZ;
+ ctrl->ldctl_iscritical = ( ( si->si_flags & LDAP_BACK_AUTH_PROXYAUTHZ_CRITICAL ) == LDAP_BACK_AUTH_PROXYAUTHZ_CRITICAL );
+
+ switch ( si->si_mode ) {
+ /* already in u:ID or dn:DN form */
+ case LDAP_BACK_IDASSERT_OTHERID:
+ case LDAP_BACK_IDASSERT_OTHERDN:
+ ber_dupbv_x( &ctrl->ldctl_value, &assertedID, op->o_tmpmemctx );
+ rs->sr_err = LDAP_SUCCESS;
+ break;
+
+ /* needs the dn: prefix */
+ default:
+ ctrl->ldctl_value.bv_len = assertedID.bv_len + STRLENOF( "dn:" );
+ ctrl->ldctl_value.bv_val = op->o_tmpalloc( ctrl->ldctl_value.bv_len + 1,
+ op->o_tmpmemctx );
+ AC_MEMCPY( ctrl->ldctl_value.bv_val, "dn:", STRLENOF( "dn:" ) );
+ AC_MEMCPY( &ctrl->ldctl_value.bv_val[ STRLENOF( "dn:" ) ],
+ assertedID.bv_val, assertedID.bv_len + 1 );
+ rs->sr_err = LDAP_SUCCESS;
+ break;
+ }
+
+ /* Older versions of <draft-weltman-ldapv3-proxy> required
+ * to encode the value of the authzID (and called it proxyDN);
+ * this hack provides compatibility with those DSAs that
+ * implement it this way */
+ if ( si->si_flags & LDAP_BACK_AUTH_OBSOLETE_ENCODING_WORKAROUND ) {
+ struct berval authzID = ctrl->ldctl_value;
+ BerElementBuffer berbuf;
+ BerElement *ber = (BerElement *)&berbuf;
+ ber_tag_t tag;
+
+ ber_init2( ber, 0, LBER_USE_DER );
+ ber_set_option( ber, LBER_OPT_BER_MEMCTX, &op->o_tmpmemctx );
+
+ tag = ber_printf( ber, "O", &authzID );
+ if ( tag == LBER_ERROR ) {
+ rs->sr_err = LDAP_OTHER;
+ goto free_ber;
+ }
+
+ if ( ber_flatten2( ber, &ctrl->ldctl_value, 1 ) == -1 ) {
+ rs->sr_err = LDAP_OTHER;
+ goto free_ber;
+ }
+
+ rs->sr_err = LDAP_SUCCESS;
+
+free_ber:;
+ op->o_tmpfree( authzID.bv_val, op->o_tmpmemctx );
+ ber_free_buf( ber );
+
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ goto done;
+ }
+
+ } else if ( si->si_flags & LDAP_BACK_AUTH_OBSOLETE_PROXY_AUTHZ ) {
+ struct berval authzID = ctrl->ldctl_value,
+ tmp;
+ BerElementBuffer berbuf;
+ BerElement *ber = (BerElement *)&berbuf;
+ ber_tag_t tag;
+
+ if ( strncasecmp( authzID.bv_val, "dn:", STRLENOF( "dn:" ) ) != 0 ) {
+ rs->sr_err = LDAP_PROTOCOL_ERROR;
+ goto done;
+ }
+
+ tmp = authzID;
+ tmp.bv_val += STRLENOF( "dn:" );
+ tmp.bv_len -= STRLENOF( "dn:" );
+
+ ber_init2( ber, 0, LBER_USE_DER );
+ ber_set_option( ber, LBER_OPT_BER_MEMCTX, &op->o_tmpmemctx );
+
+ /* apparently, Mozilla API encodes this
+ * as "SEQUENCE { LDAPDN }" */
+ tag = ber_printf( ber, "{O}", &tmp );
+ if ( tag == LBER_ERROR ) {
+ rs->sr_err = LDAP_OTHER;
+ goto free_ber2;
+ }
+
+ if ( ber_flatten2( ber, &ctrl->ldctl_value, 1 ) == -1 ) {
+ rs->sr_err = LDAP_OTHER;
+ goto free_ber2;
+ }
+
+ ctrl->ldctl_oid = LDAP_CONTROL_OBSOLETE_PROXY_AUTHZ;
+ rs->sr_err = LDAP_SUCCESS;
+
+free_ber2:;
+ op->o_tmpfree( authzID.bv_val, op->o_tmpmemctx );
+ ber_free_buf( ber );
+
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ goto done;
+ }
+ }
+
+done:;
+
+ return rs->sr_err;
+}
+
+/*
+ * Add controls;
+ *
+ * if any needs to be added, it is prepended to existing ones,
+ * in a newly allocated array. The companion function
+ * ldap_back_controls_free() must be used to restore the original
+ * status of op->o_ctrls.
+ */
+int
+ldap_back_controls_add(
+ Operation *op,
+ SlapReply *rs,
+ ldapconn_t *lc,
+ LDAPControl ***pctrls )
+{
+ ldapinfo_t *li = (ldapinfo_t *)op->o_bd->be_private;
+
+ LDAPControl **ctrls = NULL;
+ /* set to the maximum number of controls this backend can add */
+ LDAPControl c[ 2 ] = { { 0 } };
+ int n = 0, i, j1 = 0, j2 = 0, skipped = 0;
+
+ *pctrls = NULL;
+
+ rs->sr_err = LDAP_SUCCESS;
+
+ /* don't add controls if protocol is not LDAPv3 */
+ switch ( li->li_version ) {
+ case LDAP_VERSION3:
+ break;
+
+ case 0:
+ if ( op->o_protocol == 0 || op->o_protocol == LDAP_VERSION3 ) {
+ break;
+ }
+ /* fall thru */
+
+ default:
+ goto done;
+ }
+
+ /* put controls that go __before__ existing ones here */
+
+ /* proxyAuthz for identity assertion */
+ switch ( ldap_back_proxy_authz_ctrl( op, rs, &lc->lc_bound_ndn,
+ li->li_version, &li->li_idassert, &c[ j1 ] ) )
+ {
+ case SLAP_CB_CONTINUE:
+ break;
+
+ case LDAP_SUCCESS:
+ j1++;
+ break;
+
+ default:
+ goto done;
+ }
+
+ /* put controls that go __after__ existing ones here */
+
+#ifdef SLAP_CONTROL_X_SESSION_TRACKING
+ /* FIXME: according to <draft-wahl-ldap-session>,
+ * the server should check if the control can be added
+ * based on the identity of the client and so */
+
+ /* session tracking */
+ if ( LDAP_BACK_ST_REQUEST( li ) ) {
+ switch ( slap_ctrl_session_tracking_request_add( op, rs, &c[ j1 + j2 ] ) ) {
+ case SLAP_CB_CONTINUE:
+ break;
+
+ case LDAP_SUCCESS:
+ j2++;
+ break;
+
+ default:
+ goto done;
+ }
+ }
+#endif /* SLAP_CONTROL_X_SESSION_TRACKING */
+
+ if ( rs->sr_err == SLAP_CB_CONTINUE ) {
+ rs->sr_err = LDAP_SUCCESS;
+ }
+
+ /* if nothing to do, just bail out */
+ if ( j1 == 0 && j2 == 0 ) {
+ goto done;
+ }
+
+ assert( j1 + j2 <= (int) (sizeof( c )/sizeof( c[0] )) );
+
+ if ( op->o_ctrls ) {
+ for ( n = 0; op->o_ctrls[ n ]; n++ )
+ /* just count ctrls */ ;
+ }
+
+ ctrls = op->o_tmpalloc( (n + j1 + j2 + 1) * sizeof( LDAPControl * ) + ( j1 + j2 ) * sizeof( LDAPControl ),
+ op->o_tmpmemctx );
+ if ( j1 ) {
+ ctrls[ 0 ] = (LDAPControl *)&ctrls[ n + j1 + j2 + 1 ];
+ *ctrls[ 0 ] = c[ 0 ];
+ for ( i = 1; i < j1; i++ ) {
+ ctrls[ i ] = &ctrls[ 0 ][ i ];
+ *ctrls[ i ] = c[ i ];
+ }
+ }
+
+ i = 0;
+ if ( op->o_ctrls ) {
+ LDAPControl *proxyauthz = ldap_control_find(
+ LDAP_CONTROL_PROXY_AUTHZ, op->o_ctrls, NULL );
+
+ for ( i = 0; op->o_ctrls[ i ]; i++ ) {
+ /* Only replace it if we generated one */
+ if ( j1 && proxyauthz && proxyauthz == op->o_ctrls[ i ] ) {
+ /* Frontend has already checked only one is present */
+ assert( skipped == 0 );
+ skipped++;
+ continue;
+ }
+ ctrls[ i + j1 - skipped ] = op->o_ctrls[ i ];
+ }
+ }
+
+ n += j1 - skipped;
+ if ( j2 ) {
+ ctrls[ n ] = (LDAPControl *)&ctrls[ n + j2 + 1 ] + j1;
+ *ctrls[ n ] = c[ j1 ];
+ for ( i = 1; i < j2; i++ ) {
+ ctrls[ n + i ] = &ctrls[ n ][ i ];
+ *ctrls[ n + i ] = c[ i ];
+ }
+ }
+
+ ctrls[ n + j2 ] = NULL;
+
+done:;
+ if ( ctrls == NULL ) {
+ ctrls = op->o_ctrls;
+ }
+
+ *pctrls = ctrls;
+
+ return rs->sr_err;
+}
+
+int
+ldap_back_controls_free( Operation *op, SlapReply *rs, LDAPControl ***pctrls )
+{
+ LDAPControl **ctrls = *pctrls;
+
+ /* we assume that the controls added by the proxy come first,
+ * so as soon as we find op->o_ctrls[ 0 ] we can stop */
+ if ( ctrls && ctrls != op->o_ctrls ) {
+ int i = 0, n = 0, n_added;
+ LDAPControl *lower, *upper;
+
+ assert( ctrls[ 0 ] != NULL );
+
+ for ( n = 0; ctrls[ n ] != NULL; n++ )
+ /* count 'em */ ;
+
+ if ( op->o_ctrls ) {
+ for ( i = 0; op->o_ctrls[ i ] != NULL; i++ )
+ /* count 'em */ ;
+ }
+
+ n_added = n - i;
+ lower = (LDAPControl *)&ctrls[ n ];
+ upper = &lower[ n_added ];
+
+ for ( i = 0; ctrls[ i ] != NULL; i++ ) {
+ if ( ctrls[ i ] < lower || ctrls[ i ] >= upper ) {
+ /* original; don't touch */
+ continue;
+ }
+
+ if ( !BER_BVISNULL( &ctrls[ i ]->ldctl_value ) ) {
+ op->o_tmpfree( ctrls[ i ]->ldctl_value.bv_val, op->o_tmpmemctx );
+ }
+ }
+
+ op->o_tmpfree( ctrls, op->o_tmpmemctx );
+ }
+
+ *pctrls = NULL;
+
+ return 0;
+}
+
+int
+ldap_back_conn2str( const ldapconn_base_t *lc, char *buf, ber_len_t buflen )
+{
+ char tbuf[ SLAP_TEXT_BUFLEN ];
+ char *ptr = buf, *end = buf + buflen;
+ int len;
+
+ if ( ptr + sizeof("conn=") > end ) return -1;
+ ptr = lutil_strcopy( ptr, "conn=" );
+
+ len = ldap_back_connid2str( lc, ptr, (ber_len_t)(end - ptr) );
+ ptr += len;
+ if ( ptr >= end ) return -1;
+
+ if ( !BER_BVISNULL( &lc->lcb_local_ndn ) ) {
+ if ( ptr + sizeof(" DN=\"\"") + lc->lcb_local_ndn.bv_len > end ) return -1;
+ ptr = lutil_strcopy( ptr, " DN=\"" );
+ ptr = lutil_strncopy( ptr, lc->lcb_local_ndn.bv_val, lc->lcb_local_ndn.bv_len );
+ *ptr++ = '"';
+ }
+
+ if ( lc->lcb_create_time != 0 ) {
+ len = snprintf( tbuf, sizeof(tbuf), "%ld", lc->lcb_create_time );
+ if ( ptr + sizeof(" created=") + len >= end ) return -1;
+ ptr = lutil_strcopy( ptr, " created=" );
+ ptr = lutil_strcopy( ptr, tbuf );
+ }
+
+ if ( lc->lcb_time != 0 ) {
+ len = snprintf( tbuf, sizeof(tbuf), "%ld", lc->lcb_time );
+ if ( ptr + sizeof(" modified=") + len >= end ) return -1;
+ ptr = lutil_strcopy( ptr, " modified=" );
+ ptr = lutil_strcopy( ptr, tbuf );
+ }
+
+ len = snprintf( tbuf, sizeof(tbuf), "%u", lc->lcb_refcnt );
+ if ( ptr + sizeof(" refcnt=") + len >= end ) return -1;
+ ptr = lutil_strcopy( ptr, " refcnt=" );
+ ptr = lutil_strcopy( ptr, tbuf );
+
+ return ptr - buf;
+}
+
+int
+ldap_back_connid2str( const ldapconn_base_t *lc, char *buf, ber_len_t buflen )
+{
+ static struct berval conns[] = {
+ BER_BVC("ROOTDN"),
+ BER_BVC("ROOTDN-TLS"),
+ BER_BVC("ANON"),
+ BER_BVC("ANON-TLS"),
+ BER_BVC("BIND"),
+ BER_BVC("BIND-TLS"),
+ BER_BVNULL
+ };
+
+ int len = 0;
+
+ if ( LDAP_BACK_PCONN_ISPRIV( (const ldapconn_t *)lc ) ) {
+ long cid;
+ struct berval *bv;
+
+ cid = (long)lc->lcb_conn;
+ assert( cid >= LDAP_BACK_PCONN_FIRST && cid < LDAP_BACK_PCONN_LAST );
+
+ bv = &conns[ cid ];
+
+ if ( bv->bv_len >= buflen ) {
+ return bv->bv_len + 1;
+ }
+
+ len = bv->bv_len;
+ lutil_strncopy( buf, bv->bv_val, bv->bv_len + 1 );
+
+ } else {
+ len = snprintf( buf, buflen, "%lu", lc->lcb_conn->c_connid );
+ }
+
+ return len;
+}
+
+void *
+ldap_back_conn_expire_fn( void *ctx, void *arg )
+{
+ struct re_s *rtask = arg;
+ ldapinfo_t *li = (ldapinfo_t *)rtask->arg;
+ ldap_back_conn_prune( li );
+
+ return NULL;
+}
+
+/* Pick which expires first: connection TTL or idle timeout */
+static time_t
+ldap_back_conn_expire_time( ldapinfo_t *li, ldapconn_t *lc) {
+ if ( li->li_conn_ttl != 0 && li->li_idle_timeout != 0 ) {
+ return ( lc->lc_create_time + li->li_conn_ttl ) < ( lc->lc_time + li->li_idle_timeout ) ?
+ ( lc->lc_create_time + li->li_conn_ttl ) : ( lc->lc_time + li->li_idle_timeout );
+ } else if ( li->li_conn_ttl != 0 ) {
+ return lc->lc_create_time + li->li_conn_ttl;
+ } else if ( li->li_idle_timeout != 0 ) {
+ return lc->lc_time + li->li_idle_timeout;
+ }
+ return -1;
+}
+
+/*
+ * Iterate though connections and close those that are past the expiry time.
+ * Also calculate the time for next connection to expire.
+ *
+ * Note:
+ * When the client sends a request after remote connection is pruned, a new
+ * connection is created but bind cannot be replayed even if "rebind-as-user"
+ * was set to "yes". The client credentials are stored in ldapconn_t and lost
+ * when the connection is freed.
+ *
+ * LDAP_DISCONNECT is sent to signal the client that it needs to reconnect to
+ * the proxy and rebind itself (see "Bind is requested with DN but without
+ * credentials" in ldap_back_dobind_int()). Better implementation would not
+ * free ldapconn_t but instead just close the socket. This is not implemented
+ * currently as it is considerable work for what is assumed to be a corner case.
+ */
+static void
+ldap_back_conn_prune( ldapinfo_t *li )
+{
+ time_t now = slap_get_time();
+ time_t next_timeout = -1; /* -1 means uninitialized */
+ TAvlnode *edge;
+ int c;
+
+ ldap_pvt_thread_mutex_lock( &li->li_conninfo.lai_mutex );
+
+ for ( c = LDAP_BACK_PCONN_FIRST; c < LDAP_BACK_PCONN_LAST; c++ ) {
+ ldapconn_t *lc = LDAP_TAILQ_FIRST( &li->li_conn_priv[ c ].lic_priv );
+
+ while ( lc ) {
+ ldapconn_t *next = LDAP_TAILQ_NEXT( lc, lc_q );
+
+ if ( !LDAP_BACK_CONN_TAINTED( lc ) ) {
+ time_t conn_expires = ldap_back_conn_expire_time( li, lc );
+
+ if ( now >= conn_expires ) {
+ if ( lc->lc_refcnt == 0 ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "ldap_back_conn_prune: closing expired connection lc=%p\n",
+ lc );
+ ldap_back_freeconn( li, lc, 0 );
+ } else {
+ Debug( LDAP_DEBUG_TRACE,
+ "ldap_back_conn_prune: tainting expired connection lc=%p\n",
+ lc );
+ LDAP_BACK_CONN_TAINTED_SET( lc );
+ }
+ } else if ( next_timeout == -1 || conn_expires < next_timeout ) {
+ /* next_timeout was not yet initialized or current connection expires sooner */
+ next_timeout = conn_expires;
+ }
+ }
+
+ lc = next;
+ }
+ }
+
+ edge = ldap_tavl_end( li->li_conninfo.lai_tree, TAVL_DIR_LEFT );
+ while ( edge ) {
+ TAvlnode *next = ldap_tavl_next( edge, TAVL_DIR_RIGHT );
+ ldapconn_t *lc = (ldapconn_t *)edge->avl_data;
+
+ if ( !LDAP_BACK_CONN_TAINTED( lc ) ) {
+ time_t conn_expires = ldap_back_conn_expire_time( li, lc );
+
+ if ( now >= conn_expires ) {
+ if ( lc->lc_refcnt == 0 ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "ldap_back_conn_prune: closing expired connection lc=%p\n",
+ lc );
+ ldap_back_freeconn( li, lc, 0 );
+ } else {
+ Debug( LDAP_DEBUG_TRACE,
+ "ldap_back_conn_prune: tainting expired connection lc=%p\n",
+ lc );
+ LDAP_BACK_CONN_TAINTED_SET( lc );
+ }
+ } else if ( next_timeout == -1 || conn_expires < next_timeout ) {
+ next_timeout = conn_expires;
+ }
+ }
+
+ edge = next;
+ }
+
+ ldap_pvt_thread_mutex_unlock( &li->li_conninfo.lai_mutex );
+
+ /* Reschedule for next timeout or cancel the task */
+ ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
+ if ( next_timeout > 0 ) {
+ if ( ldap_pvt_runqueue_isrunning( &slapd_rq, li->li_conn_expire_task ) ) {
+ ldap_pvt_runqueue_stoptask( &slapd_rq, li->li_conn_expire_task );
+ }
+ li->li_conn_expire_task->interval.tv_sec = next_timeout - now;
+ ldap_pvt_runqueue_resched( &slapd_rq, li->li_conn_expire_task, 0 );
+
+ /*
+ * The thread that handles runqueue might have already processed all tasks
+ * before we insertered new task or rescheduled the existing task with new
+ * timeout period. Wake it up to ensure that the task will be picked up.
+ */
+ slap_wake_listener();
+ Debug( LDAP_DEBUG_TRACE,
+ "ldap_back_conn_prune: scheduled connection expiry timer to %ld sec\n",
+ li->li_conn_expire_task->interval.tv_sec );
+ } else if ( next_timeout == -1 && li->li_conn_expire_task != NULL ) {
+ if ( ldap_pvt_runqueue_isrunning( &slapd_rq, li->li_conn_expire_task ) ) {
+ ldap_pvt_runqueue_stoptask( &slapd_rq, li->li_conn_expire_task );
+ }
+ ldap_pvt_runqueue_remove( &slapd_rq, li->li_conn_expire_task );
+ li->li_conn_expire_task = NULL;
+ }
+ ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
+
+ return;
+}
+
+static void
+ldap_back_schedule_conn_expiry( ldapinfo_t *li, ldapconn_t *lc ) {
+ /* Do nothing if timeouts are not set. */
+ if ( li->li_conn_ttl == 0 && li->li_idle_timeout == 0 ) {
+ return;
+ }
+
+ /*
+ * If connection expire task is not running, create it and schedule for
+ * timeout of this connection.
+ *
+ * If the task is already running, this connection cannot be next one
+ * to expire and therefore timeout does not need to be re-calculated.
+ */
+ ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
+ if ( li->li_conn_expire_task == NULL ) {
+ li->li_conn_expire_task = ldap_pvt_runqueue_insert( &slapd_rq,
+ ldap_back_conn_expire_time( li, lc ) - slap_get_time(),
+ ldap_back_conn_expire_fn, li, "ldap_back_conn_expire_fn",
+ "ldap_back_conn_expire_timer" );
+ slap_wake_listener();
+ Debug( LDAP_DEBUG_TRACE,
+ "ldap_back_conn_prune: scheduled connection expiry timer to %ld sec\n",
+ li->li_conn_expire_task->interval.tv_sec );
+ }
+ ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
+
+ return;
+}
diff --git a/servers/slapd/back-ldap/chain.c b/servers/slapd/back-ldap/chain.c
new file mode 100644
index 0000000..fece74d
--- /dev/null
+++ b/servers/slapd/back-ldap/chain.c
@@ -0,0 +1,2356 @@
+/* chain.c - chain LDAP operations */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2003-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2003 Howard Chu.
+ * 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 the Howard Chu for inclusion
+ * in OpenLDAP Software.
+ * This work was subsequently modified by Pierangelo Masarati.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#include "lutil.h"
+#include "slap.h"
+#include "back-ldap.h"
+#include "slap-config.h"
+
+#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
+#define SLAP_CHAINING_DEFAULT LDAP_CHAINING_PREFERRED
+#define SLAP_CH_RESOLVE_SHIFT SLAP_CONTROL_SHIFT
+#define SLAP_CH_RESOLVE_MASK (0x3 << SLAP_CH_RESOLVE_SHIFT)
+#define SLAP_CH_RESOLVE_CHAINING_PREFERRED (LDAP_CHAINING_PREFERRED << SLAP_CH_RESOLVE_SHIFT)
+#define SLAP_CH_RESOLVE_CHAINING_REQUIRED (LDAP_CHAINING_REQUIRED << SLAP_CH_RESOLVE_SHIFT)
+#define SLAP_CH_RESOLVE_REFERRALS_PREFERRED (LDAP_REFERRALS_PREFERRED << SLAP_CH_RESOLVE_SHIFT)
+#define SLAP_CH_RESOLVE_REFERRALS_REQUIRED (LDAP_REFERRALS_REQUIRED << SLAP_CH_RESOLVE_SHIFT)
+#define SLAP_CH_RESOLVE_DEFAULT (SLAP_CHAINING_DEFAULT << SLAP_CH_RESOLVE_SHIFT)
+#define SLAP_CH_CONTINUATION_SHIFT (SLAP_CH_RESOLVE_SHIFT + 2)
+#define SLAP_CH_CONTINUATION_MASK (0x3 << SLAP_CH_CONTINUATION_SHIFT)
+#define SLAP_CH_CONTINUATION_CHAINING_PREFERRED (LDAP_CHAINING_PREFERRED << SLAP_CH_CONTINUATION_SHIFT)
+#define SLAP_CH_CONTINUATION_CHAINING_REQUIRED (LDAP_CHAINING_REQUIRED << SLAP_CH_CONTINUATION_SHIFT)
+#define SLAP_CH_CONTINUATION_REFERRALS_PREFERRED (LDAP_REFERRALS_PREFERRED << SLAP_CH_CONTINUATION_SHIFT)
+#define SLAP_CH_CONTINUATION_REFERRALS_REQUIRED (LDAP_REFERRALS_REQUIRED << SLAP_CH_CONTINUATION_SHIFT)
+#define SLAP_CH_CONTINUATION_DEFAULT (SLAP_CHAINING_DEFAULT << SLAP_CH_CONTINUATION_SHIFT)
+
+#define o_chaining o_ctrlflag[sc_chainingBehavior]
+#define get_chaining(op) ((op)->o_chaining & SLAP_CONTROL_MASK)
+#define get_chainingBehavior(op) ((op)->o_chaining & (SLAP_CH_RESOLVE_MASK|SLAP_CH_CONTINUATION_MASK))
+#define get_resolveBehavior(op) ((op)->o_chaining & SLAP_CH_RESOLVE_MASK)
+#define get_continuationBehavior(op) ((op)->o_chaining & SLAP_CH_CONTINUATION_MASK)
+
+static int sc_chainingBehavior;
+#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
+
+typedef enum {
+ LDAP_CH_NONE = 0,
+ LDAP_CH_RES,
+ LDAP_CH_ERR
+} ldap_chain_status_t;
+
+static BackendInfo *lback;
+
+typedef struct ldap_chain_t {
+ /*
+ * A "template" ldapinfo_t gets all common configuration items;
+ * then, for each configured URI, an entry is created in the tree;
+ * all the specific configuration items get in the current URI
+ * structure.
+ *
+ * Then, for each referral, extract the URI and lookup the
+ * related structure. If configured to do so, allow URIs
+ * not found in the structure to create a temporary one
+ * that chains anonymously; maybe it can also be added to
+ * the tree? Should be all configurable.
+ */
+
+ /* "common" configuration info (anything occurring before an "uri") */
+ ldapinfo_t *lc_common_li;
+
+ /* current configuration info */
+ ldapinfo_t *lc_cfg_li;
+
+ /* tree of configured[/generated?] "uri" info */
+ ldap_avl_info_t lc_lai;
+
+ /* max depth in nested referrals chaining */
+ int lc_max_depth;
+
+ unsigned lc_flags;
+#define LDAP_CHAIN_F_NONE (0x00U)
+#define LDAP_CHAIN_F_CHAINING (0x01U)
+#define LDAP_CHAIN_F_CACHE_URI (0x02U)
+#define LDAP_CHAIN_F_RETURN_ERR (0x04U)
+
+#define LDAP_CHAIN_ISSET(lc, f) ( ( (lc)->lc_flags & (f) ) == (f) )
+#define LDAP_CHAIN_CHAINING( lc ) LDAP_CHAIN_ISSET( (lc), LDAP_CHAIN_F_CHAINING )
+#define LDAP_CHAIN_CACHE_URI( lc ) LDAP_CHAIN_ISSET( (lc), LDAP_CHAIN_F_CACHE_URI )
+#define LDAP_CHAIN_RETURN_ERR( lc ) LDAP_CHAIN_ISSET( (lc), LDAP_CHAIN_F_RETURN_ERR )
+
+#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
+ LDAPControl lc_chaining_ctrl;
+ char lc_chaining_ctrlflag;
+#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
+} ldap_chain_t;
+
+static int ldap_chain_db_init_common( BackendDB *be );
+static int ldap_chain_db_init_one( BackendDB *be );
+static int ldap_chain_db_open_one( BackendDB *be );
+#define ldap_chain_db_close_one(be) (0)
+#define ldap_chain_db_destroy_one(be, rs) (lback)->bi_db_destroy( (be), (rs) )
+
+typedef struct ldap_chain_cb_t {
+ ldap_chain_status_t lb_status;
+ ldap_chain_t *lb_lc;
+ slap_operation_t lb_op_type;
+ char *lb_text;
+ int lb_depth;
+} ldap_chain_cb_t;
+
+static int
+ldap_chain_op(
+ Operation *op,
+ SlapReply *rs,
+ slap_operation_t op_type,
+ BerVarray ref,
+ int depth );
+
+static int
+ldap_chain_search(
+ Operation *op,
+ SlapReply *rs,
+ BerVarray ref,
+ int depth );
+
+static slap_overinst ldapchain;
+
+#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
+static int
+chaining_control_add(
+ ldap_chain_t *lc,
+ Operation *op,
+ LDAPControl ***oldctrlsp )
+{
+ LDAPControl **ctrls = NULL;
+ int c = 0;
+
+ *oldctrlsp = op->o_ctrls;
+
+ /* default chaining control not defined */
+ if ( !LDAP_CHAIN_CHAINING( lc ) ) {
+ return 0;
+ }
+
+ /* already present */
+ if ( get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
+ return 0;
+ }
+
+ /* FIXME: check other incompatibilities */
+
+ /* add to other controls */
+ if ( op->o_ctrls ) {
+ for ( c = 0; op->o_ctrls[ c ]; c++ )
+ /* count them */ ;
+ }
+
+ ctrls = ch_calloc( sizeof( LDAPControl *), c + 2 );
+ ctrls[ 0 ] = &lc->lc_chaining_ctrl;
+ if ( op->o_ctrls ) {
+ for ( c = 0; op->o_ctrls[ c ]; c++ ) {
+ ctrls[ c + 1 ] = op->o_ctrls[ c ];
+ }
+ }
+ ctrls[ c + 1 ] = NULL;
+
+ op->o_ctrls = ctrls;
+
+ op->o_chaining = lc->lc_chaining_ctrlflag;
+
+ return 0;
+}
+
+static int
+chaining_control_remove(
+ Operation *op,
+ LDAPControl ***oldctrlsp )
+{
+ LDAPControl **oldctrls = *oldctrlsp;
+
+ /* we assume that the first control is the chaining control
+ * added by the chain overlay, so it's the only one we explicitly
+ * free */
+ if ( op->o_ctrls != oldctrls ) {
+ if ( op->o_ctrls != NULL ) {
+ assert( op->o_ctrls[ 0 ] != NULL );
+
+ free( op->o_ctrls );
+
+ op->o_chaining = 0;
+ }
+ op->o_ctrls = oldctrls;
+ }
+
+ *oldctrlsp = NULL;
+
+ return 0;
+}
+#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
+
+static int
+ldap_chain_uri_cmp( const void *c1, const void *c2 )
+{
+ const ldapinfo_t *li1 = (const ldapinfo_t *)c1;
+ const ldapinfo_t *li2 = (const ldapinfo_t *)c2;
+
+ assert( li1->li_bvuri != NULL );
+ assert( !BER_BVISNULL( &li1->li_bvuri[ 0 ] ) );
+ assert( BER_BVISNULL( &li1->li_bvuri[ 1 ] ) );
+
+ assert( li2->li_bvuri != NULL );
+ assert( !BER_BVISNULL( &li2->li_bvuri[ 0 ] ) );
+ assert( BER_BVISNULL( &li2->li_bvuri[ 1 ] ) );
+
+ return ber_bvcmp( &li1->li_bvuri[ 0 ], &li2->li_bvuri[ 0 ] );
+}
+
+static int
+ldap_chain_uri_dup( void *c1, void *c2 )
+{
+ ldapinfo_t *li1 = (ldapinfo_t *)c1;
+ ldapinfo_t *li2 = (ldapinfo_t *)c2;
+
+ assert( li1->li_bvuri != NULL );
+ assert( !BER_BVISNULL( &li1->li_bvuri[ 0 ] ) );
+ assert( BER_BVISNULL( &li1->li_bvuri[ 1 ] ) );
+
+ assert( li2->li_bvuri != NULL );
+ assert( !BER_BVISNULL( &li2->li_bvuri[ 0 ] ) );
+ assert( BER_BVISNULL( &li2->li_bvuri[ 1 ] ) );
+
+ if ( ber_bvcmp( &li1->li_bvuri[ 0 ], &li2->li_bvuri[ 0 ] ) == 0 ) {
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * Search specific response that strips entryDN from entries
+ */
+static int
+ldap_chain_cb_search_response( Operation *op, SlapReply *rs )
+{
+ ldap_chain_cb_t *lb = (ldap_chain_cb_t *)op->o_callback->sc_private;
+
+ assert( op->o_tag == LDAP_REQ_SEARCH );
+
+ /* if in error, don't proceed any further */
+ if ( lb->lb_status == LDAP_CH_ERR ) {
+ return 0;
+ }
+
+ if ( rs->sr_type == REP_SEARCH ) {
+ Attribute **ap = &rs->sr_entry->e_attrs;
+
+ for ( ; *ap != NULL; ap = &(*ap)->a_next ) {
+ /* will be generated later by frontend
+ * (a cleaner solution would be that
+ * the frontend checks if it already exists */
+ if ( ad_cmp( (*ap)->a_desc, slap_schema.si_ad_entryDN ) == 0 )
+ {
+ Attribute *a = *ap;
+
+ *ap = (*ap)->a_next;
+ attr_free( a );
+
+ /* there SHOULD be one only! */
+ break;
+ }
+ }
+
+ /* tell the frontend not to add generated
+ * operational attributes */
+ rs->sr_flags |= REP_NO_OPERATIONALS;
+
+ return SLAP_CB_CONTINUE;
+
+ } else if ( rs->sr_type == REP_SEARCHREF ) {
+ /* if we get it here, it means the library was unable
+ * to chase the referral... */
+ if ( lb->lb_depth < lb->lb_lc->lc_max_depth && rs->sr_ref != NULL ) {
+ rs->sr_err = ldap_chain_search( op, rs, rs->sr_ref, lb->lb_depth );
+ }
+
+#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
+ if ( rs->sr_err == LDAP_REFERRAL && get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
+ switch ( get_continuationBehavior( op ) ) {
+ case SLAP_CH_RESOLVE_CHAINING_REQUIRED:
+ lb->lb_status = LDAP_CH_ERR;
+ return rs->sr_err = LDAP_X_CANNOT_CHAIN;
+
+ default:
+ break;
+ }
+ }
+#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
+ return SLAP_CB_CONTINUE;
+
+ } else if ( rs->sr_type == REP_RESULT ) {
+ if ( rs->sr_err == LDAP_REFERRAL
+ && lb->lb_depth < lb->lb_lc->lc_max_depth
+ && rs->sr_ref != NULL )
+ {
+ rs->sr_err = ldap_chain_op( op, rs, lb->lb_op_type,
+ rs->sr_ref, lb->lb_depth );
+ }
+
+ /* back-ldap tried to send result */
+ lb->lb_status = LDAP_CH_RES;
+ /* don't let other callbacks run, this isn't
+ * the real result for this op.
+ */
+ op->o_callback->sc_next = NULL;
+ }
+
+ return 0;
+}
+
+/*
+ * Dummy response that simply traces if back-ldap tried to send
+ * anything to the client
+ */
+static int
+ldap_chain_cb_response( Operation *op, SlapReply *rs )
+{
+ ldap_chain_cb_t *lb = (ldap_chain_cb_t *)op->o_callback->sc_private;
+
+ /* if in error, don't proceed any further */
+ if ( lb->lb_status == LDAP_CH_ERR ) {
+ return 0;
+ }
+
+ if ( rs->sr_type == REP_RESULT ) {
+retry:;
+ switch ( rs->sr_err ) {
+ case LDAP_COMPARE_TRUE:
+ case LDAP_COMPARE_FALSE:
+ if ( op->o_tag != LDAP_REQ_COMPARE ) {
+ return rs->sr_err;
+ }
+ /* fallthru */
+
+ case LDAP_SUCCESS:
+ lb->lb_status = LDAP_CH_RES;
+ break;
+
+ case LDAP_REFERRAL:
+ if ( lb->lb_depth < lb->lb_lc->lc_max_depth && rs->sr_ref != NULL ) {
+ rs->sr_err = ldap_chain_op( op, rs, lb->lb_op_type,
+ rs->sr_ref, lb->lb_depth );
+ goto retry;
+ }
+
+#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
+ if ( get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
+ switch ( get_continuationBehavior( op ) ) {
+ case SLAP_CH_RESOLVE_CHAINING_REQUIRED:
+ lb->lb_status = LDAP_CH_ERR;
+ return rs->sr_err = LDAP_X_CANNOT_CHAIN;
+
+ default:
+ break;
+ }
+ }
+#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
+ break;
+
+ default:
+ /* remember the text before it's freed in ldap_back_op_result */
+ if ( lb->lb_text ) {
+ ber_memfree_x( lb->lb_text, op->o_tmpmemctx );
+ }
+ lb->lb_text = ber_strdup_x( rs->sr_text, op->o_tmpmemctx );
+ return rs->sr_err;
+ }
+
+ } else if ( op->o_tag == LDAP_REQ_SEARCH && rs->sr_type == REP_SEARCH )
+ {
+ /* strip the entryDN attribute, but keep returning results */
+ (void)ldap_chain_cb_search_response( op, rs );
+ }
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+ldap_chain_op(
+ Operation *op,
+ SlapReply *rs,
+ slap_operation_t op_type,
+ BerVarray ref,
+ int depth )
+{
+ slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
+ ldap_chain_cb_t *lb = (ldap_chain_cb_t *)op->o_callback->sc_private;
+ ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
+ struct berval odn = op->o_req_dn,
+ ondn = op->o_req_ndn;
+ ldapinfo_t li = { 0 }, *lip = NULL;
+ struct berval bvuri[ 2 ] = { { 0 } };
+
+ /* NOTE: returned if ref is empty... */
+ int rc = LDAP_OTHER,
+ first_rc;
+
+#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
+ LDAPControl **ctrls = NULL;
+
+ (void)chaining_control_add( lc, op, &ctrls );
+#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
+
+ li.li_bvuri = bvuri;
+ first_rc = -1;
+ for ( ; !BER_BVISNULL( ref ); ref++ ) {
+ SlapReply rs2 = { 0 };
+ LDAPURLDesc *srv = NULL;
+ req_search_s save_oq_search = op->oq_search,
+ tmp_oq_search = { 0 };
+ struct berval dn = BER_BVNULL,
+ pdn = odn,
+ ndn = ondn;
+ char *filter = NULL;
+ int temporary = 0;
+ int free_dn = 0;
+
+ /* We're setting the URI of the first referral;
+ * what if there are more?
+
+Document: RFC 4511
+
+4.1.10. Referral
+ ...
+ If the client wishes to progress the operation, it MUST follow the
+ referral by contacting one of the supported services. If multiple
+ URIs are present, the client assumes that any supported URI may be
+ used to progress the operation.
+
+ * so we actually need to follow exactly one,
+ * and we can assume any is fine.
+ */
+
+ /* parse reference and use
+ * proto://[host][:port]/ only */
+ rc = ldap_url_parse_ext( ref->bv_val, &srv, LDAP_PVT_URL_PARSE_NONE );
+ if ( rc != LDAP_URL_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: unable to parse ref=\"%s\"\n",
+ op->o_log_prefix, ref->bv_val );
+
+ /* try next */
+ rc = LDAP_OTHER;
+ continue;
+ }
+
+ if ( op->o_tag == LDAP_REQ_SEARCH ) {
+ if ( srv->lud_scope != LDAP_SCOPE_DEFAULT ) {
+ /* RFC 4511: if scope is present, use it */
+ tmp_oq_search.rs_scope = srv->lud_scope;
+
+ } else {
+ /* RFC 4511: if scope is absent, use original */
+ tmp_oq_search.rs_scope = op->ors_scope;
+ }
+ }
+
+ rc = LDAP_SUCCESS;
+ srv->lud_scope = LDAP_SCOPE_DEFAULT;
+ dn.bv_val = srv->lud_dn;
+ filter = srv->lud_filter;
+
+ /* normalize DN */
+ if ( srv->lud_dn == NULL || srv->lud_dn[0] == '\0' ) {
+ if ( srv->lud_dn == NULL ) {
+ srv->lud_dn = "";
+ }
+
+ } else {
+ ber_str2bv( srv->lud_dn, 0, 0, &dn );
+ rc = dnPrettyNormal( NULL, &dn, &pdn, &ndn, op->o_tmpmemctx );
+ if ( rc == LDAP_SUCCESS ) {
+ /* remove DN essentially because later on
+ * ldap_initialize() will parse the URL
+ * as a comma-separated URL list */
+ srv->lud_dn = "";
+ free_dn = 1;
+ }
+ }
+
+ /* prepare filter */
+ if ( rc == LDAP_SUCCESS && op->o_tag == LDAP_REQ_SEARCH ) {
+ /* filter */
+ if ( srv->lud_filter != NULL
+ && srv->lud_filter[0] != '\0'
+ && strcasecmp( srv->lud_filter, "(objectClass=*)" ) != 0 )
+ {
+ /* RFC 4511: if filter is present, use it;
+ * otherwise, use original */
+ tmp_oq_search.rs_filter = str2filter_x( op, srv->lud_filter );
+ if ( tmp_oq_search.rs_filter != NULL ) {
+ filter2bv_x( op, tmp_oq_search.rs_filter, &tmp_oq_search.rs_filterstr );
+
+ } else {
+ Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: ref=\"%s\": unable to parse filter=\"%s\"\n",
+ op->o_log_prefix, ref->bv_val, srv->lud_filter );
+ rc = LDAP_OTHER;
+ }
+ }
+ }
+ srv->lud_filter = NULL;
+
+ if ( rc == LDAP_SUCCESS ) {
+ li.li_uri = ldap_url_desc2str( srv );
+ }
+
+ srv->lud_dn = dn.bv_val;
+ srv->lud_filter = filter;
+ ldap_free_urldesc( srv );
+
+ if ( rc != LDAP_SUCCESS ) {
+ /* try next */
+ rc = LDAP_OTHER;
+ continue;
+ }
+
+ if ( li.li_uri == NULL ) {
+ Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: ref=\"%s\" unable to reconstruct URI\n",
+ op->o_log_prefix, ref->bv_val );
+
+ /* try next */
+ rc = LDAP_OTHER;
+ goto further_cleanup;
+ }
+
+ Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: ref=\"%s\" -> \"%s\"\n",
+ op->o_log_prefix, ref->bv_val, li.li_uri );
+
+ op->o_req_dn = pdn;
+ op->o_req_ndn = ndn;
+
+ if ( op->o_tag == LDAP_REQ_SEARCH ) {
+ op->ors_scope = tmp_oq_search.rs_scope;
+ if ( tmp_oq_search.rs_filter != NULL ) {
+ op->ors_filter = tmp_oq_search.rs_filter;
+ op->ors_filterstr = tmp_oq_search.rs_filterstr;
+ }
+ }
+
+ ber_str2bv( li.li_uri, 0, 0, &li.li_bvuri[ 0 ] );
+
+ /* Searches for a ldapinfo in the avl tree */
+ ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
+ lip = (ldapinfo_t *)ldap_tavl_find( lc->lc_lai.lai_tree,
+ (caddr_t)&li, ldap_chain_uri_cmp );
+ ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );
+
+ if ( lip != NULL ) {
+ op->o_bd->be_private = (void *)lip;
+
+ Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: ref=\"%s\": URI=\"%s\" found in cache\n",
+ op->o_log_prefix, ref->bv_val, li.li_uri );
+
+ } else {
+ rc = ldap_chain_db_init_one( op->o_bd );
+ if ( rc != 0 ) {
+ Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: ref=\"%s\" unable to init back-ldap for URI=\"%s\"\n",
+ op->o_log_prefix, ref->bv_val, li.li_uri );
+ goto cleanup;
+ }
+ lip = (ldapinfo_t *)op->o_bd->be_private;
+ lip->li_uri = li.li_uri;
+ lip->li_bvuri = bvuri;
+ rc = ldap_chain_db_open_one( op->o_bd );
+ if ( rc != 0 ) {
+ Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: ref=\"%s\" unable to open back-ldap for URI=\"%s\"\n",
+ op->o_log_prefix, ref->bv_val, li.li_uri );
+ lip->li_uri = NULL;
+ lip->li_bvuri = NULL;
+ (void)ldap_chain_db_destroy_one( op->o_bd, NULL);
+ goto cleanup;
+ }
+
+ if ( LDAP_CHAIN_CACHE_URI( lc ) ) {
+ ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
+ if ( ldap_tavl_insert( &lc->lc_lai.lai_tree,
+ (caddr_t)lip, ldap_chain_uri_cmp, ldap_chain_uri_dup ) )
+ {
+ /* someone just inserted another;
+ * don't bother, use this and then
+ * just free it */
+ temporary = 1;
+ }
+ ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );
+
+ } else {
+ temporary = 1;
+ }
+
+ Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: ref=\"%s\" %s\n",
+ op->o_log_prefix, ref->bv_val, temporary ? "temporary" : "caching" );
+ }
+
+ lb->lb_op_type = op_type;
+ lb->lb_depth = depth + 1;
+
+ rc = (&lback->bi_op_bind)[ op_type ]( op, &rs2 );
+
+ /* note the first error */
+ if ( first_rc == -1 ) {
+ first_rc = rc;
+ }
+
+cleanup:;
+ ldap_memfree( li.li_uri );
+ li.li_uri = NULL;
+
+ if ( temporary ) {
+ lip->li_uri = NULL;
+ lip->li_bvuri = NULL;
+ (void)ldap_chain_db_close_one( op->o_bd );
+ (void)ldap_chain_db_destroy_one( op->o_bd, NULL );
+ }
+
+further_cleanup:;
+ if ( op->o_req_dn.bv_val == pdn.bv_val ) {
+ op->o_req_dn = odn;
+ op->o_req_ndn = ondn;
+ }
+
+ if ( free_dn ) {
+ op->o_tmpfree( pdn.bv_val, op->o_tmpmemctx );
+ op->o_tmpfree( ndn.bv_val, op->o_tmpmemctx );
+ }
+
+ if ( op->o_tag == LDAP_REQ_SEARCH ) {
+ if ( tmp_oq_search.rs_filter != NULL ) {
+ filter_free_x( op, tmp_oq_search.rs_filter, 1 );
+ }
+
+ if ( !BER_BVISNULL( &tmp_oq_search.rs_filterstr ) ) {
+ slap_sl_free( tmp_oq_search.rs_filterstr.bv_val, op->o_tmpmemctx );
+ }
+
+ op->oq_search = save_oq_search;
+ }
+
+ if ( rc == LDAP_SUCCESS && rs2.sr_err == LDAP_SUCCESS ) {
+ *rs = rs2;
+ break;
+ }
+
+ rc = rs2.sr_err;
+ }
+
+#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
+ (void)chaining_control_remove( op, &ctrls );
+#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
+
+ if ( rc != LDAP_SUCCESS && first_rc > 0 ) {
+ rc = first_rc;
+ }
+
+ return rc;
+}
+
+static int
+ldap_chain_search(
+ Operation *op,
+ SlapReply *rs,
+ BerVarray ref,
+ int depth )
+
+{
+ slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
+ ldap_chain_cb_t *lb = (ldap_chain_cb_t *)op->o_callback->sc_private;
+ ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
+ ldapinfo_t li = { 0 }, *lip = NULL;
+ struct berval bvuri[ 2 ] = { { 0 } };
+
+ struct berval odn = op->o_req_dn,
+ ondn = op->o_req_ndn;
+ Entry *save_entry = rs->sr_entry;
+ slap_mask_t save_flags = rs->sr_flags;
+
+ int rc = LDAP_OTHER,
+ first_rc = -1;
+
+#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
+ LDAPControl **ctrls = NULL;
+
+ (void)chaining_control_add( lc, op, &ctrls );
+#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
+
+ assert( rs->sr_type == REP_SEARCHREF );
+
+ rs->sr_type = REP_SEARCH;
+
+ /* if we parse the URI then by no means
+ * we can cache stuff or reuse connections,
+ * because in back-ldap there's no caching
+ * based on the URI value, which is supposed
+ * to be set once for all (correct?) */
+ li.li_bvuri = bvuri;
+ for ( ; !BER_BVISNULL( &ref[0] ); ref++ ) {
+ SlapReply rs2 = { REP_RESULT };
+ LDAPURLDesc *srv;
+ req_search_s save_oq_search = op->oq_search,
+ tmp_oq_search = { 0 };
+ struct berval dn,
+ pdn = op->o_req_dn,
+ ndn = op->o_req_ndn;
+ char *filter = NULL;
+ int temporary = 0;
+ int free_dn = 0;
+
+ /* parse reference and use
+ * proto://[host][:port]/ only */
+ rc = ldap_url_parse_ext( ref[0].bv_val, &srv, LDAP_PVT_URL_PARSE_NONE );
+ if ( rc != LDAP_URL_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: unable to parse ref=\"%s\"\n",
+ op->o_log_prefix, ref->bv_val );
+
+ /* try next */
+ rs->sr_err = LDAP_OTHER;
+ continue;
+ }
+
+ if ( srv->lud_scope != LDAP_SCOPE_DEFAULT ) {
+ /* RFC 4511: if scope is present, use it */
+ tmp_oq_search.rs_scope = srv->lud_scope;
+
+ } else {
+ /* RFC 4511: if scope is absent, use original */
+ /* Section 4.5.3: if scope is onelevel, use base */
+ if ( op->ors_scope == LDAP_SCOPE_ONELEVEL )
+ tmp_oq_search.rs_scope = LDAP_SCOPE_BASE;
+ else
+ tmp_oq_search.rs_scope = op->ors_scope;
+ }
+
+ rc = LDAP_SUCCESS;
+ srv->lud_scope = LDAP_SCOPE_DEFAULT;
+ dn.bv_val = srv->lud_dn;
+ filter = srv->lud_filter;
+
+ /* normalize DN */
+ if ( srv->lud_dn == NULL || srv->lud_dn[0] == '\0' ) {
+ if ( srv->lud_dn == NULL ) {
+ srv->lud_dn = "";
+ }
+
+ if ( save_entry != NULL ) {
+ /* use the "right" DN, if available */
+ pdn = save_entry->e_name;
+ ndn = save_entry->e_nname;
+ } /* else leave the original req DN in place, if any RFC 4511 */
+
+ } else {
+ /* RFC 4511: if DN is present, use it */
+ ber_str2bv( srv->lud_dn, 0, 0, &dn );
+ rc = dnPrettyNormal( NULL, &dn, &pdn, &ndn, op->o_tmpmemctx );
+ if ( rc == LDAP_SUCCESS ) {
+ /* remove DN essentially because later on
+ * ldap_initialize() will parse the URL
+ * as a comma-separated URL list */
+ srv->lud_dn = "";
+ free_dn = 1;
+ }
+ }
+
+ /* prepare filter */
+ if ( rc == LDAP_SUCCESS ) {
+ /* filter */
+ if ( srv->lud_filter != NULL
+ && srv->lud_filter[0] != '\0'
+ && strcasecmp( srv->lud_filter, "(objectClass=*)" ) != 0 )
+ {
+ /* RFC 4511: if filter is present, use it;
+ * otherwise, use original */
+ tmp_oq_search.rs_filter = str2filter_x( op, srv->lud_filter );
+ if ( tmp_oq_search.rs_filter != NULL ) {
+ filter2bv_x( op, tmp_oq_search.rs_filter, &tmp_oq_search.rs_filterstr );
+
+ } else {
+ Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: ref=\"%s\": unable to parse filter=\"%s\"\n",
+ op->o_log_prefix, ref->bv_val, srv->lud_filter );
+ rc = LDAP_OTHER;
+ }
+ }
+ }
+ srv->lud_filter = NULL;
+
+ if ( rc == LDAP_SUCCESS ) {
+ li.li_uri = ldap_url_desc2str( srv );
+ }
+
+ srv->lud_dn = dn.bv_val;
+ srv->lud_filter = filter;
+ ldap_free_urldesc( srv );
+
+ if ( rc != LDAP_SUCCESS || li.li_uri == NULL ) {
+ Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: ref=\"%s\" unable to reconstruct URI\n",
+ op->o_log_prefix, ref->bv_val );
+
+ /* try next */
+ rc = LDAP_OTHER;
+ goto further_cleanup;
+ }
+
+ Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: ref=\"%s\" -> \"%s\"\n",
+ op->o_log_prefix, ref->bv_val, li.li_uri );
+
+ op->o_req_dn = pdn;
+ op->o_req_ndn = ndn;
+ op->ors_scope = tmp_oq_search.rs_scope;
+ if ( tmp_oq_search.rs_filter != NULL ) {
+ op->ors_filter = tmp_oq_search.rs_filter;
+ op->ors_filterstr = tmp_oq_search.rs_filterstr;
+ }
+
+ ber_str2bv( li.li_uri, 0, 0, &li.li_bvuri[ 0 ] );
+
+ /* Searches for a ldapinfo in the avl tree */
+ ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
+ lip = (ldapinfo_t *)ldap_tavl_find( lc->lc_lai.lai_tree,
+ (caddr_t)&li, ldap_chain_uri_cmp );
+ ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );
+
+ if ( lip != NULL ) {
+ op->o_bd->be_private = (void *)lip;
+
+ Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: ref=\"%s\": URI=\"%s\" found in cache\n",
+ op->o_log_prefix, ref->bv_val, li.li_uri );
+
+ } else {
+ /* if none is found, create a temporary... */
+ rc = ldap_chain_db_init_one( op->o_bd );
+ if ( rc != 0 ) {
+ Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: ref=\"%s\" unable to init back-ldap for URI=\"%s\"\n",
+ op->o_log_prefix, ref->bv_val, li.li_uri );
+ goto cleanup;
+ }
+ lip = (ldapinfo_t *)op->o_bd->be_private;
+ lip->li_uri = li.li_uri;
+ lip->li_bvuri = bvuri;
+ rc = ldap_chain_db_open_one( op->o_bd );
+ if ( rc != 0 ) {
+ Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: ref=\"%s\" unable to open back-ldap for URI=\"%s\"\n",
+ op->o_log_prefix, ref->bv_val, li.li_uri );
+ lip->li_uri = NULL;
+ lip->li_bvuri = NULL;
+ (void)ldap_chain_db_destroy_one( op->o_bd, NULL );
+ goto cleanup;
+ }
+
+ if ( LDAP_CHAIN_CACHE_URI( lc ) ) {
+ ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
+ if ( ldap_tavl_insert( &lc->lc_lai.lai_tree,
+ (caddr_t)lip, ldap_chain_uri_cmp, ldap_chain_uri_dup ) )
+ {
+ /* someone just inserted another;
+ * don't bother, use this and then
+ * just free it */
+ temporary = 1;
+ }
+ ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );
+
+ } else {
+ temporary = 1;
+ }
+
+ Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: ref=\"%s\" %s\n",
+ op->o_log_prefix, ref->bv_val, temporary ? "temporary" : "caching" );
+ }
+
+ lb->lb_op_type = op_search;
+ lb->lb_depth = depth + 1;
+
+ /* FIXME: should we also copy filter and scope?
+ * according to RFC3296, no */
+ rc = lback->bi_op_search( op, &rs2 );
+ if ( first_rc == -1 ) {
+ first_rc = rc;
+ }
+
+cleanup:;
+ ldap_memfree( li.li_uri );
+ li.li_uri = NULL;
+
+ if ( temporary ) {
+ lip->li_uri = NULL;
+ lip->li_bvuri = NULL;
+ (void)ldap_chain_db_close_one( op->o_bd );
+ (void)ldap_chain_db_destroy_one( op->o_bd, NULL );
+ }
+
+further_cleanup:;
+ if ( op->o_req_dn.bv_val == pdn.bv_val ) {
+ op->o_req_dn = odn;
+ op->o_req_ndn = ondn;
+ }
+
+ if ( free_dn ) {
+ op->o_tmpfree( pdn.bv_val, op->o_tmpmemctx );
+ op->o_tmpfree( ndn.bv_val, op->o_tmpmemctx );
+ }
+
+ if ( tmp_oq_search.rs_filter != NULL ) {
+ filter_free_x( op, tmp_oq_search.rs_filter, 1 );
+ }
+
+ if ( !BER_BVISNULL( &tmp_oq_search.rs_filterstr ) ) {
+ slap_sl_free( tmp_oq_search.rs_filterstr.bv_val, op->o_tmpmemctx );
+ }
+
+ op->oq_search = save_oq_search;
+
+ if ( rc == LDAP_SUCCESS && rs2.sr_err == LDAP_SUCCESS ) {
+ *rs = rs2;
+ break;
+ }
+
+ rc = rs2.sr_err;
+ }
+
+#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
+ (void)chaining_control_remove( op, &ctrls );
+#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
+
+ rs->sr_type = REP_SEARCHREF;
+ rs->sr_entry = save_entry;
+ rs->sr_flags = save_flags;
+
+ if ( rc != LDAP_SUCCESS ) {
+ /* couldn't chase any of the referrals */
+ if ( first_rc != -1 ) {
+ rc = first_rc;
+
+ } else {
+ rc = SLAP_CB_CONTINUE;
+ }
+ }
+
+ return rc;
+}
+
+static int
+ldap_chain_response( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
+ BackendDB db, *bd = op->o_bd;
+ ldap_chain_cb_t lb = { 0 };
+ slap_callback *sc = op->o_callback,
+ sc2 = { 0 };
+ int rc = 0;
+ const char *text = NULL;
+ const char *matched;
+ BerVarray ref;
+ slap_mask_t flags = 0;
+ struct berval ndn = op->o_ndn;
+
+ int sr_err = rs->sr_err;
+ slap_reply_t sr_type = rs->sr_type;
+#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
+ slap_mask_t chain_mask = 0;
+ ber_len_t chain_shift = 0;
+#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
+
+ if ( rs->sr_err != LDAP_REFERRAL && rs->sr_type != REP_SEARCHREF ) {
+ return SLAP_CB_CONTINUE;
+ }
+ if ( !rs->sr_ref ) {
+ return SLAP_CB_CONTINUE;
+ }
+
+#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
+ if ( rs->sr_err == LDAP_REFERRAL && get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
+ switch ( get_resolveBehavior( op ) ) {
+ case SLAP_CH_RESOLVE_REFERRALS_PREFERRED:
+ case SLAP_CH_RESOLVE_REFERRALS_REQUIRED:
+ return SLAP_CB_CONTINUE;
+
+ default:
+ chain_mask = SLAP_CH_RESOLVE_MASK;
+ chain_shift = SLAP_CH_RESOLVE_SHIFT;
+ break;
+ }
+
+ } else if ( rs->sr_type == REP_SEARCHREF && get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
+ switch ( get_continuationBehavior( op ) ) {
+ case SLAP_CH_CONTINUATION_REFERRALS_PREFERRED:
+ case SLAP_CH_CONTINUATION_REFERRALS_REQUIRED:
+ return SLAP_CB_CONTINUE;
+
+ default:
+ chain_mask = SLAP_CH_CONTINUATION_MASK;
+ chain_shift = SLAP_CH_CONTINUATION_SHIFT;
+ break;
+ }
+ }
+#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
+
+ /*
+ * TODO: add checks on who/when chain operations; e.g.:
+ * a) what identities are authorized
+ * b) what request DN (e.g. only chain requests rooted at <DN>)
+ * c) what referral URIs
+ * d) what protocol scheme (e.g. only ldaps://)
+ * e) what ssf
+ */
+
+ db = *op->o_bd;
+ SLAP_DBFLAGS( &db ) &= ~SLAP_DBFLAG_MONITORING;
+ op->o_bd = &db;
+
+ text = rs->sr_text;
+ rs->sr_text = NULL;
+ matched = rs->sr_matched;
+ rs->sr_matched = NULL;
+ ref = rs->sr_ref;
+ rs->sr_ref = NULL;
+
+ flags = rs->sr_flags & (REP_MATCHED_MUSTBEFREED | REP_REF_MUSTBEFREED);
+ rs->sr_flags &= ~flags;
+
+ /* we need this to know if back-ldap returned any result */
+ lb.lb_lc = lc;
+ sc2.sc_next = sc;
+ sc2.sc_private = &lb;
+ sc2.sc_response = ldap_chain_cb_response;
+ op->o_callback = &sc2;
+
+ /* Chaining can be performed by a privileged user on behalf
+ * of normal users, using the ProxyAuthz control, by exploiting
+ * the identity assertion feature of back-ldap; see idassert-*
+ * directives in slapd-ldap(5).
+ *
+ * FIXME: the idassert-authcDN is one, will it be fine regardless
+ * of the URI we obtain from the referral?
+ */
+
+ switch ( op->o_tag ) {
+ case LDAP_REQ_BIND: {
+ struct berval rndn = op->o_req_ndn;
+ Connection *conn = op->o_conn;
+
+ /* FIXME: can we really get a referral for binds? */
+ op->o_req_ndn = slap_empty_bv;
+ op->o_conn = NULL;
+ rc = ldap_chain_op( op, rs, op_bind, ref, 0 );
+ op->o_req_ndn = rndn;
+ op->o_conn = conn;
+ }
+ break;
+
+ case LDAP_REQ_ADD:
+ rc = ldap_chain_op( op, rs, op_add, ref, 0 );
+ break;
+
+ case LDAP_REQ_DELETE:
+ rc = ldap_chain_op( op, rs, op_delete, ref, 0 );
+ break;
+
+ case LDAP_REQ_MODRDN:
+ rc = ldap_chain_op( op, rs, op_modrdn, ref, 0 );
+ break;
+
+ case LDAP_REQ_MODIFY:
+ rc = ldap_chain_op( op, rs, op_modify, ref, 0 );
+ break;
+
+ case LDAP_REQ_COMPARE:
+ rc = ldap_chain_op( op, rs, op_compare, ref, 0 );
+ if ( rs->sr_err == LDAP_COMPARE_TRUE || rs->sr_err == LDAP_COMPARE_FALSE ) {
+ rc = LDAP_SUCCESS;
+ }
+ break;
+
+ case LDAP_REQ_SEARCH:
+ if ( rs->sr_type == REP_SEARCHREF ) {
+ sc2.sc_response = ldap_chain_cb_search_response;
+ rc = ldap_chain_search( op, rs, ref, 0 );
+
+ } else {
+ /* we might get here before any database actually
+ * performed a search; in those cases, we need
+ * to check limits, to make sure safe defaults
+ * are in place */
+ if ( op->ors_limit != NULL || limits_check( op, rs ) == 0 ) {
+ rc = ldap_chain_op( op, rs, op_search, ref, 0 );
+ } else {
+ rc = SLAP_CB_CONTINUE;
+ }
+ }
+ break;
+
+ case LDAP_REQ_EXTENDED:
+ rc = ldap_chain_op( op, rs, op_extended, ref, 0 );
+ /* FIXME: ldap_back_extended() by design
+ * doesn't send result; frontend is expected
+ * to send it... */
+ /* FIXME: what about chaining? */
+ if ( rc != SLAPD_ABANDON ) {
+ rs->sr_err = rc;
+ send_ldap_extended( op, rs );
+ rc = LDAP_SUCCESS;
+ }
+ lb.lb_status = LDAP_CH_RES;
+ break;
+
+ default:
+ rc = SLAP_CB_CONTINUE;
+ break;
+ }
+
+ switch ( rc ) {
+ case SLAPD_ABANDON:
+ goto dont_chain;
+
+ case LDAP_SUCCESS:
+ case LDAP_REFERRAL:
+ sr_err = rs->sr_err;
+ /* slapd-ldap sent response */
+ if ( !op->o_abandon && lb.lb_status != LDAP_CH_RES ) {
+ /* FIXME: should we send response? */
+ Debug( LDAP_DEBUG_ANY,
+ "%s: ldap_chain_response: "
+ "overlay should have sent result.\n",
+ op->o_log_prefix );
+ }
+ break;
+
+ default:
+#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
+ if ( lb.lb_status == LDAP_CH_ERR && rs->sr_err == LDAP_X_CANNOT_CHAIN ) {
+ goto cannot_chain;
+ }
+
+ switch ( ( get_chainingBehavior( op ) & chain_mask ) >> chain_shift ) {
+ case LDAP_CHAINING_REQUIRED:
+cannot_chain:;
+ op->o_callback = NULL;
+ send_ldap_error( op, rs, LDAP_X_CANNOT_CHAIN,
+ "operation cannot be completed without chaining" );
+ goto dont_chain;
+
+ default:
+#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
+ if ( LDAP_CHAIN_RETURN_ERR( lc ) ) {
+ sr_err = rs->sr_err = rc;
+ rs->sr_text = lb.lb_text;
+ rs->sr_type = sr_type;
+
+ } else {
+ rc = SLAP_CB_CONTINUE;
+ rs->sr_err = sr_err;
+ rs->sr_type = sr_type;
+ rs->sr_text = text;
+ rs->sr_matched = matched;
+ rs->sr_ref = ref;
+ rs->sr_flags |= flags;
+ }
+#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
+ break;
+ }
+#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
+ }
+
+ if ( lb.lb_status == LDAP_CH_NONE && rc != SLAPD_ABANDON ) {
+ /* give the remaining callbacks a chance */
+ op->o_callback = sc->sc_next;
+ rc = rs->sr_err = slap_map_api2result( rs );
+ send_ldap_result( op, rs );
+ }
+
+dont_chain:;
+ rs->sr_err = sr_err;
+ rs->sr_type = sr_type;
+ rs->sr_text = text;
+ rs->sr_matched = matched;
+ rs->sr_ref = ref;
+ rs->sr_flags |= flags;
+
+ op->o_bd = bd;
+ op->o_callback = sc;
+ op->o_ndn = ndn;
+
+ if ( rs->sr_text == lb.lb_text ) {
+ rs->sr_text = NULL;
+ }
+ if ( lb.lb_text ) {
+ ber_memfree_x( lb.lb_text, op->o_tmpmemctx );
+ }
+
+ return rc;
+}
+
+#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
+static int
+ldap_chain_parse_ctrl(
+ Operation *op,
+ SlapReply *rs,
+ LDAPControl *ctrl );
+
+static int
+str2chain( const char *s )
+{
+ if ( strcasecmp( s, "chainingPreferred" ) == 0 ) {
+ return LDAP_CHAINING_PREFERRED;
+
+ } else if ( strcasecmp( s, "chainingRequired" ) == 0 ) {
+ return LDAP_CHAINING_REQUIRED;
+
+ } else if ( strcasecmp( s, "referralsPreferred" ) == 0 ) {
+ return LDAP_REFERRALS_PREFERRED;
+
+ } else if ( strcasecmp( s, "referralsRequired" ) == 0 ) {
+ return LDAP_REFERRALS_REQUIRED;
+ }
+
+ return -1;
+}
+#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
+
+/*
+ * configuration...
+ */
+
+enum {
+ CH_CHAINING = 1,
+ CH_CACHE_URI,
+ CH_MAX_DEPTH,
+ CH_RETURN_ERR,
+
+ CH_LAST
+};
+
+static ConfigDriver chain_cf_gen;
+static ConfigCfAdd chain_cfadd;
+static ConfigLDAPadd chain_ldadd;
+#ifdef SLAP_CONFIG_DELETE
+static ConfigLDAPdel chain_lddel;
+#endif
+
+static ConfigTable chaincfg[] = {
+#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
+ { "chain-chaining", "args",
+ 2, 4, 0, ARG_MAGIC|ARG_BERVAL|CH_CHAINING, chain_cf_gen,
+ "( OLcfgOvAt:3.1 NAME 'olcChainingBehavior' "
+ "DESC 'Chaining behavior control parameters (draft-sermersheim-ldap-chaining)' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
+ { "chain-cache-uri", "TRUE/FALSE",
+ 2, 2, 0, ARG_MAGIC|ARG_ON_OFF|CH_CACHE_URI, chain_cf_gen,
+ "( OLcfgOvAt:3.2 NAME 'olcChainCacheURI' "
+ "DESC 'Enables caching of URIs not present in configuration' "
+ "EQUALITY booleanMatch "
+ "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
+ { "chain-max-depth", "args",
+ 2, 2, 0, ARG_MAGIC|ARG_INT|CH_MAX_DEPTH, chain_cf_gen,
+ "( OLcfgOvAt:3.3 NAME 'olcChainMaxReferralDepth' "
+ "DESC 'max referral depth' "
+ "SYNTAX OMsInteger "
+ "EQUALITY integerMatch "
+ "SINGLE-VALUE )", NULL, NULL },
+ { "chain-return-error", "TRUE/FALSE",
+ 2, 2, 0, ARG_MAGIC|ARG_ON_OFF|CH_RETURN_ERR, chain_cf_gen,
+ "( OLcfgOvAt:3.4 NAME 'olcChainReturnError' "
+ "DESC 'Errors are returned instead of the original referral' "
+ "EQUALITY booleanMatch "
+ "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
+ { NULL, NULL, 0, 0, 0, ARG_IGNORED }
+};
+
+static ConfigOCs chainocs[] = {
+ { "( OLcfgOvOc:3.1 "
+ "NAME 'olcChainConfig' "
+ "DESC 'Chain configuration' "
+ "SUP olcOverlayConfig "
+ "MAY ( "
+#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
+ "olcChainingBehavior $ "
+#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
+ "olcChainCacheURI $ "
+ "olcChainMaxReferralDepth $ "
+ "olcChainReturnError "
+ ") )",
+ Cft_Overlay, chaincfg, NULL, chain_cfadd },
+ { "( OLcfgOvOc:3.2 "
+ "NAME 'olcChainDatabase' "
+ "DESC 'Chain remote server configuration' "
+ "AUXILIARY )",
+ Cft_Misc, NULL, chain_ldadd
+#ifdef SLAP_CONFIG_DELETE
+ , NULL, chain_lddel
+#endif
+ },
+ { NULL, 0, NULL }
+};
+
+static int
+chain_ldadd( CfEntryInfo *p, Entry *e, ConfigArgs *ca )
+{
+ slap_overinst *on;
+ ldap_chain_t *lc;
+
+ ldapinfo_t *li;
+
+ AttributeDescription *ad = NULL;
+ Attribute *at;
+ const char *text;
+
+ int rc;
+
+ if ( p->ce_type != Cft_Overlay
+ || !p->ce_bi
+ || p->ce_bi->bi_cf_ocs != chainocs )
+ {
+ return LDAP_CONSTRAINT_VIOLATION;
+ }
+
+ on = (slap_overinst *)p->ce_bi;
+ lc = (ldap_chain_t *)on->on_bi.bi_private;
+
+ assert( ca->be == NULL );
+ ca->be = (BackendDB *)ch_calloc( 1, sizeof( BackendDB ) );
+
+ ca->be->bd_info = (BackendInfo *)on;
+
+ rc = slap_str2ad( "olcDbURI", &ad, &text );
+ assert( rc == LDAP_SUCCESS );
+
+ at = attr_find( e->e_attrs, ad );
+#if 0
+ if ( lc->lc_common_li == NULL && at != NULL ) {
+ /* FIXME: we should generate an empty default entry
+ * if none is supplied */
+ Debug( LDAP_DEBUG_ANY, "slapd-chain: "
+ "first underlying database \"%s\" "
+ "cannot contain attribute \"%s\".\n",
+ e->e_name.bv_val, ad->ad_cname.bv_val );
+ rc = LDAP_CONSTRAINT_VIOLATION;
+ goto done;
+
+ } else
+#endif
+ if ( lc->lc_common_li != NULL && lc->lc_common_li != lc->lc_cfg_li && at == NULL ) {
+ /* FIXME: we should generate an empty default entry
+ * if none is supplied */
+ Debug( LDAP_DEBUG_ANY, "slapd-chain: "
+ "subsequent underlying database \"%s\" "
+ "must contain attribute \"%s\".\n",
+ e->e_name.bv_val, ad->ad_cname.bv_val );
+ rc = LDAP_CONSTRAINT_VIOLATION;
+ goto done;
+ }
+
+ if ( lc->lc_common_li == NULL ) {
+ rc = ldap_chain_db_init_common( ca->be );
+ if ( rc != 0 )
+ goto fail;
+ li = ca->be->be_private;
+ lc->lc_common_li = lc->lc_cfg_li = li;
+
+ }
+ rc = ldap_chain_db_init_one( ca->be );
+ lc->lc_cfg_li = NULL;
+
+ if ( rc != 0 ) {
+fail:
+ Debug( LDAP_DEBUG_ANY, "slapd-chain: "
+ "unable to init %sunderlying database \"%s\".\n",
+ lc->lc_common_li == NULL ? "common " : "", e->e_name.bv_val );
+ return LDAP_CONSTRAINT_VIOLATION;
+ }
+
+ li = ca->be->be_private;
+
+ if ( at ) {
+ char **urls;
+
+ urls = ldap_str2charray( at->a_vals[ 0 ].bv_val, ", \t" );
+ if ( !urls || !urls[0] || urls[1] ) {
+ ldap_charray_free( urls );
+ Debug( LDAP_DEBUG_ANY, "slapd-chain: "
+ "olcDbURI must contain exactly one url, got %s\n",
+ at->a_vals[ 0 ].bv_val );
+ rc = LDAP_CONSTRAINT_VIOLATION;
+ goto done;
+ }
+ ldap_charray_free( urls );
+
+ li->li_uri = ch_strdup( at->a_vals[ 0 ].bv_val );
+ value_add_one( &li->li_bvuri, &at->a_vals[ 0 ] );
+ if ( ldap_tavl_insert( &lc->lc_lai.lai_tree, (caddr_t)li,
+ ldap_chain_uri_cmp, ldap_chain_uri_dup ) )
+ {
+ Debug( LDAP_DEBUG_ANY, "slapd-chain: "
+ "database \"%s\" insert failed.\n",
+ e->e_name.bv_val );
+ rc = LDAP_CONSTRAINT_VIOLATION;
+ goto done;
+ }
+ }
+
+ ca->ca_private = on;
+
+done:;
+ if ( rc != LDAP_SUCCESS ) {
+ (void)ldap_chain_db_destroy_one( ca->be, NULL );
+ ch_free( ca->be );
+ ca->be = NULL;
+ }
+
+ return rc;
+}
+
+static void
+ldap_chain_cfadd_apply(
+ ldapinfo_t *li,
+ Operation *op,
+ SlapReply *rs,
+ Entry *p,
+ ConfigArgs *ca,
+ int count )
+{
+ struct berval bv;
+
+ /* FIXME: should not hardcode "olcDatabase" here */
+ bv.bv_len = snprintf( ca->cr_msg, sizeof( ca->cr_msg ),
+ "olcDatabase={%d}%s", count, lback->bi_type );
+ bv.bv_val = ca->cr_msg;
+
+ ca->be->be_private = (void *)li;
+ config_build_entry( op, rs, p->e_private, ca,
+ &bv, lback->bi_cf_ocs, &chainocs[1] );
+
+ return;
+}
+
+static int
+chain_cfadd( Operation *op, SlapReply *rs, Entry *p, ConfigArgs *ca )
+{
+ CfEntryInfo *pe = p->e_private;
+ slap_overinst *on = (slap_overinst *)pe->ce_bi;
+ ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
+ void *priv = (void *)ca->be->be_private;
+ TAvlnode *edge;
+ int count = 0;
+
+ if ( lback->bi_cf_ocs ) {
+
+ ldap_chain_cfadd_apply( lc->lc_common_li, op, rs, p, ca, count++ );
+
+ edge = ldap_tavl_end( lc->lc_lai.lai_tree, TAVL_DIR_LEFT );
+ while ( edge ) {
+ TAvlnode *next = ldap_tavl_next( edge, TAVL_DIR_RIGHT );
+ ldapinfo_t *li = (ldapinfo_t *)edge->avl_data;
+ ldap_chain_cfadd_apply( li, op, rs, p, ca, count++ );
+ edge = next;
+ }
+
+ ca->be->be_private = priv;
+ }
+
+ lc->lc_cfg_li = NULL;
+
+ return 0;
+}
+
+#ifdef SLAP_CONFIG_DELETE
+static int
+chain_lddel( CfEntryInfo *ce, Operation *op )
+{
+ CfEntryInfo *pe = ce->ce_parent;
+ slap_overinst *on = (slap_overinst *)pe->ce_bi;
+ ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
+ ldapinfo_t *li = (ldapinfo_t *) ce->ce_be->be_private;
+
+ if ( li != lc->lc_common_li ) {
+ if (! ldap_tavl_delete( &lc->lc_lai.lai_tree, li, ldap_chain_uri_cmp ) ) {
+ Debug( LDAP_DEBUG_ANY, "slapd-chain: ldap_avl_delete failed. "
+ "\"%s\" not found.\n", li->li_uri );
+ return -1;
+ }
+ } else if ( lc->lc_lai.lai_tree ) {
+ Debug( LDAP_DEBUG_ANY, "slapd-chain: cannot delete first underlying "
+ "LDAP database when other databases are still present.\n" );
+ return -1;
+ } else {
+ lc->lc_common_li = NULL;
+ }
+
+ ce->ce_be->bd_info = lback;
+
+ if ( ce->ce_be->bd_info->bi_db_close ) {
+ ce->ce_be->bd_info->bi_db_close( ce->ce_be, NULL );
+ }
+ if ( ce->ce_be->bd_info->bi_db_destroy ) {
+ ce->ce_be->bd_info->bi_db_destroy( ce->ce_be, NULL );
+ }
+
+ ch_free(ce->ce_be);
+ ce->ce_be = NULL;
+
+ return LDAP_SUCCESS;
+}
+#endif /* SLAP_CONFIG_DELETE */
+
+#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
+static slap_verbmasks chaining_mode[] = {
+ { BER_BVC("referralsRequired"), LDAP_REFERRALS_REQUIRED },
+ { BER_BVC("referralsPreferred"), LDAP_REFERRALS_PREFERRED },
+ { BER_BVC("chainingRequired"), LDAP_CHAINING_REQUIRED },
+ { BER_BVC("chainingPreferred"), LDAP_CHAINING_PREFERRED },
+ { BER_BVNULL, 0 }
+};
+#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
+
+static int
+chain_cf_gen( ConfigArgs *c )
+{
+ slap_overinst *on = (slap_overinst *)c->bi;
+ ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
+
+ int rc = 0;
+
+ if ( c->op == SLAP_CONFIG_EMIT ) {
+ switch( c->type ) {
+#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
+ case CH_CHAINING: {
+ struct berval resolve = BER_BVNULL,
+ continuation = BER_BVNULL;
+
+ if ( !LDAP_CHAIN_CHAINING( lc ) ) {
+ return 1;
+ }
+
+ enum_to_verb( chaining_mode, ( ( lc->lc_chaining_ctrlflag & SLAP_CH_RESOLVE_MASK ) >> SLAP_CH_RESOLVE_SHIFT ), &resolve );
+ enum_to_verb( chaining_mode, ( ( lc->lc_chaining_ctrlflag & SLAP_CH_CONTINUATION_MASK ) >> SLAP_CH_CONTINUATION_SHIFT ), &continuation );
+
+ c->value_bv.bv_len = STRLENOF( "resolve=" ) + resolve.bv_len
+ + STRLENOF( " " )
+ + STRLENOF( "continuation=" ) + continuation.bv_len;
+ c->value_bv.bv_val = ch_malloc( c->value_bv.bv_len + 1 );
+ snprintf( c->value_bv.bv_val, c->value_bv.bv_len + 1,
+ "resolve=%s continuation=%s",
+ resolve.bv_val, continuation.bv_val );
+
+ if ( lc->lc_chaining_ctrl.ldctl_iscritical ) {
+ c->value_bv.bv_val = ch_realloc( c->value_bv.bv_val,
+ c->value_bv.bv_len + STRLENOF( " critical" ) + 1 );
+ AC_MEMCPY( &c->value_bv.bv_val[ c->value_bv.bv_len ],
+ " critical", STRLENOF( " critical" ) + 1 );
+ c->value_bv.bv_len += STRLENOF( " critical" );
+ }
+
+ break;
+ }
+#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
+
+ case CH_CACHE_URI:
+ c->value_int = LDAP_CHAIN_CACHE_URI( lc );
+ break;
+
+ case CH_MAX_DEPTH:
+ c->value_int = lc->lc_max_depth;
+ break;
+
+ case CH_RETURN_ERR:
+ c->value_int = LDAP_CHAIN_RETURN_ERR( lc );
+ break;
+
+ default:
+ assert( 0 );
+ rc = 1;
+ }
+ return rc;
+
+ } else if ( c->op == LDAP_MOD_DELETE ) {
+ switch( c->type ) {
+ case CH_CHAINING:
+ return 1;
+
+ case CH_CACHE_URI:
+ lc->lc_flags &= ~LDAP_CHAIN_F_CACHE_URI;
+ break;
+
+ case CH_MAX_DEPTH:
+ c->value_int = 0;
+ break;
+
+ case CH_RETURN_ERR:
+ lc->lc_flags &= ~LDAP_CHAIN_F_RETURN_ERR;
+ break;
+
+ default:
+ return 1;
+ }
+ return rc;
+ }
+
+ switch( c->type ) {
+ case CH_CHAINING: {
+#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
+ char **argv = c->argv;
+ int argc = c->argc;
+ BerElementBuffer berbuf;
+ BerElement *ber = (BerElement *)&berbuf;
+ int resolve = -1,
+ continuation = -1,
+ iscritical = 0;
+ Operation op = { 0 };
+ SlapReply rs = { 0 };
+
+ lc->lc_chaining_ctrlflag = 0;
+
+ for ( argc--, argv++; argc > 0; argc--, argv++ ) {
+ if ( strncasecmp( argv[ 0 ], "resolve=", STRLENOF( "resolve=" ) ) == 0 ) {
+ resolve = str2chain( argv[ 0 ] + STRLENOF( "resolve=" ) );
+ if ( resolve == -1 ) {
+ Debug( LDAP_DEBUG_ANY, "%s: "
+ "illegal <resolve> value %s "
+ "in \"chain-chaining>\".\n",
+ c->log, argv[ 0 ] );
+ return 1;
+ }
+
+ } else if ( strncasecmp( argv[ 0 ], "continuation=", STRLENOF( "continuation=" ) ) == 0 ) {
+ continuation = str2chain( argv[ 0 ] + STRLENOF( "continuation=" ) );
+ if ( continuation == -1 ) {
+ Debug( LDAP_DEBUG_ANY, "%s: "
+ "illegal <continuation> value %s "
+ "in \"chain-chaining\".\n",
+ c->log, argv[ 0 ] );
+ return 1;
+ }
+
+ } else if ( strcasecmp( argv[ 0 ], "critical" ) == 0 ) {
+ iscritical = 1;
+
+ } else {
+ Debug( LDAP_DEBUG_ANY, "%s: "
+ "unknown option in \"chain-chaining\".\n",
+ c->log );
+ return 1;
+ }
+ }
+
+ if ( resolve != -1 || continuation != -1 ) {
+ int err;
+
+ if ( resolve == -1 ) {
+ /* default */
+ resolve = SLAP_CHAINING_DEFAULT;
+ }
+
+ ber_init2( ber, NULL, LBER_USE_DER );
+
+ err = ber_printf( ber, "{e" /* } */, resolve );
+ if ( err == -1 ) {
+ ber_free( ber, 1 );
+ Debug( LDAP_DEBUG_ANY, "%s: "
+ "chaining behavior control encoding error!\n",
+ c->log );
+ return 1;
+ }
+
+ if ( continuation > -1 ) {
+ err = ber_printf( ber, "e", continuation );
+ if ( err == -1 ) {
+ ber_free( ber, 1 );
+ Debug( LDAP_DEBUG_ANY, "%s: "
+ "chaining behavior control encoding error!\n",
+ c->log );
+ return 1;
+ }
+ }
+
+ err = ber_printf( ber, /* { */ "N}" );
+ if ( err == -1 ) {
+ ber_free( ber, 1 );
+ Debug( LDAP_DEBUG_ANY, "%s: "
+ "chaining behavior control encoding error!\n",
+ c->log );
+ return 1;
+ }
+
+ if ( ber_flatten2( ber, &lc->lc_chaining_ctrl.ldctl_value, 0 ) == -1 ) {
+ exit( EXIT_FAILURE );
+ }
+
+ } else {
+ BER_BVZERO( &lc->lc_chaining_ctrl.ldctl_value );
+ }
+
+ lc->lc_chaining_ctrl.ldctl_oid = LDAP_CONTROL_X_CHAINING_BEHAVIOR;
+ lc->lc_chaining_ctrl.ldctl_iscritical = iscritical;
+
+ if ( ldap_chain_parse_ctrl( &op, &rs, &lc->lc_chaining_ctrl ) != LDAP_SUCCESS )
+ {
+ Debug( LDAP_DEBUG_ANY, "%s: "
+ "unable to parse chaining control%s%s.\n",
+ c->log, rs.sr_text ? ": " : "",
+ rs.sr_text ? rs.sr_text : "" );
+ return 1;
+ }
+
+ lc->lc_chaining_ctrlflag = op.o_chaining;
+
+ lc->lc_flags |= LDAP_CHAIN_F_CHAINING;
+
+ rc = 0;
+#else /* ! LDAP_CONTROL_X_CHAINING_BEHAVIOR */
+ Debug( LDAP_DEBUG_ANY, "%s: "
+ "\"chaining\" control unsupported (ignored).\n",
+ c->log );
+#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
+ } break;
+
+ case CH_CACHE_URI:
+ if ( c->value_int ) {
+ lc->lc_flags |= LDAP_CHAIN_F_CACHE_URI;
+ } else {
+ lc->lc_flags &= ~LDAP_CHAIN_F_CACHE_URI;
+ }
+ break;
+
+ case CH_MAX_DEPTH:
+ if ( c->value_int < 0 ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "<%s> invalid max referral depth %d",
+ c->argv[0], c->value_int );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
+ c->log, c->cr_msg );
+ rc = 1;
+ break;
+ }
+ lc->lc_max_depth = c->value_int;
+
+ case CH_RETURN_ERR:
+ if ( c->value_int ) {
+ lc->lc_flags |= LDAP_CHAIN_F_RETURN_ERR;
+ } else {
+ lc->lc_flags &= ~LDAP_CHAIN_F_RETURN_ERR;
+ }
+ break;
+
+ default:
+ assert( 0 );
+ return 1;
+ }
+ return rc;
+}
+
+static int
+ldap_chain_db_init(
+ BackendDB *be,
+ ConfigReply *cr )
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ ldap_chain_t *lc = NULL;
+
+ if ( lback == NULL ) {
+ lback = backend_info( "ldap" );
+
+ if ( lback == NULL ) {
+ return 1;
+ }
+ }
+
+ lc = ch_malloc( sizeof( ldap_chain_t ) );
+ if ( lc == NULL ) {
+ return 1;
+ }
+ memset( lc, 0, sizeof( ldap_chain_t ) );
+ lc->lc_max_depth = 1;
+ ldap_pvt_thread_mutex_init( &lc->lc_lai.lai_mutex );
+
+ on->on_bi.bi_private = (void *)lc;
+
+ return 0;
+}
+
+static int
+ldap_chain_db_config(
+ BackendDB *be,
+ const char *fname,
+ int lineno,
+ int argc,
+ char **argv )
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
+
+ int rc = SLAP_CONF_UNKNOWN;
+
+ if ( lc->lc_common_li == NULL ) {
+ BackendDB db = *be;
+ ldap_chain_db_init_common( &db );
+ lc->lc_common_li = lc->lc_cfg_li = (ldapinfo_t *)db.be_private;
+ }
+
+ /* Something for the chain database? */
+ if ( strncasecmp( argv[ 0 ], "chain-", STRLENOF( "chain-" ) ) == 0 ) {
+ char *save_argv0 = argv[ 0 ];
+ BackendDB db = *be;
+ static char *allowed_argv[] = {
+ /* special: put URI here, so in the meanwhile
+ * it detects whether a new URI is being provided */
+ "uri",
+ "nretries",
+ "timeout",
+ /* flags */
+ "tls",
+ /* FIXME: maybe rebind-as-user should be allowed
+ * only within known URIs... */
+ "rebind-as-user",
+ "chase-referrals",
+ "t-f-support",
+ "proxy-whoami",
+ NULL
+ };
+ int which_argv = -1;
+
+ argv[ 0 ] += STRLENOF( "chain-" );
+
+ for ( which_argv = 0; allowed_argv[ which_argv ]; which_argv++ ) {
+ if ( strcasecmp( argv[ 0 ], allowed_argv[ which_argv ] ) == 0 ) {
+ break;
+ }
+ }
+
+ if ( allowed_argv[ which_argv ] == NULL ) {
+ which_argv = -1;
+
+ if ( lc->lc_cfg_li == lc->lc_common_li ) {
+ Debug( LDAP_DEBUG_ANY, "%s: line %d: "
+ "\"%s\" only allowed within a URI directive.\n.",
+ fname, lineno, argv[ 0 ] );
+ return 1;
+ }
+ }
+
+ if ( which_argv == 0 ) {
+ rc = ldap_chain_db_init_one( &db );
+ if ( rc != 0 ) {
+ Debug( LDAP_DEBUG_ANY, "%s: line %d: "
+ "underlying slapd-ldap initialization failed.\n.",
+ fname, lineno );
+ return 1;
+ }
+ lc->lc_cfg_li = db.be_private;
+ }
+
+ /* TODO: add checks on what other slapd-ldap(5) args
+ * should be put in the template; this is not quite
+ * harmful, because attributes that shouldn't don't
+ * get actually used, but the user should at least
+ * be warned.
+ */
+
+ db.bd_info = lback;
+ db.be_private = (void *)lc->lc_cfg_li;
+ db.be_cf_ocs = lback->bi_cf_ocs;
+
+ rc = config_generic_wrapper( &db, fname, lineno, argc, argv );
+
+ argv[ 0 ] = save_argv0;
+
+ if ( which_argv == 0 ) {
+private_destroy:;
+ if ( rc != 0 ) {
+ db.bd_info = lback;
+ db.be_private = (void *)lc->lc_cfg_li;
+ ldap_chain_db_destroy_one( &db, NULL );
+ lc->lc_cfg_li = NULL;
+ } else {
+ if ( lc->lc_cfg_li->li_bvuri == NULL
+ || BER_BVISNULL( &lc->lc_cfg_li->li_bvuri[ 0 ] )
+ || !BER_BVISNULL( &lc->lc_cfg_li->li_bvuri[ 1 ] ) )
+ {
+ Debug( LDAP_DEBUG_ANY, "%s: line %d: "
+ "no URI list allowed in slapo-chain.\n",
+ fname, lineno );
+ rc = 1;
+ goto private_destroy;
+ }
+
+ if ( ldap_tavl_insert( &lc->lc_lai.lai_tree,
+ (caddr_t)lc->lc_cfg_li,
+ ldap_chain_uri_cmp, ldap_chain_uri_dup ) )
+ {
+ Debug( LDAP_DEBUG_ANY, "%s: line %d: "
+ "duplicate URI in slapo-chain.\n",
+ fname, lineno );
+ rc = 1;
+ goto private_destroy;
+ }
+ }
+ }
+ }
+
+ return rc;
+}
+
+enum db_which {
+ db_open = 0,
+ db_close,
+ db_destroy,
+
+ db_last
+};
+
+static int
+ldap_chain_db_func(
+ BackendDB *be,
+ enum db_which which
+)
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
+
+ int rc = 0;
+
+ if ( lc ) {
+ BI_db_func *func = (&lback->bi_db_open)[ which ];
+
+ if ( func != NULL && lc->lc_common_li != NULL ) {
+ BackendDB db = *be;
+
+ db.bd_info = lback;
+ db.be_private = lc->lc_common_li;
+
+ rc = func( &db, NULL );
+
+ if ( rc != 0 ) {
+ return rc;
+ }
+
+ if ( lc->lc_lai.lai_tree != NULL ) {
+ TAvlnode *edge = ldap_tavl_end( lc->lc_lai.lai_tree, TAVL_DIR_LEFT );
+ while ( edge ) {
+ TAvlnode *next = ldap_tavl_next( edge, TAVL_DIR_RIGHT );
+ ldapinfo_t *li = (ldapinfo_t *)edge->avl_data;
+ db.be_private = (void *)li;
+ rc = func( &db, NULL );
+ if ( rc == 1 ) {
+ break;
+ }
+ edge = next;
+ }
+ }
+ }
+ }
+
+ return rc;
+}
+
+static int
+ldap_chain_db_open(
+ BackendDB *be,
+ ConfigReply *cr )
+{
+ slap_overinst *on = (slap_overinst *) be->bd_info;
+ ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
+ slap_mask_t monitoring;
+ int rc = 0;
+
+#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
+ rc = overlay_register_control( be, LDAP_CONTROL_X_CHAINING_BEHAVIOR );
+ if ( rc != 0 ) {
+ return rc;
+ }
+#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
+
+ if ( lc->lc_common_li == NULL ) {
+ void *be_private = be->be_private;
+ ldap_chain_db_init_common( be );
+ lc->lc_common_li = lc->lc_cfg_li = (ldapinfo_t *)be->be_private;
+ be->be_private = be_private;
+ }
+
+ /* filter out and restore monitoring */
+ monitoring = ( SLAP_DBFLAGS( be ) & SLAP_DBFLAG_MONITORING );
+ SLAP_DBFLAGS( be ) &= ~SLAP_DBFLAG_MONITORING;
+ rc = ldap_chain_db_func( be, db_open );
+ SLAP_DBFLAGS( be ) |= monitoring;
+
+ return rc;
+}
+
+static int
+ldap_chain_db_close(
+ BackendDB *be,
+ ConfigReply *cr )
+{
+#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
+#ifdef SLAP_CONFIG_DELETE
+ overlay_unregister_control( be, LDAP_CONTROL_X_CHAINING_BEHAVIOR );
+#endif /* SLAP_CONFIG_DELETE */
+#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
+ return ldap_chain_db_func( be, db_close );
+}
+
+static int
+ldap_chain_db_destroy(
+ BackendDB *be,
+ ConfigReply *cr )
+{
+ slap_overinst *on = (slap_overinst *) be->bd_info;
+ ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
+
+ int rc;
+
+ rc = ldap_chain_db_func( be, db_destroy );
+
+ if ( lc ) {
+ ldap_tavl_free( lc->lc_lai.lai_tree, NULL );
+ ldap_pvt_thread_mutex_destroy( &lc->lc_lai.lai_mutex );
+ ch_free( lc );
+ }
+
+ return rc;
+}
+
+/*
+ * inits one instance of the slapd-ldap backend, and stores
+ * the private info in be_private of the arg
+ */
+static int
+ldap_chain_db_init_common(
+ BackendDB *be )
+{
+ BackendInfo *bi = be->bd_info;
+ ldapinfo_t *li;
+ int rc;
+
+ be->bd_info = lback;
+ be->be_private = NULL;
+ rc = lback->bi_db_init( be, NULL );
+ if ( rc != 0 ) {
+ return rc;
+ }
+ li = (ldapinfo_t *)be->be_private;
+ li->li_urllist_f = NULL;
+ li->li_urllist_p = NULL;
+
+ be->bd_info = bi;
+
+ return 0;
+}
+
+/*
+ * inits one instance of the slapd-ldap backend, stores
+ * the private info in be_private of the arg and fills
+ * selected fields with data from the template.
+ *
+ * NOTE: add checks about the other fields of the template,
+ * which are ignored and SHOULD NOT be configured by the user.
+ */
+static int
+ldap_chain_db_init_one(
+ BackendDB *be )
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
+
+ BackendInfo *bi = be->bd_info;
+ ldapinfo_t *li;
+
+ slap_op_t t;
+
+ be->bd_info = lback;
+ be->be_private = NULL;
+ t = lback->bi_db_init( be, NULL );
+ if ( t != 0 ) {
+ return t;
+ }
+ li = (ldapinfo_t *)be->be_private;
+ li->li_urllist_f = NULL;
+ li->li_urllist_p = NULL;
+
+ /* copy common data */
+ li->li_nretries = lc->lc_common_li->li_nretries;
+ li->li_flags = lc->lc_common_li->li_flags;
+ li->li_version = lc->lc_common_li->li_version;
+ for ( t = 0; t < SLAP_OP_LAST; t++ ) {
+ li->li_timeout[ t ] = lc->lc_common_li->li_timeout[ t ];
+ }
+ be->bd_info = bi;
+
+ return 0;
+}
+
+static int
+ldap_chain_db_open_one(
+ BackendDB *be )
+{
+ if ( SLAP_DBMONITORING( be ) ) {
+ ldapinfo_t *li = (ldapinfo_t *)be->be_private;
+
+ if ( li->li_uri == NULL ) {
+ ber_str2bv( "cn=Common Connections", 0, 1,
+ &li->li_monitor_info.lmi_conn_rdn );
+ ber_str2bv( "cn=Operations on Common Connections", 0, 1,
+ &li->li_monitor_info.lmi_conn_rdn );
+
+ } else {
+ char *ptr;
+
+ li->li_monitor_info.lmi_conn_rdn.bv_len
+ = STRLENOF( "cn=" ) + strlen( li->li_uri );
+ ptr = li->li_monitor_info.lmi_conn_rdn.bv_val
+ = ch_malloc( li->li_monitor_info.lmi_conn_rdn.bv_len + 1 );
+ ptr = lutil_strcopy( ptr, "cn=" );
+ ptr = lutil_strcopy( ptr, li->li_uri );
+ ptr[ 0 ] = '\0';
+
+ li->li_monitor_info.lmi_ops_rdn.bv_len
+ = STRLENOF( "cn=Operations on " ) + strlen( li->li_uri );
+ ptr = li->li_monitor_info.lmi_ops_rdn.bv_val
+ = ch_malloc( li->li_monitor_info.lmi_ops_rdn.bv_len + 1 );
+ ptr = lutil_strcopy( ptr, "cn=Operations on " );
+ ptr = lutil_strcopy( ptr, li->li_uri );
+ ptr[ 0 ] = '\0';
+ }
+ }
+
+ return lback->bi_db_open( be, NULL );
+}
+
+static int
+ldap_chain_connection_destroy(
+ BackendDB *be,
+ Connection *conn
+)
+{
+ slap_overinst *on = (slap_overinst *) be->bd_info;
+ ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
+ void *private = be->be_private;
+ TAvlnode *edge;
+ int rc;
+
+ be->be_private = NULL;
+ ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
+ edge = ldap_tavl_end( lc->lc_lai.lai_tree, TAVL_DIR_LEFT );
+ while ( edge ) {
+ TAvlnode *next = ldap_tavl_next( edge, TAVL_DIR_RIGHT );
+ ldapinfo_t *li = (ldapinfo_t *)edge->avl_data;
+ be->be_private = (void *)li;
+ rc = lback->bi_connection_destroy( be, conn );
+ if ( rc == 1 ) {
+ break;
+ }
+ edge = next;
+ }
+
+
+ ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );
+ be->be_private = private;
+
+ return rc;
+}
+
+#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
+static int
+ldap_chain_parse_ctrl(
+ Operation *op,
+ SlapReply *rs,
+ LDAPControl *ctrl )
+{
+ ber_tag_t tag;
+ BerElement *ber;
+ ber_int_t mode,
+ behavior;
+
+ if ( get_chaining( op ) != SLAP_CONTROL_NONE ) {
+ rs->sr_text = "Chaining behavior control specified multiple times";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ if ( op->o_pagedresults != SLAP_CONTROL_NONE ) {
+ rs->sr_text = "Chaining behavior control specified with pagedResults control";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ if ( BER_BVISEMPTY( &ctrl->ldctl_value ) ) {
+ mode = (SLAP_CH_RESOLVE_DEFAULT|SLAP_CH_CONTINUATION_DEFAULT);
+
+ } else {
+ ber_len_t len;
+
+ /* Parse the control value
+ * ChainingBehavior ::= SEQUENCE {
+ * resolveBehavior Behavior OPTIONAL,
+ * continuationBehavior Behavior OPTIONAL }
+ *
+ * Behavior :: = ENUMERATED {
+ * chainingPreferred (0),
+ * chainingRequired (1),
+ * referralsPreferred (2),
+ * referralsRequired (3) }
+ */
+
+ ber = ber_init( &ctrl->ldctl_value );
+ if( ber == NULL ) {
+ rs->sr_text = "internal error";
+ return LDAP_OTHER;
+ }
+
+ tag = ber_scanf( ber, "{e" /* } */, &behavior );
+ /* FIXME: since the whole SEQUENCE is optional,
+ * should we accept no enumerations at all? */
+ if ( tag != LBER_ENUMERATED ) {
+ rs->sr_text = "Chaining behavior control: resolveBehavior decoding error";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ switch ( behavior ) {
+ case LDAP_CHAINING_PREFERRED:
+ mode = SLAP_CH_RESOLVE_CHAINING_PREFERRED;
+ break;
+
+ case LDAP_CHAINING_REQUIRED:
+ mode = SLAP_CH_RESOLVE_CHAINING_REQUIRED;
+ break;
+
+ case LDAP_REFERRALS_PREFERRED:
+ mode = SLAP_CH_RESOLVE_REFERRALS_PREFERRED;
+ break;
+
+ case LDAP_REFERRALS_REQUIRED:
+ mode = SLAP_CH_RESOLVE_REFERRALS_REQUIRED;
+ break;
+
+ default:
+ rs->sr_text = "Chaining behavior control: unknown resolveBehavior";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ tag = ber_peek_tag( ber, &len );
+ if ( tag == LBER_ENUMERATED ) {
+ tag = ber_scanf( ber, "e", &behavior );
+ if ( tag == LBER_ERROR ) {
+ rs->sr_text = "Chaining behavior control: continuationBehavior decoding error";
+ return LDAP_PROTOCOL_ERROR;
+ }
+ }
+
+ if ( tag == LBER_DEFAULT ) {
+ mode |= SLAP_CH_CONTINUATION_DEFAULT;
+
+ } else {
+ switch ( behavior ) {
+ case LDAP_CHAINING_PREFERRED:
+ mode |= SLAP_CH_CONTINUATION_CHAINING_PREFERRED;
+ break;
+
+ case LDAP_CHAINING_REQUIRED:
+ mode |= SLAP_CH_CONTINUATION_CHAINING_REQUIRED;
+ break;
+
+ case LDAP_REFERRALS_PREFERRED:
+ mode |= SLAP_CH_CONTINUATION_REFERRALS_PREFERRED;
+ break;
+
+ case LDAP_REFERRALS_REQUIRED:
+ mode |= SLAP_CH_CONTINUATION_REFERRALS_REQUIRED;
+ break;
+
+ default:
+ rs->sr_text = "Chaining behavior control: unknown continuationBehavior";
+ return LDAP_PROTOCOL_ERROR;
+ }
+ }
+
+ if ( ( ber_scanf( ber, /* { */ "}") ) == LBER_ERROR ) {
+ rs->sr_text = "Chaining behavior control: decoding error";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ (void) ber_free( ber, 1 );
+ }
+
+ op->o_chaining = mode | ( ctrl->ldctl_iscritical
+ ? SLAP_CONTROL_CRITICAL
+ : SLAP_CONTROL_NONCRITICAL );
+
+ return LDAP_SUCCESS;
+}
+#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
+
+int
+chain_initialize( void )
+{
+ int rc;
+
+ /* Make sure we don't exceed the bits reserved for userland */
+ config_check_userland( CH_LAST );
+
+ /* olcDatabaseDummy is defined in slapd, and Windows
+ will not let us initialize a struct element with a data pointer
+ from another library, so we have to initialize this element
+ "by hand". */
+ chainocs[1].co_table = olcDatabaseDummy;
+
+#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
+ rc = register_supported_control( LDAP_CONTROL_X_CHAINING_BEHAVIOR,
+ /* SLAP_CTRL_GLOBAL| */ SLAP_CTRL_ACCESS|SLAP_CTRL_HIDE, NULL,
+ ldap_chain_parse_ctrl, &sc_chainingBehavior );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "slapd-chain: "
+ "unable to register chaining behavior control: %d.\n",
+ rc );
+ return rc;
+ }
+#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
+
+ ldapchain.on_bi.bi_type = "chain";
+ ldapchain.on_bi.bi_db_init = ldap_chain_db_init;
+ ldapchain.on_bi.bi_db_config = ldap_chain_db_config;
+ ldapchain.on_bi.bi_db_open = ldap_chain_db_open;
+ ldapchain.on_bi.bi_db_close = ldap_chain_db_close;
+ ldapchain.on_bi.bi_db_destroy = ldap_chain_db_destroy;
+
+ ldapchain.on_bi.bi_connection_destroy = ldap_chain_connection_destroy;
+
+ ldapchain.on_response = ldap_chain_response;
+
+ ldapchain.on_bi.bi_cf_ocs = chainocs;
+
+ rc = config_register_schema( chaincfg, chainocs );
+ if ( rc ) {
+ return rc;
+ }
+
+ return overlay_register( &ldapchain );
+}
+
diff --git a/servers/slapd/back-ldap/compare.c b/servers/slapd/back-ldap/compare.c
new file mode 100644
index 0000000..1e410db
--- /dev/null
+++ b/servers/slapd/back-ldap/compare.c
@@ -0,0 +1,88 @@
+/* compare.c - ldap backend compare function */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2003-2022 The OpenLDAP Foundation.
+ * Portions Copyright 1999-2003 Howard Chu.
+ * Portions Copyright 2000-2003 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 the Howard Chu for inclusion
+ * in OpenLDAP Software and subsequently enhanced by Pierangelo
+ * Masarati.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#include "slap.h"
+#include "back-ldap.h"
+
+int
+ldap_back_compare(
+ Operation *op,
+ SlapReply *rs )
+{
+ ldapinfo_t *li = (ldapinfo_t *)op->o_bd->be_private;
+
+ ldapconn_t *lc = NULL;
+ ber_int_t msgid;
+ ldap_back_send_t retrying = LDAP_BACK_RETRYING;
+ LDAPControl **ctrls = NULL;
+ int rc = LDAP_SUCCESS;
+
+ if ( !ldap_back_dobind( &lc, op, rs, LDAP_BACK_SENDERR ) ) {
+ lc = NULL;
+ goto cleanup;
+ }
+
+retry:
+ ctrls = op->o_ctrls;
+ rc = ldap_back_controls_add( op, rs, lc, &ctrls );
+ if ( rc != LDAP_SUCCESS ) {
+ send_ldap_result( op, rs );
+ goto cleanup;
+ }
+
+ rs->sr_err = ldap_compare_ext( lc->lc_ld, op->o_req_dn.bv_val,
+ op->orc_ava->aa_desc->ad_cname.bv_val,
+ &op->orc_ava->aa_value,
+ ctrls, NULL, &msgid );
+ rc = ldap_back_op_result( lc, op, rs, msgid,
+ li->li_timeout[ SLAP_OP_COMPARE ],
+ ( LDAP_BACK_SENDRESULT | retrying ) );
+ if ( rc == LDAP_UNAVAILABLE && retrying ) {
+ retrying &= ~LDAP_BACK_RETRYING;
+ if ( ldap_back_retry( &lc, op, rs, LDAP_BACK_SENDERR ) ) {
+ /* if the identity changed, there might be need to re-authz */
+ (void)ldap_back_controls_free( op, rs, &ctrls );
+ goto retry;
+ }
+ }
+
+ ldap_pvt_thread_mutex_lock( &li->li_counter_mutex );
+ ldap_pvt_mp_add( li->li_ops_completed[ SLAP_OP_COMPARE ], 1 );
+ ldap_pvt_thread_mutex_unlock( &li->li_counter_mutex );
+
+cleanup:
+ (void)ldap_back_controls_free( op, rs, &ctrls );
+
+ if ( lc != NULL ) {
+ ldap_back_release_conn( li, lc );
+ }
+
+ return rs->sr_err;
+}
diff --git a/servers/slapd/back-ldap/config.c b/servers/slapd/back-ldap/config.c
new file mode 100644
index 0000000..fb97e8e
--- /dev/null
+++ b/servers/slapd/back-ldap/config.c
@@ -0,0 +1,2214 @@
+/* config.c - ldap backend configuration file routine */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2003-2022 The OpenLDAP Foundation.
+ * Portions Copyright 1999-2003 Howard Chu.
+ * Portions Copyright 2000-2003 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 the Howard Chu for inclusion
+ * in OpenLDAP Software and subsequently enhanced by Pierangelo
+ * Masarati.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/ctype.h>
+#include <ac/socket.h>
+
+#include "slap.h"
+#include "slap-config.h"
+#include "back-ldap.h"
+#include "lutil.h"
+#include "ldif.h"
+
+static SLAP_EXTOP_MAIN_FN ldap_back_exop_whoami;
+
+static ConfigDriver ldap_back_cf_gen;
+static ConfigDriver ldap_pbind_cf_gen;
+
+enum {
+ LDAP_BACK_CFG_URI = 1,
+ LDAP_BACK_CFG_TLS,
+ LDAP_BACK_CFG_ACL_BIND,
+ LDAP_BACK_CFG_IDASSERT_AUTHZFROM,
+ LDAP_BACK_CFG_IDASSERT_PASSTHRU,
+ LDAP_BACK_CFG_IDASSERT_BIND,
+ LDAP_BACK_CFG_REBIND,
+ LDAP_BACK_CFG_CHASE,
+ LDAP_BACK_CFG_T_F,
+ LDAP_BACK_CFG_WHOAMI,
+ LDAP_BACK_CFG_TIMEOUT,
+ LDAP_BACK_CFG_IDLE_TIMEOUT,
+ LDAP_BACK_CFG_CONN_TTL,
+ LDAP_BACK_CFG_NETWORK_TIMEOUT,
+ LDAP_BACK_CFG_VERSION,
+ LDAP_BACK_CFG_SINGLECONN,
+ LDAP_BACK_CFG_USETEMP,
+ LDAP_BACK_CFG_CONNPOOLMAX,
+ LDAP_BACK_CFG_CANCEL,
+ LDAP_BACK_CFG_QUARANTINE,
+ LDAP_BACK_CFG_ST_REQUEST,
+ LDAP_BACK_CFG_NOREFS,
+ LDAP_BACK_CFG_NOUNDEFFILTER,
+ LDAP_BACK_CFG_ONERR,
+
+ LDAP_BACK_CFG_KEEPALIVE,
+ LDAP_BACK_CFG_TCP_USER_TIMEOUT,
+
+ LDAP_BACK_CFG_OMIT_UNKNOWN_SCHEMA,
+
+ LDAP_BACK_CFG_LAST
+};
+
+static ConfigTable ldapcfg[] = {
+ { "uri", "uri", 2, 2, 0,
+ ARG_MAGIC|LDAP_BACK_CFG_URI,
+ ldap_back_cf_gen, "( OLcfgDbAt:0.14 "
+ "NAME 'olcDbURI' "
+ "DESC 'URI (list) for remote DSA' "
+ "EQUALITY caseExactMatch "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+ { "tls", "what", 2, 0, 0,
+ ARG_MAGIC|LDAP_BACK_CFG_TLS,
+ ldap_back_cf_gen, "( OLcfgDbAt:3.1 "
+ "NAME 'olcDbStartTLS' "
+ "DESC 'StartTLS' "
+ "EQUALITY caseExactMatch "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+ { "acl-bind", "args", 2, 0, 0,
+ ARG_MAGIC|LDAP_BACK_CFG_ACL_BIND,
+ ldap_back_cf_gen, "( OLcfgDbAt:3.4 "
+ "NAME 'olcDbACLBind' "
+ "DESC 'Remote ACL administrative identity auth bind configuration' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+ { "idassert-bind", "args", 2, 0, 0,
+ ARG_MAGIC|LDAP_BACK_CFG_IDASSERT_BIND,
+ ldap_back_cf_gen, "( OLcfgDbAt:3.7 "
+ "NAME 'olcDbIDAssertBind' "
+ "DESC 'Remote Identity Assertion administrative identity auth bind configuration' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+ { "idassert-authzFrom", "authzRule", 2, 2, 0,
+ ARG_MAGIC|LDAP_BACK_CFG_IDASSERT_AUTHZFROM,
+ ldap_back_cf_gen, "( OLcfgDbAt:3.9 "
+ "NAME 'olcDbIDAssertAuthzFrom' "
+ "DESC 'Remote Identity Assertion authz rules' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString "
+ "X-ORDERED 'VALUES' )",
+ NULL, NULL },
+ { "rebind-as-user", "true|FALSE", 1, 2, 0,
+ ARG_MAGIC|ARG_ON_OFF|LDAP_BACK_CFG_REBIND,
+ ldap_back_cf_gen, "( OLcfgDbAt:3.10 "
+ "NAME 'olcDbRebindAsUser' "
+ "DESC 'Rebind as user' "
+ "EQUALITY booleanMatch "
+ "SYNTAX OMsBoolean "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+ { "chase-referrals", "true|FALSE", 2, 2, 0,
+ ARG_MAGIC|ARG_ON_OFF|LDAP_BACK_CFG_CHASE,
+ ldap_back_cf_gen, "( OLcfgDbAt:3.11 "
+ "NAME 'olcDbChaseReferrals' "
+ "DESC 'Chase referrals' "
+ "EQUALITY booleanMatch "
+ "SYNTAX OMsBoolean "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+ { "t-f-support", "true|FALSE|discover", 2, 2, 0,
+ ARG_MAGIC|LDAP_BACK_CFG_T_F,
+ ldap_back_cf_gen, "( OLcfgDbAt:3.12 "
+ "NAME 'olcDbTFSupport' "
+ "DESC 'Absolute filters support' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+ { "proxy-whoami", "true|FALSE", 1, 2, 0,
+ ARG_MAGIC|ARG_ON_OFF|LDAP_BACK_CFG_WHOAMI,
+ ldap_back_cf_gen, "( OLcfgDbAt:3.13 "
+ "NAME 'olcDbProxyWhoAmI' "
+ "DESC 'Proxy whoAmI exop' "
+ "EQUALITY booleanMatch "
+ "SYNTAX OMsBoolean "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+ { "timeout", "timeout(list)", 2, 0, 0,
+ ARG_MAGIC|LDAP_BACK_CFG_TIMEOUT,
+ ldap_back_cf_gen, "( OLcfgDbAt:3.14 "
+ "NAME 'olcDbTimeout' "
+ "DESC 'Per-operation timeouts' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+ { "idle-timeout", "timeout", 2, 2, 0,
+ ARG_MAGIC|LDAP_BACK_CFG_IDLE_TIMEOUT,
+ ldap_back_cf_gen, "( OLcfgDbAt:3.15 "
+ "NAME 'olcDbIdleTimeout' "
+ "DESC 'connection idle timeout' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+ { "conn-ttl", "ttl", 2, 2, 0,
+ ARG_MAGIC|LDAP_BACK_CFG_CONN_TTL,
+ ldap_back_cf_gen, "( OLcfgDbAt:3.16 "
+ "NAME 'olcDbConnTtl' "
+ "DESC 'connection ttl' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+ { "network-timeout", "timeout", 2, 2, 0,
+ ARG_MAGIC|LDAP_BACK_CFG_NETWORK_TIMEOUT,
+ ldap_back_cf_gen, "( OLcfgDbAt:3.17 "
+ "NAME 'olcDbNetworkTimeout' "
+ "DESC 'connection network timeout' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+ { "protocol-version", "version", 2, 2, 0,
+ ARG_MAGIC|ARG_INT|LDAP_BACK_CFG_VERSION,
+ ldap_back_cf_gen, "( OLcfgDbAt:3.18 "
+ "NAME 'olcDbProtocolVersion' "
+ "DESC 'protocol version' "
+ "EQUALITY integerMatch "
+ "SYNTAX OMsInteger "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+ { "single-conn", "true|FALSE", 2, 2, 0,
+ ARG_MAGIC|ARG_ON_OFF|LDAP_BACK_CFG_SINGLECONN,
+ ldap_back_cf_gen, "( OLcfgDbAt:3.19 "
+ "NAME 'olcDbSingleConn' "
+ "DESC 'cache a single connection per identity' "
+ "EQUALITY booleanMatch "
+ "SYNTAX OMsBoolean "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+ { "cancel", "ABANDON|ignore|exop", 2, 2, 0,
+ ARG_MAGIC|LDAP_BACK_CFG_CANCEL,
+ ldap_back_cf_gen, "( OLcfgDbAt:3.20 "
+ "NAME 'olcDbCancel' "
+ "DESC 'abandon/ignore/exop operations when appropriate' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+ { "quarantine", "retrylist", 2, 2, 0,
+ ARG_MAGIC|LDAP_BACK_CFG_QUARANTINE,
+ ldap_back_cf_gen, "( OLcfgDbAt:3.21 "
+ "NAME 'olcDbQuarantine' "
+ "DESC 'Quarantine database if connection fails and retry according to rule' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+ { "use-temporary-conn", "true|FALSE", 2, 2, 0,
+ ARG_MAGIC|ARG_ON_OFF|LDAP_BACK_CFG_USETEMP,
+ ldap_back_cf_gen, "( OLcfgDbAt:3.22 "
+ "NAME 'olcDbUseTemporaryConn' "
+ "DESC 'Use temporary connections if the cached one is busy' "
+ "EQUALITY booleanMatch "
+ "SYNTAX OMsBoolean "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+ { "conn-pool-max", "<n>", 2, 2, 0,
+ ARG_MAGIC|ARG_INT|LDAP_BACK_CFG_CONNPOOLMAX,
+ ldap_back_cf_gen, "( OLcfgDbAt:3.23 "
+ "NAME 'olcDbConnectionPoolMax' "
+ "DESC 'Max size of privileged connections pool' "
+ "EQUALITY integerMatch "
+ "SYNTAX OMsInteger "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+#ifdef SLAP_CONTROL_X_SESSION_TRACKING
+ { "session-tracking-request", "true|FALSE", 2, 2, 0,
+ ARG_MAGIC|ARG_ON_OFF|LDAP_BACK_CFG_ST_REQUEST,
+ ldap_back_cf_gen, "( OLcfgDbAt:3.24 "
+ "NAME 'olcDbSessionTrackingRequest' "
+ "DESC 'Add session tracking control to proxied requests' "
+ "EQUALITY booleanMatch "
+ "SYNTAX OMsBoolean "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+#endif /* SLAP_CONTROL_X_SESSION_TRACKING */
+ { "norefs", "true|FALSE", 2, 2, 0,
+ ARG_MAGIC|ARG_ON_OFF|LDAP_BACK_CFG_NOREFS,
+ ldap_back_cf_gen, "( OLcfgDbAt:3.25 "
+ "NAME 'olcDbNoRefs' "
+ "DESC 'Do not return search reference responses' "
+ "EQUALITY booleanMatch "
+ "SYNTAX OMsBoolean "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+ { "noundeffilter", "true|FALSE", 2, 2, 0,
+ ARG_MAGIC|ARG_ON_OFF|LDAP_BACK_CFG_NOUNDEFFILTER,
+ ldap_back_cf_gen, "( OLcfgDbAt:3.26 "
+ "NAME 'olcDbNoUndefFilter' "
+ "DESC 'Do not propagate undefined search filters' "
+ "EQUALITY booleanMatch "
+ "SYNTAX OMsBoolean "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+ { "onerr", "CONTINUE|report|stop", 2, 2, 0,
+ ARG_MAGIC|LDAP_BACK_CFG_ONERR,
+ ldap_back_cf_gen, "( OLcfgDbAt:3.108 "
+ "NAME 'olcDbOnErr' "
+ "DESC 'error handling' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+ { "idassert-passThru", "authzRule", 2, 2, 0,
+ ARG_MAGIC|LDAP_BACK_CFG_IDASSERT_PASSTHRU,
+ ldap_back_cf_gen, "( OLcfgDbAt:3.27 "
+ "NAME 'olcDbIDAssertPassThru' "
+ "DESC 'Remote Identity Assertion passthru rules' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString "
+ "X-ORDERED 'VALUES' )",
+ NULL, NULL },
+ { "omit-unknown-schema", "true|FALSE", 2, 2, 0,
+ ARG_MAGIC|ARG_ON_OFF|LDAP_BACK_CFG_OMIT_UNKNOWN_SCHEMA,
+ ldap_back_cf_gen, "( OLcfgDbAt:3.28 "
+ "NAME 'olcDbRemoveUnknownSchema' "
+ "DESC 'Omit unknown schema when returning search results' "
+ "EQUALITY booleanMatch "
+ "SYNTAX OMsBoolean "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+ { "keepalive", "keepalive", 2, 2, 0,
+ ARG_MAGIC|LDAP_BACK_CFG_KEEPALIVE,
+ ldap_back_cf_gen, "( OLcfgDbAt:3.29 "
+ "NAME 'olcDbKeepalive' "
+ "DESC 'TCP keepalive' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+ { "tcp-user-timeout", "milliseconds", 2, 2, 0,
+ ARG_MAGIC|ARG_UINT|LDAP_BACK_CFG_TCP_USER_TIMEOUT,
+ ldap_back_cf_gen, "( OLcfgDbAt:3.30 "
+ "NAME 'olcDbTcpUserTimeout' "
+ "DESC 'TCP User Timeout' "
+ "SYNTAX OMsInteger "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+ { NULL, NULL, 0, 0, 0, ARG_IGNORED,
+ NULL, NULL, NULL, NULL }
+};
+
+static ConfigOCs ldapocs[] = {
+ { "( OLcfgDbOc:3.1 "
+ "NAME 'olcLDAPConfig' "
+ "DESC 'LDAP backend configuration' "
+ "SUP olcDatabaseConfig "
+ "MAY ( olcDbURI "
+ "$ olcDbStartTLS "
+ "$ olcDbACLBind "
+ "$ olcDbIDAssertBind "
+ "$ olcDbIDAssertAuthzFrom "
+ "$ olcDbIDAssertPassThru "
+ "$ olcDbRebindAsUser "
+ "$ olcDbChaseReferrals "
+ "$ olcDbTFSupport "
+ "$ olcDbProxyWhoAmI "
+ "$ olcDbTimeout "
+ "$ olcDbIdleTimeout "
+ "$ olcDbConnTtl "
+ "$ olcDbNetworkTimeout "
+ "$ olcDbProtocolVersion "
+ "$ olcDbSingleConn "
+ "$ olcDbCancel "
+ "$ olcDbQuarantine "
+ "$ olcDbUseTemporaryConn "
+ "$ olcDbConnectionPoolMax "
+#ifdef SLAP_CONTROL_X_SESSION_TRACKING
+ "$ olcDbSessionTrackingRequest "
+#endif /* SLAP_CONTROL_X_SESSION_TRACKING */
+ "$ olcDbNoRefs "
+ "$ olcDbNoUndefFilter "
+ "$ olcDbOnErr "
+ "$ olcDbKeepalive "
+ ") )",
+ Cft_Database, ldapcfg},
+ { NULL, 0, NULL }
+};
+
+static ConfigTable pbindcfg[] = {
+ { "uri", "uri", 2, 2, 0,
+ ARG_MAGIC|LDAP_BACK_CFG_URI,
+ ldap_pbind_cf_gen, "( OLcfgDbAt:0.14 "
+ "NAME 'olcDbURI' "
+ "DESC 'URI (list) for remote DSA' "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+ { "tls", "what", 2, 0, 0,
+ ARG_MAGIC|LDAP_BACK_CFG_TLS,
+ ldap_pbind_cf_gen, "( OLcfgDbAt:3.1 "
+ "NAME 'olcDbStartTLS' "
+ "DESC 'StartTLS' "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+ { "network-timeout", "timeout", 2, 2, 0,
+ ARG_MAGIC|LDAP_BACK_CFG_NETWORK_TIMEOUT,
+ ldap_pbind_cf_gen, "( OLcfgDbAt:3.17 "
+ "NAME 'olcDbNetworkTimeout' "
+ "DESC 'connection network timeout' "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+ { "quarantine", "retrylist", 2, 2, 0,
+ ARG_MAGIC|LDAP_BACK_CFG_QUARANTINE,
+ ldap_pbind_cf_gen, "( OLcfgDbAt:3.21 "
+ "NAME 'olcDbQuarantine' "
+ "DESC 'Quarantine database if connection fails and retry according to rule' "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+ { NULL, NULL, 0, 0, 0, ARG_IGNORED,
+ NULL, NULL, NULL, NULL }
+};
+
+static ConfigOCs pbindocs[] = {
+ { "( OLcfgOvOc:3.3 "
+ "NAME 'olcPBindConfig' "
+ "DESC 'Proxy Bind configuration' "
+ "SUP olcOverlayConfig "
+ "MUST olcDbURI "
+ "MAY ( olcDbStartTLS "
+ "$ olcDbNetworkTimeout "
+ "$ olcDbQuarantine "
+ ") )",
+ Cft_Overlay, pbindcfg},
+ { NULL, 0, NULL }
+};
+
+static slap_verbmasks idassert_mode[] = {
+ { BER_BVC("self"), LDAP_BACK_IDASSERT_SELF },
+ { BER_BVC("anonymous"), LDAP_BACK_IDASSERT_ANONYMOUS },
+ { BER_BVC("none"), LDAP_BACK_IDASSERT_NOASSERT },
+ { BER_BVC("legacy"), LDAP_BACK_IDASSERT_LEGACY },
+ { BER_BVNULL, 0 }
+};
+
+static slap_verbmasks tls_mode[] = {
+ { BER_BVC( "propagate" ), LDAP_BACK_F_TLS_PROPAGATE_MASK },
+ { BER_BVC( "try-propagate" ), LDAP_BACK_F_PROPAGATE_TLS },
+ { BER_BVC( "start" ), LDAP_BACK_F_TLS_USE_MASK },
+ { BER_BVC( "try-start" ), LDAP_BACK_F_USE_TLS },
+ { BER_BVC( "ldaps" ), LDAP_BACK_F_TLS_LDAPS },
+ { BER_BVC( "none" ), LDAP_BACK_F_NONE },
+ { BER_BVNULL, 0 }
+};
+
+static slap_verbmasks t_f_mode[] = {
+ { BER_BVC( "yes" ), LDAP_BACK_F_T_F },
+ { BER_BVC( "discover" ), LDAP_BACK_F_T_F_DISCOVER },
+ { BER_BVC( "no" ), LDAP_BACK_F_NONE },
+ { BER_BVNULL, 0 }
+};
+
+static slap_verbmasks cancel_mode[] = {
+ { BER_BVC( "ignore" ), LDAP_BACK_F_CANCEL_IGNORE },
+ { BER_BVC( "exop" ), LDAP_BACK_F_CANCEL_EXOP },
+ { BER_BVC( "exop-discover" ), LDAP_BACK_F_CANCEL_EXOP_DISCOVER },
+ { BER_BVC( "abandon" ), LDAP_BACK_F_CANCEL_ABANDON },
+ { BER_BVNULL, 0 }
+};
+
+static slap_verbmasks onerr_mode[] = {
+ { BER_BVC( "stop" ), LDAP_BACK_F_ONERR_STOP },
+ { BER_BVC( "report" ), LDAP_BACK_F_ONERR_STOP }, /* same behavior */
+ { BER_BVC( "continue" ), LDAP_BACK_F_NONE },
+ { BER_BVNULL, 0 }
+};
+
+/* see enum in slap.h */
+static slap_cf_aux_table timeout_table[] = {
+ { BER_BVC("bind="), SLAP_OP_BIND * sizeof( time_t ), 'u', 0, NULL },
+ /* unbind makes no sense */
+ { BER_BVC("add="), SLAP_OP_ADD * sizeof( time_t ), 'u', 0, NULL },
+ { BER_BVC("delete="), SLAP_OP_DELETE * sizeof( time_t ), 'u', 0, NULL },
+ { BER_BVC("modrdn="), SLAP_OP_MODRDN * sizeof( time_t ), 'u', 0, NULL },
+ { BER_BVC("modify="), SLAP_OP_MODIFY * sizeof( time_t ), 'u', 0, NULL },
+ { BER_BVC("compare="), SLAP_OP_COMPARE * sizeof( time_t ), 'u', 0, NULL },
+ { BER_BVC("search="), SLAP_OP_SEARCH * sizeof( time_t ), 'u', 0, NULL },
+ /* abandon makes little sense */
+#if 0 /* not implemented yet */
+ { BER_BVC("extended="), SLAP_OP_EXTENDED * sizeof( time_t ), 'u', 0, NULL },
+#endif
+ { BER_BVNULL, 0, 0, 0, NULL }
+};
+
+int
+slap_retry_info_parse(
+ char *in,
+ slap_retry_info_t *ri,
+ char *buf,
+ ber_len_t buflen )
+{
+ char **retrylist = NULL;
+ int rc = 0;
+ int i;
+
+ slap_str2clist( &retrylist, in, " ;" );
+ if ( retrylist == NULL ) {
+ return 1;
+ }
+
+ for ( i = 0; retrylist[ i ] != NULL; i++ )
+ /* count */ ;
+
+ ri->ri_interval = ch_calloc( sizeof( time_t ), i + 1 );
+ ri->ri_num = ch_calloc( sizeof( int ), i + 1 );
+
+ for ( i = 0; retrylist[ i ] != NULL; i++ ) {
+ unsigned long t;
+ char *sep = strchr( retrylist[ i ], ',' );
+
+ if ( sep == NULL ) {
+ snprintf( buf, buflen,
+ "missing comma in retry pattern #%d \"%s\"",
+ i, retrylist[ i ] );
+ rc = 1;
+ goto done;
+ }
+
+ *sep++ = '\0';
+
+ if ( lutil_parse_time( retrylist[ i ], &t ) ) {
+ snprintf( buf, buflen,
+ "unable to parse interval #%d \"%s\"",
+ i, retrylist[ i ] );
+ rc = 1;
+ goto done;
+ }
+ ri->ri_interval[ i ] = (time_t)t;
+
+ if ( strcmp( sep, "+" ) == 0 ) {
+ if ( retrylist[ i + 1 ] != NULL ) {
+ snprintf( buf, buflen,
+ "extra cruft after retry pattern "
+ "#%d \"%s,+\" with \"forever\" mark",
+ i, retrylist[ i ] );
+ rc = 1;
+ goto done;
+ }
+ ri->ri_num[ i ] = SLAP_RETRYNUM_FOREVER;
+
+ } else if ( lutil_atoi( &ri->ri_num[ i ], sep ) ) {
+ snprintf( buf, buflen,
+ "unable to parse retry num #%d \"%s\"",
+ i, sep );
+ rc = 1;
+ goto done;
+ }
+ }
+
+ ri->ri_num[ i ] = SLAP_RETRYNUM_TAIL;
+
+ ri->ri_idx = 0;
+ ri->ri_count = 0;
+ ri->ri_last = (time_t)(-1);
+
+done:;
+ ldap_charray_free( retrylist );
+
+ if ( rc ) {
+ slap_retry_info_destroy( ri );
+ }
+
+ return rc;
+}
+
+int
+slap_retry_info_unparse(
+ slap_retry_info_t *ri,
+ struct berval *bvout )
+{
+ char buf[ BUFSIZ * 2 ],
+ *ptr = buf;
+ int i, len, restlen = (int) sizeof( buf );
+ struct berval bv;
+
+ assert( ri != NULL );
+ assert( bvout != NULL );
+
+ BER_BVZERO( bvout );
+
+ for ( i = 0; ri->ri_num[ i ] != SLAP_RETRYNUM_TAIL; i++ ) {
+ if ( i > 0 ) {
+ if ( --restlen <= 0 ) {
+ return 1;
+ }
+ *ptr++ = ';';
+ }
+
+ if ( lutil_unparse_time( ptr, restlen, ri->ri_interval[i] ) < 0 ) {
+ return 1;
+ }
+ len = (int) strlen( ptr );
+ if ( (restlen -= len + 1) <= 0 ) {
+ return 1;
+ }
+ ptr += len;
+ *ptr++ = ',';
+
+ if ( ri->ri_num[i] == SLAP_RETRYNUM_FOREVER ) {
+ if ( --restlen <= 0 ) {
+ return 1;
+ }
+ *ptr++ = '+';
+
+ } else {
+ len = snprintf( ptr, restlen, "%d", ri->ri_num[i] );
+ if ( (restlen -= len) <= 0 || len < 0 ) {
+ return 1;
+ }
+ ptr += len;
+ }
+ }
+
+ bv.bv_val = buf;
+ bv.bv_len = ptr - buf;
+ ber_dupbv( bvout, &bv );
+
+ return 0;
+}
+
+void
+slap_retry_info_destroy(
+ slap_retry_info_t *ri )
+{
+ assert( ri != NULL );
+
+ assert( ri->ri_interval != NULL );
+ ch_free( ri->ri_interval );
+ ri->ri_interval = NULL;
+
+ assert( ri->ri_num != NULL );
+ ch_free( ri->ri_num );
+ ri->ri_num = NULL;
+}
+
+int
+slap_idassert_authzfrom_parse( ConfigArgs *c, slap_idassert_t *si )
+{
+ struct berval bv;
+ struct berval in;
+ int rc;
+
+ if ( strcmp( c->argv[ 1 ], "*" ) == 0
+ || strcmp( c->argv[ 1 ], "dn:*" ) == 0
+ || strcasecmp( c->argv[ 1 ], "dn.regex:.*" ) == 0 )
+ {
+ if ( si->si_authz != NULL ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "\"%s <authz>\": "
+ "\"%s\" conflicts with existing authz rules",
+ c->argv[ 0 ], c->argv[ 1 ] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+ }
+
+ si->si_flags |= LDAP_BACK_AUTH_AUTHZ_ALL;
+
+ return 0;
+
+ } else if ( ( si->si_flags & LDAP_BACK_AUTH_AUTHZ_ALL ) ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "\"%s <authz>\": "
+ "\"<authz>\" conflicts with \"*\"", c->argv[0] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+ }
+
+ ber_str2bv( c->argv[ 1 ], 0, 0, &in );
+ rc = authzNormalize( 0, NULL, NULL, &in, &bv, NULL );
+ if ( rc != LDAP_SUCCESS ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "\"%s <authz>\": "
+ "invalid syntax", c->argv[0] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+ }
+
+ if ( c->valx == -1 ) {
+ ber_bvarray_add( &si->si_authz, &bv );
+
+ } else {
+ int i = 0;
+ if ( si->si_authz != NULL ) {
+ for ( ; !BER_BVISNULL( &si->si_authz[ i ] ); i++ )
+ ;
+ }
+
+ if ( i <= c->valx ) {
+ ber_bvarray_add( &si->si_authz, &bv );
+
+ } else {
+ BerVarray tmp = ber_memrealloc( si->si_authz,
+ sizeof( struct berval )*( i + 2 ) );
+ if ( tmp == NULL ) {
+ return -1;
+ }
+ si->si_authz = tmp;
+ for ( ; i > c->valx; i-- ) {
+ si->si_authz[ i ] = si->si_authz[ i - 1 ];
+ }
+ si->si_authz[ c->valx ] = bv;
+ }
+ }
+
+ return 0;
+}
+
+static int
+slap_idassert_passthru_parse( ConfigArgs *c, slap_idassert_t *si )
+{
+ struct berval bv;
+ struct berval in;
+ int rc;
+
+ ber_str2bv( c->argv[ 1 ], 0, 0, &in );
+ rc = authzNormalize( 0, NULL, NULL, &in, &bv, NULL );
+ if ( rc != LDAP_SUCCESS ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "\"%s <authz>\": "
+ "invalid syntax", c->argv[0] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+ }
+
+ if ( c->valx == -1 ) {
+ ber_bvarray_add( &si->si_passthru, &bv );
+
+ } else {
+ int i = 0;
+ if ( si->si_passthru != NULL ) {
+ for ( ; !BER_BVISNULL( &si->si_passthru[ i ] ); i++ )
+ ;
+ }
+
+ if ( i <= c->valx ) {
+ ber_bvarray_add( &si->si_passthru, &bv );
+
+ } else {
+ BerVarray tmp = ber_memrealloc( si->si_passthru,
+ sizeof( struct berval )*( i + 2 ) );
+ if ( tmp == NULL ) {
+ return -1;
+ }
+ si->si_passthru = tmp;
+ for ( ; i > c->valx; i-- ) {
+ si->si_passthru[ i ] = si->si_passthru[ i - 1 ];
+ }
+ si->si_passthru[ c->valx ] = bv;
+ }
+ }
+
+ return 0;
+}
+
+int
+slap_idassert_parse( ConfigArgs *c, slap_idassert_t *si )
+{
+ int i;
+
+ /* set default */
+ si->si_mode = LDAP_BACK_IDASSERT_LEGACY;
+
+ for ( i = 1; i < c->argc; i++ ) {
+ if ( strncasecmp( c->argv[ i ], "mode=", STRLENOF( "mode=" ) ) == 0 ) {
+ char *argvi = c->argv[ i ] + STRLENOF( "mode=" );
+ int j;
+
+ j = verb_to_mask( argvi, idassert_mode );
+ if ( BER_BVISNULL( &idassert_mode[ j ].word ) ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "\"%s <args>\": "
+ "unknown mode \"%s\"",
+ c->argv[0], argvi );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+ }
+
+ si->si_mode = idassert_mode[ j ].mask;
+
+ } else if ( strncasecmp( c->argv[ i ], "authz=", STRLENOF( "authz=" ) ) == 0 ) {
+ char *argvi = c->argv[ i ] + STRLENOF( "authz=" );
+
+ if ( strcasecmp( argvi, "native" ) == 0 ) {
+ if ( si->si_bc.sb_method != LDAP_AUTH_SASL ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "\"%s <args>\": "
+ "authz=\"native\" incompatible "
+ "with auth method", c->argv[0] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+ }
+ si->si_flags |= LDAP_BACK_AUTH_NATIVE_AUTHZ;
+
+ } else if ( strcasecmp( argvi, "proxyAuthz" ) == 0 ) {
+ si->si_flags &= ~LDAP_BACK_AUTH_NATIVE_AUTHZ;
+
+ } else {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "\"%s <args>\": "
+ "unknown authz \"%s\"",
+ c->argv[0], argvi );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+ }
+
+ } else if ( strncasecmp( c->argv[ i ], "flags=", STRLENOF( "flags=" ) ) == 0 ) {
+ char *argvi = c->argv[ i ] + STRLENOF( "flags=" );
+ char **flags = ldap_str2charray( argvi, "," );
+ int j, err = 0;
+
+ if ( flags == NULL ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "\"%s <args>\": "
+ "unable to parse flags \"%s\"",
+ c->argv[0], argvi );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+ }
+
+ for ( j = 0; flags[ j ] != NULL; j++ ) {
+
+ if ( strcasecmp( flags[ j ], "override" ) == 0 ) {
+ si->si_flags |= LDAP_BACK_AUTH_OVERRIDE;
+
+ } else if ( strcasecmp( flags[ j ], "prescriptive" ) == 0 ) {
+ si->si_flags |= LDAP_BACK_AUTH_PRESCRIPTIVE;
+
+ } else if ( strcasecmp( flags[ j ], "non-prescriptive" ) == 0 ) {
+ si->si_flags &= ( ~LDAP_BACK_AUTH_PRESCRIPTIVE );
+
+ } else if ( strcasecmp( flags[ j ], "obsolete-proxy-authz" ) == 0 ) {
+ if ( si->si_flags & LDAP_BACK_AUTH_OBSOLETE_ENCODING_WORKAROUND ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "\"%s <args>\": "
+ "\"obsolete-proxy-authz\" flag "
+ "incompatible with previously issued \"obsolete-encoding-workaround\" flag.",
+ c->argv[0] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ err = 1;
+ break;
+
+ } else {
+ si->si_flags |= LDAP_BACK_AUTH_OBSOLETE_PROXY_AUTHZ;
+ }
+
+ } else if ( strcasecmp( flags[ j ], "obsolete-encoding-workaround" ) == 0 ) {
+ if ( si->si_flags & LDAP_BACK_AUTH_OBSOLETE_PROXY_AUTHZ ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "\"%s <args>\": "
+ "\"obsolete-encoding-workaround\" flag "
+ "incompatible with previously issued \"obsolete-proxy-authz\" flag.",
+ c->argv[0] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ err = 1;
+ break;
+
+ } else {
+ si->si_flags |= LDAP_BACK_AUTH_OBSOLETE_ENCODING_WORKAROUND;
+ }
+
+ } else if ( strcasecmp( flags[ j ], "proxy-authz-critical" ) == 0 ) {
+ si->si_flags |= LDAP_BACK_AUTH_PROXYAUTHZ_CRITICAL;
+
+ } else if ( strcasecmp( flags[ j ], "proxy-authz-non-critical" ) == 0 ) {
+ si->si_flags &= ~LDAP_BACK_AUTH_PROXYAUTHZ_CRITICAL;
+
+ } else if ( strcasecmp( flags[ j ], "dn-none" ) == 0 ) {
+ si->si_flags &= ~LDAP_BACK_AUTH_DN_MASK;
+
+ } else if ( strcasecmp( flags[ j ], "dn-authzid" ) == 0 ) {
+ si->si_flags &= ~LDAP_BACK_AUTH_DN_MASK;
+ si->si_flags |= LDAP_BACK_AUTH_DN_AUTHZID;
+
+ } else if ( strcasecmp( flags[ j ], "dn-whoami" ) == 0 ) {
+ si->si_flags &= ~LDAP_BACK_AUTH_DN_MASK;
+ si->si_flags |= LDAP_BACK_AUTH_DN_WHOAMI;
+
+ } else {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "\"%s <args>\": "
+ "unknown flag \"%s\"",
+ c->argv[0], flags[ j ] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ err = 1;
+ break;
+ }
+ }
+
+ ldap_charray_free( flags );
+ if ( err ) {
+ return 1;
+ }
+
+ } else if ( bindconf_parse( c->argv[ i ], &si->si_bc ) ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "\"%s <args>\": "
+ "unable to parse field \"%s\"",
+ c->argv[0], c->argv[ i ] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+ }
+ }
+
+ if ( si->si_bc.sb_method == LDAP_AUTH_SIMPLE ) {
+ if ( BER_BVISNULL( &si->si_bc.sb_binddn )
+ || BER_BVISNULL( &si->si_bc.sb_cred ) )
+ {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "\"%s <args>\": "
+ "SIMPLE needs \"binddn\" and \"credentials\"", c->argv[0] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+ }
+
+ } else if ( si->si_bc.sb_method == LDAP_AUTH_SASL ) {
+ if ( BER_BVISNULL( &si->si_bc.sb_binddn ) &&
+ !(si->si_flags & LDAP_BACK_AUTH_DN_MASK) )
+ {
+ static struct berval authid = BER_BVC("cn=auth");
+ ber_dupbv( &si->si_bc.sb_binddn, &authid );
+ }
+ }
+
+ bindconf_tls_defaults( &si->si_bc );
+#ifdef HAVE_TLS
+ if ( si->si_bc.sb_tls_ctx ) {
+ ldap_pvt_tls_ctx_free( si->si_bc.sb_tls_ctx );
+ si->si_bc.sb_tls_ctx = NULL;
+ }
+#endif
+
+ return 0;
+}
+
+/* NOTE: temporary, until back-meta is ported to back-config */
+int
+slap_idassert_passthru_parse_cf( const char *fname, int lineno, const char *arg, slap_idassert_t *si )
+{
+ ConfigArgs c = { 0 };
+ char *argv[ 3 ];
+
+ snprintf( c.log, sizeof( c.log ), "%s: line %d", fname, lineno );
+ c.argc = 2;
+ c.argv = argv;
+ argv[ 0 ] = "idassert-passThru";
+ argv[ 1 ] = (char *)arg;
+ argv[ 2 ] = NULL;
+
+ return slap_idassert_passthru_parse( &c, si );
+}
+
+static int
+ldap_back_cf_gen( ConfigArgs *c )
+{
+ ldapinfo_t *li = ( ldapinfo_t * )c->be->be_private;
+ int rc = 0;
+ int i;
+
+ if ( c->op == SLAP_CONFIG_EMIT ) {
+ struct berval bv = BER_BVNULL;
+
+ if ( li == NULL ) {
+ return 1;
+ }
+
+ switch( c->type ) {
+ case LDAP_BACK_CFG_URI:
+ if ( li->li_uri != NULL ) {
+ struct berval bv, bv2;
+
+ ber_str2bv( li->li_uri, 0, 0, &bv );
+ bv2.bv_len = bv.bv_len + STRLENOF( "\"\"" );
+ bv2.bv_val = ch_malloc( bv2.bv_len + 1 );
+ snprintf( bv2.bv_val, bv2.bv_len + 1,
+ "\"%s\"", bv.bv_val );
+ ber_bvarray_add( &c->rvalue_vals, &bv2 );
+
+ } else {
+ rc = 1;
+ }
+ break;
+
+ case LDAP_BACK_CFG_TLS: {
+ struct berval bc = BER_BVNULL, bv2;
+ enum_to_verb( tls_mode, ( li->li_flags & LDAP_BACK_F_TLS_MASK ), &bv );
+ assert( !BER_BVISNULL( &bv ) );
+ bindconf_tls_unparse( &li->li_tls, &bc );
+
+ if ( !BER_BVISEMPTY( &bc )) {
+ bv2.bv_len = bv.bv_len + bc.bv_len + 1;
+ bv2.bv_val = ch_malloc( bv2.bv_len + 1 );
+ strcpy( bv2.bv_val, bv.bv_val );
+ bv2.bv_val[bv.bv_len] = ' ';
+ strcpy( &bv2.bv_val[bv.bv_len + 1], bc.bv_val );
+ ber_bvarray_add( &c->rvalue_vals, &bv2 );
+
+ } else {
+ value_add_one( &c->rvalue_vals, &bv );
+ }
+ ber_memfree( bc.bv_val );
+ }
+ break;
+
+ case LDAP_BACK_CFG_ACL_BIND: {
+ int i;
+
+ if ( li->li_acl_authmethod == LDAP_AUTH_NONE ) {
+ return 1;
+ }
+
+ bindconf_unparse( &li->li_acl, &bv );
+
+ for ( i = 0; isspace( (unsigned char) bv.bv_val[ i ] ); i++ )
+ /* count spaces */ ;
+
+ if ( i ) {
+ bv.bv_len -= i;
+ AC_MEMCPY( bv.bv_val, &bv.bv_val[ i ],
+ bv.bv_len + 1 );
+ }
+
+ ber_bvarray_add( &c->rvalue_vals, &bv );
+ break;
+ }
+
+ case LDAP_BACK_CFG_IDASSERT_AUTHZFROM:
+ case LDAP_BACK_CFG_IDASSERT_PASSTHRU: {
+ BerVarray *bvp;
+ int i;
+ struct berval bv = BER_BVNULL;
+ char buf[SLAP_TEXT_BUFLEN];
+
+ switch ( c->type ) {
+ case LDAP_BACK_CFG_IDASSERT_AUTHZFROM: bvp = &li->li_idassert_authz; break;
+ case LDAP_BACK_CFG_IDASSERT_PASSTHRU: bvp = &li->li_idassert_passthru; break;
+ default: assert( 0 ); break;
+ }
+
+ if ( *bvp == NULL ) {
+ if ( bvp == &li->li_idassert_authz
+ && ( li->li_idassert_flags & LDAP_BACK_AUTH_AUTHZ_ALL ) )
+ {
+ BER_BVSTR( &bv, "*" );
+ value_add_one( &c->rvalue_vals, &bv );
+
+ } else {
+ rc = 1;
+ }
+ break;
+ }
+
+ for ( i = 0; !BER_BVISNULL( &((*bvp)[ i ]) ); i++ ) {
+ char *ptr;
+ int len = snprintf( buf, sizeof( buf ), SLAP_X_ORDERED_FMT, i );
+ bv.bv_len = ((*bvp)[ i ]).bv_len + len;
+ bv.bv_val = ber_memrealloc( bv.bv_val, bv.bv_len + 1 );
+ ptr = bv.bv_val;
+ ptr = lutil_strcopy( ptr, buf );
+ ptr = lutil_strncopy( ptr, ((*bvp)[ i ]).bv_val, ((*bvp)[ i ]).bv_len );
+ value_add_one( &c->rvalue_vals, &bv );
+ }
+ if ( bv.bv_val ) {
+ ber_memfree( bv.bv_val );
+ }
+ break;
+ }
+
+ case LDAP_BACK_CFG_IDASSERT_BIND: {
+ int i;
+ struct berval bc = BER_BVNULL;
+ char *ptr;
+
+ if ( li->li_idassert_authmethod == LDAP_AUTH_NONE ) {
+ return 1;
+ }
+
+ if ( li->li_idassert_authmethod != LDAP_AUTH_NONE ) {
+ ber_len_t len;
+
+ switch ( li->li_idassert_mode ) {
+ case LDAP_BACK_IDASSERT_OTHERID:
+ case LDAP_BACK_IDASSERT_OTHERDN:
+ break;
+
+ default: {
+ struct berval mode = BER_BVNULL;
+
+ enum_to_verb( idassert_mode, li->li_idassert_mode, &mode );
+ if ( BER_BVISNULL( &mode ) ) {
+ /* there's something wrong... */
+ assert( 0 );
+ rc = 1;
+
+ } else {
+ bv.bv_len = STRLENOF( "mode=" ) + mode.bv_len;
+ bv.bv_val = ch_malloc( bv.bv_len + 1 );
+
+ ptr = lutil_strcopy( bv.bv_val, "mode=" );
+ ptr = lutil_strcopy( ptr, mode.bv_val );
+ }
+ break;
+ }
+ }
+
+ if ( li->li_idassert_flags & LDAP_BACK_AUTH_NATIVE_AUTHZ ) {
+ len = bv.bv_len + STRLENOF( "authz=native" );
+
+ if ( !BER_BVISEMPTY( &bv ) ) {
+ len += STRLENOF( " " );
+ }
+
+ bv.bv_val = ch_realloc( bv.bv_val, len + 1 );
+
+ ptr = &bv.bv_val[ bv.bv_len ];
+
+ if ( !BER_BVISEMPTY( &bv ) ) {
+ ptr = lutil_strcopy( ptr, " " );
+ }
+
+ (void)lutil_strcopy( ptr, "authz=native" );
+ }
+
+ len = bv.bv_len + STRLENOF( "flags=non-prescriptive,override,obsolete-encoding-workaround,proxy-authz-non-critical,dn-authzid" );
+ /* flags */
+ if ( !BER_BVISEMPTY( &bv ) ) {
+ len += STRLENOF( " " );
+ }
+
+ bv.bv_val = ch_realloc( bv.bv_val, len + 1 );
+
+ ptr = &bv.bv_val[ bv.bv_len ];
+
+ if ( !BER_BVISEMPTY( &bv ) ) {
+ ptr = lutil_strcopy( ptr, " " );
+ }
+
+ ptr = lutil_strcopy( ptr, "flags=" );
+
+ if ( li->li_idassert_flags & LDAP_BACK_AUTH_PRESCRIPTIVE ) {
+ ptr = lutil_strcopy( ptr, "prescriptive" );
+ } else {
+ ptr = lutil_strcopy( ptr, "non-prescriptive" );
+ }
+
+ if ( li->li_idassert_flags & LDAP_BACK_AUTH_OVERRIDE ) {
+ ptr = lutil_strcopy( ptr, ",override" );
+ }
+
+ if ( li->li_idassert_flags & LDAP_BACK_AUTH_OBSOLETE_PROXY_AUTHZ ) {
+ ptr = lutil_strcopy( ptr, ",obsolete-proxy-authz" );
+
+ } else if ( li->li_idassert_flags & LDAP_BACK_AUTH_OBSOLETE_ENCODING_WORKAROUND ) {
+ ptr = lutil_strcopy( ptr, ",obsolete-encoding-workaround" );
+ }
+
+ if ( li->li_idassert_flags & LDAP_BACK_AUTH_PROXYAUTHZ_CRITICAL ) {
+ ptr = lutil_strcopy( ptr, ",proxy-authz-critical" );
+
+ } else {
+ ptr = lutil_strcopy( ptr, ",proxy-authz-non-critical" );
+ }
+
+ switch ( li->li_idassert_flags & LDAP_BACK_AUTH_DN_MASK ) {
+ case LDAP_BACK_AUTH_DN_AUTHZID:
+ ptr = lutil_strcopy( ptr, ",dn-authzid" );
+ break;
+
+ case LDAP_BACK_AUTH_DN_WHOAMI:
+ ptr = lutil_strcopy( ptr, ",dn-whoami" );
+ break;
+
+ default:
+#if 0 /* implicit */
+ ptr = lutil_strcopy( ptr, ",dn-none" );
+#endif
+ break;
+ }
+
+ bv.bv_len = ( ptr - bv.bv_val );
+ /* end-of-flags */
+ }
+
+ bindconf_unparse( &li->li_idassert.si_bc, &bc );
+
+ if ( !BER_BVISNULL( &bv ) ) {
+ ber_len_t len = bv.bv_len + bc.bv_len;
+
+ bv.bv_val = ch_realloc( bv.bv_val, len + 1 );
+
+ assert( bc.bv_val[ 0 ] == ' ' );
+
+ ptr = lutil_strcopy( &bv.bv_val[ bv.bv_len ], bc.bv_val );
+ free( bc.bv_val );
+ bv.bv_len = ptr - bv.bv_val;
+
+ } else {
+ for ( i = 0; isspace( (unsigned char) bc.bv_val[ i ] ); i++ )
+ /* count spaces */ ;
+
+ if ( i ) {
+ bc.bv_len -= i;
+ AC_MEMCPY( bc.bv_val, &bc.bv_val[ i ], bc.bv_len + 1 );
+ }
+
+ bv = bc;
+ }
+
+ ber_bvarray_add( &c->rvalue_vals, &bv );
+
+ break;
+ }
+
+ case LDAP_BACK_CFG_REBIND:
+ c->value_int = LDAP_BACK_SAVECRED( li );
+ break;
+
+ case LDAP_BACK_CFG_CHASE:
+ c->value_int = LDAP_BACK_CHASE_REFERRALS( li );
+ break;
+
+ case LDAP_BACK_CFG_T_F:
+ enum_to_verb( t_f_mode, (li->li_flags & LDAP_BACK_F_T_F_MASK2), &bv );
+ if ( BER_BVISNULL( &bv ) ) {
+ /* there's something wrong... */
+ assert( 0 );
+ rc = 1;
+
+ } else {
+ value_add_one( &c->rvalue_vals, &bv );
+ }
+ break;
+
+ case LDAP_BACK_CFG_WHOAMI:
+ c->value_int = LDAP_BACK_PROXY_WHOAMI( li );
+ break;
+
+ case LDAP_BACK_CFG_TIMEOUT:
+ BER_BVZERO( &bv );
+
+ for ( i = 0; i < SLAP_OP_LAST; i++ ) {
+ if ( li->li_timeout[ i ] != 0 ) {
+ break;
+ }
+ }
+
+ if ( i == SLAP_OP_LAST ) {
+ return 1;
+ }
+
+ slap_cf_aux_table_unparse( li->li_timeout, &bv, timeout_table );
+
+ if ( BER_BVISNULL( &bv ) ) {
+ return 1;
+ }
+
+ for ( i = 0; isspace( (unsigned char) bv.bv_val[ i ] ); i++ )
+ /* count spaces */ ;
+
+ if ( i ) {
+ bv.bv_len -= i;
+ AC_MEMCPY( bv.bv_val, &bv.bv_val[ i ],
+ bv.bv_len + 1 );
+ }
+
+ ber_bvarray_add( &c->rvalue_vals, &bv );
+ break;
+
+ case LDAP_BACK_CFG_IDLE_TIMEOUT: {
+ char buf[ SLAP_TEXT_BUFLEN ];
+
+ if ( li->li_idle_timeout == 0 ) {
+ return 1;
+ }
+
+ lutil_unparse_time( buf, sizeof( buf ), li->li_idle_timeout );
+ ber_str2bv( buf, 0, 0, &bv );
+ value_add_one( &c->rvalue_vals, &bv );
+ } break;
+
+ case LDAP_BACK_CFG_CONN_TTL: {
+ char buf[ SLAP_TEXT_BUFLEN ];
+
+ if ( li->li_conn_ttl == 0 ) {
+ return 1;
+ }
+
+ lutil_unparse_time( buf, sizeof( buf ), li->li_conn_ttl );
+ ber_str2bv( buf, 0, 0, &bv );
+ value_add_one( &c->rvalue_vals, &bv );
+ } break;
+
+ case LDAP_BACK_CFG_NETWORK_TIMEOUT: {
+ char buf[ SLAP_TEXT_BUFLEN ];
+
+ if ( li->li_network_timeout == 0 ) {
+ return 1;
+ }
+
+ lutil_unparse_time( buf, sizeof( buf ), li->li_network_timeout );
+ ber_str2bv( buf, 0, 0, &bv );
+ value_add_one( &c->rvalue_vals, &bv );
+ } break;
+
+ case LDAP_BACK_CFG_VERSION:
+ if ( li->li_version == 0 ) {
+ return 1;
+ }
+
+ c->value_int = li->li_version;
+ break;
+
+ case LDAP_BACK_CFG_SINGLECONN:
+ c->value_int = LDAP_BACK_SINGLECONN( li );
+ break;
+
+ case LDAP_BACK_CFG_USETEMP:
+ c->value_int = LDAP_BACK_USE_TEMPORARIES( li );
+ break;
+
+ case LDAP_BACK_CFG_CONNPOOLMAX:
+ c->value_int = li->li_conn_priv_max;
+ break;
+
+ case LDAP_BACK_CFG_CANCEL: {
+ slap_mask_t mask = LDAP_BACK_F_CANCEL_MASK2;
+
+ if ( LDAP_BACK_CANCEL_DISCOVER( li ) ) {
+ mask &= ~LDAP_BACK_F_CANCEL_EXOP;
+ }
+ enum_to_verb( cancel_mode, (li->li_flags & mask), &bv );
+ if ( BER_BVISNULL( &bv ) ) {
+ /* there's something wrong... */
+ assert( 0 );
+ rc = 1;
+
+ } else {
+ value_add_one( &c->rvalue_vals, &bv );
+ }
+ } break;
+
+ case LDAP_BACK_CFG_QUARANTINE:
+ if ( !LDAP_BACK_QUARANTINE( li ) ) {
+ rc = 1;
+ break;
+ }
+
+ rc = slap_retry_info_unparse( &li->li_quarantine, &bv );
+ if ( rc == 0 ) {
+ ber_bvarray_add( &c->rvalue_vals, &bv );
+ }
+ break;
+
+#ifdef SLAP_CONTROL_X_SESSION_TRACKING
+ case LDAP_BACK_CFG_ST_REQUEST:
+ c->value_int = LDAP_BACK_ST_REQUEST( li );
+ break;
+#endif /* SLAP_CONTROL_X_SESSION_TRACKING */
+
+ case LDAP_BACK_CFG_NOREFS:
+ c->value_int = LDAP_BACK_NOREFS( li );
+ break;
+
+ case LDAP_BACK_CFG_NOUNDEFFILTER:
+ c->value_int = LDAP_BACK_NOUNDEFFILTER( li );
+ break;
+
+ case LDAP_BACK_CFG_OMIT_UNKNOWN_SCHEMA:
+ c->value_int = LDAP_BACK_OMIT_UNKNOWN_SCHEMA( li );
+ break;
+
+ case LDAP_BACK_CFG_ONERR:
+ enum_to_verb( onerr_mode, li->li_flags & LDAP_BACK_F_ONERR_STOP, &bv );
+ if ( BER_BVISNULL( &bv )) {
+ rc = 1;
+ } else {
+ value_add_one( &c->rvalue_vals, &bv );
+ }
+ break;
+
+ case LDAP_BACK_CFG_KEEPALIVE: {
+ struct berval bv;
+ char buf[AC_LINE_MAX];
+ bv.bv_len = AC_LINE_MAX;
+ bv.bv_val = &buf[0];
+ slap_keepalive_parse(&bv, &li->li_tls.sb_keepalive, 0, 0, 1);
+ value_add_one( &c->rvalue_vals, &bv );
+ break;
+ }
+
+ case LDAP_BACK_CFG_TCP_USER_TIMEOUT:
+ c->value_uint = li->li_tls.sb_tcp_user_timeout;
+ break;
+
+ default:
+ /* FIXME: we need to handle all... */
+ assert( 0 );
+ break;
+ }
+ return rc;
+
+ } else if ( c->op == LDAP_MOD_DELETE ) {
+ switch( c->type ) {
+ case LDAP_BACK_CFG_URI:
+ if ( li->li_uri != NULL ) {
+ ch_free( li->li_uri );
+ li->li_uri = NULL;
+
+ assert( li->li_bvuri != NULL );
+ ber_bvarray_free( li->li_bvuri );
+ li->li_bvuri = NULL;
+ }
+
+ /* better cleanup the cached connections... */
+ /* NOTE: don't worry about locking: if we got here,
+ * other threads are suspended. */
+ if ( li->li_conninfo.lai_tree != NULL ) {
+ ldap_tavl_free( li->li_conninfo.lai_tree, ldap_back_conn_free );
+ li->li_conninfo.lai_tree = NULL;
+ }
+
+ break;
+
+ case LDAP_BACK_CFG_TLS:
+ rc = 1;
+ break;
+
+ case LDAP_BACK_CFG_ACL_BIND:
+ bindconf_free( &li->li_acl );
+ break;
+
+ case LDAP_BACK_CFG_IDASSERT_AUTHZFROM:
+ case LDAP_BACK_CFG_IDASSERT_PASSTHRU: {
+ BerVarray *bvp;
+
+ switch ( c->type ) {
+ case LDAP_BACK_CFG_IDASSERT_AUTHZFROM: bvp = &li->li_idassert_authz; break;
+ case LDAP_BACK_CFG_IDASSERT_PASSTHRU: bvp = &li->li_idassert_passthru; break;
+ default: assert( 0 ); break;
+ }
+
+ if ( c->valx < 0 ) {
+ if ( *bvp != NULL ) {
+ ber_bvarray_free( *bvp );
+ *bvp = NULL;
+ }
+
+ } else {
+ int i;
+
+ if ( *bvp == NULL ) {
+ rc = 1;
+ break;
+ }
+
+ for ( i = 0; !BER_BVISNULL( &((*bvp)[ i ]) ); i++ )
+ ;
+
+ if ( i >= c->valx ) {
+ rc = 1;
+ break;
+ }
+ ber_memfree( ((*bvp)[ c->valx ]).bv_val );
+ for ( i = c->valx; !BER_BVISNULL( &((*bvp)[ i + 1 ]) ); i++ ) {
+ (*bvp)[ i ] = (*bvp)[ i + 1 ];
+ }
+ BER_BVZERO( &((*bvp)[ i ]) );
+ }
+ } break;
+
+ case LDAP_BACK_CFG_IDASSERT_BIND:
+ bindconf_free( &li->li_idassert.si_bc );
+ memset( &li->li_idassert, 0, sizeof( slap_idassert_t ) );
+ break;
+
+ case LDAP_BACK_CFG_REBIND:
+ case LDAP_BACK_CFG_CHASE:
+ case LDAP_BACK_CFG_T_F:
+ case LDAP_BACK_CFG_WHOAMI:
+ case LDAP_BACK_CFG_CANCEL:
+ rc = 1;
+ break;
+
+ case LDAP_BACK_CFG_TIMEOUT:
+ for ( i = 0; i < SLAP_OP_LAST; i++ ) {
+ li->li_timeout[ i ] = 0;
+ }
+ break;
+
+ case LDAP_BACK_CFG_IDLE_TIMEOUT:
+ li->li_idle_timeout = 0;
+ break;
+
+ case LDAP_BACK_CFG_CONN_TTL:
+ li->li_conn_ttl = 0;
+ break;
+
+ case LDAP_BACK_CFG_NETWORK_TIMEOUT:
+ li->li_network_timeout = 0;
+ break;
+
+ case LDAP_BACK_CFG_VERSION:
+ li->li_version = 0;
+ break;
+
+ case LDAP_BACK_CFG_SINGLECONN:
+ li->li_flags &= ~LDAP_BACK_F_SINGLECONN;
+ break;
+
+ case LDAP_BACK_CFG_USETEMP:
+ li->li_flags &= ~LDAP_BACK_F_USE_TEMPORARIES;
+ break;
+
+ case LDAP_BACK_CFG_CONNPOOLMAX:
+ li->li_conn_priv_max = LDAP_BACK_CONN_PRIV_MIN;
+ break;
+
+ case LDAP_BACK_CFG_QUARANTINE:
+ if ( !LDAP_BACK_QUARANTINE( li ) ) {
+ break;
+ }
+
+ slap_retry_info_destroy( &li->li_quarantine );
+ ldap_pvt_thread_mutex_destroy( &li->li_quarantine_mutex );
+ li->li_isquarantined = 0;
+ li->li_flags &= ~LDAP_BACK_F_QUARANTINE;
+ break;
+
+#ifdef SLAP_CONTROL_X_SESSION_TRACKING
+ case LDAP_BACK_CFG_ST_REQUEST:
+ li->li_flags &= ~LDAP_BACK_F_ST_REQUEST;
+ break;
+#endif /* SLAP_CONTROL_X_SESSION_TRACKING */
+
+ case LDAP_BACK_CFG_NOREFS:
+ li->li_flags &= ~LDAP_BACK_F_NOREFS;
+ break;
+
+ case LDAP_BACK_CFG_NOUNDEFFILTER:
+ li->li_flags &= ~LDAP_BACK_F_NOUNDEFFILTER;
+ break;
+
+ case LDAP_BACK_CFG_OMIT_UNKNOWN_SCHEMA:
+ li->li_flags &= ~LDAP_BACK_F_OMIT_UNKNOWN_SCHEMA;
+ break;
+
+ case LDAP_BACK_CFG_ONERR:
+ li->li_flags &= ~LDAP_BACK_F_ONERR_STOP;
+ break;
+
+ case LDAP_BACK_CFG_KEEPALIVE:
+ li->li_tls.sb_keepalive.sk_idle = 0;
+ li->li_tls.sb_keepalive.sk_probes = 0;
+ li->li_tls.sb_keepalive.sk_interval = 0;
+ break;
+
+ case LDAP_BACK_CFG_TCP_USER_TIMEOUT:
+ li->li_tls.sb_tcp_user_timeout = 0;
+ break;
+
+ default:
+ /* FIXME: we need to handle all... */
+ assert( 0 );
+ break;
+ }
+ return rc;
+
+ }
+
+ switch( c->type ) {
+ case LDAP_BACK_CFG_URI: {
+ LDAPURLDesc *tmpludp, *lud;
+ char **urllist = NULL;
+ int urlrc = LDAP_URL_SUCCESS, i;
+
+ if ( li->li_uri != NULL ) {
+ ch_free( li->li_uri );
+ li->li_uri = NULL;
+
+ assert( li->li_bvuri != NULL );
+ ber_bvarray_free( li->li_bvuri );
+ li->li_bvuri = NULL;
+ }
+
+ /* PARANOID: DN and more are not required nor allowed */
+ urlrc = ldap_url_parselist_ext( &lud, c->argv[ 1 ], ", \t", LDAP_PVT_URL_PARSE_NONE );
+ if ( urlrc != LDAP_URL_SUCCESS ) {
+ char *why;
+
+ switch ( urlrc ) {
+ case LDAP_URL_ERR_MEM:
+ why = "no memory";
+ break;
+ case LDAP_URL_ERR_PARAM:
+ why = "parameter is bad";
+ break;
+ case LDAP_URL_ERR_BADSCHEME:
+ why = "URL doesn't begin with \"[c]ldap[si]://\"";
+ break;
+ case LDAP_URL_ERR_BADENCLOSURE:
+ why = "URL is missing trailing \">\"";
+ break;
+ case LDAP_URL_ERR_BADURL:
+ why = "URL is bad";
+ break;
+ case LDAP_URL_ERR_BADHOST:
+ why = "host/port is bad";
+ break;
+ case LDAP_URL_ERR_BADATTRS:
+ why = "bad (or missing) attributes";
+ break;
+ case LDAP_URL_ERR_BADSCOPE:
+ why = "scope string is invalid (or missing)";
+ break;
+ case LDAP_URL_ERR_BADFILTER:
+ why = "bad or missing filter";
+ break;
+ case LDAP_URL_ERR_BADEXTS:
+ why = "bad or missing extensions";
+ break;
+ default:
+ why = "unknown reason";
+ break;
+ }
+ snprintf( c->cr_msg, sizeof( c->cr_msg),
+ "unable to parse uri \"%s\" "
+ "in \"uri <uri>\" line: %s",
+ c->value_string, why );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ urlrc = 1;
+ goto done_url;
+ }
+
+ for ( i = 0, tmpludp = lud;
+ tmpludp;
+ i++, tmpludp = tmpludp->lud_next )
+ {
+ if ( ( tmpludp->lud_dn != NULL
+ && tmpludp->lud_dn[0] != '\0' )
+ || tmpludp->lud_attrs != NULL
+ /* || tmpludp->lud_scope != LDAP_SCOPE_DEFAULT */
+ || tmpludp->lud_filter != NULL
+ || tmpludp->lud_exts != NULL )
+ {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "warning, only protocol, "
+ "host and port allowed "
+ "in \"uri <uri>\" statement "
+ "for uri #%d of \"%s\"",
+ i, c->argv[ 1 ] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ }
+ }
+
+ for ( i = 0, tmpludp = lud;
+ tmpludp;
+ i++, tmpludp = tmpludp->lud_next )
+ /* just count */
+ ;
+ urllist = ch_calloc( sizeof( char * ), i + 1 );
+
+ for ( i = 0, tmpludp = lud;
+ tmpludp;
+ i++, tmpludp = tmpludp->lud_next )
+ {
+ LDAPURLDesc tmplud;
+
+ tmplud = *tmpludp;
+ tmplud.lud_dn = "";
+ tmplud.lud_attrs = NULL;
+ tmplud.lud_filter = NULL;
+ if ( !ldap_is_ldapi_url( tmplud.lud_scheme ) ) {
+ tmplud.lud_exts = NULL;
+ tmplud.lud_crit_exts = 0;
+ }
+
+ urllist[ i ] = ldap_url_desc2str( &tmplud );
+
+ if ( urllist[ i ] == NULL ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg),
+ "unable to rebuild uri "
+ "in \"uri <uri>\" statement "
+ "for \"%s\"",
+ c->argv[ 1 ] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ urlrc = 1;
+ goto done_url;
+ }
+ }
+
+ li->li_uri = ldap_charray2str( urllist, " " );
+ for ( i = 0; urllist[ i ] != NULL; i++ ) {
+ struct berval bv;
+
+ ber_str2bv( urllist[ i ], 0, 0, &bv );
+ ber_bvarray_add( &li->li_bvuri, &bv );
+ urllist[ i ] = NULL;
+ }
+ ldap_memfree( urllist );
+ urllist = NULL;
+
+done_url:;
+ if ( urllist ) {
+ ldap_charray_free( urllist );
+ }
+ if ( lud ) {
+ ldap_free_urllist( lud );
+ }
+ if ( urlrc != LDAP_URL_SUCCESS ) {
+ return 1;
+ }
+ break;
+ }
+
+ case LDAP_BACK_CFG_TLS:
+ i = verb_to_mask( c->argv[1], tls_mode );
+ if ( BER_BVISNULL( &tls_mode[i].word ) ) {
+ return 1;
+ }
+ li->li_flags &= ~LDAP_BACK_F_TLS_MASK;
+ li->li_flags |= tls_mode[i].mask;
+ if ( c->argc > 2 ) {
+ for ( i=2; i<c->argc; i++ ) {
+ if ( bindconf_tls_parse( c->argv[i], &li->li_tls ))
+ return 1;
+ }
+ bindconf_tls_defaults( &li->li_tls );
+ }
+#ifdef HAVE_TLS
+ if ( li->li_tls.sb_tls_ctx ) {
+ ldap_pvt_tls_ctx_free( li->li_tls.sb_tls_ctx );
+ li->li_tls.sb_tls_ctx = NULL;
+ }
+#endif
+ break;
+
+ case LDAP_BACK_CFG_ACL_BIND:
+ for ( i = 1; i < c->argc; i++ ) {
+ if ( bindconf_parse( c->argv[ i ], &li->li_acl ) ) {
+ return 1;
+ }
+ }
+ bindconf_tls_defaults( &li->li_acl );
+#ifdef HAVE_TLS
+ if ( li->li_acl.sb_tls_ctx ) {
+ ldap_pvt_tls_ctx_free( li->li_acl.sb_tls_ctx );
+ li->li_acl.sb_tls_ctx = NULL;
+ }
+#endif
+ break;
+
+ case LDAP_BACK_CFG_IDASSERT_AUTHZFROM:
+ rc = slap_idassert_authzfrom_parse( c, &li->li_idassert );
+ break;
+
+ case LDAP_BACK_CFG_IDASSERT_PASSTHRU:
+ rc = slap_idassert_passthru_parse( c, &li->li_idassert );
+ break;
+
+ case LDAP_BACK_CFG_IDASSERT_BIND:
+ rc = slap_idassert_parse( c, &li->li_idassert );
+ break;
+
+ case LDAP_BACK_CFG_REBIND:
+ if ( c->argc == 1 || c->value_int ) {
+ li->li_flags |= LDAP_BACK_F_SAVECRED;
+
+ } else {
+ li->li_flags &= ~LDAP_BACK_F_SAVECRED;
+ }
+ break;
+
+ case LDAP_BACK_CFG_CHASE:
+ if ( c->argc == 1 || c->value_int ) {
+ li->li_flags |= LDAP_BACK_F_CHASE_REFERRALS;
+
+ } else {
+ li->li_flags &= ~LDAP_BACK_F_CHASE_REFERRALS;
+ }
+ break;
+
+ case LDAP_BACK_CFG_T_F: {
+ slap_mask_t mask;
+
+ i = verb_to_mask( c->argv[1], t_f_mode );
+ if ( BER_BVISNULL( &t_f_mode[i].word ) ) {
+ return 1;
+ }
+
+ mask = t_f_mode[i].mask;
+
+ if ( LDAP_BACK_ISOPEN( li )
+ && mask == LDAP_BACK_F_T_F_DISCOVER
+ && !LDAP_BACK_T_F( li ) )
+ {
+ slap_bindconf sb = { BER_BVNULL };
+ int rc;
+
+ if ( li->li_uri == NULL ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "need URI to discover absolute filters support "
+ "in \"t-f-support discover\"" );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+ }
+
+ ber_str2bv( li->li_uri, 0, 0, &sb.sb_uri );
+ sb.sb_version = li->li_version;
+ sb.sb_method = LDAP_AUTH_SIMPLE;
+ BER_BVSTR( &sb.sb_binddn, "" );
+
+ rc = slap_discover_feature( &sb,
+ slap_schema.si_ad_supportedFeatures->ad_cname.bv_val,
+ LDAP_FEATURE_ABSOLUTE_FILTERS );
+ if ( rc == LDAP_COMPARE_TRUE ) {
+ mask |= LDAP_BACK_F_T_F;
+ }
+ }
+
+ li->li_flags &= ~LDAP_BACK_F_T_F_MASK2;
+ li->li_flags |= mask;
+ } break;
+
+ case LDAP_BACK_CFG_WHOAMI:
+ if ( c->argc == 1 || c->value_int ) {
+ li->li_flags |= LDAP_BACK_F_PROXY_WHOAMI;
+ load_extop( (struct berval *)&slap_EXOP_WHOAMI,
+ 0, ldap_back_exop_whoami );
+
+ } else {
+ li->li_flags &= ~LDAP_BACK_F_PROXY_WHOAMI;
+ }
+ break;
+
+ case LDAP_BACK_CFG_TIMEOUT:
+ for ( i = 1; i < c->argc; i++ ) {
+ if ( isdigit( (unsigned char) c->argv[ i ][ 0 ] ) ) {
+ int j;
+ unsigned u;
+
+ if ( lutil_atoux( &u, c->argv[ i ], 0 ) != 0 ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg),
+ "unable to parse timeout \"%s\"",
+ c->argv[ i ] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+ }
+
+ for ( j = 0; j < SLAP_OP_LAST; j++ ) {
+ li->li_timeout[ j ] = u;
+ }
+
+ continue;
+ }
+
+ if ( slap_cf_aux_table_parse( c->argv[ i ], li->li_timeout, timeout_table, "slapd-ldap timeout" ) ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg),
+ "unable to parse timeout \"%s\"",
+ c->argv[ i ] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+ }
+ }
+ break;
+
+ case LDAP_BACK_CFG_IDLE_TIMEOUT: {
+ unsigned long t;
+
+ if ( lutil_parse_time( c->argv[ 1 ], &t ) != 0 ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg),
+ "unable to parse idle timeout \"%s\"",
+ c->argv[ 1 ] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+ }
+ li->li_idle_timeout = (time_t)t;
+ } break;
+
+ case LDAP_BACK_CFG_CONN_TTL: {
+ unsigned long t;
+
+ if ( lutil_parse_time( c->argv[ 1 ], &t ) != 0 ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg),
+ "unable to parse conn ttl\"%s\"",
+ c->argv[ 1 ] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+ }
+ li->li_conn_ttl = (time_t)t;
+ } break;
+
+ case LDAP_BACK_CFG_NETWORK_TIMEOUT: {
+ unsigned long t;
+
+ if ( lutil_parse_time( c->argv[ 1 ], &t ) != 0 ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg),
+ "unable to parse network timeout \"%s\"",
+ c->argv[ 1 ] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+ }
+ li->li_network_timeout = (time_t)t;
+ } break;
+
+ case LDAP_BACK_CFG_VERSION:
+ if ( c->value_int != 0 && ( c->value_int < LDAP_VERSION_MIN || c->value_int > LDAP_VERSION_MAX ) ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "unsupported version \"%s\" "
+ "in \"protocol-version <version>\"",
+ c->argv[ 1 ] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+ }
+
+ li->li_version = c->value_int;
+ break;
+
+ case LDAP_BACK_CFG_SINGLECONN:
+ if ( c->value_int ) {
+ li->li_flags |= LDAP_BACK_F_SINGLECONN;
+
+ } else {
+ li->li_flags &= ~LDAP_BACK_F_SINGLECONN;
+ }
+ break;
+
+ case LDAP_BACK_CFG_USETEMP:
+ if ( c->value_int ) {
+ li->li_flags |= LDAP_BACK_F_USE_TEMPORARIES;
+
+ } else {
+ li->li_flags &= ~LDAP_BACK_F_USE_TEMPORARIES;
+ }
+ break;
+
+ case LDAP_BACK_CFG_CONNPOOLMAX:
+ if ( c->value_int < LDAP_BACK_CONN_PRIV_MIN
+ || c->value_int > LDAP_BACK_CONN_PRIV_MAX )
+ {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "invalid max size " "of privileged "
+ "connections pool \"%s\" "
+ "in \"conn-pool-max <n> "
+ "(must be between %d and %d)\"",
+ c->argv[ 1 ],
+ LDAP_BACK_CONN_PRIV_MIN,
+ LDAP_BACK_CONN_PRIV_MAX );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+ }
+ li->li_conn_priv_max = c->value_int;
+ break;
+
+ case LDAP_BACK_CFG_CANCEL: {
+ slap_mask_t mask;
+
+ i = verb_to_mask( c->argv[1], cancel_mode );
+ if ( BER_BVISNULL( &cancel_mode[i].word ) ) {
+ return 1;
+ }
+
+ mask = cancel_mode[i].mask;
+
+ if ( LDAP_BACK_ISOPEN( li )
+ && mask == LDAP_BACK_F_CANCEL_EXOP_DISCOVER
+ && !LDAP_BACK_CANCEL( li ) )
+ {
+ slap_bindconf sb = { BER_BVNULL };
+ int rc;
+
+ if ( li->li_uri == NULL ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "need URI to discover \"cancel\" support "
+ "in \"cancel exop-discover\"" );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+ }
+
+ ber_str2bv( li->li_uri, 0, 0, &sb.sb_uri );
+ sb.sb_version = li->li_version;
+ sb.sb_method = LDAP_AUTH_SIMPLE;
+ BER_BVSTR( &sb.sb_binddn, "" );
+
+ rc = slap_discover_feature( &sb,
+ slap_schema.si_ad_supportedExtension->ad_cname.bv_val,
+ LDAP_EXOP_CANCEL );
+ if ( rc == LDAP_COMPARE_TRUE ) {
+ mask |= LDAP_BACK_F_CANCEL_EXOP;
+ }
+ }
+
+ li->li_flags &= ~LDAP_BACK_F_CANCEL_MASK2;
+ li->li_flags |= mask;
+ } break;
+
+ case LDAP_BACK_CFG_QUARANTINE:
+ if ( LDAP_BACK_QUARANTINE( li ) ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "quarantine already defined" );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+ }
+ rc = slap_retry_info_parse( c->argv[1], &li->li_quarantine,
+ c->cr_msg, sizeof( c->cr_msg ) );
+ if ( rc ) {
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+
+ } else {
+ ldap_pvt_thread_mutex_init( &li->li_quarantine_mutex );
+ /* give it a chance to retry if the pattern gets reset
+ * via back-config */
+ li->li_isquarantined = 0;
+ li->li_flags |= LDAP_BACK_F_QUARANTINE;
+ }
+ break;
+
+#ifdef SLAP_CONTROL_X_SESSION_TRACKING
+ case LDAP_BACK_CFG_ST_REQUEST:
+ if ( c->value_int ) {
+ li->li_flags |= LDAP_BACK_F_ST_REQUEST;
+
+ } else {
+ li->li_flags &= ~LDAP_BACK_F_ST_REQUEST;
+ }
+ break;
+#endif /* SLAP_CONTROL_X_SESSION_TRACKING */
+
+ case LDAP_BACK_CFG_NOREFS:
+ if ( c->value_int ) {
+ li->li_flags |= LDAP_BACK_F_NOREFS;
+
+ } else {
+ li->li_flags &= ~LDAP_BACK_F_NOREFS;
+ }
+ break;
+
+ case LDAP_BACK_CFG_NOUNDEFFILTER:
+ if ( c->value_int ) {
+ li->li_flags |= LDAP_BACK_F_NOUNDEFFILTER;
+
+ } else {
+ li->li_flags &= ~LDAP_BACK_F_NOUNDEFFILTER;
+ }
+ break;
+
+ case LDAP_BACK_CFG_ONERR:
+ /* onerr? */
+ i = verb_to_mask( c->argv[1], onerr_mode );
+ if ( BER_BVISNULL( &onerr_mode[i].word ) ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "%s unknown argument \"%s\"",
+ c->argv[0], c->argv[1] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+ }
+ li->li_flags &= ~LDAP_BACK_F_ONERR_STOP;
+ li->li_flags |= onerr_mode[i].mask;
+ break;
+
+ case LDAP_BACK_CFG_OMIT_UNKNOWN_SCHEMA:
+ if ( c->value_int ) {
+ li->li_flags |= LDAP_BACK_F_OMIT_UNKNOWN_SCHEMA;
+
+ } else {
+ li->li_flags &= ~LDAP_BACK_F_OMIT_UNKNOWN_SCHEMA;
+ }
+ break;
+
+ case LDAP_BACK_CFG_KEEPALIVE: {
+ struct berval bv;
+ ber_str2bv( c->argv[1], 0, 1, &bv );
+ slap_keepalive_parse( &bv, &li->li_tls.sb_keepalive, 0, 0, 0 );
+ }
+ break;
+
+ case LDAP_BACK_CFG_TCP_USER_TIMEOUT:
+ li->li_tls.sb_tcp_user_timeout = c->value_uint;
+ break;
+
+ default:
+ /* FIXME: try to catch inconsistencies */
+ assert( 0 );
+ break;
+ }
+
+ return rc;
+}
+
+int
+ldap_back_init_cf( BackendInfo *bi )
+{
+ int rc;
+
+ /* Make sure we don't exceed the bits reserved for userland */
+ config_check_userland( LDAP_BACK_CFG_LAST );
+
+ bi->bi_cf_ocs = ldapocs;
+
+ rc = config_register_schema( ldapcfg, ldapocs );
+ if ( rc ) {
+ return rc;
+ }
+
+ return 0;
+}
+
+static int
+ldap_pbind_cf_gen( ConfigArgs *c )
+{
+ slap_overinst *on = (slap_overinst *)c->bi;
+ void *private = c->be->be_private;
+ int rc;
+
+ c->be->be_private = on->on_bi.bi_private;
+ rc = ldap_back_cf_gen( c );
+ c->be->be_private = private;
+ return rc;
+}
+
+int
+ldap_pbind_init_cf( BackendInfo *bi )
+{
+ bi->bi_cf_ocs = pbindocs;
+
+ return config_register_schema( pbindcfg, pbindocs );
+}
+
+static int
+ldap_back_exop_whoami(
+ Operation *op,
+ SlapReply *rs )
+{
+ struct berval *bv = NULL;
+
+ if ( op->oq_extended.rs_reqdata != NULL ) {
+ /* no request data should be provided */
+ rs->sr_text = "no request data expected";
+ return rs->sr_err = LDAP_PROTOCOL_ERROR;
+ }
+
+ Debug( LDAP_DEBUG_STATS, "%s WHOAMI\n",
+ op->o_log_prefix );
+
+ rs->sr_err = backend_check_restrictions( op, rs,
+ (struct berval *)&slap_EXOP_WHOAMI );
+ if( rs->sr_err != LDAP_SUCCESS ) return rs->sr_err;
+
+ /* if auth'd by back-ldap and request is proxied, forward it */
+ if ( op->o_conn->c_authz_backend
+ && !strcmp( op->o_conn->c_authz_backend->be_type, "ldap" )
+ && !dn_match( &op->o_ndn, &op->o_conn->c_ndn ) )
+ {
+ ldapconn_t *lc = NULL;
+ LDAPControl c, *ctrls[2] = {NULL, NULL};
+ LDAPMessage *res;
+ Operation op2 = *op;
+ ber_int_t msgid;
+ int doretry = 1;
+ char *ptr;
+
+ ctrls[0] = &c;
+ op2.o_ndn = op->o_conn->c_ndn;
+ if ( !ldap_back_dobind( &lc, &op2, rs, LDAP_BACK_SENDERR ) ) {
+ return -1;
+ }
+ c.ldctl_oid = LDAP_CONTROL_PROXY_AUTHZ;
+ c.ldctl_iscritical = 1;
+ c.ldctl_value.bv_val = op->o_tmpalloc(
+ op->o_ndn.bv_len + STRLENOF( "dn:" ) + 1,
+ op->o_tmpmemctx );
+ c.ldctl_value.bv_len = op->o_ndn.bv_len + 3;
+ ptr = c.ldctl_value.bv_val;
+ ptr = lutil_strcopy( ptr, "dn:" );
+ ptr = lutil_strncopy( ptr, op->o_ndn.bv_val, op->o_ndn.bv_len );
+ ptr[ 0 ] = '\0';
+
+retry:
+ rs->sr_err = ldap_whoami( lc->lc_ld, ctrls, NULL, &msgid );
+ if ( rs->sr_err == LDAP_SUCCESS ) {
+ /* by now, make sure no timeout is used (ITS#6282) */
+ struct timeval tv = { -1, 0 };
+ if ( ldap_result( lc->lc_ld, msgid, LDAP_MSG_ALL, &tv, &res ) == -1 ) {
+ ldap_get_option( lc->lc_ld, LDAP_OPT_ERROR_NUMBER,
+ &rs->sr_err );
+ if ( rs->sr_err == LDAP_SERVER_DOWN && doretry ) {
+ doretry = 0;
+ if ( ldap_back_retry( &lc, op, rs, LDAP_BACK_SENDERR ) ) {
+ goto retry;
+ }
+ }
+
+ } else {
+ /* NOTE: are we sure "bv" will be malloc'ed
+ * with the appropriate memory? */
+ rs->sr_err = ldap_parse_whoami( lc->lc_ld, res, &bv );
+ ldap_msgfree(res);
+ }
+ }
+ op->o_tmpfree( c.ldctl_value.bv_val, op->o_tmpmemctx );
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ rs->sr_err = slap_map_api2result( rs );
+ }
+
+ if ( lc != NULL ) {
+ ldap_back_release_conn( (ldapinfo_t *)op2.o_bd->be_private, lc );
+ }
+
+ } else {
+ /* else just do the same as before */
+ bv = (struct berval *) ch_malloc( sizeof( struct berval ) );
+ if ( !BER_BVISEMPTY( &op->o_dn ) ) {
+ bv->bv_len = op->o_dn.bv_len + STRLENOF( "dn:" );
+ bv->bv_val = ch_malloc( bv->bv_len + 1 );
+ AC_MEMCPY( bv->bv_val, "dn:", STRLENOF( "dn:" ) );
+ AC_MEMCPY( &bv->bv_val[ STRLENOF( "dn:" ) ], op->o_dn.bv_val,
+ op->o_dn.bv_len );
+ bv->bv_val[ bv->bv_len ] = '\0';
+
+ } else {
+ bv->bv_len = 0;
+ bv->bv_val = NULL;
+ }
+ }
+
+ rs->sr_rspdata = bv;
+ return rs->sr_err;
+}
+
+
diff --git a/servers/slapd/back-ldap/delete.c b/servers/slapd/back-ldap/delete.c
new file mode 100644
index 0000000..470aa0b
--- /dev/null
+++ b/servers/slapd/back-ldap/delete.c
@@ -0,0 +1,85 @@
+/* delete.c - ldap backend delete function */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2003-2022 The OpenLDAP Foundation.
+ * Portions Copyright 1999-2003 Howard Chu.
+ * Portions Copyright 2000-2003 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 the Howard Chu for inclusion
+ * in OpenLDAP Software and subsequently enhanced by Pierangelo
+ * Masarati.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#include "slap.h"
+#include "back-ldap.h"
+
+int
+ldap_back_delete(
+ Operation *op,
+ SlapReply *rs )
+{
+ ldapinfo_t *li = (ldapinfo_t *)op->o_bd->be_private;
+
+ ldapconn_t *lc = NULL;
+ ber_int_t msgid;
+ LDAPControl **ctrls = NULL;
+ ldap_back_send_t retrying = LDAP_BACK_RETRYING;
+ int rc = LDAP_SUCCESS;
+
+ if ( !ldap_back_dobind( &lc, op, rs, LDAP_BACK_SENDERR ) ) {
+ return rs->sr_err;
+ }
+
+retry:
+ ctrls = op->o_ctrls;
+ rc = ldap_back_controls_add( op, rs, lc, &ctrls );
+ if ( rc != LDAP_SUCCESS ) {
+ send_ldap_result( op, rs );
+ goto cleanup;
+ }
+
+ rs->sr_err = ldap_delete_ext( lc->lc_ld, op->o_req_dn.bv_val,
+ ctrls, NULL, &msgid );
+ rc = ldap_back_op_result( lc, op, rs, msgid,
+ li->li_timeout[ SLAP_OP_DELETE ],
+ ( LDAP_BACK_SENDRESULT | retrying ) );
+ if ( rs->sr_err == LDAP_UNAVAILABLE && retrying ) {
+ retrying &= ~LDAP_BACK_RETRYING;
+ if ( ldap_back_retry( &lc, op, rs, LDAP_BACK_SENDERR ) ) {
+ /* if the identity changed, there might be need to re-authz */
+ (void)ldap_back_controls_free( op, rs, &ctrls );
+ goto retry;
+ }
+ }
+
+ ldap_pvt_thread_mutex_lock( &li->li_counter_mutex );
+ ldap_pvt_mp_add( li->li_ops_completed[ SLAP_OP_DELETE ], 1 );
+ ldap_pvt_thread_mutex_unlock( &li->li_counter_mutex );
+
+cleanup:
+ (void)ldap_back_controls_free( op, rs, &ctrls );
+
+ if ( lc != NULL ) {
+ ldap_back_release_conn( li, lc );
+ }
+
+ return rs->sr_err;
+}
diff --git a/servers/slapd/back-ldap/distproc.c b/servers/slapd/back-ldap/distproc.c
new file mode 100644
index 0000000..a2417a3
--- /dev/null
+++ b/servers/slapd/back-ldap/distproc.c
@@ -0,0 +1,998 @@
+/* distproc.c - implement distributed procedures */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2005-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2003 Howard Chu.
+ * 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.
+ * Based on back-ldap and slapo-chain, developed by Howard Chu
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#include "slap.h"
+
+#ifdef SLAP_DISTPROC
+
+#include "back-ldap.h"
+
+#include "slap-config.h"
+
+/*
+ * From <draft-sermersheim-ldap-distproc>
+ *
+
+ ContinuationReference ::= SET {
+ referralURI [0] SET SIZE (1..MAX) OF URI,
+ localReference [2] LDAPDN,
+ referenceType [3] ReferenceType,
+ remainingName [4] RelativeLDAPDN OPTIONAL,
+ searchScope [5] SearchScope OPTIONAL,
+ searchedSubtrees [6] SearchedSubtrees OPTIONAL,
+ failedName [7] LDAPDN OPTIONAL,
+ ... }
+
+ ReferenceType ::= ENUMERATED {
+ superior (0),
+ subordinate (1),
+ cross (2),
+ nonSpecificSubordinate (3),
+ supplier (4),
+ master (5),
+ immediateSuperior (6),
+ self (7),
+ ... }
+
+ SearchScope ::= ENUMERATED {
+ baseObject (0),
+ singleLevel (1),
+ wholeSubtree (2),
+ subordinateSubtree (3),
+ ... }
+
+ SearchedSubtrees ::= SET OF RelativeLDAPDN
+
+ LDAPDN, RelativeLDAPDN, and LDAPString, are defined in [RFC2251].
+
+ */
+
+typedef enum ReferenceType_t {
+ LDAP_DP_RT_UNKNOWN = -1,
+ LDAP_DP_RT_SUPERIOR = 0,
+ LDAP_DP_RT_SUBORDINATE = 1,
+ LDAP_DP_RT_CROSS = 2,
+ LDAP_DP_RT_NONSPECIFICSUBORDINATE = 3,
+ LDAP_DP_RT_SUPPLIER = 4,
+ LDAP_DP_RT_MASTER = 5,
+ LDAP_DP_RT_IMMEDIATESUPERIOR = 6,
+ LDAP_DP_RT_SELF = 7,
+ LDAP_DP_RT_LAST
+} ReferenceType_t;
+
+typedef enum SearchScope_t {
+ LDAP_DP_SS_UNKNOWN = -1,
+ LDAP_DP_SS_BASEOBJECT = 0,
+ LDAP_DP_SS_SINGLELEVEL = 1,
+ LDAP_DP_SS_WHOLESUBTREE = 2,
+ LDAP_DP_SS_SUBORDINATESUBTREE = 3,
+ LDAP_DP_SS_LAST
+} SearchScope_t;
+
+typedef struct ContinuationReference_t {
+ BerVarray cr_referralURI;
+ /* ? [1] ? */
+ struct berval cr_localReference;
+ ReferenceType_t cr_referenceType;
+ struct berval cr_remainingName;
+ SearchScope_t cr_searchScope;
+ BerVarray cr_searchedSubtrees;
+ struct berval cr_failedName;
+} ContinuationReference_t;
+#define CR_INIT { NULL, BER_BVNULL, LDAP_DP_RT_UNKNOWN, BER_BVNULL, LDAP_DP_SS_UNKNOWN, NULL, BER_BVNULL }
+
+#ifdef unused
+static struct berval bv2rt[] = {
+ BER_BVC( "superior" ),
+ BER_BVC( "subordinate" ),
+ BER_BVC( "cross" ),
+ BER_BVC( "nonSpecificSubordinate" ),
+ BER_BVC( "supplier" ),
+ BER_BVC( "master" ),
+ BER_BVC( "immediateSuperior" ),
+ BER_BVC( "self" ),
+ BER_BVNULL
+};
+
+static struct berval bv2ss[] = {
+ BER_BVC( "baseObject" ),
+ BER_BVC( "singleLevel" ),
+ BER_BVC( "wholeSubtree" ),
+ BER_BVC( "subordinateSubtree" ),
+ BER_BVNULL
+};
+
+static struct berval *
+ldap_distproc_rt2bv( ReferenceType_t rt )
+{
+ return &bv2rt[ rt ];
+}
+
+static const char *
+ldap_distproc_rt2str( ReferenceType_t rt )
+{
+ return bv2rt[ rt ].bv_val;
+}
+
+static ReferenceType_t
+ldap_distproc_bv2rt( struct berval *bv )
+{
+ ReferenceType_t rt;
+
+ for ( rt = 0; !BER_BVISNULL( &bv2rt[ rt ] ); rt++ ) {
+ if ( ber_bvstrcasecmp( bv, &bv2rt[ rt ] ) == 0 ) {
+ return rt;
+ }
+ }
+
+ return LDAP_DP_RT_UNKNOWN;
+}
+
+static ReferenceType_t
+ldap_distproc_str2rt( const char *s )
+{
+ struct berval bv;
+
+ ber_str2bv( s, 0, 0, &bv );
+ return ldap_distproc_bv2rt( &bv );
+}
+
+static struct berval *
+ldap_distproc_ss2bv( SearchScope_t ss )
+{
+ return &bv2ss[ ss ];
+}
+
+static const char *
+ldap_distproc_ss2str( SearchScope_t ss )
+{
+ return bv2ss[ ss ].bv_val;
+}
+
+static SearchScope_t
+ldap_distproc_bv2ss( struct berval *bv )
+{
+ ReferenceType_t ss;
+
+ for ( ss = 0; !BER_BVISNULL( &bv2ss[ ss ] ); ss++ ) {
+ if ( ber_bvstrcasecmp( bv, &bv2ss[ ss ] ) == 0 ) {
+ return ss;
+ }
+ }
+
+ return LDAP_DP_SS_UNKNOWN;
+}
+
+static SearchScope_t
+ldap_distproc_str2ss( const char *s )
+{
+ struct berval bv;
+
+ ber_str2bv( s, 0, 0, &bv );
+ return ldap_distproc_bv2ss( &bv );
+}
+#endif /* unused */
+
+/*
+ * NOTE: this overlay assumes that the chainingBehavior control
+ * is registered by the chain overlay; it may move here some time.
+ * This overlay provides support for that control as well.
+ */
+
+
+static int sc_returnContRef;
+#define o_returnContRef o_ctrlflag[sc_returnContRef]
+#define get_returnContRef(op) ((op)->o_returnContRef & SLAP_CONTROL_MASK)
+
+static struct berval slap_EXOP_CHAINEDREQUEST = BER_BVC( LDAP_EXOP_X_CHAINEDREQUEST );
+static struct berval slap_FEATURE_CANCHAINOPS = BER_BVC( LDAP_FEATURE_X_CANCHAINOPS );
+
+static BackendInfo *lback;
+
+typedef struct ldap_distproc_t {
+ /* "common" configuration info (anything occurring before an "uri") */
+ ldapinfo_t *lc_common_li;
+
+ /* current configuration info */
+ ldapinfo_t *lc_cfg_li;
+
+ /* tree of configured[/generated?] "uri" info */
+ ldap_avl_info_t lc_lai;
+
+ unsigned lc_flags;
+#define LDAP_DISTPROC_F_NONE (0x00U)
+#define LDAP_DISTPROC_F_CHAINING (0x01U)
+#define LDAP_DISTPROC_F_CACHE_URI (0x10U)
+
+#define LDAP_DISTPROC_CHAINING( lc ) ( ( (lc)->lc_flags & LDAP_DISTPROC_F_CHAINING ) == LDAP_DISTPROC_F_CHAINING )
+#define LDAP_DISTPROC_CACHE_URI( lc ) ( ( (lc)->lc_flags & LDAP_DISTPROC_F_CACHE_URI ) == LDAP_DISTPROC_F_CACHE_URI )
+
+} ldap_distproc_t;
+
+static int ldap_distproc_db_init_common( BackendDB *be );
+static int ldap_distproc_db_init_one( BackendDB *be );
+#define ldap_distproc_db_open_one(be) (lback)->bi_db_open( (be) )
+#define ldap_distproc_db_close_one(be) (0)
+#define ldap_distproc_db_destroy_one(be, ca) (lback)->bi_db_destroy( (be), (ca) )
+
+static int
+ldap_distproc_uri_cmp( const void *c1, const void *c2 )
+{
+ const ldapinfo_t *li1 = (const ldapinfo_t *)c1;
+ const ldapinfo_t *li2 = (const ldapinfo_t *)c2;
+
+ assert( li1->li_bvuri != NULL );
+ assert( !BER_BVISNULL( &li1->li_bvuri[ 0 ] ) );
+ assert( BER_BVISNULL( &li1->li_bvuri[ 1 ] ) );
+
+ assert( li2->li_bvuri != NULL );
+ assert( !BER_BVISNULL( &li2->li_bvuri[ 0 ] ) );
+ assert( BER_BVISNULL( &li2->li_bvuri[ 1 ] ) );
+
+ /* If local DNs don't match, it is definitely not a match */
+ return ber_bvcmp( &li1->li_bvuri[ 0 ], &li2->li_bvuri[ 0 ] );
+}
+
+static int
+ldap_distproc_uri_dup( void *c1, void *c2 )
+{
+ ldapinfo_t *li1 = (ldapinfo_t *)c1;
+ ldapinfo_t *li2 = (ldapinfo_t *)c2;
+
+ assert( li1->li_bvuri != NULL );
+ assert( !BER_BVISNULL( &li1->li_bvuri[ 0 ] ) );
+ assert( BER_BVISNULL( &li1->li_bvuri[ 1 ] ) );
+
+ assert( li2->li_bvuri != NULL );
+ assert( !BER_BVISNULL( &li2->li_bvuri[ 0 ] ) );
+ assert( BER_BVISNULL( &li2->li_bvuri[ 1 ] ) );
+
+ /* Cannot have more than one shared session with same DN */
+ if ( ber_bvcmp( &li1->li_bvuri[ 0 ], &li2->li_bvuri[ 0 ] ) == 0 ) {
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+ldap_distproc_operational( Operation *op, SlapReply *rs )
+{
+ /* Trap entries generated by back-ldap.
+ *
+ * FIXME: we need a better way to recognize them; a cleaner
+ * solution would be to be able to intercept the response
+ * of be_operational(), so that we can divert only those
+ * calls that fail because operational attributes were
+ * requested for entries that do not belong to the underlying
+ * database. This fix is likely to intercept also entries
+ * generated by back-perl and so. */
+ if ( rs->sr_entry->e_private == NULL ) {
+ return LDAP_SUCCESS;
+ }
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+ldap_distproc_response( Operation *op, SlapReply *rs )
+{
+ return SLAP_CB_CONTINUE;
+}
+
+/*
+ * configuration...
+ */
+
+enum {
+ /* NOTE: the chaining behavior control is registered
+ * by the chain overlay; it may move here some time */
+ DP_CHAINING = 1,
+ DP_CACHE_URI,
+
+ DP_LAST
+};
+
+static ConfigDriver distproc_cfgen;
+static ConfigCfAdd distproc_cfadd;
+static ConfigLDAPadd distproc_ldadd;
+
+static ConfigTable distproc_cfg[] = {
+ { "distproc-chaining", "args",
+ 2, 4, 0, ARG_MAGIC|ARG_BERVAL|DP_CHAINING, distproc_cfgen,
+ /* NOTE: using the same attributeTypes defined
+ * for the "chain" overlay */
+ "( OLcfgOvAt:3.1 NAME 'olcChainingBehavior' "
+ "DESC 'Chaining behavior control parameters (draft-sermersheim-ldap-chaining)' "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+ { "distproc-cache-uri", "TRUE/FALSE",
+ 2, 2, 0, ARG_MAGIC|ARG_ON_OFF|DP_CACHE_URI, distproc_cfgen,
+ "( OLcfgOvAt:3.2 NAME 'olcChainCacheURI' "
+ "DESC 'Enables caching of URIs not present in configuration' "
+ "SYNTAX OMsBoolean "
+ "SINGLE-VALUE )", NULL, NULL },
+ { NULL, NULL, 0, 0, 0, ARG_IGNORED }
+};
+
+static ConfigOCs distproc_ocs[] = {
+ { "( OLcfgOvOc:7.1 "
+ "NAME 'olcDistProcConfig' "
+ "DESC 'Distributed procedures <draft-sermersheim-ldap-distproc> configuration' "
+ "SUP olcOverlayConfig "
+ "MAY ( "
+ "olcChainingBehavior $ "
+ "olcChainCacheURI "
+ ") )",
+ Cft_Overlay, distproc_cfg, NULL, distproc_cfadd },
+ { "( OLcfgOvOc:7.2 "
+ "NAME 'olcDistProcDatabase' "
+ "DESC 'Distributed procedure remote server configuration' "
+ "AUXILIARY )",
+ Cft_Misc, distproc_cfg, distproc_ldadd },
+ { NULL, 0, NULL }
+};
+
+static int
+distproc_ldadd( CfEntryInfo *p, Entry *e, ConfigArgs *ca )
+{
+ slap_overinst *on;
+ ldap_distproc_t *lc;
+
+ ldapinfo_t *li;
+
+ AttributeDescription *ad = NULL;
+ Attribute *at;
+ const char *text;
+
+ int rc;
+
+ if ( p->ce_type != Cft_Overlay
+ || !p->ce_bi
+ || p->ce_bi->bi_cf_ocs != distproc_ocs )
+ {
+ return LDAP_CONSTRAINT_VIOLATION;
+ }
+
+ on = (slap_overinst *)p->ce_bi;
+ lc = (ldap_distproc_t *)on->on_bi.bi_private;
+
+ assert( ca->be == NULL );
+ ca->be = (BackendDB *)ch_calloc( 1, sizeof( BackendDB ) );
+
+ ca->be->bd_info = (BackendInfo *)on;
+
+ rc = slap_str2ad( "olcDbURI", &ad, &text );
+ assert( rc == LDAP_SUCCESS );
+
+ at = attr_find( e->e_attrs, ad );
+ if ( lc->lc_common_li == NULL && at != NULL ) {
+ /* FIXME: we should generate an empty default entry
+ * if none is supplied */
+ Debug( LDAP_DEBUG_ANY, "slapd-distproc: "
+ "first underlying database \"%s\" "
+ "cannot contain attribute \"%s\".\n",
+ e->e_name.bv_val, ad->ad_cname.bv_val );
+ rc = LDAP_CONSTRAINT_VIOLATION;
+ goto done;
+
+ } else if ( lc->lc_common_li != NULL && at == NULL ) {
+ /* FIXME: we should generate an empty default entry
+ * if none is supplied */
+ Debug( LDAP_DEBUG_ANY, "slapd-distproc: "
+ "subsequent underlying database \"%s\" "
+ "must contain attribute \"%s\".\n",
+ e->e_name.bv_val, ad->ad_cname.bv_val );
+ rc = LDAP_CONSTRAINT_VIOLATION;
+ goto done;
+ }
+
+ if ( lc->lc_common_li == NULL ) {
+ rc = ldap_distproc_db_init_common( ca->be );
+
+ } else {
+ rc = ldap_distproc_db_init_one( ca->be );
+ }
+
+ if ( rc != 0 ) {
+ Debug( LDAP_DEBUG_ANY, "slapd-distproc: "
+ "unable to init %sunderlying database \"%s\".\n",
+ lc->lc_common_li == NULL ? "common " : "", e->e_name.bv_val );
+ rc = LDAP_CONSTRAINT_VIOLATION;
+ goto done;
+ }
+
+ li = ca->be->be_private;
+
+ if ( lc->lc_common_li == NULL ) {
+ lc->lc_common_li = li;
+
+ } else if ( ldap_tavl_insert( &lc->lc_lai.lai_tree, (caddr_t)li,
+ ldap_distproc_uri_cmp, ldap_distproc_uri_dup ) )
+ {
+ Debug( LDAP_DEBUG_ANY, "slapd-distproc: "
+ "database \"%s\" insert failed.\n",
+ e->e_name.bv_val );
+ rc = LDAP_CONSTRAINT_VIOLATION;
+ goto done;
+ }
+
+done:;
+ if ( rc != LDAP_SUCCESS ) {
+ (void)ldap_distproc_db_destroy_one( ca->be, NULL );
+ ch_free( ca->be );
+ ca->be = NULL;
+ }
+
+ return rc;
+}
+
+typedef struct ldap_distproc_cfadd_apply_t {
+ Operation *op;
+ SlapReply *rs;
+ Entry *p;
+ ConfigArgs *ca;
+ int count;
+} ldap_distproc_cfadd_apply_t;
+
+static void
+ldap_distproc_cfadd_apply(
+ ldapinfo_t *li,
+ Operation *op,
+ SlapReply *rs,
+ Entry *p,
+ ConfigArgs *ca,
+ int count )
+{
+ struct berval bv;
+
+ /* FIXME: should not hardcode "olcDatabase" here */
+ bv.bv_len = snprintf( ca->cr_msg, sizeof( ca->cr_msg ),
+ "olcDatabase={%d}%s", count, lback->bi_type );
+ bv.bv_val = ca->cr_msg;
+
+ ca->be->be_private = (void *)li;
+ config_build_entry( op, rs, p->e_private, ca,
+ &bv, lback->bi_cf_ocs, &distproc_ocs[ 1 ] );
+
+ return;
+}
+
+static int
+distproc_cfadd( Operation *op, SlapReply *rs, Entry *p, ConfigArgs *ca )
+{
+ CfEntryInfo *pe = p->e_private;
+ slap_overinst *on = (slap_overinst *)pe->ce_bi;
+ ldap_distproc_t *lc = (ldap_distproc_t *)on->on_bi.bi_private;
+ void *priv = (void *)ca->be->be_private;
+ TAvlnode *edge;
+ int count = 0;
+
+ if ( lback->bi_cf_ocs ) {
+ ldap_distproc_cfadd_apply_t lca = { 0 };
+
+ lca.op = op;
+ lca.rs = rs;
+ lca.p = p;
+ lca.ca = ca;
+ lca.count = 0;
+
+ ldap_distproc_cfadd_apply( lc->lc_common_li, op, rs, p, ca, count++ );
+
+ edge = ldap_tavl_end( lc->lc_lai.lai_tree, TAVL_DIR_LEFT );
+ while ( edge ) {
+ TAvlnode *next = ldap_tavl_next( edge, TAVL_DIR_RIGHT );
+ ldapinfo_t *li = (ldapinfo_t *)edge->avl_data;
+ ldap_distproc_cfadd_apply( li, op, rs, p, ca, count++ );
+ edge = next;
+ }
+
+ ca->be->be_private = priv;
+ }
+
+ return 0;
+}
+
+static int
+distproc_cfgen( ConfigArgs *c )
+{
+ slap_overinst *on = (slap_overinst *)c->bi;
+ ldap_distproc_t *lc = (ldap_distproc_t *)on->on_bi.bi_private;
+
+ int rc = 0;
+
+ if ( c->op == SLAP_CONFIG_EMIT ) {
+ switch( c->type ) {
+ case DP_CACHE_URI:
+ c->value_int = LDAP_DISTPROC_CACHE_URI( lc );
+ break;
+
+ default:
+ assert( 0 );
+ rc = 1;
+ }
+ return rc;
+
+ } else if ( c->op == LDAP_MOD_DELETE ) {
+ switch( c->type ) {
+ case DP_CHAINING:
+ return 1;
+
+ case DP_CACHE_URI:
+ lc->lc_flags &= ~LDAP_DISTPROC_F_CACHE_URI;
+ break;
+
+ default:
+ return 1;
+ }
+ return rc;
+ }
+
+ switch( c->type ) {
+ case DP_CACHE_URI:
+ if ( c->value_int ) {
+ lc->lc_flags |= LDAP_DISTPROC_F_CACHE_URI;
+ } else {
+ lc->lc_flags &= ~LDAP_DISTPROC_F_CACHE_URI;
+ }
+ break;
+
+ default:
+ assert( 0 );
+ return 1;
+ }
+
+ return rc;
+}
+
+static int
+ldap_distproc_db_init(
+ BackendDB *be,
+ ConfigReply *cr )
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ ldap_distproc_t *lc = NULL;
+
+ if ( lback == NULL ) {
+ lback = backend_info( "ldap" );
+
+ if ( lback == NULL ) {
+ return 1;
+ }
+ }
+
+ lc = ch_malloc( sizeof( ldap_distproc_t ) );
+ if ( lc == NULL ) {
+ return 1;
+ }
+ memset( lc, 0, sizeof( ldap_distproc_t ) );
+ ldap_pvt_thread_mutex_init( &lc->lc_lai.lai_mutex );
+
+ on->on_bi.bi_private = (void *)lc;
+
+ return 0;
+}
+
+static int
+ldap_distproc_db_config(
+ BackendDB *be,
+ const char *fname,
+ int lineno,
+ int argc,
+ char **argv )
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ ldap_distproc_t *lc = (ldap_distproc_t *)on->on_bi.bi_private;
+
+ int rc = SLAP_CONF_UNKNOWN;
+
+ if ( lc->lc_common_li == NULL ) {
+ void *be_private = be->be_private;
+ ldap_distproc_db_init_common( be );
+ lc->lc_common_li = lc->lc_cfg_li = (ldapinfo_t *)be->be_private;
+ be->be_private = be_private;
+ }
+
+ /* Something for the distproc database? */
+ if ( strncasecmp( argv[ 0 ], "distproc-", STRLENOF( "distproc-" ) ) == 0 ) {
+ char *save_argv0 = argv[ 0 ];
+ BackendInfo *bd_info = be->bd_info;
+ void *be_private = be->be_private;
+ ConfigOCs *be_cf_ocs = be->be_cf_ocs;
+ int is_uri = 0;
+
+ argv[ 0 ] += STRLENOF( "distproc-" );
+
+ if ( strcasecmp( argv[ 0 ], "uri" ) == 0 ) {
+ rc = ldap_distproc_db_init_one( be );
+ if ( rc != 0 ) {
+ Debug( LDAP_DEBUG_ANY, "%s: line %d: "
+ "underlying slapd-ldap initialization failed.\n.",
+ fname, lineno );
+ return 1;
+ }
+ lc->lc_cfg_li = be->be_private;
+ is_uri = 1;
+ }
+
+ /* TODO: add checks on what other slapd-ldap(5) args
+ * should be put in the template; this is not quite
+ * harmful, because attributes that shouldn't don't
+ * get actually used, but the user should at least
+ * be warned.
+ */
+
+ be->bd_info = lback;
+ be->be_private = (void *)lc->lc_cfg_li;
+ be->be_cf_ocs = lback->bi_cf_ocs;
+
+ rc = config_generic_wrapper( be, fname, lineno, argc, argv );
+
+ argv[ 0 ] = save_argv0;
+ be->be_cf_ocs = be_cf_ocs;
+ be->be_private = be_private;
+ be->bd_info = bd_info;
+
+ if ( is_uri ) {
+private_destroy:;
+ if ( rc != 0 ) {
+ BackendDB db = *be;
+
+ db.bd_info = lback;
+ db.be_private = (void *)lc->lc_cfg_li;
+ ldap_distproc_db_destroy_one( &db, NULL );
+ lc->lc_cfg_li = NULL;
+
+ } else {
+ if ( lc->lc_cfg_li->li_bvuri == NULL
+ || BER_BVISNULL( &lc->lc_cfg_li->li_bvuri[ 0 ] )
+ || !BER_BVISNULL( &lc->lc_cfg_li->li_bvuri[ 1 ] ) )
+ {
+ Debug( LDAP_DEBUG_ANY, "%s: line %d: "
+ "no URI list allowed in slapo-distproc.\n",
+ fname, lineno );
+ rc = 1;
+ goto private_destroy;
+ }
+
+ if ( ldap_tavl_insert( &lc->lc_lai.lai_tree,
+ (caddr_t)lc->lc_cfg_li,
+ ldap_distproc_uri_cmp, ldap_distproc_uri_dup ) )
+ {
+ Debug( LDAP_DEBUG_ANY, "%s: line %d: "
+ "duplicate URI in slapo-distproc.\n",
+ fname, lineno );
+ rc = 1;
+ goto private_destroy;
+ }
+ }
+ }
+ }
+
+ return rc;
+}
+
+enum db_which {
+ db_open = 0,
+ db_close,
+ db_destroy,
+
+ db_last
+};
+
+static int
+ldap_distproc_db_func(
+ BackendDB *be,
+ enum db_which which
+)
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ ldap_distproc_t *lc = (ldap_distproc_t *)on->on_bi.bi_private;
+
+ int rc = 0;
+
+ if ( lc ) {
+ BI_db_func *func = (&lback->bi_db_open)[ which ];
+
+ if ( func != NULL && lc->lc_common_li != NULL ) {
+ BackendDB db = *be;
+
+ db.bd_info = lback;
+ db.be_private = lc->lc_common_li;
+
+ rc = func( &db, NULL );
+
+ if ( rc != 0 ) {
+ return rc;
+ }
+
+ if ( lc->lc_lai.lai_tree != NULL ) {
+ TAvlnode *edge = ldap_tavl_end( lc->lc_lai.lai_tree, TAVL_DIR_LEFT );
+ while ( edge ) {
+ TAvlnode *next = ldap_tavl_next( edge, TAVL_DIR_RIGHT );
+ ldapinfo_t *li = (ldapinfo_t *)edge->avl_data;
+ be->be_private = (void *)li;
+ rc = func( &db, NULL );
+ if ( rc == 1 ) {
+ break;
+ }
+ edge = next;
+ }
+ }
+ }
+ }
+
+ return rc;
+}
+
+static int
+ldap_distproc_db_open(
+ BackendDB *be,
+ ConfigReply *cr )
+{
+ return ldap_distproc_db_func( be, db_open );
+}
+
+static int
+ldap_distproc_db_close(
+ BackendDB *be,
+ ConfigReply *cr )
+{
+ return ldap_distproc_db_func( be, db_close );
+}
+
+static int
+ldap_distproc_db_destroy(
+ BackendDB *be,
+ ConfigReply *cr )
+{
+ slap_overinst *on = (slap_overinst *) be->bd_info;
+ ldap_distproc_t *lc = (ldap_distproc_t *)on->on_bi.bi_private;
+
+ int rc;
+
+ rc = ldap_distproc_db_func( be, db_destroy );
+
+ if ( lc ) {
+ ldap_tavl_free( lc->lc_lai.lai_tree, NULL );
+ ldap_pvt_thread_mutex_destroy( &lc->lc_lai.lai_mutex );
+ ch_free( lc );
+ }
+
+ return rc;
+}
+
+/*
+ * inits one instance of the slapd-ldap backend, and stores
+ * the private info in be_private of the arg
+ */
+static int
+ldap_distproc_db_init_common(
+ BackendDB *be )
+{
+ BackendInfo *bi = be->bd_info;
+ int t;
+
+ be->bd_info = lback;
+ be->be_private = NULL;
+ t = lback->bi_db_init( be, NULL );
+ if ( t != 0 ) {
+ return t;
+ }
+ be->bd_info = bi;
+
+ return 0;
+}
+
+/*
+ * inits one instance of the slapd-ldap backend, stores
+ * the private info in be_private of the arg and fills
+ * selected fields with data from the template.
+ *
+ * NOTE: add checks about the other fields of the template,
+ * which are ignored and SHOULD NOT be configured by the user.
+ */
+static int
+ldap_distproc_db_init_one(
+ BackendDB *be )
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ ldap_distproc_t *lc = (ldap_distproc_t *)on->on_bi.bi_private;
+
+ BackendInfo *bi = be->bd_info;
+ ldapinfo_t *li;
+
+ slap_op_t t;
+
+ be->bd_info = lback;
+ be->be_private = NULL;
+ t = lback->bi_db_init( be, NULL );
+ if ( t != 0 ) {
+ return t;
+ }
+ li = (ldapinfo_t *)be->be_private;
+
+ /* copy common data */
+ li->li_nretries = lc->lc_common_li->li_nretries;
+ li->li_flags = lc->lc_common_li->li_flags;
+ li->li_version = lc->lc_common_li->li_version;
+ for ( t = 0; t < SLAP_OP_LAST; t++ ) {
+ li->li_timeout[ t ] = lc->lc_common_li->li_timeout[ t ];
+ }
+ be->bd_info = bi;
+
+ return 0;
+}
+
+static int
+ldap_distproc_connection_destroy(
+ BackendDB *be,
+ Connection *conn
+)
+{
+ slap_overinst *on = (slap_overinst *) be->bd_info;
+ ldap_distproc_t *lc = (ldap_distproc_t *)on->on_bi.bi_private;
+ void *private = be->be_private;
+ int rc;
+ TAvlnode *edge;
+
+ be->be_private = NULL;
+ ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
+ edge = ldap_tavl_end( lc->lc_lai.lai_tree, TAVL_DIR_LEFT );
+ while ( edge ) {
+ TAvlnode *next = ldap_tavl_next( edge, TAVL_DIR_RIGHT );
+ ldapinfo_t *li = (ldapinfo_t *)edge->avl_data;
+ be->be_private = (void *)li;
+ rc = lback->bi_connection_destroy( be, conn );
+ if ( rc == 1 ) {
+ break;
+ }
+ edge = next;
+ }
+ ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );
+ be->be_private = private;
+
+ return rc;
+}
+
+static int
+ldap_distproc_parse_returnContRef_ctrl(
+ Operation *op,
+ SlapReply *rs,
+ LDAPControl *ctrl )
+{
+ if ( get_returnContRef( op ) != SLAP_CONTROL_NONE ) {
+ rs->sr_text = "returnContinuationReference control specified multiple times";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ if ( op->o_pagedresults != SLAP_CONTROL_NONE ) {
+ rs->sr_text = "returnContinuationReference control specified with pagedResults control";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ if ( !BER_BVISEMPTY( &ctrl->ldctl_value ) ) {
+ rs->sr_text = "returnContinuationReference control: value must be NULL";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ op->o_returnContRef = ctrl->ldctl_iscritical ? SLAP_CONTROL_CRITICAL : SLAP_CONTROL_NONCRITICAL;
+
+ return LDAP_SUCCESS;
+}
+
+static int
+ldap_exop_chained_request(
+ Operation *op,
+ SlapReply *rs )
+{
+ Debug( LDAP_DEBUG_STATS, "%s CHAINED REQUEST\n",
+ op->o_log_prefix );
+
+ rs->sr_err = backend_check_restrictions( op, rs,
+ (struct berval *)&slap_EXOP_CHAINEDREQUEST );
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ return rs->sr_err;
+ }
+
+ /* by now, just reject requests */
+ rs->sr_text = "under development";
+ return LDAP_UNWILLING_TO_PERFORM;
+}
+
+
+static slap_overinst distproc;
+
+int
+distproc_initialize( void )
+{
+ int rc;
+
+ /* Make sure we don't exceed the bits reserved for userland */
+ config_check_userland( DP_LAST );
+
+ rc = load_extop( (struct berval *)&slap_EXOP_CHAINEDREQUEST,
+ SLAP_EXOP_HIDE, ldap_exop_chained_request );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "slapd-distproc: "
+ "unable to register chainedRequest exop: %d.\n",
+ rc );
+ return rc;
+ }
+
+ rc = supported_feature_load( &slap_FEATURE_CANCHAINOPS );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "slapd-distproc: "
+ "unable to register canChainOperations supported feature: %d.\n",
+ rc );
+ return rc;
+ }
+
+ rc = register_supported_control( LDAP_CONTROL_X_RETURNCONTREF,
+ SLAP_CTRL_GLOBAL|SLAP_CTRL_ACCESS|SLAP_CTRL_HIDE, NULL,
+ ldap_distproc_parse_returnContRef_ctrl, &sc_returnContRef );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "slapd-distproc: "
+ "unable to register returnContinuationReference control: %d.\n",
+ rc );
+ return rc;
+ }
+
+ distproc.on_bi.bi_type = "distproc";
+ distproc.on_bi.bi_db_init = ldap_distproc_db_init;
+ distproc.on_bi.bi_db_config = ldap_distproc_db_config;
+ distproc.on_bi.bi_db_open = ldap_distproc_db_open;
+ distproc.on_bi.bi_db_close = ldap_distproc_db_close;
+ distproc.on_bi.bi_db_destroy = ldap_distproc_db_destroy;
+
+ /* ... otherwise the underlying backend's function would be called,
+ * likely passing an invalid entry; on the contrary, the requested
+ * operational attributes should have been returned while chasing
+ * the referrals. This all in all is a bit messy, because part
+ * of the operational attributes are generated by the backend;
+ * part by the frontend; back-ldap should receive all the available
+ * ones from the remote server, but then, on its own, it strips those
+ * it assumes will be (re)generated by the frontend (e.g.
+ * subschemaSubentry, entryDN, ...) */
+ distproc.on_bi.bi_operational = ldap_distproc_operational;
+
+ distproc.on_bi.bi_connection_destroy = ldap_distproc_connection_destroy;
+
+ distproc.on_response = ldap_distproc_response;
+
+ distproc.on_bi.bi_cf_ocs = distproc_ocs;
+
+ rc = config_register_schema( distproc_cfg, distproc_ocs );
+ if ( rc ) {
+ return rc;
+ }
+
+ return overlay_register( &distproc );
+}
+
+#endif /* SLAP_DISTPROC */
diff --git a/servers/slapd/back-ldap/extended.c b/servers/slapd/back-ldap/extended.c
new file mode 100644
index 0000000..b6f4ec5
--- /dev/null
+++ b/servers/slapd/back-ldap/extended.c
@@ -0,0 +1,386 @@
+/* extended.c - ldap backend extended routines */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2003-2022 The OpenLDAP Foundation.
+ * 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 the Howard Chu for inclusion
+ * in OpenLDAP Software and subsequently enhanced by Pierangelo
+ * Masarati.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+
+#include "slap.h"
+#include "back-ldap.h"
+#include "lber_pvt.h"
+
+typedef int (ldap_back_exop_f)( Operation *op, SlapReply *rs, ldapconn_t **lc );
+
+static ldap_back_exop_f ldap_back_exop_passwd;
+static ldap_back_exop_f ldap_back_exop_generic;
+
+static struct exop {
+ struct berval oid;
+ ldap_back_exop_f *extended;
+} exop_table[] = {
+ { BER_BVC(LDAP_EXOP_MODIFY_PASSWD), ldap_back_exop_passwd },
+ { BER_BVNULL, NULL }
+};
+
+static int
+ldap_back_extended_one( Operation *op, SlapReply *rs, ldap_back_exop_f exop )
+{
+ ldapinfo_t *li = (ldapinfo_t *) op->o_bd->be_private;
+
+ ldapconn_t *lc = NULL;
+ LDAPControl **ctrls = NULL, **oldctrls = NULL;
+ int rc;
+
+ /* FIXME: this needs to be called here, so it is
+ * called twice; maybe we could avoid the
+ * ldap_back_dobind() call inside each extended()
+ * call ... */
+ if ( !ldap_back_dobind( &lc, op, rs, LDAP_BACK_DONTSEND ) ) {
+ return rs->sr_err;
+ }
+
+ ctrls = oldctrls = op->o_ctrls;
+ if ( ldap_back_controls_add( op, rs, lc, &ctrls ) )
+ {
+ op->o_ctrls = oldctrls;
+ rc = rs->sr_err;
+ goto done;
+ }
+
+ op->o_ctrls = ctrls;
+ rc = exop( op, rs, &lc );
+
+ op->o_ctrls = oldctrls;
+ (void)ldap_back_controls_free( op, rs, &ctrls );
+
+done:;
+ if ( lc != NULL ) {
+ ldap_back_release_conn( li, lc );
+ }
+
+ return rc;
+}
+
+int
+ldap_back_extended(
+ Operation *op,
+ SlapReply *rs )
+{
+ int i;
+
+ RS_ASSERT( !(rs->sr_flags & REP_ENTRY_MASK) );
+ rs->sr_flags &= ~REP_ENTRY_MASK; /* paranoia */
+
+ for ( i = 0; exop_table[i].extended != NULL; i++ ) {
+ if ( bvmatch( &exop_table[i].oid, &op->oq_extended.rs_reqoid ) )
+ {
+ return ldap_back_extended_one( op, rs, exop_table[i].extended );
+ }
+ }
+
+ /* if we get here, the exop is known; the best that we can do
+ * is pass it thru as is */
+ /* FIXME: maybe a list of OIDs to pass thru would be safer */
+ return ldap_back_extended_one( op, rs, ldap_back_exop_generic );
+}
+
+static int
+ldap_back_exop_passwd(
+ Operation *op,
+ SlapReply *rs,
+ ldapconn_t **lcp )
+{
+ ldapinfo_t *li = (ldapinfo_t *) op->o_bd->be_private;
+
+ ldapconn_t *lc = *lcp;
+ req_pwdexop_s *qpw = &op->oq_pwdexop;
+ LDAPMessage *res;
+ ber_int_t msgid;
+ int rc, isproxy, freedn = 0;
+ int do_retry = 1;
+ char *text = NULL;
+ struct berval dn = op->o_req_dn,
+ ndn = op->o_req_ndn;
+
+ assert( lc != NULL );
+ assert( rs->sr_ctrls == NULL );
+
+ if ( BER_BVISNULL( &ndn ) && op->ore_reqdata != NULL ) {
+ /* NOTE: most of this code is mutated
+ * from slap_passwd_parse();
+ * But here we only need
+ * the first berval... */
+
+ ber_tag_t tag;
+ ber_len_t len = -1;
+ BerElementBuffer berbuf;
+ BerElement *ber = (BerElement *)&berbuf;
+
+ struct berval tmpid = BER_BVNULL;
+
+ if ( op->ore_reqdata->bv_len == 0 ) {
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ /* ber_init2 uses reqdata directly, doesn't allocate new buffers */
+ ber_init2( ber, op->ore_reqdata, 0 );
+
+ tag = ber_scanf( ber, "{" /*}*/ );
+
+ if ( tag == LBER_ERROR ) {
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ tag = ber_peek_tag( ber, &len );
+ if ( tag == LDAP_TAG_EXOP_MODIFY_PASSWD_ID ) {
+ tag = ber_get_stringbv( ber, &tmpid, LBER_BV_NOTERM );
+
+ if ( tag == LBER_ERROR ) {
+ return LDAP_PROTOCOL_ERROR;
+ }
+ }
+
+ if ( !BER_BVISEMPTY( &tmpid ) ) {
+ char idNull = tmpid.bv_val[tmpid.bv_len];
+ tmpid.bv_val[tmpid.bv_len] = '\0';
+ rs->sr_err = dnPrettyNormal( NULL, &tmpid, &dn,
+ &ndn, op->o_tmpmemctx );
+ tmpid.bv_val[tmpid.bv_len] = idNull;
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ /* should have been successfully parsed earlier! */
+ return rs->sr_err;
+ }
+ freedn = 1;
+
+ } else {
+ dn = op->o_dn;
+ ndn = op->o_ndn;
+ }
+ }
+
+ isproxy = ber_bvcmp( &ndn, &op->o_ndn );
+
+ Debug( LDAP_DEBUG_ARGS, "==> ldap_back_exop_passwd(\"%s\")%s\n",
+ dn.bv_val, isproxy ? " (proxy)" : "" );
+
+retry:
+ rc = ldap_passwd( lc->lc_ld, &dn,
+ qpw->rs_old.bv_val ? &qpw->rs_old : NULL,
+ qpw->rs_new.bv_val ? &qpw->rs_new : NULL,
+ op->o_ctrls, NULL, &msgid );
+
+ if ( rc == LDAP_SUCCESS ) {
+ /* TODO: set timeout? */
+ /* by now, make sure no timeout is used (ITS#6282) */
+ struct timeval tv = { -1, 0 };
+ if ( ldap_result( lc->lc_ld, msgid, LDAP_MSG_ALL, &tv, &res ) == -1 ) {
+ ldap_get_option( lc->lc_ld, LDAP_OPT_ERROR_NUMBER, &rc );
+ rs->sr_err = rc;
+
+ } else {
+ /* only touch when activity actually took place... */
+ if ( li->li_idle_timeout ) {
+ lc->lc_time = op->o_time;
+ }
+
+ /* sigh. parse twice, because parse_passwd
+ * doesn't give us the err / match / msg info.
+ */
+ rc = ldap_parse_result( lc->lc_ld, res, &rs->sr_err,
+ (char **)&rs->sr_matched,
+ &text,
+ NULL, &rs->sr_ctrls, 0 );
+
+ if ( rc == LDAP_SUCCESS ) {
+ if ( rs->sr_err == LDAP_SUCCESS ) {
+ struct berval newpw;
+
+ /* this never happens because
+ * the frontend is generating
+ * the new password, so when
+ * the passwd exop is proxied,
+ * it never delegates password
+ * generation to the remote server
+ */
+ rc = ldap_parse_passwd( lc->lc_ld, res,
+ &newpw );
+ if ( rc == LDAP_SUCCESS &&
+ !BER_BVISNULL( &newpw ) )
+ {
+ rs->sr_type = REP_EXTENDED;
+ rs->sr_rspdata = slap_passwd_return( &newpw );
+ free( newpw.bv_val );
+ }
+
+ } else {
+ rc = rs->sr_err;
+ }
+ }
+ ldap_msgfree( res );
+ }
+ }
+
+ if ( text ) {
+ /* copy to tmpmem, doesn't need to be freed */
+ rs->sr_text = op->o_tmpalloc( strlen( text ) + 1, op->o_tmpmemctx );
+ strcpy( rs->sr_text, text );
+ ch_free( text );
+ }
+ if ( rs->sr_matched )
+ rs->sr_flags |= REP_MATCHED_MUSTBEFREED;
+ if ( rs->sr_ctrls )
+ rs->sr_flags |= REP_CTRLS_MUSTBEFREED;
+
+ if ( rc != LDAP_SUCCESS ) {
+ rs->sr_err = slap_map_api2result( rs );
+ if ( rs->sr_err == LDAP_UNAVAILABLE && do_retry ) {
+ do_retry = 0;
+ if ( ldap_back_retry( &lc, op, rs, LDAP_BACK_DONTSEND ) ) {
+ goto retry;
+ }
+ }
+
+ if ( LDAP_BACK_QUARANTINE( li ) ) {
+ ldap_back_quarantine( op, rs );
+ }
+
+ } else if ( LDAP_BACK_QUARANTINE( li ) ) {
+ ldap_back_quarantine( op, rs );
+ }
+
+ ldap_pvt_thread_mutex_lock( &li->li_counter_mutex );
+ ldap_pvt_mp_add( li->li_ops_completed[ SLAP_OP_EXTENDED ], 1 );
+ ldap_pvt_thread_mutex_unlock( &li->li_counter_mutex );
+
+ if ( freedn ) {
+ op->o_tmpfree( dn.bv_val, op->o_tmpmemctx );
+ op->o_tmpfree( ndn.bv_val, op->o_tmpmemctx );
+ }
+
+ /* in case, cleanup handler */
+ if ( lc == NULL ) {
+ *lcp = NULL;
+ }
+
+ return rc;
+}
+
+static int
+ldap_back_exop_generic(
+ Operation *op,
+ SlapReply *rs,
+ ldapconn_t **lcp )
+{
+ ldapinfo_t *li = (ldapinfo_t *) op->o_bd->be_private;
+
+ ldapconn_t *lc = *lcp;
+ LDAPMessage *res;
+ ber_int_t msgid;
+ int rc;
+ int do_retry = 1;
+ char *text = NULL;
+
+ Debug( LDAP_DEBUG_ARGS, "==> ldap_back_exop_generic(%s, \"%s\")\n",
+ op->ore_reqoid.bv_val, op->o_req_dn.bv_val );
+ assert( lc != NULL );
+ assert( rs->sr_ctrls == NULL );
+
+retry:
+ rc = ldap_extended_operation( lc->lc_ld,
+ op->ore_reqoid.bv_val, op->ore_reqdata,
+ op->o_ctrls, NULL, &msgid );
+
+ if ( rc == LDAP_SUCCESS ) {
+ /* TODO: set timeout? */
+ /* by now, make sure no timeout is used (ITS#6282) */
+ struct timeval tv = { -1, 0 };
+ if ( ldap_result( lc->lc_ld, msgid, LDAP_MSG_ALL, &tv, &res ) == -1 ) {
+ ldap_get_option( lc->lc_ld, LDAP_OPT_ERROR_NUMBER, &rc );
+ rs->sr_err = rc;
+
+ } else {
+ /* only touch when activity actually took place... */
+ if ( li->li_idle_timeout ) {
+ lc->lc_time = op->o_time;
+ }
+
+ /* sigh. parse twice, because parse_passwd
+ * doesn't give us the err / match / msg info.
+ */
+ rc = ldap_parse_result( lc->lc_ld, res, &rs->sr_err,
+ (char **)&rs->sr_matched,
+ &text,
+ NULL, &rs->sr_ctrls, 0 );
+ if ( rc == LDAP_SUCCESS ) {
+ if ( rs->sr_err == LDAP_SUCCESS ) {
+ rc = ldap_parse_extended_result( lc->lc_ld, res,
+ (char **)&rs->sr_rspoid, &rs->sr_rspdata, 0 );
+ if ( rc == LDAP_SUCCESS ) {
+ rs->sr_type = REP_EXTENDED;
+ }
+
+ } else {
+ rc = rs->sr_err;
+ }
+ }
+ ldap_msgfree( res );
+ }
+ }
+
+ if ( rc != LDAP_SUCCESS ) {
+ rs->sr_err = slap_map_api2result( rs );
+ if ( rs->sr_err == LDAP_UNAVAILABLE && do_retry ) {
+ do_retry = 0;
+ if ( ldap_back_retry( &lc, op, rs, LDAP_BACK_DONTSEND ) ) {
+ goto retry;
+ }
+ }
+
+ if ( LDAP_BACK_QUARANTINE( li ) ) {
+ ldap_back_quarantine( op, rs );
+ }
+ } else if ( LDAP_BACK_QUARANTINE( li ) ) {
+ ldap_back_quarantine( op, rs );
+ }
+
+ ldap_pvt_thread_mutex_lock( &li->li_counter_mutex );
+ ldap_pvt_mp_add( li->li_ops_completed[ SLAP_OP_EXTENDED ], 1 );
+ ldap_pvt_thread_mutex_unlock( &li->li_counter_mutex );
+
+ if ( text ) {
+ /* copy to tmpmem, doesn't need to be freed */
+ rs->sr_text = op->o_tmpalloc( strlen( text ) + 1, op->o_tmpmemctx );
+ strcpy( rs->sr_text, text );
+ ch_free( text );
+ }
+ if ( rs->sr_matched )
+ rs->sr_flags |= REP_MATCHED_MUSTBEFREED;
+ if ( rs->sr_ctrls )
+ rs->sr_flags |= REP_CTRLS_MUSTBEFREED;
+
+ /* in case, cleanup handler */
+ if ( lc == NULL ) {
+ *lcp = NULL;
+ }
+
+ return rc;
+}
diff --git a/servers/slapd/back-ldap/init.c b/servers/slapd/back-ldap/init.c
new file mode 100644
index 0000000..e2db2a0
--- /dev/null
+++ b/servers/slapd/back-ldap/init.c
@@ -0,0 +1,374 @@
+/* init.c - initialize ldap backend */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2003-2022 The OpenLDAP Foundation.
+ * Portions Copyright 1999-2003 Howard Chu.
+ * Portions Copyright 2000-2003 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 the Howard Chu for inclusion
+ * in OpenLDAP Software and subsequently enhanced by Pierangelo
+ * Masarati.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#include "slap.h"
+#include "slap-config.h"
+#include "back-ldap.h"
+#include "ldap_rq.h"
+
+static const ldap_extra_t ldap_extra = {
+ ldap_back_proxy_authz_ctrl,
+ ldap_back_controls_free,
+ slap_idassert_authzfrom_parse,
+ slap_idassert_passthru_parse_cf,
+ slap_idassert_parse,
+ slap_retry_info_destroy,
+ slap_retry_info_parse,
+ slap_retry_info_unparse,
+ ldap_back_connid2str
+};
+
+int
+ldap_back_open( BackendInfo *bi )
+{
+ bi->bi_controls = slap_known_controls;
+ return 0;
+}
+
+int
+ldap_back_initialize( BackendInfo *bi )
+{
+ int rc;
+
+ bi->bi_flags =
+#ifdef LDAP_DYNAMIC_OBJECTS
+ /* this is set because all the support a proxy has to provide
+ * is the capability to forward the refresh exop, and to
+ * pass thru entries that contain the dynamicObject class
+ * and the entryTtl attribute */
+ SLAP_BFLAG_DYNAMIC |
+#endif /* LDAP_DYNAMIC_OBJECTS */
+
+ /* back-ldap recognizes RFC4525 increment;
+ * let the remote server complain, if needed (ITS#5912) */
+ SLAP_BFLAG_INCREMENT;
+
+ bi->bi_open = ldap_back_open;
+ bi->bi_config = 0;
+ bi->bi_close = 0;
+ bi->bi_destroy = 0;
+
+ bi->bi_db_init = ldap_back_db_init;
+ bi->bi_db_config = config_generic_wrapper;
+ bi->bi_db_open = ldap_back_db_open;
+ bi->bi_db_close = ldap_back_db_close;
+ bi->bi_db_destroy = ldap_back_db_destroy;
+
+ bi->bi_op_bind = ldap_back_bind;
+ bi->bi_op_unbind = 0;
+ bi->bi_op_search = ldap_back_search;
+ bi->bi_op_compare = ldap_back_compare;
+ bi->bi_op_modify = ldap_back_modify;
+ bi->bi_op_modrdn = ldap_back_modrdn;
+ bi->bi_op_add = ldap_back_add;
+ bi->bi_op_delete = ldap_back_delete;
+ bi->bi_op_abandon = 0;
+
+ bi->bi_extended = ldap_back_extended;
+
+ bi->bi_chk_referrals = 0;
+ bi->bi_entry_get_rw = ldap_back_entry_get;
+
+ bi->bi_connection_init = 0;
+ bi->bi_connection_destroy = ldap_back_conn_destroy;
+
+ bi->bi_extra = (void *)&ldap_extra;
+
+ rc = ldap_back_init_cf( bi );
+ if ( rc ) {
+ return rc;
+ }
+
+ rc = chain_initialize();
+ if ( rc ) {
+ return rc;
+ }
+
+ rc = pbind_initialize();
+ if ( rc ) {
+ return rc;
+ }
+
+#ifdef SLAP_DISTPROC
+ rc = distproc_initialize();
+ if ( rc ) {
+ return rc;
+ }
+#endif
+ return rc;
+}
+
+int
+ldap_back_db_init( Backend *be, ConfigReply *cr )
+{
+ ldapinfo_t *li;
+ int rc;
+ unsigned i;
+
+ li = (ldapinfo_t *)ch_calloc( 1, sizeof( ldapinfo_t ) );
+ if ( li == NULL ) {
+ return -1;
+ }
+
+ li->li_rebind_f = ldap_back_default_rebind;
+ li->li_urllist_f = ldap_back_default_urllist;
+ li->li_urllist_p = li;
+ ldap_pvt_thread_mutex_init( &li->li_uri_mutex );
+
+ BER_BVZERO( &li->li_acl_authcID );
+ BER_BVZERO( &li->li_acl_authcDN );
+ BER_BVZERO( &li->li_acl_passwd );
+
+ li->li_acl_authmethod = LDAP_AUTH_NONE;
+ BER_BVZERO( &li->li_acl_sasl_mech );
+ li->li_acl.sb_tls = SB_TLS_DEFAULT;
+
+ li->li_idassert_mode = LDAP_BACK_IDASSERT_LEGACY;
+
+ BER_BVZERO( &li->li_idassert_authcID );
+ BER_BVZERO( &li->li_idassert_authcDN );
+ BER_BVZERO( &li->li_idassert_passwd );
+
+ BER_BVZERO( &li->li_idassert_authzID );
+
+ li->li_idassert_authmethod = LDAP_AUTH_NONE;
+ BER_BVZERO( &li->li_idassert_sasl_mech );
+ li->li_idassert_tls = SB_TLS_DEFAULT;
+
+ /* by default, use proxyAuthz control on each operation */
+ li->li_idassert_flags = LDAP_BACK_AUTH_PRESCRIPTIVE;
+
+ li->li_idassert_authz = NULL;
+
+ /* initialize flags */
+ li->li_flags = LDAP_BACK_F_CHASE_REFERRALS;
+
+ /* initialize version */
+ li->li_version = LDAP_VERSION3;
+
+ ldap_pvt_thread_mutex_init( &li->li_conninfo.lai_mutex );
+
+ for ( i = LDAP_BACK_PCONN_FIRST; i < LDAP_BACK_PCONN_LAST; i++ ) {
+ li->li_conn_priv[ i ].lic_num = 0;
+ LDAP_TAILQ_INIT( &li->li_conn_priv[ i ].lic_priv );
+ }
+ li->li_conn_priv_max = LDAP_BACK_CONN_PRIV_DEFAULT;
+
+ ldap_pvt_thread_mutex_init( &li->li_counter_mutex );
+ for ( i = 0; i < SLAP_OP_LAST; i++ ) {
+ ldap_pvt_mp_init( li->li_ops_completed[ i ] );
+ }
+
+ li->li_conn_expire_task = NULL;
+
+ be->be_private = li;
+ SLAP_DBFLAGS( be ) |= SLAP_DBFLAG_NOLASTMOD;
+
+ be->be_cf_ocs = be->bd_info->bi_cf_ocs;
+
+ rc = ldap_back_monitor_db_init( be );
+ if ( rc != 0 ) {
+ /* ignore, by now */
+ rc = 0;
+ }
+
+ return rc;
+}
+
+int
+ldap_back_db_open( BackendDB *be, ConfigReply *cr )
+{
+ ldapinfo_t *li = (ldapinfo_t *)be->be_private;
+
+ slap_bindconf sb = { BER_BVNULL };
+ int rc = 0;
+
+ Debug( LDAP_DEBUG_TRACE,
+ "ldap_back_db_open: URI=%s\n",
+ li->li_uri != NULL ? li->li_uri : "" );
+
+ /* by default, use proxyAuthz control on each operation */
+ switch ( li->li_idassert_mode ) {
+ case LDAP_BACK_IDASSERT_LEGACY:
+ case LDAP_BACK_IDASSERT_SELF:
+ /* however, since admin connections are pooled and shared,
+ * only static authzIDs can be native */
+ li->li_idassert_flags &= ~LDAP_BACK_AUTH_NATIVE_AUTHZ;
+ break;
+
+ default:
+ break;
+ }
+
+ ber_str2bv( li->li_uri, 0, 0, &sb.sb_uri );
+ sb.sb_version = li->li_version;
+ sb.sb_method = LDAP_AUTH_SIMPLE;
+ BER_BVSTR( &sb.sb_binddn, "" );
+
+ if ( LDAP_BACK_T_F_DISCOVER( li ) && !LDAP_BACK_T_F( li ) ) {
+ rc = slap_discover_feature( &sb,
+ slap_schema.si_ad_supportedFeatures->ad_cname.bv_val,
+ LDAP_FEATURE_ABSOLUTE_FILTERS );
+ if ( rc == LDAP_COMPARE_TRUE ) {
+ li->li_flags |= LDAP_BACK_F_T_F;
+ }
+ }
+
+ if ( LDAP_BACK_CANCEL_DISCOVER( li ) && !LDAP_BACK_CANCEL( li ) ) {
+ rc = slap_discover_feature( &sb,
+ slap_schema.si_ad_supportedExtension->ad_cname.bv_val,
+ LDAP_EXOP_CANCEL );
+ if ( rc == LDAP_COMPARE_TRUE ) {
+ li->li_flags |= LDAP_BACK_F_CANCEL_EXOP;
+ }
+ }
+
+ /* monitor setup */
+ rc = ldap_back_monitor_db_open( be );
+ if ( rc != 0 ) {
+ /* ignore by now */
+ rc = 0;
+ }
+
+ li->li_flags |= LDAP_BACK_F_ISOPEN;
+
+ return rc;
+}
+
+void
+ldap_back_conn_free( void *v_lc )
+{
+ ldapconn_t *lc = v_lc;
+
+ if ( lc->lc_ld != NULL ) {
+ ldap_unbind_ext( lc->lc_ld, NULL, NULL );
+ }
+ if ( !BER_BVISNULL( &lc->lc_bound_ndn ) ) {
+ ch_free( lc->lc_bound_ndn.bv_val );
+ }
+ if ( !BER_BVISNULL( &lc->lc_cred ) ) {
+ memset( lc->lc_cred.bv_val, 0, lc->lc_cred.bv_len );
+ ch_free( lc->lc_cred.bv_val );
+ }
+ if ( !BER_BVISNULL( &lc->lc_local_ndn ) ) {
+ ch_free( lc->lc_local_ndn.bv_val );
+ }
+ lc->lc_q.tqe_prev = NULL;
+ lc->lc_q.tqe_next = NULL;
+ ch_free( lc );
+}
+
+int
+ldap_back_db_close( Backend *be, ConfigReply *cr )
+{
+ int rc = 0;
+
+ if ( be->be_private ) {
+ rc = ldap_back_monitor_db_close( be );
+ }
+
+ return rc;
+}
+
+int
+ldap_back_db_destroy( Backend *be, ConfigReply *cr )
+{
+ if ( be->be_private ) {
+ ldapinfo_t *li = ( ldapinfo_t * )be->be_private;
+ unsigned i;
+
+ (void)ldap_back_monitor_db_destroy( be );
+
+ /* Stop and remove the task that prunes expired connections */
+ if ( li->li_conn_expire_task != NULL ) {
+ ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
+ if ( ldap_pvt_runqueue_isrunning( &slapd_rq, li->li_conn_expire_task ) ) {
+ ldap_pvt_runqueue_stoptask( &slapd_rq, li->li_conn_expire_task );
+ }
+ ldap_pvt_runqueue_remove( &slapd_rq, li->li_conn_expire_task );
+ ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
+ }
+
+ ldap_pvt_thread_mutex_lock( &li->li_conninfo.lai_mutex );
+
+ if ( li->li_uri != NULL ) {
+ ch_free( li->li_uri );
+ li->li_uri = NULL;
+
+ assert( li->li_bvuri != NULL );
+ ber_bvarray_free( li->li_bvuri );
+ li->li_bvuri = NULL;
+ }
+
+ bindconf_free( &li->li_tls );
+ bindconf_free( &li->li_acl );
+ bindconf_free( &li->li_idassert.si_bc );
+
+ if ( li->li_idassert_authz != NULL ) {
+ ber_bvarray_free( li->li_idassert_authz );
+ li->li_idassert_authz = NULL;
+ }
+ if ( li->li_conninfo.lai_tree ) {
+ ldap_tavl_free( li->li_conninfo.lai_tree, ldap_back_conn_free );
+ }
+ for ( i = LDAP_BACK_PCONN_FIRST; i < LDAP_BACK_PCONN_LAST; i++ ) {
+ while ( !LDAP_TAILQ_EMPTY( &li->li_conn_priv[ i ].lic_priv ) ) {
+ ldapconn_t *lc = LDAP_TAILQ_FIRST( &li->li_conn_priv[ i ].lic_priv );
+
+ LDAP_TAILQ_REMOVE( &li->li_conn_priv[ i ].lic_priv, lc, lc_q );
+ ldap_back_conn_free( lc );
+ }
+ }
+ if ( LDAP_BACK_QUARANTINE( li ) ) {
+ slap_retry_info_destroy( &li->li_quarantine );
+ ldap_pvt_thread_mutex_destroy( &li->li_quarantine_mutex );
+ }
+
+ ldap_pvt_thread_mutex_unlock( &li->li_conninfo.lai_mutex );
+ ldap_pvt_thread_mutex_destroy( &li->li_conninfo.lai_mutex );
+ ldap_pvt_thread_mutex_destroy( &li->li_uri_mutex );
+
+ for ( i = 0; i < SLAP_OP_LAST; i++ ) {
+ ldap_pvt_mp_clear( li->li_ops_completed[ i ] );
+ }
+ ldap_pvt_thread_mutex_destroy( &li->li_counter_mutex );
+ }
+
+ ch_free( be->be_private );
+
+ return 0;
+}
+
+#if SLAPD_LDAP == SLAPD_MOD_DYNAMIC
+
+/* conditionally define the init_module() function */
+SLAP_BACKEND_INIT_MODULE( ldap )
+
+#endif /* SLAPD_LDAP == SLAPD_MOD_DYNAMIC */
diff --git a/servers/slapd/back-ldap/modify.c b/servers/slapd/back-ldap/modify.c
new file mode 100644
index 0000000..53e8a68
--- /dev/null
+++ b/servers/slapd/back-ldap/modify.c
@@ -0,0 +1,136 @@
+/* modify.c - ldap backend modify function */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1999-2022 The OpenLDAP Foundation.
+ * Portions Copyright 1999-2003 Howard Chu.
+ * Portions Copyright 2000-2003 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 the Howard Chu for inclusion
+ * in OpenLDAP Software and subsequently enhanced by Pierangelo
+ * Masarati.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#include "slap.h"
+#include "back-ldap.h"
+
+int
+ldap_back_modify(
+ Operation *op,
+ SlapReply *rs )
+{
+ ldapinfo_t *li = (ldapinfo_t *)op->o_bd->be_private;
+
+ ldapconn_t *lc = NULL;
+ LDAPMod **modv = NULL,
+ *mods = NULL;
+ Modifications *ml;
+ int i, j, rc;
+ ber_int_t msgid;
+ int isupdate;
+ ldap_back_send_t retrying = LDAP_BACK_RETRYING;
+ LDAPControl **ctrls = NULL;
+
+ if ( !ldap_back_dobind( &lc, op, rs, LDAP_BACK_SENDERR ) ) {
+ return rs->sr_err;
+ }
+
+ for ( i = 0, ml = op->orm_modlist; ml; i++, ml = ml->sml_next )
+ /* just count mods */ ;
+
+ modv = (LDAPMod **)ch_malloc( ( i + 1 )*sizeof( LDAPMod * )
+ + i*sizeof( LDAPMod ) );
+ if ( modv == NULL ) {
+ rc = LDAP_NO_MEMORY;
+ goto cleanup;
+ }
+ mods = (LDAPMod *)&modv[ i + 1 ];
+
+ isupdate = be_shadow_update( op );
+ for ( i = 0, ml = op->orm_modlist; ml; ml = ml->sml_next ) {
+ if ( !isupdate && !get_relax( op ) && ml->sml_desc->ad_type->sat_no_user_mod )
+ {
+ continue;
+ }
+
+ modv[ i ] = &mods[ i ];
+ mods[ i ].mod_op = ( ml->sml_op | LDAP_MOD_BVALUES );
+ mods[ i ].mod_type = ml->sml_desc->ad_cname.bv_val;
+
+ if ( ml->sml_values != NULL ) {
+ for ( j = 0; !BER_BVISNULL( &ml->sml_values[ j ] ); j++ )
+ /* just count mods */ ;
+ mods[ i ].mod_bvalues =
+ (struct berval **)ch_malloc( ( j + 1 )*sizeof( struct berval * ) );
+ for ( j = 0; !BER_BVISNULL( &ml->sml_values[ j ] ); j++ )
+ {
+ mods[ i ].mod_bvalues[ j ] = &ml->sml_values[ j ];
+ }
+ mods[ i ].mod_bvalues[ j ] = NULL;
+
+ } else {
+ mods[ i ].mod_bvalues = NULL;
+ }
+
+ i++;
+ }
+ modv[ i ] = 0;
+
+retry:;
+ ctrls = op->o_ctrls;
+ rc = ldap_back_controls_add( op, rs, lc, &ctrls );
+ if ( rc != LDAP_SUCCESS ) {
+ send_ldap_result( op, rs );
+ goto cleanup;
+ }
+
+ rs->sr_err = ldap_modify_ext( lc->lc_ld, op->o_req_dn.bv_val, modv,
+ ctrls, NULL, &msgid );
+ rc = ldap_back_op_result( lc, op, rs, msgid,
+ li->li_timeout[ SLAP_OP_MODIFY ],
+ ( LDAP_BACK_SENDRESULT | retrying ) );
+ if ( rs->sr_err == LDAP_UNAVAILABLE && retrying ) {
+ retrying &= ~LDAP_BACK_RETRYING;
+ if ( ldap_back_retry( &lc, op, rs, LDAP_BACK_SENDERR ) ) {
+ /* if the identity changed, there might be need to re-authz */
+ (void)ldap_back_controls_free( op, rs, &ctrls );
+ goto retry;
+ }
+ }
+
+ ldap_pvt_thread_mutex_lock( &li->li_counter_mutex );
+ ldap_pvt_mp_add( li->li_ops_completed[ SLAP_OP_MODIFY ], 1 );
+ ldap_pvt_thread_mutex_unlock( &li->li_counter_mutex );
+
+cleanup:;
+ (void)ldap_back_controls_free( op, rs, &ctrls );
+
+ for ( i = 0; modv[ i ]; i++ ) {
+ ch_free( modv[ i ]->mod_bvalues );
+ }
+ ch_free( modv );
+
+ if ( lc != NULL ) {
+ ldap_back_release_conn( li, lc );
+ }
+
+ return rs->sr_err;
+}
+
diff --git a/servers/slapd/back-ldap/modrdn.c b/servers/slapd/back-ldap/modrdn.c
new file mode 100644
index 0000000..9c441eb
--- /dev/null
+++ b/servers/slapd/back-ldap/modrdn.c
@@ -0,0 +1,123 @@
+/* modrdn.c - ldap backend modrdn function */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1999-2022 The OpenLDAP Foundation.
+ * Portions Copyright 1999-2003 Howard Chu.
+ * Portions Copyright 2000-2003 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 the Howard Chu for inclusion
+ * in OpenLDAP Software and subsequently enhanced by Pierangelo
+ * Masarati.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/socket.h>
+#include <ac/string.h>
+
+#include "slap.h"
+#include "back-ldap.h"
+
+int
+ldap_back_modrdn(
+ Operation *op,
+ SlapReply *rs )
+{
+ ldapinfo_t *li = (ldapinfo_t *)op->o_bd->be_private;
+
+ ldapconn_t *lc = NULL;
+ ber_int_t msgid;
+ LDAPControl **ctrls = NULL;
+ ldap_back_send_t retrying = LDAP_BACK_RETRYING;
+ int rc = LDAP_SUCCESS;
+ char *newSup = NULL;
+ struct berval newrdn = BER_BVNULL;
+
+ if ( !ldap_back_dobind( &lc, op, rs, LDAP_BACK_SENDERR ) ) {
+ return rs->sr_err;
+ }
+
+ if ( op->orr_newSup ) {
+ /* needs LDAPv3 */
+ switch ( li->li_version ) {
+ case LDAP_VERSION3:
+ break;
+
+ case 0:
+ if ( op->o_protocol == 0 || op->o_protocol == LDAP_VERSION3 ) {
+ break;
+ }
+ /* fall thru */
+
+ default:
+ /* op->o_protocol cannot be anything but LDAPv3,
+ * otherwise wouldn't be here */
+ rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+ send_ldap_result( op, rs );
+ goto cleanup;
+ }
+
+ newSup = op->orr_newSup->bv_val;
+ }
+
+ /* NOTE: we need to copy the newRDN in case it was formed
+ * from a DN by simply changing the length (ITS#5397) */
+ newrdn = op->orr_newrdn;
+ if ( newrdn.bv_val[ newrdn.bv_len ] != '\0' ) {
+ ber_dupbv_x( &newrdn, &op->orr_newrdn, op->o_tmpmemctx );
+ }
+
+retry:
+ ctrls = op->o_ctrls;
+ rc = ldap_back_controls_add( op, rs, lc, &ctrls );
+ if ( rc != LDAP_SUCCESS ) {
+ send_ldap_result( op, rs );
+ goto cleanup;
+ }
+
+ rs->sr_err = ldap_rename( lc->lc_ld, op->o_req_dn.bv_val,
+ newrdn.bv_val, newSup,
+ op->orr_deleteoldrdn, ctrls, NULL, &msgid );
+ rc = ldap_back_op_result( lc, op, rs, msgid,
+ li->li_timeout[ SLAP_OP_MODRDN ],
+ ( LDAP_BACK_SENDRESULT | retrying ) );
+ if ( rs->sr_err == LDAP_UNAVAILABLE && retrying ) {
+ retrying &= ~LDAP_BACK_RETRYING;
+ if ( ldap_back_retry( &lc, op, rs, LDAP_BACK_SENDERR ) ) {
+ /* if the identity changed, there might be need to re-authz */
+ (void)ldap_back_controls_free( op, rs, &ctrls );
+ goto retry;
+ }
+ }
+
+ ldap_pvt_thread_mutex_lock( &li->li_counter_mutex );
+ ldap_pvt_mp_add( li->li_ops_completed[ SLAP_OP_MODRDN ], 1 );
+ ldap_pvt_thread_mutex_unlock( &li->li_counter_mutex );
+
+cleanup:
+ (void)ldap_back_controls_free( op, rs, &ctrls );
+
+ if ( newrdn.bv_val != op->orr_newrdn.bv_val ) {
+ op->o_tmpfree( newrdn.bv_val, op->o_tmpmemctx );
+ }
+
+ if ( lc != NULL ) {
+ ldap_back_release_conn( li, lc );
+ }
+
+ return rs->sr_err;
+}
+
diff --git a/servers/slapd/back-ldap/monitor.c b/servers/slapd/back-ldap/monitor.c
new file mode 100644
index 0000000..16c2d6f
--- /dev/null
+++ b/servers/slapd/back-ldap/monitor.c
@@ -0,0 +1,1074 @@
+/* monitor.c - monitor ldap backend */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2003-2022 The OpenLDAP Foundation.
+ * Portions Copyright 1999-2003 Howard Chu.
+ * Portions Copyright 2000-2003 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 the Howard Chu for inclusion
+ * in OpenLDAP Software and subsequently enhanced by Pierangelo
+ * Masarati.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+#include <ac/unistd.h>
+#include <ac/stdlib.h>
+#include <ac/errno.h>
+#include <sys/stat.h>
+#include "lutil.h"
+#include "back-ldap.h"
+
+#include "slap-config.h"
+
+static ObjectClass *oc_olmLDAPDatabase;
+static ObjectClass *oc_olmLDAPConnection;
+
+static ObjectClass *oc_monitorContainer;
+static ObjectClass *oc_monitorCounterObject;
+
+static AttributeDescription *ad_olmDbURIList;
+static AttributeDescription *ad_olmDbOperations;
+static AttributeDescription *ad_olmDbBoundDN;
+static AttributeDescription *ad_olmDbConnFlags;
+static AttributeDescription *ad_olmDbConnURI;
+static AttributeDescription *ad_olmDbPeerAddress;
+
+/*
+ * Stolen from back-monitor/operations.c
+ * We don't need the normalized rdn's though.
+ */
+struct ldap_back_monitor_ops_t {
+ struct berval rdn;
+} ldap_back_monitor_op[] = {
+ { BER_BVC( "cn=Bind" ) },
+ { BER_BVC( "cn=Unbind" ) },
+ { BER_BVC( "cn=Search" ) },
+ { BER_BVC( "cn=Compare" ) },
+ { BER_BVC( "cn=Modify" ) },
+ { BER_BVC( "cn=Modrdn" ) },
+ { BER_BVC( "cn=Add" ) },
+ { BER_BVC( "cn=Delete" ) },
+ { BER_BVC( "cn=Abandon" ) },
+ { BER_BVC( "cn=Extended" ) },
+
+ { BER_BVNULL }
+};
+
+/* Corresponds to connection flags in back-ldap.h */
+static struct {
+ unsigned flag;
+ struct berval name;
+} s_flag[] = {
+ { LDAP_BACK_FCONN_ISBOUND, BER_BVC( "bound" ) },
+ { LDAP_BACK_FCONN_ISANON, BER_BVC( "anonymous" ) },
+ { LDAP_BACK_FCONN_ISPRIV, BER_BVC( "privileged" ) },
+ { LDAP_BACK_FCONN_ISTLS, BER_BVC( "TLS" ) },
+ { LDAP_BACK_FCONN_BINDING, BER_BVC( "binding" ) },
+ { LDAP_BACK_FCONN_TAINTED, BER_BVC( "tainted" ) },
+ { LDAP_BACK_FCONN_ABANDON, BER_BVC( "abandon" ) },
+ { LDAP_BACK_FCONN_ISIDASR, BER_BVC( "idassert" ) },
+ { LDAP_BACK_FCONN_CACHED, BER_BVC( "cached" ) },
+
+ { 0 }
+};
+
+
+/*
+ * NOTE: there's some confusion in monitor OID arc;
+ * by now, let's consider:
+ *
+ * Subsystems monitor attributes 1.3.6.1.4.1.4203.666.1.55.0
+ * Databases monitor attributes 1.3.6.1.4.1.4203.666.1.55.0.1
+ * LDAP database monitor attributes 1.3.6.1.4.1.4203.666.1.55.0.1.2
+ *
+ * Subsystems monitor objectclasses 1.3.6.1.4.1.4203.666.3.16.0
+ * Databases monitor objectclasses 1.3.6.1.4.1.4203.666.3.16.0.1
+ * LDAP database monitor objectclasses 1.3.6.1.4.1.4203.666.3.16.0.1.2
+ */
+
+static struct {
+ char *name;
+ char *oid;
+} s_oid[] = {
+ { "olmLDAPAttributes", "olmDatabaseAttributes:2" },
+ { "olmLDAPObjectClasses", "olmDatabaseObjectClasses:2" },
+
+ { NULL }
+};
+
+static struct {
+ char *desc;
+ AttributeDescription **ad;
+} s_at[] = {
+ { "( olmLDAPAttributes:1 "
+ "NAME ( 'olmDbURIList' ) "
+ "DESC 'List of URIs a proxy is serving; can be modified run-time' "
+ "SUP managedInfo )",
+ &ad_olmDbURIList },
+ { "( olmLDAPAttributes:2 "
+ "NAME ( 'olmDbOperation' ) "
+ "DESC 'monitor operations performed' "
+ "SUP monitorCounter "
+ "NO-USER-MODIFICATION "
+ "USAGE dSAOperation )",
+ &ad_olmDbOperations },
+ { "( olmLDAPAttributes:3 "
+ "NAME ( 'olmDbBoundDN' ) "
+ "DESC 'monitor connection authorization DN' "
+ "SUP monitorConnectionAuthzDN "
+ "NO-USER-MODIFICATION "
+ "USAGE dSAOperation )",
+ &ad_olmDbBoundDN },
+ { "( olmLDAPAttributes:4 "
+ "NAME ( 'olmDbConnFlags' ) "
+ "DESC 'monitor connection flags' "
+ "SUP monitoredInfo "
+ "NO-USER-MODIFICATION "
+ "USAGE dSAOperation )",
+ &ad_olmDbConnFlags },
+ { "( olmLDAPAttributes:5 "
+ "NAME ( 'olmDbConnURI' ) "
+ "DESC 'monitor connection URI' "
+ "SUP monitorConnectionPeerAddress "
+ "NO-USER-MODIFICATION "
+ "USAGE dSAOperation )",
+ &ad_olmDbConnURI },
+ { "( olmLDAPAttributes:6 "
+ "NAME ( 'olmDbConnPeerAddress' ) "
+ "DESC 'monitor connection peer address' "
+ "SUP monitorConnectionPeerAddress "
+ "NO-USER-MODIFICATION "
+ "USAGE dSAOperation )",
+ &ad_olmDbPeerAddress },
+
+ { NULL }
+};
+
+static struct {
+ char *name;
+ ObjectClass **oc;
+} s_moc[] = {
+ { "monitorContainer", &oc_monitorContainer },
+ { "monitorCounterObject", &oc_monitorCounterObject },
+
+ { NULL }
+};
+
+static struct {
+ char *desc;
+ ObjectClass **oc;
+} s_oc[] = {
+ /* augments an existing object, so it must be AUXILIARY
+ * FIXME: derive from some ABSTRACT "monitoredEntity"? */
+ { "( olmLDAPObjectClasses:1 "
+ "NAME ( 'olmLDAPDatabase' ) "
+ "SUP top AUXILIARY "
+ "MAY ( "
+ "olmDbURIList "
+ ") )",
+ &oc_olmLDAPDatabase },
+ { "( olmLDAPObjectClasses:2 "
+ "NAME ( 'olmLDAPConnection' ) "
+ "SUP monitorConnection STRUCTURAL "
+ "MAY ( "
+ "olmDbBoundDN "
+ "$ olmDbConnFlags "
+ "$ olmDbConnURI "
+ "$ olmDbConnPeerAddress "
+ ") )",
+ &oc_olmLDAPConnection },
+
+ { NULL }
+};
+
+static int
+ldap_back_monitor_update(
+ Operation *op,
+ SlapReply *rs,
+ Entry *e,
+ void *priv )
+{
+ ldapinfo_t *li = (ldapinfo_t *)priv;
+
+ Attribute *a;
+
+ /* update olmDbURIList */
+ a = attr_find( e->e_attrs, ad_olmDbURIList );
+ if ( a != NULL ) {
+ struct berval bv;
+
+ assert( a->a_vals != NULL );
+ assert( !BER_BVISNULL( &a->a_vals[ 0 ] ) );
+ assert( BER_BVISNULL( &a->a_vals[ 1 ] ) );
+
+ ldap_pvt_thread_mutex_lock( &li->li_uri_mutex );
+ if ( li->li_uri ) {
+ ber_str2bv( li->li_uri, 0, 0, &bv );
+ if ( !bvmatch( &a->a_vals[ 0 ], &bv ) ) {
+ ber_bvreplace( &a->a_vals[ 0 ], &bv );
+ }
+ }
+ ldap_pvt_thread_mutex_unlock( &li->li_uri_mutex );
+ }
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+ldap_back_monitor_modify(
+ Operation *op,
+ SlapReply *rs,
+ Entry *e,
+ void *priv )
+{
+ ldapinfo_t *li = (ldapinfo_t *) priv;
+
+ Attribute *save_attrs = NULL;
+ Modifications *ml,
+ *ml_olmDbURIList = NULL;
+ struct berval ul = BER_BVNULL;
+ int got = 0;
+
+ for ( ml = op->orm_modlist; ml; ml = ml->sml_next ) {
+ if ( ml->sml_desc == ad_olmDbURIList ) {
+ if ( ml_olmDbURIList != NULL ) {
+ rs->sr_err = LDAP_CONSTRAINT_VIOLATION;
+ rs->sr_text = "conflicting modifications";
+ goto done;
+ }
+
+ if ( ml->sml_op != LDAP_MOD_REPLACE ) {
+ rs->sr_err = LDAP_CONSTRAINT_VIOLATION;
+ rs->sr_text = "modification not allowed";
+ goto done;
+ }
+
+ ml_olmDbURIList = ml;
+ got++;
+ continue;
+ }
+ }
+
+ if ( got == 0 ) {
+ return SLAP_CB_CONTINUE;
+ }
+
+ save_attrs = attrs_dup( e->e_attrs );
+
+ if ( ml_olmDbURIList != NULL ) {
+ Attribute *a = NULL;
+ LDAPURLDesc *ludlist = NULL;
+ int rc;
+
+ ml = ml_olmDbURIList;
+ assert( ml->sml_nvalues != NULL );
+
+ if ( BER_BVISNULL( &ml->sml_nvalues[ 0 ] ) ) {
+ rs->sr_err = LDAP_CONSTRAINT_VIOLATION;
+ rs->sr_text = "no value provided";
+ goto done;
+ }
+
+ if ( !BER_BVISNULL( &ml->sml_nvalues[ 1 ] ) ) {
+ rs->sr_err = LDAP_CONSTRAINT_VIOLATION;
+ rs->sr_text = "multiple values provided";
+ goto done;
+ }
+
+ rc = ldap_url_parselist_ext( &ludlist,
+ ml->sml_nvalues[ 0 ].bv_val, NULL,
+ LDAP_PVT_URL_PARSE_NOEMPTY_HOST
+ | LDAP_PVT_URL_PARSE_DEF_PORT );
+ if ( rc != LDAP_URL_SUCCESS ) {
+ rs->sr_err = LDAP_INVALID_SYNTAX;
+ rs->sr_text = "unable to parse URI list";
+ goto done;
+ }
+
+ ul.bv_val = ldap_url_list2urls( ludlist );
+ ldap_free_urllist( ludlist );
+ if ( ul.bv_val == NULL ) {
+ rs->sr_err = LDAP_OTHER;
+ goto done;
+ }
+ ul.bv_len = strlen( ul.bv_val );
+
+ a = attr_find( e->e_attrs, ad_olmDbURIList );
+ if ( a != NULL ) {
+ if ( a->a_nvals == a->a_vals ) {
+ a->a_nvals = ch_calloc( sizeof( struct berval ), 2 );
+ }
+
+ ber_bvreplace( &a->a_vals[ 0 ], &ul );
+ ber_bvreplace( &a->a_nvals[ 0 ], &ul );
+
+ } else {
+ attr_merge_normalize_one( e, ad_olmDbURIList, &ul, NULL );
+ }
+ }
+
+ /* apply changes */
+ if ( !BER_BVISNULL( &ul ) ) {
+ ldap_pvt_thread_mutex_lock( &li->li_uri_mutex );
+ if ( li->li_uri ) {
+ ch_free( li->li_uri );
+ }
+ li->li_uri = ul.bv_val;
+ ldap_pvt_thread_mutex_unlock( &li->li_uri_mutex );
+
+ BER_BVZERO( &ul );
+ }
+
+done:;
+ if ( !BER_BVISNULL( &ul ) ) {
+ ldap_memfree( ul.bv_val );
+ }
+
+ if ( rs->sr_err == LDAP_SUCCESS ) {
+ attrs_free( save_attrs );
+ return SLAP_CB_CONTINUE;
+ }
+
+ attrs_free( e->e_attrs );
+ e->e_attrs = save_attrs;
+
+ return rs->sr_err;
+}
+
+static int
+ldap_back_monitor_free(
+ Entry *e,
+ void **priv )
+{
+ ldapinfo_t *li = (ldapinfo_t *)(*priv);
+
+ *priv = NULL;
+
+ if ( !slapd_shutdown ) {
+ memset( &li->li_monitor_info, 0, sizeof( li->li_monitor_info ) );
+ }
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+ldap_back_monitor_subsystem_destroy(
+ BackendDB *be,
+ monitor_subsys_t *ms)
+{
+ free(ms->mss_dn.bv_val);
+ BER_BVZERO(&ms->mss_dn);
+
+ free(ms->mss_ndn.bv_val);
+ BER_BVZERO(&ms->mss_ndn);
+
+ return LDAP_SUCCESS;
+}
+
+/*
+ * Connection monitoring subsystem:
+ * Tries to mimic what the cn=connections,cn=monitor subsystem does
+ * by creating volatile entries for each connection and populating them
+ * according to the information attached to the connection.
+ * At this moment the only exposed information is the DN used to bind it.
+ * Also note that the connection IDs are not and probably never will be
+ * stable.
+ */
+
+struct ldap_back_monitor_conn_arg {
+ Operation *op;
+ monitor_subsys_t *ms;
+ Entry **ep;
+};
+
+/* code stolen from daemon.c */
+static int
+ldap_back_monitor_conn_peername(
+ LDAP *ld,
+ struct berval *bv)
+{
+ Sockbuf *sockbuf;
+ ber_socket_t socket;
+ Sockaddr sa;
+ socklen_t salen = sizeof(sa);
+ const char *peeraddr = NULL;
+ /* we assume INET6_ADDRSTRLEN > INET_ADDRSTRLEN */
+ char addr[INET6_ADDRSTRLEN];
+#ifdef LDAP_PF_LOCAL
+ char peername[MAXPATHLEN + sizeof("PATH=")];
+#elif defined(LDAP_PF_INET6)
+ char peername[sizeof("IP=[ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff]:65535")];
+#else /* ! LDAP_PF_LOCAL && ! LDAP_PF_INET6 */
+ char peername[sizeof("IP=255.255.255.255:65336")];
+#endif /* LDAP_PF_LOCAL */
+
+ assert( bv != NULL );
+
+ ldap_get_option( ld, LDAP_OPT_SOCKBUF, (void **)&sockbuf );
+ ber_sockbuf_ctrl( sockbuf, LBER_SB_OPT_GET_FD, &socket );
+ getpeername( socket, (struct sockaddr *)&sa, &salen );
+
+ switch ( sa.sa_addr.sa_family ) {
+#ifdef LDAP_PF_LOCAL
+ case AF_LOCAL:
+ sprintf( peername, "PATH=%s", sa.sa_un_addr.sun_path );
+ break;
+#endif /* LDAP_PF_LOCAL */
+
+#ifdef LDAP_PF_INET6
+ case AF_INET6:
+ if ( IN6_IS_ADDR_V4MAPPED(&sa.sa_in6_addr.sin6_addr) ) {
+#if defined( HAVE_GETADDRINFO ) && defined( HAVE_INET_NTOP )
+ peeraddr = inet_ntop( AF_INET,
+ ((struct in_addr *)&sa.sa_in6_addr.sin6_addr.s6_addr[12]),
+ addr, sizeof(addr) );
+#else /* ! HAVE_GETADDRINFO || ! HAVE_INET_NTOP */
+ peeraddr = inet_ntoa( *((struct in_addr *)
+ &sa.sa_in6_addr.sin6_addr.s6_addr[12]) );
+#endif /* ! HAVE_GETADDRINFO || ! HAVE_INET_NTOP */
+ if ( !peeraddr ) peeraddr = SLAP_STRING_UNKNOWN;
+ sprintf( peername, "IP=%s:%d", peeraddr,
+ (unsigned) ntohs( sa.sa_in6_addr.sin6_port ) );
+ } else {
+ peeraddr = inet_ntop( AF_INET6,
+ &sa.sa_in6_addr.sin6_addr,
+ addr, sizeof addr );
+ if ( !peeraddr ) peeraddr = SLAP_STRING_UNKNOWN;
+ sprintf( peername, "IP=[%s]:%d", peeraddr,
+ (unsigned) ntohs( sa.sa_in6_addr.sin6_port ) );
+ }
+ break;
+#endif /* LDAP_PF_INET6 */
+
+ case AF_INET: {
+#if defined( HAVE_GETADDRINFO ) && defined( HAVE_INET_NTOP )
+ peeraddr = inet_ntop( AF_INET, &sa.sa_in_addr.sin_addr,
+ addr, sizeof(addr) );
+#else /* ! HAVE_GETADDRINFO || ! HAVE_INET_NTOP */
+ peeraddr = inet_ntoa( sa.sa_in_addr.sin_addr );
+#endif /* ! HAVE_GETADDRINFO || ! HAVE_INET_NTOP */
+ if ( !peeraddr ) peeraddr = SLAP_STRING_UNKNOWN;
+ sprintf( peername, "IP=%s:%d", peeraddr,
+ (unsigned) ntohs( sa.sa_in_addr.sin_port ) );
+ } break;
+
+ default:
+ sprintf( peername, SLAP_STRING_UNKNOWN );
+ }
+
+ ber_str2bv( peername, 0, 1, bv );
+ return LDAP_SUCCESS;
+}
+
+static int
+ldap_back_monitor_conn_entry(
+ ldapconn_t *lc,
+ struct ldap_back_monitor_conn_arg *arg )
+{
+ Entry *e;
+ monitor_entry_t *mp;
+ monitor_extra_t *mbe = arg->op->o_bd->bd_info->bi_extra;
+ char buf[SLAP_TEXT_BUFLEN];
+ char *ptr;
+ struct berval bv;
+ int i;
+
+ bv.bv_val = buf;
+ bv.bv_len = snprintf( bv.bv_val, SLAP_TEXT_BUFLEN,
+ "cn=Connection %lu", lc->lc_connid );
+
+ e = mbe->entry_stub( &arg->ms->mss_dn, &arg->ms->mss_ndn, &bv,
+ oc_monitorContainer, NULL, NULL );
+
+ attr_merge_normalize_one( e, ad_olmDbBoundDN, &lc->lc_bound_ndn, NULL );
+
+ for ( i = 0; s_flag[i].flag; i++ )
+ {
+ if ( lc->lc_flags & s_flag[i].flag )
+ {
+ attr_merge_normalize_one( e, ad_olmDbConnFlags, &s_flag[i].name, NULL );
+ }
+ }
+
+ ldap_get_option( lc->lc_ld, LDAP_OPT_URI, &bv.bv_val );
+ ptr = strchr( bv.bv_val, ' ' );
+ bv.bv_len = ptr ? ptr - bv.bv_val : strlen(bv.bv_val);
+ attr_merge_normalize_one( e, ad_olmDbConnURI, &bv, NULL );
+ ch_free( bv.bv_val );
+
+ ldap_back_monitor_conn_peername( lc->lc_ld, &bv );
+ attr_merge_normalize_one( e, ad_olmDbPeerAddress, &bv, NULL );
+ ch_free( bv.bv_val );
+
+ mp = mbe->entrypriv_create();
+ e->e_private = mp;
+ mp->mp_info = arg->ms;
+ mp->mp_flags = MONITOR_F_SUB | MONITOR_F_VOLATILE;
+
+ *arg->ep = e;
+ arg->ep = &mp->mp_next;
+
+ return 0;
+}
+
+static int
+ldap_back_monitor_conn_create(
+ Operation *op,
+ SlapReply *rs,
+ struct berval *ndn,
+ Entry *e_parent,
+ Entry **ep )
+{
+ monitor_entry_t *mp_parent;
+ monitor_subsys_t *ms;
+ ldapinfo_t *li;
+ ldapconn_t *lc;
+
+ struct ldap_back_monitor_conn_arg *arg;
+ int conn_type;
+ TAvlnode *edge;
+
+ assert( e_parent->e_private != NULL );
+
+ mp_parent = e_parent->e_private;
+ ms = (monitor_subsys_t *)mp_parent->mp_info;
+ li = (ldapinfo_t *)ms->mss_private;
+
+ arg = ch_calloc( 1, sizeof(struct ldap_back_monitor_conn_arg) );
+ arg->op = op;
+ arg->ep = ep;
+ arg->ms = ms;
+
+ for ( conn_type = LDAP_BACK_PCONN_FIRST;
+ conn_type < LDAP_BACK_PCONN_LAST;
+ conn_type++ )
+ {
+ LDAP_TAILQ_FOREACH( lc,
+ &li->li_conn_priv[ conn_type ].lic_priv,
+ lc_q )
+ {
+ ldap_back_monitor_conn_entry( lc, arg );
+ }
+ }
+
+ edge = ldap_tavl_end( li->li_conninfo.lai_tree, TAVL_DIR_LEFT );
+ while ( edge ) {
+ TAvlnode *next = ldap_tavl_next( edge, TAVL_DIR_RIGHT );
+ ldapconn_t *lc = (ldapconn_t *)edge->avl_data;
+ ldap_back_monitor_conn_entry( lc, arg );
+ edge = next;
+ }
+
+ ch_free( arg );
+
+ return 0;
+}
+
+static int
+ldap_back_monitor_conn_init(
+ BackendDB *be,
+ monitor_subsys_t *ms )
+{
+ ldapinfo_t *li = (ldapinfo_t *) ms->mss_private;
+ monitor_extra_t *mbe;
+
+ Entry *e;
+ int rc;
+
+ assert( be != NULL );
+ mbe = (monitor_extra_t *) be->bd_info->bi_extra;
+
+ ms->mss_dn = ms->mss_ndn = li->li_monitor_info.lmi_ndn;
+ ms->mss_rdn = li->li_monitor_info.lmi_conn_rdn;
+ ms->mss_create = ldap_back_monitor_conn_create;
+ ms->mss_destroy = ldap_back_monitor_subsystem_destroy;
+
+ e = mbe->entry_stub( &ms->mss_dn, &ms->mss_ndn,
+ &ms->mss_rdn, oc_monitorContainer, NULL, NULL );
+ if ( e == NULL ) {
+ Debug( LDAP_DEBUG_ANY,
+ "ldap_back_monitor_conn_init: "
+ "unable to create entry \"%s,%s\"\n",
+ li->li_monitor_info.lmi_conn_rdn.bv_val,
+ ms->mss_ndn.bv_val );
+ return( -1 );
+ }
+
+ ber_dupbv( &ms->mss_dn, &e->e_name );
+ ber_dupbv( &ms->mss_ndn, &e->e_nname );
+
+ rc = mbe->register_entry( e, NULL, ms, MONITOR_F_VOLATILE_CH );
+
+ /* add labeledURI and special, modifiable URI value */
+ if ( rc == LDAP_SUCCESS && li->li_uri != NULL ) {
+ struct berval bv;
+ Attribute *a;
+ LDAPURLDesc *ludlist = NULL;
+ monitor_callback_t *cb = NULL;
+
+ a = attr_alloc( ad_olmDbURIList );
+
+ ber_str2bv( li->li_uri, 0, 0, &bv );
+ attr_valadd( a, &bv, NULL, 1 );
+ attr_normalize( a->a_desc, a->a_vals, &a->a_nvals, NULL );
+
+ rc = ldap_url_parselist_ext( &ludlist,
+ li->li_uri, NULL,
+ LDAP_PVT_URL_PARSE_NOEMPTY_HOST
+ | LDAP_PVT_URL_PARSE_DEF_PORT );
+ if ( rc != LDAP_URL_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY,
+ "ldap_back_monitor_db_open: "
+ "unable to parse URI list (ignored)\n" );
+ } else {
+ Attribute *a2 = attr_alloc( slap_schema.si_ad_labeledURI );
+
+ a->a_next = a2;
+
+ for ( ; ludlist != NULL; ) {
+ LDAPURLDesc *next = ludlist->lud_next;
+
+ bv.bv_val = ldap_url_desc2str( ludlist );
+ assert( bv.bv_val != NULL );
+ ldap_free_urldesc( ludlist );
+ bv.bv_len = strlen( bv.bv_val );
+ attr_valadd( a2, &bv, NULL, 1 );
+ ch_free( bv.bv_val );
+
+ ludlist = next;
+ }
+
+ attr_normalize( a2->a_desc, a2->a_vals, &a2->a_nvals, NULL );
+ }
+
+ cb = ch_calloc( sizeof( monitor_callback_t ), 1 );
+ cb->mc_update = ldap_back_monitor_update;
+ cb->mc_modify = ldap_back_monitor_modify;
+ cb->mc_free = ldap_back_monitor_free;
+ cb->mc_private = (void *)li;
+
+ rc = mbe->register_entry_attrs( &ms->mss_ndn, a, cb, NULL, -1, NULL );
+
+ attr_free( a->a_next );
+ attr_free( a );
+
+ if ( rc != LDAP_SUCCESS )
+ {
+ ch_free( cb );
+ }
+ }
+
+ entry_free( e );
+
+ return rc;
+}
+
+/*
+ * Operation monitoring subsystem:
+ * Looks a lot like the cn=operations,cn=monitor subsystem except that at this
+ * moment, only completed operations are counted. Each entry has a separate
+ * callback with all the needed information linked there in the structure
+ * below so that the callback need not locate it over and over again.
+ */
+
+struct ldap_back_monitor_op_counter {
+ ldap_pvt_mp_t *data;
+ ldap_pvt_thread_mutex_t *mutex;
+};
+
+static void
+ldap_back_monitor_ops_dispose(
+ void **priv)
+{
+ struct ldap_back_monitor_op_counter *counter = *priv;
+
+ ch_free( counter );
+ counter = NULL;
+}
+
+static int
+ldap_back_monitor_ops_free(
+ Entry *e,
+ void **priv)
+{
+ ldap_back_monitor_ops_dispose( priv );
+ return LDAP_SUCCESS;
+}
+
+static int
+ldap_back_monitor_ops_update(
+ Operation *op,
+ SlapReply *rs,
+ Entry *e,
+ void *priv )
+{
+ struct ldap_back_monitor_op_counter *counter = priv;
+ Attribute *a;
+
+ /*TODO
+ * what about initiated/completed?
+ */
+ a = attr_find( e->e_attrs, ad_olmDbOperations );
+ assert( a != NULL );
+
+ ldap_pvt_thread_mutex_lock( counter->mutex );
+ UI2BV( &a->a_vals[ 0 ], *counter->data );
+ ldap_pvt_thread_mutex_unlock( counter->mutex );
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+ldap_back_monitor_ops_init(
+ BackendDB *be,
+ monitor_subsys_t *ms )
+{
+ ldapinfo_t *li = (ldapinfo_t *) ms->mss_private;
+
+ monitor_extra_t *mbe;
+ Entry *e, *parent;
+ int rc;
+ slap_op_t op;
+ struct berval value = BER_BVC( "0" );
+
+ assert( be != NULL );
+
+ mbe = (monitor_extra_t *) be->bd_info->bi_extra;
+
+ ms->mss_dn = ms->mss_ndn = li->li_monitor_info.lmi_ndn;
+ ms->mss_rdn = li->li_monitor_info.lmi_ops_rdn;
+ ms->mss_destroy = ldap_back_monitor_subsystem_destroy;
+
+ parent = mbe->entry_stub( &ms->mss_dn, &ms->mss_ndn,
+ &ms->mss_rdn, oc_monitorContainer, NULL, NULL );
+ if ( parent == NULL ) {
+ Debug( LDAP_DEBUG_ANY,
+ "ldap_back_monitor_ops_init: "
+ "unable to create entry \"%s,%s\"\n",
+ li->li_monitor_info.lmi_ops_rdn.bv_val,
+ ms->mss_ndn.bv_val );
+ return( -1 );
+ }
+
+ ber_dupbv( &ms->mss_dn, &parent->e_name );
+ ber_dupbv( &ms->mss_ndn, &parent->e_nname );
+
+ rc = mbe->register_entry( parent, NULL, ms, MONITOR_F_PERSISTENT_CH );
+ if ( rc != LDAP_SUCCESS )
+ {
+ Debug( LDAP_DEBUG_ANY,
+ "ldap_back_monitor_ops_init: "
+ "unable to register entry \"%s\" for monitoring\n",
+ parent->e_name.bv_val );
+ goto done;
+ }
+
+ for ( op = 0; op < SLAP_OP_LAST; op++ )
+ {
+ monitor_callback_t *cb;
+ struct ldap_back_monitor_op_counter *counter;
+
+ e = mbe->entry_stub( &parent->e_name, &parent->e_nname,
+ &ldap_back_monitor_op[op].rdn,
+ oc_monitorCounterObject, NULL, NULL );
+ if ( e == NULL ) {
+ Debug( LDAP_DEBUG_ANY,
+ "ldap_back_monitor_ops_init: "
+ "unable to create entry \"%s,%s\"\n",
+ ldap_back_monitor_op[op].rdn.bv_val,
+ parent->e_nname.bv_val );
+ return( -1 );
+ }
+
+ attr_merge_normalize_one( e, ad_olmDbOperations, &value, NULL );
+
+ counter = ch_malloc( sizeof( struct ldap_back_monitor_op_counter ) );
+ counter->data = &li->li_ops_completed[ op ];
+ counter->mutex = &li->li_counter_mutex;
+
+ /*
+ * We cannot share a single callback between entries.
+ *
+ * monitor_cache_destroy() tries to free all callbacks and it's called
+ * before mss_destroy() so we have no chance of handling it ourselves
+ */
+ cb = ch_calloc( sizeof( monitor_callback_t ), 1 );
+ cb->mc_update = ldap_back_monitor_ops_update;
+ cb->mc_free = ldap_back_monitor_ops_free;
+ cb->mc_dispose = ldap_back_monitor_ops_dispose;
+ cb->mc_private = (void *)counter;
+
+ rc = mbe->register_entry( e, cb, ms, 0 );
+
+ /* TODO: register_entry has stored a duplicate so we might actually reuse it
+ * instead of recreating it every time... */
+ entry_free( e );
+
+ if ( rc != LDAP_SUCCESS )
+ {
+ Debug( LDAP_DEBUG_ANY,
+ "ldap_back_monitor_ops_init: "
+ "unable to register entry \"%s\" for monitoring\n",
+ e->e_name.bv_val );
+ ch_free( cb );
+ break;
+ }
+ }
+
+done:
+ entry_free( parent );
+
+ return rc;
+}
+
+/*
+ * call from within ldap_back_initialize()
+ */
+static int
+ldap_back_monitor_initialize( void )
+{
+ int i, code;
+ ConfigArgs c;
+ char *argv[ 3 ];
+
+ static int ldap_back_monitor_initialized = 0;
+
+ /* set to 0 when successfully initialized; otherwise, remember failure */
+ static int ldap_back_monitor_initialized_failure = 1;
+
+ /* register schema here */
+
+ if ( ldap_back_monitor_initialized++ ) {
+ return ldap_back_monitor_initialized_failure;
+ }
+
+ if ( backend_info( "monitor" ) == NULL ) {
+ return -1;
+ }
+
+ argv[ 0 ] = "back-ldap monitor";
+ c.argv = argv;
+ c.argc = 3;
+ c.fname = argv[0];
+ for ( i = 0; s_oid[ i ].name; i++ ) {
+
+ argv[ 1 ] = s_oid[ i ].name;
+ argv[ 2 ] = s_oid[ i ].oid;
+
+ if ( parse_oidm( &c, 0, NULL ) != 0 ) {
+ Debug( LDAP_DEBUG_ANY,
+ "ldap_back_monitor_initialize: unable to add "
+ "objectIdentifier \"%s=%s\"\n",
+ s_oid[ i ].name, s_oid[ i ].oid );
+ return 2;
+ }
+ }
+
+ for ( i = 0; s_at[ i ].desc != NULL; i++ ) {
+ code = register_at( s_at[ i ].desc, s_at[ i ].ad, 1 );
+ if ( code != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY,
+ "ldap_back_monitor_initialize: register_at failed for attributeType (%s)\n",
+ s_at[ i ].desc );
+ return 3;
+
+ } else {
+ (*s_at[ i ].ad)->ad_type->sat_flags |= SLAP_AT_HIDE;
+ }
+ }
+
+ for ( i = 0; s_oc[ i ].desc != NULL; i++ ) {
+ code = register_oc( s_oc[ i ].desc, s_oc[ i ].oc, 1 );
+ if ( code != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY,
+ "ldap_back_monitor_initialize: register_oc failed for objectClass (%s)\n",
+ s_oc[ i ].desc );
+ return 4;
+
+ } else {
+ (*s_oc[ i ].oc)->soc_flags |= SLAP_OC_HIDE;
+ }
+ }
+
+ for ( i = 0; s_moc[ i ].name != NULL; i++ ) {
+ *s_moc[i].oc = oc_find( s_moc[ i ].name );
+ if ( ! *s_moc[i].oc ) {
+ Debug( LDAP_DEBUG_ANY,
+ "ldap_back_monitor_initialize: failed to find objectClass (%s)\n",
+ s_moc[ i ].name );
+ return 5;
+
+ }
+ }
+
+ return ( ldap_back_monitor_initialized_failure = LDAP_SUCCESS );
+}
+
+/*
+ * call from within ldap_back_db_init()
+ */
+int
+ldap_back_monitor_db_init( BackendDB *be )
+{
+ int rc;
+
+ rc = ldap_back_monitor_initialize();
+ if ( rc != LDAP_SUCCESS ) {
+ return rc;
+ }
+
+#if 0 /* uncomment to turn monitoring on by default */
+ SLAP_DBFLAGS( be ) |= SLAP_DBFLAG_MONITORING;
+#endif
+
+ return 0;
+}
+
+/*
+ * call from within ldap_back_db_open()
+ */
+int
+ldap_back_monitor_db_open( BackendDB *be )
+{
+ ldapinfo_t *li = (ldapinfo_t *) be->be_private;
+ monitor_subsys_t *mss = li->li_monitor_info.lmi_mss;
+ int rc = 0;
+ BackendInfo *mi;
+ monitor_extra_t *mbe;
+
+ if ( !SLAP_DBMONITORING( be ) ) {
+ return 0;
+ }
+
+ /* check if monitor is configured and usable */
+ mi = backend_info( "monitor" );
+ if ( !mi || !mi->bi_extra ) {
+ SLAP_DBFLAGS( be ) ^= SLAP_DBFLAG_MONITORING;
+ return 0;
+ }
+ mbe = mi->bi_extra;
+
+ /* don't bother if monitor is not configured */
+ if ( !mbe->is_configured() ) {
+ static int warning = 0;
+
+ if ( warning++ == 0 ) {
+ Debug( LDAP_DEBUG_CONFIG, "ldap_back_monitor_db_open: "
+ "monitoring disabled; "
+ "configure monitor database to enable\n" );
+ }
+
+ return 0;
+ }
+
+ /* caller (e.g. an overlay based on back-ldap) may want to use
+ * a different DN and RDNs... */
+ if ( BER_BVISNULL( &li->li_monitor_info.lmi_ndn ) ) {
+ rc = mbe->register_database( be, &li->li_monitor_info.lmi_ndn );
+ if ( rc != 0 ) {
+ Debug( LDAP_DEBUG_ANY, "ldap_back_monitor_db_open: "
+ "failed to register the database with back-monitor\n" );
+ }
+ }
+ if ( BER_BVISNULL( &li->li_monitor_info.lmi_conn_rdn ) ) {
+ ber_str2bv( "cn=Connections", 0, 1,
+ &li->li_monitor_info.lmi_conn_rdn );
+ }
+ if ( BER_BVISNULL( &li->li_monitor_info.lmi_ops_rdn ) ) {
+ ber_str2bv( "cn=Operations", 0, 1,
+ &li->li_monitor_info.lmi_ops_rdn );
+ }
+
+ /* set up the subsystems used to create the operation and
+ * volatile connection entries */
+
+ mss->mss_name = "back-ldap connections";
+ mss->mss_flags = MONITOR_F_VOLATILE_CH;
+ mss->mss_open = ldap_back_monitor_conn_init;
+ mss->mss_private = li;
+
+ if ( mbe->register_subsys_late( mss ) )
+ {
+ Debug( LDAP_DEBUG_ANY,
+ "ldap_back_monitor_db_open: "
+ "failed to register connection subsystem" );
+ return -1;
+ }
+
+ mss++;
+
+ mss->mss_name = "back-ldap operations";
+ mss->mss_flags = MONITOR_F_PERSISTENT_CH;
+ mss->mss_open = ldap_back_monitor_ops_init;
+ mss->mss_private = li;
+
+ if ( mbe->register_subsys_late( mss ) )
+ {
+ Debug( LDAP_DEBUG_ANY,
+ "ldap_back_monitor_db_open: "
+ "failed to register operation subsystem" );
+ return -1;
+ }
+
+ return rc;
+}
+
+/*
+ * call from within ldap_back_db_close()
+ */
+int
+ldap_back_monitor_db_close( BackendDB *be )
+{
+ ldapinfo_t *li = (ldapinfo_t *) be->be_private;
+
+ if ( li && !BER_BVISNULL( &li->li_monitor_info.lmi_ndn ) ) {
+ BackendInfo *mi;
+ monitor_extra_t *mbe;
+
+ /* check if monitor is configured and usable */
+ mi = backend_info( "monitor" );
+ if ( mi && mi->bi_extra ) {
+ mbe = mi->bi_extra;
+
+ /*TODO
+ * Unregister all entries our subsystems have created.
+ * Will only really be necessary when
+ * SLAPD_CONFIG_DELETE is enabled.
+ *
+ * Might need a way to unregister subsystems instead.
+ */
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * call from within ldap_back_db_destroy()
+ */
+int
+ldap_back_monitor_db_destroy( BackendDB *be )
+{
+ ldapinfo_t *li = (ldapinfo_t *) be->be_private;
+
+ if ( li ) {
+ memset( &li->li_monitor_info, 0, sizeof( li->li_monitor_info ) );
+ }
+
+ return 0;
+}
+
diff --git a/servers/slapd/back-ldap/pbind.c b/servers/slapd/back-ldap/pbind.c
new file mode 100644
index 0000000..f5841e9
--- /dev/null
+++ b/servers/slapd/back-ldap/pbind.c
@@ -0,0 +1,173 @@
+/* pbind.c - passthru Bind overlay */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2003-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2003-2010 Howard Chu.
+ * 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 the Howard Chu for inclusion
+ * in OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#include "lutil.h"
+#include "slap.h"
+#include "back-ldap.h"
+#include "slap-config.h"
+
+static BackendInfo *lback;
+
+static slap_overinst ldappbind;
+
+static int
+ldap_pbind_bind(
+ Operation *op,
+ SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
+ void *private = op->o_bd->be_private;
+ void *bi = op->o_bd->bd_info;
+ int rc;
+
+ op->o_bd->bd_info = lback;
+ op->o_bd->be_private = on->on_bi.bi_private;
+ rc = lback->bi_op_bind( op, rs );
+ op->o_bd->be_private = private;
+ op->o_bd->bd_info = bi;
+
+ return rc;
+}
+
+static int
+ldap_pbind_db_init(
+ BackendDB *be,
+ ConfigReply *cr )
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ ConfigOCs *be_cf_ocs = be->be_cf_ocs;
+ void *private = be->be_private;
+ int rc;
+
+ if ( lback == NULL ) {
+ lback = backend_info( "ldap" );
+
+ if ( lback == NULL ) {
+ return 1;
+ }
+ }
+
+ rc = lback->bi_db_init( be, cr );
+ on->on_bi.bi_private = be->be_private;
+ be->be_cf_ocs = be_cf_ocs;
+ be->be_private = private;
+
+ return rc;
+}
+
+static int
+ldap_pbind_db_open(
+ BackendDB *be,
+ ConfigReply *cr )
+{
+ slap_overinst *on = (slap_overinst *) be->bd_info;
+ void *private = be->be_private;
+ int rc;
+ int monitoring;
+
+ be->be_private = on->on_bi.bi_private;
+ monitoring = ( SLAP_DBFLAGS( be ) & SLAP_DBFLAG_MONITORING );
+ SLAP_DBFLAGS( be ) &= ~SLAP_DBFLAG_MONITORING;
+ rc = lback->bi_db_open( be, cr );
+ SLAP_DBFLAGS( be ) |= monitoring;
+ be->be_private = private;
+
+ return rc;
+}
+
+static int
+ldap_pbind_db_close(
+ BackendDB *be,
+ ConfigReply *cr )
+{
+ slap_overinst *on = (slap_overinst *) be->bd_info;
+ void *private = be->be_private;
+ int rc;
+
+ be->be_private = on->on_bi.bi_private;
+ rc = lback->bi_db_close( be, cr );
+ be->be_private = private;
+
+ return rc;
+}
+
+static int
+ldap_pbind_db_destroy(
+ BackendDB *be,
+ ConfigReply *cr )
+{
+ slap_overinst *on = (slap_overinst *) be->bd_info;
+ void *private = be->be_private;
+ int rc;
+
+ be->be_private = on->on_bi.bi_private;
+ rc = lback->bi_db_close( be, cr );
+ on->on_bi.bi_private = be->be_private;
+ be->be_private = private;
+
+ return rc;
+}
+
+static int
+ldap_pbind_connection_destroy(
+ BackendDB *be,
+ Connection *conn
+)
+{
+ slap_overinst *on = (slap_overinst *) be->bd_info;
+ void *private = be->be_private;
+ int rc;
+
+ be->be_private = on->on_bi.bi_private;
+ rc = lback->bi_connection_destroy( be, conn );
+ be->be_private = private;
+
+ return rc;
+}
+
+int
+pbind_initialize( void )
+{
+ int rc;
+
+ ldappbind.on_bi.bi_type = "pbind";
+ ldappbind.on_bi.bi_db_init = ldap_pbind_db_init;
+ ldappbind.on_bi.bi_db_open = ldap_pbind_db_open;
+ ldappbind.on_bi.bi_db_close = ldap_pbind_db_close;
+ ldappbind.on_bi.bi_db_destroy = ldap_pbind_db_destroy;
+
+ ldappbind.on_bi.bi_op_bind = ldap_pbind_bind;
+ ldappbind.on_bi.bi_connection_destroy = ldap_pbind_connection_destroy;
+
+ rc = ldap_pbind_init_cf( &ldappbind.on_bi );
+ if ( rc ) {
+ return rc;
+ }
+
+ return overlay_register( &ldappbind );
+}
diff --git a/servers/slapd/back-ldap/proto-ldap.h b/servers/slapd/back-ldap/proto-ldap.h
new file mode 100644
index 0000000..445d551
--- /dev/null
+++ b/servers/slapd/back-ldap/proto-ldap.h
@@ -0,0 +1,124 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2003-2022 The OpenLDAP Foundation.
+ * 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 the Howard Chu for inclusion
+ * in OpenLDAP Software and subsequently enhanced by Pierangelo
+ * Masarati.
+ */
+
+#ifndef PROTO_LDAP_H
+#define PROTO_LDAP_H
+
+LDAP_BEGIN_DECL
+
+extern BI_init ldap_back_initialize;
+extern BI_open ldap_back_open;
+
+extern BI_db_init ldap_back_db_init;
+extern BI_db_open ldap_back_db_open;
+extern BI_db_close ldap_back_db_close;
+extern BI_db_destroy ldap_back_db_destroy;
+
+extern BI_op_bind ldap_back_bind;
+extern BI_op_search ldap_back_search;
+extern BI_op_compare ldap_back_compare;
+extern BI_op_modify ldap_back_modify;
+extern BI_op_modrdn ldap_back_modrdn;
+extern BI_op_add ldap_back_add;
+extern BI_op_delete ldap_back_delete;
+extern BI_op_abandon ldap_back_abandon;
+extern BI_op_extended ldap_back_extended;
+
+extern BI_connection_destroy ldap_back_conn_destroy;
+
+extern BI_entry_get_rw ldap_back_entry_get;
+
+void ldap_back_release_conn_lock( ldapinfo_t *li, ldapconn_t **lcp, int dolock );
+#define ldap_back_release_conn(li, lc) ldap_back_release_conn_lock((li), &(lc), 1)
+int ldap_back_dobind( ldapconn_t **lcp, Operation *op, SlapReply *rs, ldap_back_send_t sendok );
+int ldap_back_retry( ldapconn_t **lcp, Operation *op, SlapReply *rs, ldap_back_send_t sendok );
+int ldap_back_map_result( SlapReply *rs );
+int ldap_back_op_result( ldapconn_t *lc, Operation *op, SlapReply *rs,
+ ber_int_t msgid, time_t timeout, ldap_back_send_t sendok );
+int ldap_back_cancel( ldapconn_t *lc, Operation *op, SlapReply *rs, ber_int_t msgid, ldap_back_send_t sendok );
+
+int ldap_back_init_cf( BackendInfo *bi );
+int ldap_pbind_init_cf( BackendInfo *bi );
+
+extern int ldap_back_conndn_cmp( const void *c1, const void *c2);
+extern int ldap_back_conn_cmp( const void *c1, const void *c2);
+extern int ldap_back_conndn_dup( void *c1, void *c2 );
+extern void ldap_back_conn_free( void *c );
+
+extern ldapconn_t * ldap_back_conn_delete( ldapinfo_t *li, ldapconn_t *lc );
+
+extern int ldap_back_conn2str( const ldapconn_base_t *lc, char *buf, ber_len_t buflen );
+extern int ldap_back_connid2str( const ldapconn_base_t *lc, char *buf, ber_len_t buflen );
+
+extern int
+ldap_back_proxy_authz_ctrl(
+ Operation *op,
+ SlapReply *rs,
+ struct berval *bound_ndn,
+ int version,
+ slap_idassert_t *si,
+ LDAPControl *ctrl );
+
+extern int
+ldap_back_controls_add(
+ Operation *op,
+ SlapReply *rs,
+ ldapconn_t *lc,
+ LDAPControl ***pctrls );
+
+extern int
+ldap_back_controls_free( Operation *op, SlapReply *rs, LDAPControl ***pctrls );
+
+extern void
+ldap_back_quarantine(
+ Operation *op,
+ SlapReply *rs );
+
+#ifdef LDAP_BACK_PRINT_CONNTREE
+extern void
+ldap_back_print_conntree( ldapinfo_t *li, char *msg );
+#endif /* LDAP_BACK_PRINT_CONNTREE */
+
+extern void slap_retry_info_destroy( slap_retry_info_t *ri );
+extern int slap_retry_info_parse( char *in, slap_retry_info_t *ri,
+ char *buf, ber_len_t buflen );
+extern int slap_retry_info_unparse( slap_retry_info_t *ri, struct berval *bvout );
+
+extern int slap_idassert_authzfrom_parse( struct config_args_s *ca, slap_idassert_t *si );
+extern int slap_idassert_passthru_parse_cf( const char *fname, int lineno, const char *arg, slap_idassert_t *si );
+extern int slap_idassert_parse( struct config_args_s *ca, slap_idassert_t *si );
+
+extern int chain_initialize( void );
+extern int pbind_initialize( void );
+#ifdef SLAP_DISTPROC
+extern int distproc_initialize( void );
+#endif
+
+extern int ldap_back_monitor_db_init( BackendDB *be );
+extern int ldap_back_monitor_db_open( BackendDB *be );
+extern int ldap_back_monitor_db_close( BackendDB *be );
+extern int ldap_back_monitor_db_destroy( BackendDB *be );
+
+extern LDAP_REBIND_PROC ldap_back_default_rebind;
+extern LDAP_URLLIST_PROC ldap_back_default_urllist;
+
+LDAP_END_DECL
+
+#endif /* PROTO_LDAP_H */
diff --git a/servers/slapd/back-ldap/search.c b/servers/slapd/back-ldap/search.c
new file mode 100644
index 0000000..90b5b65
--- /dev/null
+++ b/servers/slapd/back-ldap/search.c
@@ -0,0 +1,1042 @@
+/* search.c - ldap backend search function */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1999-2022 The OpenLDAP Foundation.
+ * Portions Copyright 1999-2003 Howard Chu.
+ * Portions Copyright 2000-2003 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 the Howard Chu for inclusion
+ * in OpenLDAP Software and subsequently enhanced by Pierangelo
+ * Masarati.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "slap.h"
+#include "back-ldap.h"
+#include "../../../libraries/liblber/lber-int.h"
+
+#include "lutil.h"
+
+static int
+ldap_build_entry( Operation *op, LDAPMessage *e, Entry *ent,
+ struct berval *bdn, int remove_unknown_schema );
+
+
+static ObjectClass *
+oc_bvfind_undef_ex( struct berval *ocname, int flag )
+{
+ ObjectClass *oc = oc_bvfind( ocname );
+
+ if ( oc || flag ) {
+ /* oc defined or remove-unknown-schema flag set */
+ return oc;
+ }
+
+ return oc_bvfind_undef( ocname );
+}
+
+
+/*
+ * replaces (&) with (objectClass=*) and (|) with (!(objectClass=*))
+ * as the best replacement for RFC 4526 absolute true/absolute false
+ * filters; the only difference (AFAIK) is that they require search
+ * access to objectClass.
+ *
+ * filter->bv_val may be alloc'd on the thread's slab, if equal to
+ * op->ors_filterstr.bv_val, or realloc'd on the thread's slab otherwise.
+ */
+static int
+ldap_back_munge_filter(
+ Operation *op,
+ struct berval *filter )
+{
+ char *ptr;
+ int gotit = 0;
+
+ Debug( LDAP_DEBUG_ARGS, "=> ldap_back_munge_filter \"%s\"\n",
+ filter->bv_val );
+
+ for ( ptr = strchr( filter->bv_val, '(' );
+ ptr;
+ ptr = strchr( ptr, '(' ) )
+ {
+ static struct berval
+ bv_t = BER_BVC( "(&)" ),
+ bv_f = BER_BVC( "(|)" ),
+ bv_T = BER_BVC( "(objectClass=*)" ),
+ bv_F = BER_BVC( "(!(objectClass=*))" );
+ struct berval *oldbv = NULL,
+ *newbv = NULL,
+ oldfilter = BER_BVNULL;
+
+ if ( ptr[2] != ')' ) {
+ ptr++;
+ continue;
+ }
+
+ switch ( ptr[1] ) {
+ case '&':
+ oldbv = &bv_t;
+ newbv = &bv_T;
+ break;
+
+ case '|':
+ oldbv = &bv_f;
+ newbv = &bv_F;
+ break;
+
+ default:
+ /* should be an error */
+ continue;
+ }
+
+ oldfilter = *filter;
+ filter->bv_len += newbv->bv_len - oldbv->bv_len;
+ if ( filter->bv_val == op->ors_filterstr.bv_val ) {
+ filter->bv_val = op->o_tmpalloc( filter->bv_len + 1,
+ op->o_tmpmemctx );
+
+ AC_MEMCPY( filter->bv_val, op->ors_filterstr.bv_val,
+ ptr - oldfilter.bv_val );
+
+ } else {
+ filter->bv_val = op->o_tmprealloc( filter->bv_val,
+ filter->bv_len + 1, op->o_tmpmemctx );
+ }
+
+ ptr = filter->bv_val + ( ptr - oldfilter.bv_val );
+
+ AC_MEMCPY( &ptr[ newbv->bv_len ],
+ &ptr[ oldbv->bv_len ],
+ oldfilter.bv_len - ( ptr - filter->bv_val ) - oldbv->bv_len + 1 );
+ AC_MEMCPY( ptr, newbv->bv_val, newbv->bv_len );
+
+ ptr += newbv->bv_len;
+
+ gotit++;
+ }
+
+ Debug( LDAP_DEBUG_ARGS, "<= ldap_back_munge_filter \"%s\" (%d)\n",
+ filter->bv_val, gotit );
+
+ return gotit;
+}
+
+int
+ldap_back_search(
+ Operation *op,
+ SlapReply *rs )
+{
+ ldapinfo_t *li = (ldapinfo_t *) op->o_bd->be_private;
+
+ ldapconn_t *lc = NULL;
+ struct timeval tv;
+ time_t stoptime = (time_t)(-1);
+ LDAPMessage *res,
+ *e;
+ int rc = 0,
+ msgid;
+ struct berval match = BER_BVNULL,
+ filter = BER_BVNULL;
+ int i, x;
+ char **attrs = NULL;
+ int freetext = 0, filter_undef = 0;
+ int do_retry = 1, dont_retry = 0;
+ LDAPControl **ctrls = NULL;
+ char **references = NULL;
+ int remove_unknown_schema =
+ LDAP_BACK_OMIT_UNKNOWN_SCHEMA (li);
+
+ rs_assert_ready( rs );
+ rs->sr_flags &= ~REP_ENTRY_MASK; /* paranoia, we can set rs = non-entry */
+
+ if ( !ldap_back_dobind( &lc, op, rs, LDAP_BACK_SENDERR ) ) {
+ return rs->sr_err;
+ }
+
+ /*
+ * FIXME: in case of values return filter, we might want
+ * to map attrs and maybe rewrite value
+ */
+
+ if ( op->ors_tlimit != SLAP_NO_LIMIT ) {
+ tv.tv_sec = op->ors_tlimit;
+ tv.tv_usec = 0;
+ stoptime = op->o_time + op->ors_tlimit;
+
+ } else {
+ LDAP_BACK_TV_SET( &tv );
+ }
+
+ i = 0;
+ if ( op->ors_attrs ) {
+ for ( ; !BER_BVISNULL( &op->ors_attrs[i].an_name ); i++ )
+ /* just count attrs */ ;
+ }
+
+ x = 0;
+ if ( op->o_bd->be_extra_anlist ) {
+ for ( ; !BER_BVISNULL( &op->o_bd->be_extra_anlist[x].an_name ); x++ )
+ /* just count attrs */ ;
+ }
+
+ if ( i > 0 || x > 0 ) {
+ int j = 0;
+
+ attrs = op->o_tmpalloc( ( i + x + 1 )*sizeof( char * ),
+ op->o_tmpmemctx );
+ if ( attrs == NULL ) {
+ rs->sr_err = LDAP_NO_MEMORY;
+ rc = -1;
+ goto finish;
+ }
+
+ if ( i > 0 ) {
+ for ( i = 0; !BER_BVISNULL( &op->ors_attrs[i].an_name ); i++, j++ ) {
+ attrs[ j ] = op->ors_attrs[i].an_name.bv_val;
+ }
+ }
+
+ if ( x > 0 ) {
+ for ( x = 0; !BER_BVISNULL( &op->o_bd->be_extra_anlist[x].an_name ); x++, j++ ) {
+ if ( op->o_bd->be_extra_anlist[x].an_desc &&
+ ad_inlist( op->o_bd->be_extra_anlist[x].an_desc, op->ors_attrs ) )
+ {
+ continue;
+ }
+
+ attrs[ j ] = op->o_bd->be_extra_anlist[x].an_name.bv_val;
+ }
+ }
+
+ attrs[ j ] = NULL;
+ }
+
+ ctrls = op->o_ctrls;
+ rc = ldap_back_controls_add( op, rs, lc, &ctrls );
+ if ( rc != LDAP_SUCCESS ) {
+ goto finish;
+ }
+
+ /* deal with <draft-zeilenga-ldap-t-f> filters */
+ filter = op->ors_filterstr;
+retry:
+ /* this goes after retry because ldap_back_munge_filter()
+ * optionally replaces RFC 4526 T-F filters (&) (|)
+ * if already computed, they will be re-installed
+ * by filter2bv_undef_x() later */
+ if ( !LDAP_BACK_T_F( li ) ) {
+ ldap_back_munge_filter( op, &filter );
+ }
+
+ rs->sr_err = ldap_pvt_search( lc->lc_ld, op->o_req_dn.bv_val,
+ op->ors_scope, filter.bv_val,
+ attrs, op->ors_attrsonly, ctrls, NULL,
+ tv.tv_sec ? &tv : NULL,
+ op->ors_slimit, op->ors_deref, &msgid );
+
+ ldap_pvt_thread_mutex_lock( &li->li_counter_mutex );
+ ldap_pvt_mp_add( li->li_ops_completed[ SLAP_OP_SEARCH ], 1 );
+ ldap_pvt_thread_mutex_unlock( &li->li_counter_mutex );
+
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ switch ( rs->sr_err ) {
+ case LDAP_SERVER_DOWN:
+ if ( do_retry ) {
+ do_retry = 0;
+ if ( ldap_back_retry( &lc, op, rs, LDAP_BACK_DONTSEND ) ) {
+ goto retry;
+ }
+ }
+
+ if ( lc == NULL ) {
+ /* reset by ldap_back_retry ... */
+ rs->sr_err = slap_map_api2result( rs );
+
+ } else {
+ rc = ldap_back_op_result( lc, op, rs, msgid, 0, LDAP_BACK_DONTSEND );
+ }
+
+ goto finish;
+
+ case LDAP_FILTER_ERROR:
+ /* first try? */
+ if ( !filter_undef &&
+ strstr( filter.bv_val, "(?" ) &&
+ !LDAP_BACK_NOUNDEFFILTER( li ) )
+ {
+ BER_BVZERO( &filter );
+ filter2bv_undef_x( op, op->ors_filter, 1, &filter );
+ filter_undef = 1;
+ goto retry;
+ }
+
+ /* invalid filters return success with no data */
+ rs->sr_err = LDAP_SUCCESS;
+ rs->sr_text = NULL;
+ goto finish;
+
+ default:
+ rs->sr_err = slap_map_api2result( rs );
+ rs->sr_text = NULL;
+ goto finish;
+ }
+ }
+
+ /* if needed, initialize timeout */
+ if ( li->li_timeout[ SLAP_OP_SEARCH ] ) {
+ if ( tv.tv_sec == 0 || tv.tv_sec > li->li_timeout[ SLAP_OP_SEARCH ] ) {
+ tv.tv_sec = li->li_timeout[ SLAP_OP_SEARCH ];
+ tv.tv_usec = 0;
+ }
+ }
+
+ /* We pull apart the ber result, stuff it into a slapd entry, and
+ * let send_search_entry stuff it back into ber format. Slow & ugly,
+ * but this is necessary for version matching, and for ACL processing.
+ */
+
+ for ( rc = -2; rc != -1; rc = ldap_result( lc->lc_ld, msgid, LDAP_MSG_ONE, &tv, &res ) )
+ {
+ /* check for abandon */
+ if ( op->o_abandon || LDAP_BACK_CONN_ABANDON( lc ) ) {
+ if ( rc > 0 ) {
+ ldap_msgfree( res );
+ }
+ (void)ldap_back_cancel( lc, op, rs, msgid, LDAP_BACK_DONTSEND );
+ rc = SLAPD_ABANDON;
+ goto finish;
+ }
+
+ if ( rc == 0 || rc == -2 ) {
+ ldap_pvt_thread_yield();
+
+ /* check timeout */
+ if ( li->li_timeout[ SLAP_OP_SEARCH ] ) {
+ if ( rc == 0 ) {
+ (void)ldap_back_cancel( lc, op, rs, msgid, LDAP_BACK_DONTSEND );
+ rs->sr_text = "Operation timed out";
+ rc = rs->sr_err = op->o_protocol >= LDAP_VERSION3 ?
+ LDAP_ADMINLIMIT_EXCEEDED : LDAP_OTHER;
+ goto finish;
+ }
+
+ } else {
+ LDAP_BACK_TV_SET( &tv );
+ }
+
+ /* check time limit */
+ if ( op->ors_tlimit != SLAP_NO_LIMIT
+ && slap_get_time() > stoptime )
+ {
+ (void)ldap_back_cancel( lc, op, rs, msgid, LDAP_BACK_DONTSEND );
+ rc = rs->sr_err = LDAP_TIMELIMIT_EXCEEDED;
+ goto finish;
+ }
+ continue;
+
+ } else {
+ /* only touch when activity actually took place... */
+ if ( li->li_idle_timeout ) {
+ lc->lc_time = op->o_time;
+ }
+
+ /* don't retry any more */
+ dont_retry = 1;
+ }
+
+
+ if ( rc == LDAP_RES_SEARCH_ENTRY ) {
+ Entry ent = { 0 };
+ struct berval bdn = BER_BVNULL;
+
+ do_retry = 0;
+
+ e = ldap_first_entry( lc->lc_ld, res );
+ rc = ldap_build_entry( op, e, &ent, &bdn,
+ remove_unknown_schema);
+ if ( rc == LDAP_SUCCESS ) {
+ ldap_get_entry_controls( lc->lc_ld, res, &rs->sr_ctrls );
+ rs->sr_entry = &ent;
+ rs->sr_attrs = op->ors_attrs;
+ rs->sr_operational_attrs = NULL;
+ rs->sr_flags = 0;
+ rs->sr_err = LDAP_SUCCESS;
+ rc = rs->sr_err = send_search_entry( op, rs );
+ if ( rs->sr_ctrls ) {
+ ldap_controls_free( rs->sr_ctrls );
+ rs->sr_ctrls = NULL;
+ }
+ rs->sr_entry = NULL;
+ rs->sr_flags = 0;
+ if ( !BER_BVISNULL( &ent.e_name ) ) {
+ assert( ent.e_name.bv_val != bdn.bv_val );
+ op->o_tmpfree( ent.e_name.bv_val, op->o_tmpmemctx );
+ BER_BVZERO( &ent.e_name );
+ }
+ if ( !BER_BVISNULL( &ent.e_nname ) ) {
+ op->o_tmpfree( ent.e_nname.bv_val, op->o_tmpmemctx );
+ BER_BVZERO( &ent.e_nname );
+ }
+ entry_clean( &ent );
+ }
+ ldap_msgfree( res );
+ switch ( rc ) {
+ case LDAP_SUCCESS:
+ case LDAP_INSUFFICIENT_ACCESS:
+ break;
+
+ default:
+ if ( rc == LDAP_UNAVAILABLE ) {
+ rc = rs->sr_err = LDAP_OTHER;
+ } else {
+ (void)ldap_back_cancel( lc, op, rs, msgid, LDAP_BACK_DONTSEND );
+ }
+ goto finish;
+ }
+
+ } else if ( rc == LDAP_RES_SEARCH_REFERENCE ) {
+ if ( LDAP_BACK_NOREFS( li ) ) {
+ ldap_msgfree( res );
+ continue;
+ }
+
+ do_retry = 0;
+ rc = ldap_parse_reference( lc->lc_ld, res,
+ &references, &rs->sr_ctrls, 1 );
+
+ if ( rc != LDAP_SUCCESS ) {
+ continue;
+ }
+
+ /* FIXME: there MUST be at least one */
+ if ( references && references[ 0 ] && references[ 0 ][ 0 ] ) {
+ int cnt;
+
+ for ( cnt = 0; references[ cnt ]; cnt++ )
+ /* NO OP */ ;
+
+ /* FIXME: there MUST be at least one */
+ rs->sr_ref = op->o_tmpalloc( ( cnt + 1 ) * sizeof( struct berval ),
+ op->o_tmpmemctx );
+
+ for ( cnt = 0; references[ cnt ]; cnt++ ) {
+ ber_str2bv( references[ cnt ], 0, 0, &rs->sr_ref[ cnt ] );
+ }
+ BER_BVZERO( &rs->sr_ref[ cnt ] );
+
+ /* ignore return value by now */
+ RS_ASSERT( !(rs->sr_flags & REP_ENTRY_MASK) );
+ rs->sr_entry = NULL;
+ ( void )send_search_reference( op, rs );
+
+ } else {
+ Debug( LDAP_DEBUG_ANY,
+ "%s ldap_back_search: "
+ "got SEARCH_REFERENCE "
+ "with no referrals\n",
+ op->o_log_prefix );
+ }
+
+ /* cleanup */
+ if ( references ) {
+ ber_memvfree( (void **)references );
+ op->o_tmpfree( rs->sr_ref, op->o_tmpmemctx );
+ rs->sr_ref = NULL;
+ references = NULL;
+ }
+
+ if ( rs->sr_ctrls ) {
+ ldap_controls_free( rs->sr_ctrls );
+ rs->sr_ctrls = NULL;
+ }
+
+ } else if ( rc == LDAP_RES_INTERMEDIATE ) {
+ /* FIXME: response controls
+ * are passed without checks */
+ rc = ldap_parse_intermediate( lc->lc_ld,
+ res,
+ (char **)&rs->sr_rspoid,
+ &rs->sr_rspdata,
+ &rs->sr_ctrls,
+ 0 );
+ if ( rc != LDAP_SUCCESS ) {
+ continue;
+ }
+
+ slap_send_ldap_intermediate( op, rs );
+
+ if ( rs->sr_rspoid != NULL ) {
+ ber_memfree( (char *)rs->sr_rspoid );
+ rs->sr_rspoid = NULL;
+ }
+
+ if ( rs->sr_rspdata != NULL ) {
+ ber_bvfree( rs->sr_rspdata );
+ rs->sr_rspdata = NULL;
+ }
+
+ if ( rs->sr_ctrls != NULL ) {
+ ldap_controls_free( rs->sr_ctrls );
+ rs->sr_ctrls = NULL;
+ }
+
+ } else {
+ char *err = NULL;
+
+ rc = ldap_parse_result( lc->lc_ld, res, &rs->sr_err,
+ &match.bv_val, &err,
+ &references, &rs->sr_ctrls, 1 );
+ if ( rc == LDAP_SUCCESS ) {
+ if ( err ) {
+ rs->sr_text = err;
+ freetext = 1;
+ }
+ } else {
+ rs->sr_err = rc;
+ }
+ rs->sr_err = slap_map_api2result( rs );
+
+ /* RFC 4511: referrals can only appear
+ * if result code is LDAP_REFERRAL */
+ if ( references
+ && references[ 0 ]
+ && references[ 0 ][ 0 ] )
+ {
+ if ( rs->sr_err != LDAP_REFERRAL ) {
+ Debug( LDAP_DEBUG_ANY,
+ "%s ldap_back_search: "
+ "got referrals with err=%d\n",
+ op->o_log_prefix,
+ rs->sr_err );
+
+ } else {
+ int cnt;
+
+ for ( cnt = 0; references[ cnt ]; cnt++ )
+ /* NO OP */ ;
+
+ rs->sr_ref = op->o_tmpalloc( ( cnt + 1 ) * sizeof( struct berval ),
+ op->o_tmpmemctx );
+
+ for ( cnt = 0; references[ cnt ]; cnt++ ) {
+ /* duplicating ...*/
+ ber_str2bv( references[ cnt ], 0, 0, &rs->sr_ref[ cnt ] );
+ }
+ BER_BVZERO( &rs->sr_ref[ cnt ] );
+ }
+
+ } else if ( rs->sr_err == LDAP_REFERRAL ) {
+ Debug( LDAP_DEBUG_ANY,
+ "%s ldap_back_search: "
+ "got err=%d with null "
+ "or empty referrals\n",
+ op->o_log_prefix,
+ rs->sr_err );
+
+ rs->sr_err = LDAP_NO_SUCH_OBJECT;
+ }
+
+ if ( match.bv_val != NULL ) {
+ match.bv_len = strlen( match.bv_val );
+ }
+
+ rc = 0;
+ break;
+ }
+
+ /* if needed, restore timeout */
+ if ( li->li_timeout[ SLAP_OP_SEARCH ] ) {
+ if ( tv.tv_sec == 0 || tv.tv_sec > li->li_timeout[ SLAP_OP_SEARCH ] ) {
+ tv.tv_sec = li->li_timeout[ SLAP_OP_SEARCH ];
+ tv.tv_usec = 0;
+ }
+ }
+ }
+
+ if ( rc == -1 ) {
+ if ( dont_retry == 0 ) {
+ if ( do_retry ) {
+ do_retry = 0;
+ if ( ldap_back_retry( &lc, op, rs, LDAP_BACK_DONTSEND ) ) {
+ goto retry;
+ }
+ }
+
+ rs->sr_err = LDAP_SERVER_DOWN;
+ rs->sr_err = slap_map_api2result( rs );
+ goto finish;
+
+ } else if ( LDAP_BACK_ONERR_STOP( li ) ) {
+ /* if onerr == STOP */
+ rs->sr_err = LDAP_SERVER_DOWN;
+ rs->sr_err = slap_map_api2result( rs );
+ goto finish;
+ }
+ }
+
+ /*
+ * Rewrite the matched portion of the search base, if required
+ */
+ if ( !BER_BVISNULL( &match ) && !BER_BVISEMPTY( &match ) ) {
+ struct berval pmatch;
+
+ if ( dnPretty( NULL, &match, &pmatch, op->o_tmpmemctx ) != LDAP_SUCCESS ) {
+ pmatch.bv_val = match.bv_val;
+ match.bv_val = NULL;
+ }
+ rs->sr_matched = pmatch.bv_val;
+ rs->sr_flags |= REP_MATCHED_MUSTBEFREED;
+ }
+
+finish:;
+ if ( !BER_BVISNULL( &match ) ) {
+ ber_memfree( match.bv_val );
+ }
+
+ if ( rs->sr_v2ref ) {
+ rs->sr_err = LDAP_REFERRAL;
+ }
+
+ if ( LDAP_BACK_QUARANTINE( li ) ) {
+ ldap_back_quarantine( op, rs );
+ }
+
+ if ( filter.bv_val != op->ors_filterstr.bv_val ) {
+ op->o_tmpfree( filter.bv_val, op->o_tmpmemctx );
+ }
+
+#if 0
+ /* let send_ldap_result play cleanup handlers (ITS#4645) */
+ if ( rc != SLAPD_ABANDON )
+#endif
+ {
+ send_ldap_result( op, rs );
+ }
+
+ (void)ldap_back_controls_free( op, rs, &ctrls );
+
+ if ( rs->sr_ctrls ) {
+ ldap_controls_free( rs->sr_ctrls );
+ rs->sr_ctrls = NULL;
+ }
+
+ if ( rs->sr_text ) {
+ if ( freetext ) {
+ ber_memfree( (char *)rs->sr_text );
+ }
+ rs->sr_text = NULL;
+ }
+
+ if ( rs->sr_ref ) {
+ op->o_tmpfree( rs->sr_ref, op->o_tmpmemctx );
+ rs->sr_ref = NULL;
+ }
+
+ if ( references ) {
+ ber_memvfree( (void **)references );
+ }
+
+ if ( attrs ) {
+ op->o_tmpfree( attrs, op->o_tmpmemctx );
+ }
+
+ if ( lc != NULL ) {
+ ldap_back_release_conn( li, lc );
+ }
+
+ if ( rs->sr_err == LDAP_UNAVAILABLE &&
+ /* if we originally bound and wanted rebind-as-user, must drop
+ * the connection now because we just discarded the credentials.
+ * ITS#7464, #8142
+ */
+ LDAP_BACK_SAVECRED( li ) && SLAP_IS_AUTHZ_BACKEND( op ) )
+ rs->sr_err = SLAPD_DISCONNECT;
+ return rs->sr_err;
+}
+
+static int
+ldap_build_entry(
+ Operation *op,
+ LDAPMessage *e,
+ Entry *ent,
+ struct berval *bdn,
+ int remove_unknown_schema)
+{
+ struct berval a;
+ BerElement ber = *ldap_get_message_ber( e );
+ Attribute *attr, **attrp;
+ const char *text;
+ int last;
+ char *lastb;
+ ber_len_t len;
+
+ /* safe assumptions ... */
+ assert( ent != NULL );
+ BER_BVZERO( &ent->e_bv );
+
+ if ( ber_scanf( &ber, "{m", bdn ) == LBER_ERROR ) {
+ return LDAP_DECODING_ERROR;
+ }
+
+ /*
+ * Note: this may fail if the target host(s) schema differs
+ * from the one known to the meta, and a DN with unknown
+ * attributes is returned.
+ *
+ * FIXME: should we log anything, or delegate to dnNormalize?
+ */
+ /* Note: if the distinguished values or the naming attributes
+ * change, should we massage them as well?
+ */
+ if ( dnPrettyNormal( NULL, bdn, &ent->e_name, &ent->e_nname,
+ op->o_tmpmemctx ) != LDAP_SUCCESS )
+ {
+ return LDAP_INVALID_DN_SYNTAX;
+ }
+
+ ent->e_attrs = NULL;
+ if ( ber_first_element( &ber, &len, &lastb ) != LBER_SEQUENCE ) {
+ return LDAP_SUCCESS;
+ }
+
+ attrp = &ent->e_attrs;
+ while ( ber_next_element( &ber, &len, lastb ) == LBER_SEQUENCE &&
+ ber_scanf( &ber, "{m", &a ) != LBER_ERROR ) {
+ int i;
+ slap_syntax_validate_func *validate;
+ slap_syntax_transform_func *pretty;
+
+ attr = attr_alloc( NULL );
+ if ( attr == NULL ) {
+ return LDAP_OTHER;
+ }
+ if ( slap_bv2ad( &a, &attr->a_desc, &text )
+ != LDAP_SUCCESS )
+ {
+ if ( slap_bv2undef_ad( &a, &attr->a_desc, &text,
+ (remove_unknown_schema ? SLAP_AD_NOINSERT : SLAP_AD_PROXIED )) != LDAP_SUCCESS )
+ {
+ Debug( LDAP_DEBUG_ANY,
+ "%s ldap_build_entry: "
+ "slap_bv2undef_ad(%s): %s\n",
+ op->o_log_prefix, a.bv_val, text );
+
+ ( void )ber_scanf( &ber, "x" /* [W] */ );
+ attr_free( attr );
+ continue;
+ }
+ }
+
+ /* no subschemaSubentry */
+ if ( attr->a_desc == slap_schema.si_ad_subschemaSubentry
+ || attr->a_desc == slap_schema.si_ad_entryDN )
+ {
+
+ /*
+ * We eat target's subschemaSubentry because
+ * a search for this value is likely not
+ * to resolve to the appropriate backend;
+ * later, the local subschemaSubentry is
+ * added.
+ *
+ * We also eat entryDN because the frontend
+ * will reattach it without checking if already
+ * present...
+ */
+ ( void )ber_scanf( &ber, "x" /* [W] */ );
+ attr_free( attr );
+ continue;
+ }
+
+ if ( ber_scanf( &ber, "[W]", &attr->a_vals ) == LBER_ERROR
+ || attr->a_vals == NULL )
+ {
+ /*
+ * Note: attr->a_vals can be null when using
+ * values result filter
+ */
+ attr->a_vals = (struct berval *)&slap_dummy_bv;
+ }
+
+ validate = attr->a_desc->ad_type->sat_syntax->ssyn_validate;
+ pretty = attr->a_desc->ad_type->sat_syntax->ssyn_pretty;
+
+ if ( !validate && !pretty ) {
+ attr->a_nvals = NULL;
+ attr_free( attr );
+ goto next_attr;
+ }
+
+ for ( i = 0; !BER_BVISNULL( &attr->a_vals[i] ); i++ ) ;
+ last = i;
+
+ /*
+ * check that each value is valid per syntax
+ * and pretty if appropriate
+ */
+ for ( i = 0; i<last; i++ ) {
+ struct berval pval;
+ int rc;
+
+ if ( pretty ) {
+ rc = ordered_value_pretty( attr->a_desc,
+ &attr->a_vals[i], &pval, NULL );
+
+ } else {
+ rc = ordered_value_validate( attr->a_desc,
+ &attr->a_vals[i], 0 );
+ }
+
+ if ( rc != LDAP_SUCCESS ) {
+ ObjectClass *oc;
+
+ /* check if, by chance, it's an undefined objectClass */
+ if ( attr->a_desc == slap_schema.si_ad_objectClass &&
+ ( oc = oc_bvfind_undef_ex( &attr->a_vals[i],
+ remove_unknown_schema ) ) != NULL )
+ {
+ ber_dupbv( &pval, &oc->soc_cname );
+ rc = LDAP_SUCCESS;
+
+ } else {
+ ber_memfree( attr->a_vals[i].bv_val );
+ if ( --last == i ) {
+ BER_BVZERO( &attr->a_vals[i] );
+ break;
+ }
+ attr->a_vals[i] = attr->a_vals[last];
+ BER_BVZERO( &attr->a_vals[last] );
+ i--;
+ }
+ }
+
+ if ( rc == LDAP_SUCCESS && pretty ) {
+ ber_memfree( attr->a_vals[i].bv_val );
+ attr->a_vals[i] = pval;
+ }
+ }
+ attr->a_numvals = last = i;
+ if ( last == 0 && attr->a_vals != &slap_dummy_bv ) {
+ attr->a_nvals = NULL;
+ attr_free( attr );
+ goto next_attr;
+ }
+
+ if ( last && attr->a_desc->ad_type->sat_equality &&
+ attr->a_desc->ad_type->sat_equality->smr_normalize )
+ {
+ attr->a_nvals = ch_malloc( ( last + 1 )*sizeof( struct berval ) );
+ for ( i = 0; i < last; i++ ) {
+ int rc;
+
+ rc = ordered_value_normalize(
+ SLAP_MR_VALUE_OF_ATTRIBUTE_SYNTAX,
+ attr->a_desc,
+ attr->a_desc->ad_type->sat_equality,
+ &attr->a_vals[i], &attr->a_nvals[i],
+ NULL );
+
+ if ( rc != LDAP_SUCCESS ) {
+ ber_memfree( attr->a_vals[i].bv_val );
+ if ( --last == i ) {
+ BER_BVZERO( &attr->a_vals[i] );
+ break;
+ }
+ attr->a_vals[i] = attr->a_vals[last];
+ BER_BVZERO( &attr->a_vals[last] );
+ i--;
+ }
+ }
+ BER_BVZERO( &attr->a_nvals[i] );
+ if ( last == 0 ) {
+ attr_free( attr );
+ goto next_attr;
+ }
+
+ } else {
+ attr->a_nvals = attr->a_vals;
+ }
+
+ attr->a_numvals = last;
+
+ /* Handle sorted vals, strip dups but keep the attr */
+ if ( attr->a_desc->ad_type->sat_flags & SLAP_AT_SORTED_VAL ) {
+ while ( attr->a_numvals > 1 ) {
+ int rc = slap_sort_vals( (Modifications *)attr, &text, &i, op->o_tmpmemctx );
+ if ( rc != LDAP_TYPE_OR_VALUE_EXISTS )
+ break;
+
+ /* Strip duplicate values */
+ if ( attr->a_nvals != attr->a_vals )
+ ber_memfree( attr->a_nvals[i].bv_val );
+ ber_memfree( attr->a_vals[i].bv_val );
+ attr->a_numvals--;
+
+ assert( i >= 0 );
+ if ( (unsigned)i < attr->a_numvals ) {
+ attr->a_vals[i] = attr->a_vals[attr->a_numvals];
+ if ( attr->a_nvals != attr->a_vals )
+ attr->a_nvals[i] = attr->a_nvals[attr->a_numvals];
+ }
+ BER_BVZERO(&attr->a_vals[attr->a_numvals]);
+ if ( attr->a_nvals != attr->a_vals )
+ BER_BVZERO(&attr->a_nvals[attr->a_numvals]);
+ }
+ attr->a_flags |= SLAP_ATTR_SORTED_VALS;
+ }
+
+ *attrp = attr;
+ attrp = &attr->a_next;
+
+next_attr:;
+ }
+
+ return LDAP_SUCCESS;
+}
+
+/* return 0 IFF we can retrieve the entry with ndn
+ */
+int
+ldap_back_entry_get(
+ Operation *op,
+ struct berval *ndn,
+ ObjectClass *oc,
+ AttributeDescription *at,
+ int rw,
+ Entry **ent )
+{
+ ldapinfo_t *li = (ldapinfo_t *) op->o_bd->be_private;
+
+ ldapconn_t *lc = NULL;
+ int rc;
+ struct berval bdn;
+ LDAPMessage *result = NULL,
+ *e = NULL;
+ char *attr[3], **attrp = NULL;
+ char *filter = NULL;
+ SlapReply rs;
+ int do_retry = 1;
+ LDAPControl **ctrls = NULL;
+ Operation op2 = *op;
+
+ int remove_unknown_schema =
+ LDAP_BACK_OMIT_UNKNOWN_SCHEMA (li);
+ *ent = NULL;
+
+ /* Tell getconn this is a privileged op */
+ op2.o_do_not_cache = 1;
+ /* use rootdn to be doubly explicit this is privileged */
+ op2.o_dn = op->o_bd->be_rootdn;
+ op2.o_ndn = op->o_bd->be_rootndn;
+ /* ldap_back_entry_get() is an entry lookup, so it does not need
+ * to know what the entry is being looked up for */
+ op2.o_tag = LDAP_REQ_SEARCH;
+ op2.o_ctrls = NULL;
+ rc = ldap_back_dobind( &lc, &op2, &rs, LDAP_BACK_DONTSEND );
+ if ( !rc ) {
+ return rs.sr_err;
+ }
+
+ if ( at ) {
+ attrp = attr;
+ if ( oc && at != slap_schema.si_ad_objectClass ) {
+ attr[0] = slap_schema.si_ad_objectClass->ad_cname.bv_val;
+ attr[1] = at->ad_cname.bv_val;
+ attr[2] = NULL;
+
+ } else {
+ attr[0] = at->ad_cname.bv_val;
+ attr[1] = NULL;
+ }
+ }
+
+ if ( oc ) {
+ char *ptr;
+
+ filter = op->o_tmpalloc( STRLENOF( "(objectClass=" ")" )
+ + oc->soc_cname.bv_len + 1, op->o_tmpmemctx );
+ ptr = lutil_strcopy( filter, "(objectClass=" );
+ ptr = lutil_strcopy( ptr, oc->soc_cname.bv_val );
+ *ptr++ = ')';
+ *ptr++ = '\0';
+ }
+
+retry:
+ ctrls = NULL;
+ rc = ldap_back_controls_add( &op2, &rs, lc, &ctrls );
+ if ( rc != LDAP_SUCCESS ) {
+ goto cleanup;
+ }
+
+ /* TODO: timeout? */
+ rc = ldap_pvt_search_s( lc->lc_ld, ndn->bv_val, LDAP_SCOPE_BASE, filter,
+ attrp, LDAP_DEREF_NEVER, ctrls, NULL,
+ NULL, LDAP_NO_LIMIT, 0, &result );
+ if ( rc != LDAP_SUCCESS ) {
+ if ( rc == LDAP_SERVER_DOWN && do_retry ) {
+ do_retry = 0;
+ if ( ldap_back_retry( &lc, &op2, &rs, LDAP_BACK_DONTSEND ) ) {
+ /* if the identity changed, there might be need to re-authz */
+ (void)ldap_back_controls_free( &op2, &rs, &ctrls );
+ goto retry;
+ }
+ }
+ goto cleanup;
+ }
+
+ e = ldap_first_entry( lc->lc_ld, result );
+ if ( e == NULL ) {
+ /* the entry exists, but it doesn't match the filter? */
+ rc = LDAP_NO_RESULTS_RETURNED;
+ goto cleanup;
+ }
+
+ *ent = entry_alloc();
+ if ( *ent == NULL ) {
+ rc = LDAP_NO_MEMORY;
+ goto cleanup;
+ }
+
+ rc = ldap_build_entry( op, e, *ent, &bdn, remove_unknown_schema );
+
+ if ( rc != LDAP_SUCCESS ) {
+ entry_free( *ent );
+ *ent = NULL;
+ }
+
+cleanup:
+ (void)ldap_back_controls_free( &op2, &rs, &ctrls );
+
+ if ( result ) {
+ ldap_msgfree( result );
+ }
+
+ if ( filter ) {
+ op->o_tmpfree( filter, op->o_tmpmemctx );
+ }
+
+ if ( lc != NULL ) {
+ ldap_back_release_conn( li, lc );
+ }
+
+ return rc;
+}
diff --git a/servers/slapd/back-ldap/unbind.c b/servers/slapd/back-ldap/unbind.c
new file mode 100644
index 0000000..071380e
--- /dev/null
+++ b/servers/slapd/back-ldap/unbind.c
@@ -0,0 +1,78 @@
+/* unbind.c - ldap backend unbind function */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1999-2022 The OpenLDAP Foundation.
+ * Portions Copyright 1999-2003 Howard Chu.
+ * Portions Copyright 2000-2003 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 the Howard Chu for inclusion
+ * in OpenLDAP Software and subsequently enhanced by Pierangelo
+ * Masarati.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/errno.h>
+#include <ac/socket.h>
+#include <ac/string.h>
+
+#include "slap.h"
+#include "back-ldap.h"
+
+int
+ldap_back_conn_destroy(
+ Backend *be,
+ Connection *conn
+)
+{
+ ldapinfo_t *li = (ldapinfo_t *) be->be_private;
+ ldapconn_t *lc = NULL, lc_curr;
+
+ Debug( LDAP_DEBUG_TRACE,
+ "=>ldap_back_conn_destroy: fetching conn %ld\n",
+ conn->c_connid );
+
+ lc_curr.lc_conn = conn;
+
+ ldap_pvt_thread_mutex_lock( &li->li_conninfo.lai_mutex );
+#if LDAP_BACK_PRINT_CONNTREE > 0
+ ldap_back_print_conntree( li, ">>> ldap_back_conn_destroy" );
+#endif /* LDAP_BACK_PRINT_CONNTREE */
+ while ( ( lc = ldap_tavl_delete( &li->li_conninfo.lai_tree, (caddr_t)&lc_curr, ldap_back_conn_cmp ) ) != NULL )
+ {
+ assert( !LDAP_BACK_PCONN_ISPRIV( lc ) );
+ Debug( LDAP_DEBUG_TRACE,
+ "=>ldap_back_conn_destroy: destroying conn %lu "
+ "refcnt=%d flags=0x%08x\n",
+ lc->lc_conn->c_connid, lc->lc_refcnt, lc->lc_lcflags );
+
+ if ( lc->lc_refcnt > 0 ) {
+ /* someone else might be accessing the connection;
+ * mark for deletion */
+ LDAP_BACK_CONN_CACHED_CLEAR( lc );
+ LDAP_BACK_CONN_TAINTED_SET( lc );
+
+ } else {
+ ldap_back_conn_free( lc );
+ }
+ }
+#if LDAP_BACK_PRINT_CONNTREE > 0
+ ldap_back_print_conntree( li, "<<< ldap_back_conn_destroy" );
+#endif /* LDAP_BACK_PRINT_CONNTREE */
+ ldap_pvt_thread_mutex_unlock( &li->li_conninfo.lai_mutex );
+
+ return 0;
+}