From: Pierangelo Masarati Date: Fri, 5 Jan 2007 10:47:09 +0000 (+0000) Subject: import selected fixes/improvements to proxy backends from HEAD: X-Git-Tag: OPENLDAP_REL_ENG_2_3_33~16 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=2f605e14088d2b3f7e6038171defeccbcddea687;p=thirdparty%2Fopenldap.git import selected fixes/improvements to proxy backends from HEAD: Fixed slapd-ldap/meta privileged conn caching (ITS#4547) Fixed slapd-ldap chase-referrals switch (ITS#4557) Fixed slapd-ldap bind behavior when idassert is always used (ITS#4781) Fixed slapd-ldap response handling bugs (ITS#4782) Fixed slapd-ldap/meta privileged connections handling (ITS#4791) Fixed slapd-meta retrying (ITS#4594, 4762) Fixed slapo-chain referral DN use (ITS#4776) --- diff --git a/CHANGES b/CHANGES index 11c893a3f8..bd1a4cd901 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,13 @@ OpenLDAP 2.3 Change Log OpenLDAP 2.3.33 Engineering + Fixed slapd-ldap/meta privileged conn caching (ITS#4547) + Fixed slapd-ldap chase-referrals switch (ITS#4557) + Fixed slapd-ldap bind behavior when idassert is always used (ITS#4781) + Fixed slapd-ldap response handling bugs (ITS#4782) + Fixed slapd-ldap/meta privileged connections handling (ITS#4791) + Fixed slapd-meta retrying (ITS#4594, 4762) + Fixed slapo-chain referral DN use (ITS#4776) OpenLDAP 2.3.32 Release (2007/01/04) Fixed slapd add redundant duplicate value check (ITS#4600) diff --git a/include/lutil.h b/include/lutil.h index 1fc523dc97..6b2bf76bef 100644 --- a/include/lutil.h +++ b/include/lutil.h @@ -296,6 +296,32 @@ lutil_parse_time( const char *in, unsigned long *tp ); LDAP_LUTIL_F (int) lutil_unparse_time( char *buf, size_t buflen, unsigned long t ); +#ifdef timerdiv +#define lutil_timerdiv timerdiv +#else /* ! timerdiv */ +/* works inplace (x == t) */ +#define lutil_timerdiv(t,d,x) \ + do { \ + time_t s = (t)->tv_sec; \ + assert( d > 0 ); \ + (x)->tv_sec = s / d; \ + (x)->tv_usec = ( (t)->tv_usec + 1000000 * ( s % d ) ) / d; \ + } while ( 0 ) +#endif /* ! timerdiv */ + +#ifdef timermul +#define lutil_timermul timermul +#else /* ! timermul */ +/* works inplace (x == t) */ +#define lutil_timermul(t,m,x) \ + do { \ + time_t u = (t)->tv_usec * m; \ + assert( m > 0 ); \ + (x)->tv_sec = (t)->tv_sec * m + u / 1000000; \ + (x)->tv_usec = u % 1000000; \ + } while ( 0 ); +#endif /* ! timermul */ + LDAP_END_DECL #endif /* _LUTIL_H */ diff --git a/servers/slapd/back-ldap/add.c b/servers/slapd/back-ldap/add.c index fb3b8546d3..b5e07df05c 100644 --- a/servers/slapd/back-ldap/add.c +++ b/servers/slapd/back-ldap/add.c @@ -36,26 +36,25 @@ ldap_back_add( Operation *op, SlapReply *rs ) { - ldapinfo_t *li = (ldapinfo_t *)op->o_bd->be_private; - - ldapconn_t *lc; - int i = 0, - j = 0; - Attribute *a; - LDAPMod **attrs = NULL, - *attrs2 = NULL; - ber_int_t msgid; - int isupdate; - int do_retry = 1; - LDAPControl **ctrls = NULL; + 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, 0, 0 ); - lc = ldap_back_getconn( op, rs, LDAP_BACK_SENDERR ); - if ( !lc || !ldap_back_dobind( lc, op, rs, LDAP_BACK_SENDERR ) ) { + if ( !ldap_back_dobind( &lc, op, rs, LDAP_BACK_SENDERR ) ) { lc = NULL; goto cleanup; } @@ -94,7 +93,8 @@ ldap_back_add( retry: ctrls = op->o_ctrls; - rs->sr_err = ldap_back_proxy_authz_ctrl( lc, op, rs, &ctrls ); + rs->sr_err = ldap_back_proxy_authz_ctrl( &lc->lc_bound_ndn, + li->li_version, &li->li_idassert, op, rs, &ctrls ); if ( rs->sr_err != LDAP_SUCCESS ) { send_ldap_result( op, rs ); goto cleanup; @@ -103,9 +103,10 @@ retry: 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[ LDAP_BACK_OP_ADD ], LDAP_BACK_SENDRESULT ); - if ( rs->sr_err == LDAP_UNAVAILABLE && do_retry ) { - do_retry = 0; + 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_proxy_authz_ctrl_free( op, &ctrls ); diff --git a/servers/slapd/back-ldap/back-ldap.h b/servers/slapd/back-ldap/back-ldap.h index 320e4f8e5d..d4442655ee 100644 --- a/servers/slapd/back-ldap/back-ldap.h +++ b/servers/slapd/back-ldap/back-ldap.h @@ -26,41 +26,93 @@ LDAP_BEGIN_DECL +struct ldapinfo_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_t { Connection *lc_conn; -#define LDAP_BACK_PCONN ((void *)0x0) -#define LDAP_BACK_PCONN_TLS ((void *)0x1) -#define LDAP_BACK_PCONN_ID(c) ((void *)(c) > LDAP_BACK_PCONN_TLS ? (c)->c_connid : -1) +#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)) +#define LDAP_BACK_PCONN_ID(lc) (LDAP_BACK_PCONN_ISPRIV((lc)) ? \ + ( -1 - (long)(lc)->lc_conn ) : (lc)->lc_conn->c_connid ) #ifdef HAVE_TLS -#define LDAP_BACK_PCONN_SET(op) ((op)->o_conn->c_is_tls ? LDAP_BACK_PCONN_TLS : LDAP_BACK_PCONN) +#define LDAP_BACK_PCONN_ROOTDN_SET(lc, op) \ + ((lc)->lc_conn = (void *)((op)->o_conn->c_is_tls ? LDAP_BACK_PCONN_ROOTDN_TLS : LDAP_BACK_PCONN_ROOTDN)) +#define LDAP_BACK_PCONN_ANON_SET(lc, op) \ + ((lc)->lc_conn = (void *)((op)->o_conn->c_is_tls ? LDAP_BACK_PCONN_ANON_TLS : LDAP_BACK_PCONN_ANON)) +#define LDAP_BACK_PCONN_BIND_SET(lc, op) \ + ((lc)->lc_conn = (void *)((op)->o_conn->c_is_tls ? LDAP_BACK_PCONN_BIND_TLS : LDAP_BACK_PCONN_BIND)) #else /* ! HAVE_TLS */ -#define LDAP_BACK_PCONN_SET(op) (LDAP_BACK_PCONN) +#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))) LDAP *lc_ld; struct berval lc_cred; struct berval lc_bound_ndn; struct berval lc_local_ndn; unsigned lc_lcflags; -#define LDAP_BACK_CONN_ISSET(lc,f) ((lc)->lc_lcflags & (f)) -#define LDAP_BACK_CONN_SET(lc,f) ((lc)->lc_lcflags |= (f)) -#define LDAP_BACK_CONN_CLEAR(lc,f) ((lc)->lc_lcflags &= ~(f)) -#define LDAP_BACK_CONN_CPY(lc,f,mlc) \ +#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) & (mlc)->lc_lcflags) == (f) ) { \ - (lc)->lc_lcflags |= (f); \ + if ( ((f) & *(mfp)) == (f) ) { \ + *(fp) |= (f); \ } else { \ - (lc)->lc_lcflags &= ~(f); \ + *(fp) &= ~(f); \ } \ } while ( 0 ) -#define LDAP_BACK_FCONN_ISBOUND (0x01) -#define LDAP_BACK_FCONN_ISANON (0x02) +#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 (0x04) -#define LDAP_BACK_FCONN_ISTLS (0x08) -#define LDAP_BACK_FCONN_BINDING (0x10) -#define LDAP_BACK_FCONN_TAINTED (0x20) +#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_ISIDASR (0x00000040U) +#define LDAP_BACK_FCONN_CACHED (0x00000080U) #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) @@ -84,105 +136,159 @@ typedef struct ldapconn_t { #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_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) unsigned lc_refcnt; unsigned lc_binding; unsigned lc_flags; time_t lc_create_time; time_t lc_time; + + LDAP_TAILQ_ENTRY(ldapconn_t) lc_q; } ldapconn_t; +typedef struct ldap_avl_info_t { + ldap_pvt_thread_mutex_t lai_mutex; + Avlnode *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 */ -enum { +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 li_idassert_flags li_idassert.si_flags + + BerVarray si_authz; +#define li_idassert_authz li_idassert.si_authz +} slap_idassert_t; /* - * operation enumeration for timeouts + * Hook to allow mucking with ldapinfo_t when quarantine is over */ -enum { - LDAP_BACK_OP_ADD = 0, - LDAP_BACK_OP_DELETE, - LDAP_BACK_OP_MODIFY, - LDAP_BACK_OP_MODRDN, - LDAP_BACK_OP_LAST -}; - -typedef struct ldap_avl_info_t { - ldap_pvt_thread_mutex_t lai_mutex; - Avlnode *lai_tree; -} ldap_avl_info_t; +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; + 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; + BerVarray li_bvuri; + ldap_pvt_thread_mutex_t li_uri_mutex; + + LDAP_REBIND_PROC *li_rebind_f; + void *li_urllist_p; - 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 + 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 +#define li_acl_secprops li_acl.sb_secprops /* ID assert stuff */ - int li_idassert_mode; - - slap_bindconf li_idassert; -#define li_idassert_authcID li_idassert.sb_authcId -#define li_idassert_authcDN li_idassert.sb_binddn -#define li_idassert_passwd li_idassert.sb_cred -#define li_idassert_authzID li_idassert.sb_authzId -#define li_idassert_authmethod li_idassert.sb_method -#define li_idassert_sasl_mech li_idassert.sb_saslmech -#define li_idassert_sasl_realm li_idassert.sb_realm -#define li_idassert_secprops li_idassert.sb_secprops - - unsigned li_idassert_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 - - BerVarray li_idassert_authz; + slap_idassert_t li_idassert; /* end of ID assert stuff */ - int li_nretries; + 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; -#define LDAP_BACK_F_NONE 0x00U -#define LDAP_BACK_F_SAVECRED 0x01U -#define LDAP_BACK_F_USE_TLS 0x02U -#define LDAP_BACK_F_PROPAGATE_TLS 0x04U -#define LDAP_BACK_F_TLS_CRITICAL 0x08U + unsigned li_flags; + +/* 0xFFF00000U 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_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) -#define LDAP_BACK_F_CHASE_REFERRALS 0x10U -#define LDAP_BACK_F_PROXY_WHOAMI 0x20U +#define LDAP_BACK_F_CHASE_REFERRALS (0x00000010U) +#define LDAP_BACK_F_PROXY_WHOAMI (0x00000020U) + +#define LDAP_BACK_F_T_F (0x00000040U) +#define LDAP_BACK_F_T_F_DISCOVER (0x00000080U) +#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 (0x00000100U) +#define LDAP_BACK_F_SINGLECONN (0x00000200U) +#define LDAP_BACK_F_USE_TEMPORARIES (0x00000400U) + +#define LDAP_BACK_F_ISOPEN (0x00000800U) + +#define LDAP_BACK_F_CANCEL_ABANDON (0x00000000U) +#define LDAP_BACK_F_CANCEL_IGNORE (0x00001000U) +#define LDAP_BACK_F_CANCEL_EXOP (0x00002000U) +#define LDAP_BACK_F_CANCEL_EXOP_DISCOVER (0x00004000U) +#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_SUPPORT_T_F 0x80U -#define LDAP_BACK_F_SUPPORT_T_F_DISCOVER 0x40U -#define LDAP_BACK_F_SUPPORT_T_F_MASK (LDAP_BACK_F_SUPPORT_T_F|LDAP_BACK_F_SUPPORT_T_F_DISCOVER) +#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_ISSET(li,f) ( ( (li)->li_flags & (f) ) == (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 ) @@ -190,14 +296,55 @@ typedef struct ldapinfo_t { #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 ) - int li_version; +#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 ) + + int li_version; - ldap_avl_info_t li_conninfo; + /* 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) - time_t li_network_timeout; - time_t li_conn_ttl; - time_t li_idle_timeout; - time_t li_timeout[ LDAP_BACK_OP_LAST ]; + 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; +#define LDAP_BACK_QUARANTINE(li) ( (li)->li_quarantine.ri_num != NULL ) + 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 ]; } ldapinfo_t; typedef enum ldap_back_send_t { @@ -206,10 +353,19 @@ typedef enum ldap_back_send_t { 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_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 */ @@ -224,6 +380,10 @@ typedef enum ldap_back_send_t { (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 */ + LDAP_END_DECL #include "proto-ldap.h" diff --git a/servers/slapd/back-ldap/bind.c b/servers/slapd/back-ldap/bind.c index ae7ca9e2e6..83c12237f3 100644 --- a/servers/slapd/back-ldap/bind.c +++ b/servers/slapd/back-ldap/bind.c @@ -32,24 +32,103 @@ #define AVL_INTERNAL #include "slap.h" #include "back-ldap.h" +#undef ldap_debug /* silence a warning in ldap-int.h */ +#include "../../../libraries/libldap/ldap-int.h" -#include - -#ifndef PRINT_CONNTREE -#define PRINT_CONNTREE 0 -#endif /* !PRINT_CONNTREE */ +#include "lutil_ldap.h" #define LDAP_CONTROL_OBSOLETE_PROXY_AUTHZ "2.16.840.1.113730.3.4.12" -static LDAP_REBIND_PROC ldap_back_default_rebind; +#if LDAP_BACK_PRINT_CONNTREE > 0 +static void +ldap_back_ravl_print( Avlnode *root, int depth ) +{ + int i; + ldapconn_t *lc; + + if ( root == 0 ) { + return; + } + + ldap_back_ravl_print( root->avl_right, depth+1 ); + + for ( i = 0; i < depth; i++ ) { + fprintf( stderr, "-" ); + } + + lc = root->avl_data; + fprintf( stderr, "lc=%p local=\"%s\" conn=%p %s refcnt=%d flags=0x%08x\n", + (void *)lc, + lc->lc_local_ndn.bv_val ? lc->lc_local_ndn.bv_val : "", + (void *)lc->lc_conn, + avl_bf2str( root->avl_bf ), lc->lc_refcnt, lc->lc_lcflags ); + + ldap_back_ravl_print( root->avl_left, depth+1 ); +} + +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] lc=%p local=\"%s\" conn=%p refcnt=%d flags=0x%08x\n", + i, + (void *)lc, + lc->lc_local_ndn.bv_val ? lc->lc_local_ndn.bv_val : "", + (void *)lc->lc_conn, lc->lc_refcnt, lc->lc_lcflags ); + i++; + } + } + + if ( li->li_conninfo.lai_tree == 0 ) { + fprintf( stderr, "\t(empty)\n" ); + + } else { + ldap_back_ravl_print( li->li_conninfo.lai_tree, 0 ); + } + + fprintf( stderr, "<======== %s\n", msg ); +} +#endif /* LDAP_BACK_PRINT_CONNTREE */ + +static int +ldap_back_freeconn( Operation *op, 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 ); -LDAP_REBIND_PROC *ldap_back_rebind_f = ldap_back_default_rebind; +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 ); +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 **lcp, Operation *op, SlapReply *rs, ldap_back_send_t sendok ); +ldap_back_prepare_conn( ldapconn_t **lcp, Operation *op, SlapReply *rs, + ldap_back_send_t sendok ); static int ldap_back_conndnlc_cmp( const void *c1, const void *c2 ); @@ -57,64 +136,89 @@ ldap_back_conndnlc_cmp( const void *c1, const void *c2 ); int ldap_back_bind( Operation *op, SlapReply *rs ) { - ldapinfo_t *li = (ldapinfo_t *) op->o_bd->be_private; - ldapconn_t *lc; + ldapinfo_t *li = (ldapinfo_t *) op->o_bd->be_private; + ldapconn_t *lc; - int rc = 0; - ber_int_t msgid; + int rc = 0; + ber_int_t msgid; + ldap_back_send_t retrying = LDAP_BACK_RETRYING; - lc = ldap_back_getconn( op, rs, LDAP_BACK_BIND_SERR ); + 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 ); +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, op->o_ctrls, NULL, &msgid ); - rc = ldap_back_op_result( lc, op, rs, msgid, 0, LDAP_BACK_SENDERR ); + /* 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 ( rc == LDAP_SUCCESS ) { /* If defined, proxyAuthz will be used also when * back-ldap is the authorizing backend; for this - * purpose, a successful bind is followed by a - * bind with the configured identity assertion */ + * 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_proxy_authz_bind( lc, op, rs, LDAP_BACK_SENDERR ); - if ( !LDAP_BACK_CONN_ISBOUND( lc ) ) { - rc = 1; - goto done; - } + assert( lc->lc_binding == 1 ); + lc->lc_binding = 0; + ldap_back_release_conn( op, rs, 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 ) ) { - if ( !BER_BVISNULL( &lc->lc_cred ) ) { - memset( lc->lc_cred.bv_val, 0, - lc->lc_cred.bv_len ); - } ber_bvreplace( &lc->lc_cred, &op->orb_cred ); - ldap_set_rebind_proc( lc->lc_ld, ldap_back_rebind_f, lc ); + ldap_set_rebind_proc( lc->lc_ld, li->li_rebind_f, lc ); + + } else { + lc->lc_cred.bv_len = 0; } } -done:; assert( lc->lc_binding == 1 ); lc->lc_binding = 0; /* must re-insert if local DN changed as result of bind */ if ( !LDAP_BACK_CONN_ISBOUND( lc ) - || ( LDAP_BACK_CONN_ISBOUND( lc ) - && !dn_match( &op->o_req_ndn, &lc->lc_local_ndn ) ) ) + || ( !dn_match( &op->o_req_ndn, &lc->lc_local_ndn ) + && !LDAP_BACK_PCONN_ISPRIV( lc ) ) ) { int lerr = -1; ldapconn_t *tmplc; @@ -128,20 +232,74 @@ retry_lock:; 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 ); - tmplc = avl_delete( &li->li_conninfo.lai_tree, (caddr_t)lc, - ldap_back_conndnlc_cmp ); - assert( tmplc == NULL || lc == tmplc ); + if ( LDAP_BACK_PCONN_ISPRIV( lc ) ) { + /* this can happen, for example, if the bind fails + * for some reason... */ + if ( lc->lc_q.tqe_prev != NULL ) { + assert( LDAP_BACK_CONN_CACHED( lc ) ); + assert( li->li_conn_priv[ LDAP_BACK_CONN2PRIV( lc ) ].lic_num > 0 ); + LDAP_TAILQ_REMOVE( &li->li_conn_priv[ LDAP_BACK_CONN2PRIV( lc ) ].lic_priv, lc, lc_q ); + li->li_conn_priv[ LDAP_BACK_CONN2PRIV( lc ) ].lic_num--; + lc->lc_q.tqe_prev = NULL; + lc->lc_q.tqe_next = NULL; + + } else { + assert( !LDAP_BACK_CONN_CACHED( lc ) ); + } + + } else { + tmplc = avl_delete( &li->li_conninfo.lai_tree, (caddr_t)lc, + ldap_back_conndnlc_cmp ); + assert( ( LDAP_BACK_CONN_TAINTED( lc ) && tmplc == NULL ) || lc == tmplc ); + } + LDAP_BACK_CONN_CACHED_CLEAR( lc ); + + /* delete all cached connections with the current connection */ + if ( LDAP_BACK_SINGLECONN( li ) ) { + while ( ( tmplc = avl_delete( &li->li_conninfo.lai_tree, (caddr_t)lc, ldap_back_conn_cmp ) ) != NULL ) + { + Debug( LDAP_DEBUG_TRACE, + "=>ldap_back_bind: destroying conn %ld (refcnt=%u)\n", + LDAP_BACK_PCONN_ID( lc ), lc->lc_refcnt, 0 ); + + 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 = avl_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: @@ -255,64 +413,53 @@ ldap_back_conndn_dup( void *c1, void *c2 ) return 0; } -#if PRINT_CONNTREE > 0 -static void -ravl_print( Avlnode *root, int depth ) +static int +ldap_back_freeconn( Operation *op, ldapconn_t *lc, int dolock ) { - int i; - ldapconn_t *lc; - - if ( root == 0 ) { - return; - } - - ravl_print( root->avl_right, depth+1 ); - - for ( i = 0; i < depth; i++ ) { - fprintf( stderr, "-" ); + ldapinfo_t *li = (ldapinfo_t *) op->o_bd->be_private; + + if ( dolock ) { + ldap_pvt_thread_mutex_lock( &li->li_conninfo.lai_mutex ); } - lc = root->avl_data; - fprintf( stderr, "lc=%p local=\"%s\" conn=%p %s refcnt=%d\n", - (void *)lc, lc->lc_local_ndn.bv_val, (void *)lc->lc_conn, - avl_bf2str( root->avl_bf ), lc->lc_refcnt ); - - ravl_print( root->avl_left, depth+1 ); -} +#if LDAP_BACK_PRINT_CONNTREE > 0 + ldap_back_print_conntree( li, ">>> ldap_back_freeconn" ); +#endif /* LDAP_BACK_PRINT_CONNTREE */ -static void -myprint( Avlnode *root ) -{ - fprintf( stderr, "========>\n" ); - - if ( root == 0 ) { - fprintf( stderr, "\tNULL\n" ); + if ( LDAP_BACK_PCONN_ISPRIV( lc ) ) { + if ( lc->lc_q.tqe_prev != NULL ) { + assert( LDAP_BACK_CONN_CACHED( lc ) ); + 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_BACK_CONN_CACHED_CLEAR( lc ); - } else { - ravl_print( root, 0 ); - } - - fprintf( stderr, "<========\n" ); -} -#endif /* PRINT_CONNTREE */ + } else { + assert( !LDAP_BACK_CONN_CACHED( lc ) ); + } + lc->lc_q.tqe_prev = NULL; + lc->lc_q.tqe_next = NULL; -int -ldap_back_freeconn( Operation *op, ldapconn_t *lc, int dolock ) -{ - ldapinfo_t *li = (ldapinfo_t *) op->o_bd->be_private; - ldapconn_t *tmplc; + } else { + ldapconn_t *tmplc = NULL; - if ( dolock ) { - ldap_pvt_thread_mutex_lock( &li->li_conninfo.lai_mutex ); + if ( LDAP_BACK_CONN_CACHED( lc ) ) { + tmplc = avl_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 ); } - tmplc = avl_delete( &li->li_conninfo.lai_tree, (caddr_t)lc, - ldap_back_conndnlc_cmp ); - assert( LDAP_BACK_CONN_TAINTED( lc ) || tmplc == 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 ); } @@ -332,13 +479,9 @@ ldap_back_start_tls( const char **text ) { int rc = LDAP_SUCCESS; - ldapinfo_t dummy; - - /* this is ridiculous... */ - dummy.li_flags = flags; /* start TLS ("tls-[try-]{start,propagate}" statements) */ - if ( ( LDAP_BACK_USE_TLS( &dummy ) || ( *is_tls && LDAP_BACK_PROPAGATE_TLS( &dummy ) ) ) + if ( ( LDAP_BACK_USE_TLS_F( flags ) || ( *is_tls && LDAP_BACK_PROPAGATE_TLS_F( flags ) ) ) && !ldap_is_ldaps_url( url ) ) { #ifdef SLAP_STARTTLS_ASYNCHRONOUS @@ -354,13 +497,15 @@ ldap_back_start_tls( } if ( protocol < LDAP_VERSION3 ) { - protocol = LDAP_VERSION3; - /* Set LDAP version */ - ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION, - (const void *)&protocol ); + /* 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 ); } - rc = ldap_start_tls( ld, NULL, NULL, &msgid ); if ( rc == LDAP_SUCCESS ) { LDAPMessage *res = NULL; struct timeval tv; @@ -445,7 +590,7 @@ retry:; break; default: - if ( LDAP_BACK_TLS_CRITICAL( &dummy ) ) { + if ( LDAP_BACK_TLS_CRITICAL_F( flags ) ) { *text = "could not start TLS"; break; } @@ -468,15 +613,18 @@ static int ldap_back_prepare_conn( ldapconn_t **lcp, Operation *op, SlapReply *rs, ldap_back_send_t sendok ) { ldapinfo_t *li = (ldapinfo_t *)op->o_bd->be_private; - int vers = op->o_protocol; + int version; LDAP *ld = NULL; #ifdef HAVE_TLS int is_tls = op->o_conn->c_is_tls; + time_t lc_time = (time_t)(-1); #endif /* HAVE_TLS */ assert( lcp != NULL ); + 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; } @@ -484,11 +632,17 @@ ldap_back_prepare_conn( ldapconn_t **lcp, Operation *op, SlapReply *rs, ldap_bac /* Set LDAP version. This will always succeed: If the client * bound with a particular version, then so can we. */ - if ( vers == 0 ) { + 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 */ - vers = LDAP_VERSION3; + version = LDAP_VERSION3; } - ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION, (const void *)&vers ); + 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, @@ -503,17 +657,23 @@ ldap_back_prepare_conn( ldapconn_t **lcp, Operation *op, SlapReply *rs, ldap_bac } #ifdef HAVE_TLS + ldap_pvt_thread_mutex_lock( &li->li_uri_mutex ); rs->sr_err = ldap_back_start_tls( ld, op->o_protocol, &is_tls, li->li_uri, li->li_flags, li->li_nretries, &rs->sr_text ); + ldap_pvt_thread_mutex_unlock( &li->li_uri_mutex ); if ( rs->sr_err != LDAP_SUCCESS ) { ldap_unbind_ext( ld, NULL, NULL ); goto error_return; + + } else if ( li->li_idle_timeout ) { + /* only touch when activity actually took place... */ + lc_time = op->o_time; } #endif /* HAVE_TLS */ if ( *lcp == NULL ) { *lcp = (ldapconn_t *)ch_calloc( 1, sizeof( ldapconn_t ) ); - (*lcp)->lc_flags= li->li_flags; + (*lcp)->lc_flags = li->li_flags; } (*lcp)->lc_ld = ld; (*lcp)->lc_refcnt = 1; @@ -524,6 +684,9 @@ ldap_back_prepare_conn( ldapconn_t **lcp, Operation *op, SlapReply *rs, ldap_bac } else { LDAP_BACK_CONN_ISTLS_CLEAR( *lcp ); } + if ( lc_time != (time_t)(-1) ) { + (*lcp)->lc_time = lc_time; + } #endif /* HAVE_TLS */ error_return:; @@ -546,48 +709,179 @@ error_return:; return rs->sr_err; } -ldapconn_t * -ldap_back_getconn( Operation *op, SlapReply *rs, ldap_back_send_t sendok ) +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, binding = 1; + int refcnt = 1, + binding = 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 ) ) { + 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; - lc_curr.lc_conn = LDAP_BACK_PCONN_SET( op ); + 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 ( isproxyauthz == -1 ) { + return NULL; + } + if ( op->o_tag == LDAP_REQ_BIND ) { + op->o_dn = save_o_dn; + op->o_ndn = save_o_ndn; + } + lc_curr.lc_local_ndn = op->o_ndn; - /* Explicit binds must not be shared */ - if ( op->o_tag == LDAP_REQ_BIND || SLAP_IS_AUTHZ_BACKEND( op ) ) { + /* 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 { - lc_curr.lc_conn = LDAP_BACK_PCONN_SET( op ); + 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 ( !( sendok & LDAP_BACK_BINDING ) ) { - /* Searches for a ldapconn in the avl tree */ + 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, + ldapconn_t, lc_q ) ) + { + LDAP_TAILQ_REMOVE( &li->li_conn_priv[ LDAP_BACK_CONN2PRIV( lc ) ].lic_priv, + lc, lc_q ); + lc->lc_q.tqe_prev = NULL; + lc->lc_q.tqe_next = NULL; + 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 *)avl_find( li->li_conninfo.lai_tree, + (caddr_t)&lc_curr, ldap_back_conndn_cmp ); + } - lc = (ldapconn_t *)avl_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 ) ) { - ldap_pvt_thread_mutex_unlock( &li->li_conninfo.lai_mutex ); - ldap_pvt_thread_yield(); - goto retry_lock; + 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; + binding = ++lc->lc_binding; } - refcnt = ++lc->lc_refcnt; - binding = ++lc->lc_binding; } ldap_pvt_thread_mutex_unlock( &li->li_conninfo.lai_mutex ); } @@ -597,29 +891,52 @@ retry_lock: if ( ldap_back_prepare_conn( &lc, op, rs, sendok ) != LDAP_SUCCESS ) { 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 acl_authcDN is to be used; however, in some cases + * one already configured identity assertion with a highly + * privileged idassert_authcDN, so if acl_authcDN is NULL + * and idassert_authcDN is not, use the second 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 ) ) { - ber_dupbv( &lc->lc_cred, &li->li_acl_passwd ); - ber_dupbv( &lc->lc_bound_ndn, &li->li_acl_authcDN ); + if ( BER_BVISNULL( &li->li_acl_authcDN ) && !BER_BVISNULL( &li->li_idassert_authcDN ) ) { + 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 0 - /* FIXME: if we set lc_bound_ndn = o_ndn - * we end up with a bind with DN but no password! */ if ( !BER_BVISEMPTY( &op->o_ndn ) && SLAP_IS_AUTHZ_BACKEND( op ) ) { ber_dupbv( &lc->lc_bound_ndn, &op->o_ndn ); } -#endif } #ifdef HAVE_TLS @@ -627,15 +944,22 @@ retry_lock: * check if the non-TLS connection was already * in cache; in case, destroy the newly created * connection and use the existing one */ - if ( lc->lc_conn == LDAP_BACK_PCONN_TLS + if ( LDAP_BACK_PCONN_ISTLS( lc ) && !ldap_tls_inplace( lc->lc_ld ) ) { - ldapconn_t *tmplc; + ldapconn_t *tmplc = NULL; + int idx = LDAP_BACK_CONN2PRIV( &lc_curr ) - 1; - lc_curr.lc_conn = LDAP_BACK_PCONN; ldap_pvt_thread_mutex_lock( &li->li_conninfo.lai_mutex ); - tmplc = (ldapconn_t *)avl_find( li->li_conninfo.lai_tree, - (caddr_t)&lc_curr, ldap_back_conndn_cmp ); + 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; binding = ++tmplc->lc_binding; @@ -650,57 +974,86 @@ retry_lock: } #endif /* HAVE_TLS */ - LDAP_BACK_CONN_ISBOUND_CLEAR( lc ); - /* 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 ); + assert( lc->lc_refcnt == 1 ); assert( lc->lc_binding == 1 ); - rs->sr_err = avl_insert( &li->li_conninfo.lai_tree, (caddr_t)lc, - ldap_back_conndn_cmp, ldap_back_conndn_dup ); -#if PRINT_CONNTREE > 0 - myprint( li->li_conninfo.lai_tree ); -#endif /* PRINT_CONNTREE */ +#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 = avl_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: conn %p inserted refcnt=%u binding=%u\n", - (void *)lc, refcnt, binding ); + if ( StatslogTest( LDAP_DEBUG_TRACE ) ) { + char buf[ SLAP_TEXT_BUFLEN ]; + + snprintf( buf, sizeof( buf ), + "lc=%p inserted refcnt=%u binding=%u rc=%d", + (void *)lc, refcnt, binding, rs->sr_err ); + + Debug( LDAP_DEBUG_TRACE, + "=>ldap_back_getconn: %s: %s\n", + op->o_log_prefix, buf, 0 ); + } - /* Err could be -1 in case a duplicate ldapconn is inserted */ - switch ( rs->sr_err ) { - case 0: - break; + 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: - if ( !( sendok & LDAP_BACK_BINDING ) ) { - /* duplicate: free and try to get the newly created one */ - goto retry_lock; - } - /* taint connection, so that it'll be freed when released */ - ldap_pvt_thread_mutex_lock( &li->li_conninfo.lai_mutex ); - (void *)avl_delete( &li->li_conninfo.lai_tree, (caddr_t)lc, - ldap_back_conndnlc_cmp ); - ldap_pvt_thread_mutex_unlock( &li->li_conninfo.lai_mutex ); - LDAP_BACK_CONN_TAINTED_SET( lc ); - 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; + } - default: - 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 ); - rs->sr_text = NULL; + /* 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 ); + rs->sr_text = NULL; + } + return NULL; } - return NULL; } } else { - char buf[ SLAP_TEXT_BUFLEN ]; int expiring = 0; if ( ( li->li_idle_timeout != 0 && op->o_time > lc->lc_time + li->li_idle_timeout ) @@ -711,28 +1064,53 @@ retry_lock: /* 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 ); - (void *)avl_delete( &li->li_conninfo.lai_tree, (caddr_t)lc, + +#if LDAP_BACK_PRINT_CONNTREE > 0 + ldap_back_print_conntree( li, ">>> ldap_back_getconn(timeout)" ); +#endif /* LDAP_BACK_PRINT_CONNTREE */ + + if ( LDAP_BACK_PCONN_ISPRIV( lc ) ) { + if ( lc->lc_q.tqe_prev != NULL ) { + assert( LDAP_BACK_CONN_CACHED( lc ) ); + assert( li->li_conn_priv[ LDAP_BACK_CONN2PRIV( lc ) ].lic_num > 0 ); + LDAP_TAILQ_REMOVE( &li->li_conn_priv[ LDAP_BACK_CONN2PRIV( lc ) ].lic_priv, + lc, lc_q ); + li->li_conn_priv[ LDAP_BACK_CONN2PRIV( lc ) ].lic_num--; + lc->lc_q.tqe_prev = NULL; + lc->lc_q.tqe_next = NULL; + + } else { + assert( !LDAP_BACK_CONN_CACHED( lc ) ); + } + + } else { + (void)avl_delete( &li->li_conninfo.lai_tree, (caddr_t)lc, ldap_back_conndnlc_cmp ); - ldap_pvt_thread_mutex_unlock( &li->li_conninfo.lai_mutex ); + } LDAP_BACK_CONN_TAINTED_SET( lc ); + LDAP_BACK_CONN_CACHED_CLEAR( 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 ); } - { + if ( StatslogTest( LDAP_DEBUG_TRACE ) ) { + char buf[ SLAP_TEXT_BUFLEN ]; + snprintf( buf, sizeof( buf ), "conn %p fetched refcnt=%u binding=%u%s", (void *)lc, refcnt, binding, expiring ? " expiring" : "" ); Debug( LDAP_DEBUG_TRACE, "=>ldap_back_getconn: %s.\n", buf, 0, 0 ); } - } #ifdef HAVE_TLS done:; #endif /* HAVE_TLS */ - if ( li->li_idle_timeout && lc ) { - lc->lc_time = op->o_time; - } return lc; } @@ -741,11 +1119,13 @@ void ldap_back_release_conn_lock( Operation *op, SlapReply *rs, - ldapconn_t *lc, + ldapconn_t **lcp, int dolock ) { ldapinfo_t *li = (ldapinfo_t *)op->o_bd->be_private; + ldapconn_t *lc = *lcp; + if ( dolock ) { ldap_pvt_thread_mutex_lock( &li->li_conninfo.lai_mutex ); } @@ -754,24 +1134,92 @@ ldap_back_release_conn_lock( lc->lc_refcnt--; if ( LDAP_BACK_CONN_TAINTED( lc ) ) { ldap_back_freeconn( op, 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, 0, 0 ); + + 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: + break; + } + + 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 ); +} + /* - * ldap_back_dobind - * - * Note: as the check for the value of lc->lc_bound was already here, I removed - * it from all the callers, and I made the function return the flag, so - * it can be used to simplify the check. + * 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 *lc, + ldapconn_t **lcp, Operation *op, SlapReply *rs, ldap_back_send_t sendok, @@ -780,11 +1228,33 @@ ldap_back_dobind_int( { ldapinfo_t *li = (ldapinfo_t *)op->o_bd->be_private; - int rc, binding = 0; + ldapconn_t *lc; + struct berval binddn = slap_empty_bv, + bindcred = slap_empty_bv; + + int rc = 0, + isbound, + binding = 0; ber_int_t msgid; + 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 ); @@ -792,8 +1262,8 @@ retry_lock:; if ( binding == 0 ) { /* check if already bound */ - rc = LDAP_BACK_CONN_ISBOUND( lc ); - if ( rc ) { + rc = isbound = LDAP_BACK_CONN_ISBOUND( lc ); + if ( isbound ) { lc->lc_binding--; if ( dolock ) { ldap_pvt_thread_mutex_unlock( &li->li_conninfo.lai_mutex ); @@ -830,24 +1300,6 @@ retry_lock:; ldap_pvt_thread_mutex_unlock( &li->li_conninfo.lai_mutex ); } -#if 0 - while ( lc->lc_refcnt > 1 ) { - ldap_pvt_thread_yield(); - rc = LDAP_BACK_CONN_ISBOUND( lc ); - if ( rc ) { - return rc; - } - } - - if ( dolock ) { - ldap_pvt_thread_mutex_lock( &li->li_conninfo.lai_mutex ); - } - LDAP_BACK_CONN_BINDING_SET( lc ); - if ( dolock ) { - ldap_pvt_thread_mutex_unlock( &li->li_conninfo.lai_mutex ); - } -#endif - /* * FIXME: we need to let clients use proxyAuthz * otherwise we cannot do symmetric pools of servers; @@ -870,12 +1322,14 @@ retry_lock:; * but the "override" flag is given to idassert. * It allows to use SASL bind and yet proxyAuthz users */ - if ( op->o_conn != NULL && - !op->o_do_not_cache && - ( BER_BVISNULL( &lc->lc_bound_ndn ) || - ( li->li_idassert_flags & LDAP_BACK_AUTH_OVERRIDE ) ) ) - { - (void)ldap_back_proxy_authz_bind( lc, op, rs, sendok ); + 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 ); + assert( rc == 1 ); + } + rc = ldap_back_proxy_authz_bind( lc, op, rs, sendok, &binddn, &bindcred ); goto done; } @@ -887,12 +1341,12 @@ retry_lock:; if ( li->li_acl_secprops != NULL ) { rc = ldap_set_option( lc->lc_ld, - LDAP_OPT_X_SASL_SECPROPS, li->li_acl_secprops); + LDAP_OPT_X_SASL_SECPROPS, li->li_acl_secprops ); if ( rc != LDAP_OPT_SUCCESS ) { Debug( LDAP_DEBUG_ANY, "Error: ldap_set_option " - "(%s,SECPROPS,\"%s\") failed!\n", - li->li_uri, li->li_acl_secprops, 0 ); + "(SECPROPS,\"%s\") failed!\n", + li->li_acl_secprops, 0, 0 ); goto done; } } @@ -915,18 +1369,25 @@ retry_lock:; rs->sr_err = slap_map_api2result( rs ); if ( rs->sr_err != LDAP_SUCCESS ) { LDAP_BACK_CONN_ISBOUND_CLEAR( lc ); - send_ldap_result( op, rs ); + if ( sendok & LDAP_BACK_SENDERR ) { + send_ldap_result( op, rs ); + } } else { LDAP_BACK_CONN_ISBOUND_SET( lc ); } + + if ( LDAP_BACK_QUARANTINE( li ) ) { + ldap_back_quarantine( op, rs ); + } + goto done; } #endif /* HAVE_CYRUS_SASL */ retry:; rs->sr_err = ldap_sasl_bind( lc->lc_ld, - lc->lc_bound_ndn.bv_val, + BER_BVISNULL( &lc->lc_cred ) ? "" : lc->lc_bound_ndn.bv_val, LDAP_SASL_SIMPLE, &lc->lc_cred, NULL, NULL, &msgid ); @@ -970,13 +1431,29 @@ retry:; } } + /* FIXME: one binding-- too many? */ + lc->lc_binding--; + assert( lc->lc_refcnt == 1 ); + lc->lc_refcnt = 0; ldap_back_freeconn( op, 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 ) ) + { + send_ldap_result( op, rs ); + } + return 0; } - rc = ldap_back_op_result( lc, op, rs, msgid, 0, sendok ); + rc = ldap_back_op_result( lc, op, rs, msgid, + -1, (sendok|LDAP_BACK_BINDING) ); if ( rc == LDAP_SUCCESS ) { LDAP_BACK_CONN_ISBOUND_SET( lc ); } @@ -986,18 +1463,27 @@ done:; LDAP_BACK_CONN_BINDING_CLEAR( lc ); rc = LDAP_BACK_CONN_ISBOUND( lc ); if ( !rc ) { - ldap_back_release_conn_lock( op, rs, lc, dolock ); + ldap_back_release_conn_lock( op, rs, lcp, dolock ); + + } else if ( LDAP_BACK_SAVECRED( li ) ) { + ldap_set_rebind_proc( lc->lc_ld, li->li_rebind_f, lc ); } 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 *lc, Operation *op, SlapReply *rs, ldap_back_send_t sendok ) +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( lc, op, rs, sendok, li->li_nretries, 1 ); + return ldap_back_dobind_int( lcp, op, rs, + ( sendok | LDAP_BACK_GETCONN ), li->li_nretries, 1 ); } /* @@ -1006,7 +1492,7 @@ ldap_back_dobind( ldapconn_t *lc, Operation *op, SlapReply *rs, ldap_back_send_t * This is a callback used for chasing referrals using the same * credentials as the original user on this session. */ -static int +int ldap_back_default_rebind( LDAP *ld, LDAP_CONST char *url, ber_tag_t request, ber_int_t msgid, void *params ) { @@ -1031,10 +1517,36 @@ ldap_back_default_rebind( LDAP *ld, LDAP_CONST char *url, ber_tag_t request, /* FIXME: add checks on the URL/identity? */ - return ldap_sasl_bind_s( ld, lc->lc_bound_ndn.bv_val, + 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 ); } +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_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, @@ -1044,14 +1556,19 @@ ldap_back_op_result( time_t timeout, ldap_back_send_t sendok ) { + ldapinfo_t *li = (ldapinfo_t *)op->o_bd->be_private; + char *match = NULL; - LDAPMessage *res = NULL; char *text = NULL; + char **refs = NULL; + LDAPControl **ctrls = NULL; #define ERR_OK(err) ((err) == LDAP_SUCCESS || (err) == LDAP_COMPARE_FALSE || (err) == LDAP_COMPARE_TRUE) 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 @@ -1059,28 +1576,68 @@ ldap_back_op_result( if ( 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 ) { - tv.tv_sec = timeout; - tv.tv_usec = 0; + /* 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 ); - } else { - LDAP_BACK_TV_SET( &tv ); + 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 ) { - (void)ldap_abandon_ext( lc->lc_ld, msgid, NULL, NULL ); - rs->sr_err = op->o_protocol >= LDAP_VERSION3 ? - LDAP_ADMINLIMIT_EXCEEDED : LDAP_OPERATIONS_ERROR; - rs->sr_text = "Operation timed out"; + if ( timeout && slap_get_time() > stoptime ) { + if ( sendok & LDAP_BACK_BINDING ) { + ldap_unbind_ext( lc->lc_ld, NULL, NULL ); + lc->lc_ld = NULL; + LDAP_BACK_CONN_TAINTED_SET( lc ); + + } 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; @@ -1096,22 +1653,32 @@ retry:; * structure (this includes * LDAP_COMPARE_{TRUE|FALSE}) */ default: - rc = ldap_parse_result( lc->lc_ld, res, &rs->sr_err, - &match, &text, NULL, NULL, 1 ); -#ifndef LDAP_NULL_IS_NULL - if ( match != NULL && match[ 0 ] == '\0' ) { - ldap_memfree( match ); - match = NULL; - } - if ( text != NULL && text[ 0 ] == '\0' ) { - ldap_memfree( text ); - text = NULL; + /* only touch when activity actually took place... */ + if ( li->li_idle_timeout && lc ) { + lc->lc_time = op->o_time; } -#endif /* LDAP_NULL_IS_NULL */ + + rc = ldap_parse_result( lc->lc_ld, res, &rs->sr_err, + &match, &text, &refs, &ctrls, 1 ); rs->sr_text = text; if ( rc != LDAP_SUCCESS ) { rs->sr_err = rc; } + if ( refs != NULL ) { + 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 ] ); + } + if ( ctrls != NULL ) { + rs->sr_ctrls = ctrls; + } } } @@ -1130,12 +1697,24 @@ retry:; rs->sr_matched = match; } } - if ( op->o_conn && - ( ( sendok & LDAP_BACK_SENDOK ) - || ( ( sendok & LDAP_BACK_SENDERR ) && rs->sr_err != LDAP_SUCCESS ) ) ) + + 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 ) ) { + send_ldap_result( op, rs ); + } + } + + } else if ( op->o_conn && + ( ( ( sendok & LDAP_BACK_SENDOK ) && ERR_OK( rs->sr_err ) ) + || ( ( sendok & LDAP_BACK_SENDERR ) && rs->sr_err != LDAP_SUCCESS ) ) ) { send_ldap_result( op, rs ); } + if ( match ) { if ( rs->sr_matched != match ) { free( (char *)rs->sr_matched ); @@ -1143,10 +1722,25 @@ retry:; rs->sr_matched = NULL; ldap_memfree( match ); } + if ( text ) { ldap_memfree( text ); } rs->sr_text = NULL; + + if ( rs->sr_ref ) { + assert( refs != NULL ); + ber_memvfree( (void **)refs ); + op->o_tmpfree( rs->sr_ref, op->o_tmpmemctx ); + rs->sr_ref = NULL; + } + + if ( ctrls ) { + assert( rs->sr_ctrls != NULL ); + ldap_controls_free( ctrls ); + rs->sr_ctrls = NULL; + } + return( ERR_OK( rs->sr_err ) ? LDAP_SUCCESS : rs->sr_err ); } @@ -1154,8 +1748,9 @@ retry:; int ldap_back_retry( ldapconn_t **lcp, Operation *op, SlapReply *rs, ldap_back_send_t sendok ) { - int rc = 0; ldapinfo_t *li = (ldapinfo_t *)op->o_bd->be_private; + int rc = 0, + binding; assert( lcp != NULL ); assert( *lcp != NULL ); @@ -1163,11 +1758,15 @@ ldap_back_retry( ldapconn_t **lcp, Operation *op, SlapReply *rs, ldap_back_send_ ldap_pvt_thread_mutex_lock( &li->li_conninfo.lai_mutex ); if ( (*lcp)->lc_refcnt == 1 ) { + 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; @@ -1176,12 +1775,25 @@ ldap_back_retry( ldapconn_t **lcp, Operation *op, SlapReply *rs, ldap_back_send_ /* 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 ) { - rc = 0; + /* freeit, because lc_refcnt == 1 */ + (*lcp)->lc_refcnt = 0; + (void)ldap_back_freeconn( op, *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 ) { + 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; + LDAP_BACK_CONN_TAINTED_SET( *lcp ); + (void)ldap_back_freeconn( op, *lcp, 0 ); *lcp = NULL; } } @@ -1191,8 +1803,9 @@ ldap_back_retry( ldapconn_t **lcp, Operation *op, SlapReply *rs, ldap_back_send_ "ldap_back_retry: conn %p refcnt=%u unable to retry.\n", (void *)(*lcp), (*lcp)->lc_refcnt, 0 ); - ldap_back_release_conn_lock( op, rs, *lcp, 0 ); - *lcp = NULL; + LDAP_BACK_CONN_TAINTED_SET( *lcp ); + ldap_back_release_conn_lock( op, rs, lcp, 0 ); + assert( *lcp == NULL ); if ( sendok ) { rs->sr_err = LDAP_UNAVAILABLE; @@ -1207,15 +1820,40 @@ ldap_back_retry( ldapconn_t **lcp, Operation *op, SlapReply *rs, ldap_back_send_ } static int -ldap_back_proxy_authz_bind( ldapconn_t *lc, Operation *op, SlapReply *rs, ldap_back_send_t sendok ) +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 binddn = slap_empty_bv; - struct berval bindcred = slap_empty_bv; struct berval ndn; int dobind = 0; - int msgid; - int rc; + + 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; @@ -1224,36 +1862,13 @@ ldap_back_proxy_authz_bind( ldapconn_t *lc, Operation *op, SlapReply *rs, ldap_b ndn = op->o_ndn; } - /* - * 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 "proxyauthzdn" is set, then bind as - * "proxyauthzdn" and explicitly add the proxyAuthz - * control to every operation with the dn bound - * to the connection as control value. - */ - - /* bind as proxyauthzdn only if no idassert mode - * is requested, or if the client's identity - * is authorized */ 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; + *binddn = li->li_idassert_authcDN; + *bindcred = li->li_idassert_passwd; dobind = 1; } } @@ -1266,13 +1881,13 @@ ldap_back_proxy_authz_bind( ldapconn_t *lc, Operation *op, SlapReply *rs, ldap_b rs->sr_err = LDAP_INAPPROPRIATE_AUTH; if ( sendok & LDAP_BACK_SENDERR ) { send_ldap_result( op, rs ); + dobind = -1; } - LDAP_BACK_CONN_ISBOUND_CLEAR( lc ); } else { rs->sr_err = LDAP_SUCCESS; - binddn = slap_empty_bv; - bindcred = slap_empty_bv; + *binddn = slap_empty_bv; + *bindcred = slap_empty_bv; break; } @@ -1293,13 +1908,13 @@ ldap_back_proxy_authz_bind( ldapconn_t *lc, Operation *op, SlapReply *rs, ldap_b if ( li->li_idassert_flags & LDAP_BACK_AUTH_PRESCRIPTIVE ) { if ( sendok & LDAP_BACK_SENDERR ) { send_ldap_result( op, rs ); + dobind = -1; } - LDAP_BACK_CONN_ISBOUND_CLEAR( lc ); } else { rs->sr_err = LDAP_SUCCESS; - binddn = slap_empty_bv; - bindcred = slap_empty_bv; + *binddn = slap_empty_bv; + *bindcred = slap_empty_bv; break; } @@ -1307,13 +1922,38 @@ ldap_back_proxy_authz_bind( ldapconn_t *lc, Operation *op, SlapReply *rs, ldap_b } } - binddn = li->li_idassert_authcDN; - bindcred = li->li_idassert_passwd; + *binddn = li->li_idassert_authcDN; + *bindcred = li->li_idassert_passwd; dobind = 1; break; } - if ( dobind && li->li_idassert_authmethod == LDAP_AUTH_SASL ) { +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; @@ -1374,7 +2014,7 @@ ldap_back_proxy_authz_bind( ldapconn_t *lc, Operation *op, SlapReply *rs, ldap_b li->li_idassert_passwd.bv_val, authzID.bv_val ); - rs->sr_err = ldap_sasl_interactive_bind_s( lc->lc_ld, binddn.bv_val, + rs->sr_err = ldap_sasl_interactive_bind_s( lc->lc_ld, binddn->bv_val, li->li_idassert_sasl_mech.bv_val, NULL, NULL, LDAP_SASL_QUIET, lutil_sasl_interact, defaults ); @@ -1401,13 +2041,17 @@ ldap_back_proxy_authz_bind( ldapconn_t *lc, Operation *op, SlapReply *rs, ldap_b switch ( li->li_idassert_authmethod ) { case LDAP_AUTH_NONE: - LDAP_BACK_CONN_ISBOUND_SET( lc ); - goto done; + /* 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 ); + binddn->bv_val, LDAP_SASL_SIMPLE, + bindcred, NULL, NULL, &msgid ); + rc = ldap_back_op_result( lc, op, rs, msgid, + -1, (sendok|LDAP_BACK_BINDING) ); break; default: @@ -1420,9 +2064,25 @@ ldap_back_proxy_authz_bind( ldapconn_t *lc, Operation *op, SlapReply *rs, ldap_b goto done; } - rc = ldap_back_op_result( lc, op, rs, msgid, 0, sendok ); 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 ); + 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 ) ) { + 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 ); @@ -1456,27 +2116,43 @@ done:; */ int ldap_back_proxy_authz_ctrl( - ldapconn_t *lc, + struct berval *bound_ndn, + int version, + slap_idassert_t *si, Operation *op, SlapReply *rs, LDAPControl ***pctrls ) { - ldapinfo_t *li = (ldapinfo_t *) op->o_bd->be_private; - LDAPControl **ctrls = NULL; - int i = 0, - mode; - struct berval assertedID, - ndn; + LDAPControl **ctrls = NULL; + int i = 0; + slap_idassert_mode_t mode; + struct berval assertedID, + ndn; *pctrls = NULL; rs->sr_err = LDAP_SUCCESS; + /* don't proxyAuthz if protocol is not LDAPv3 */ + switch ( version ) { + case LDAP_VERSION3: + break; + + case 0: + if ( op->o_protocol == 0 || op->o_protocol == LDAP_VERSION3 ) { + break; + } + /* fall thru */ + + default: + goto done; + } + /* 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( &li->li_idassert_authcID ) || BER_BVISEMPTY( &li->li_idassert_authcID ) ) - && ( BER_BVISNULL( &li->li_idassert_authcDN ) || BER_BVISEMPTY( &li->li_idassert_authcDN ) ) ) + 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 ) ) ) { goto done; } @@ -1495,7 +2171,7 @@ ldap_back_proxy_authz_ctrl( ndn = op->o_ndn; } - if ( li->li_idassert_mode == LDAP_BACK_IDASSERT_LEGACY ) { + if ( si->si_mode == LDAP_BACK_IDASSERT_LEGACY ) { if ( op->o_proxy_authz ) { /* * FIXME: we do not want to perform proxyAuthz @@ -1514,7 +2190,7 @@ ldap_back_proxy_authz_ctrl( goto done; } - if ( !BER_BVISNULL( &lc->lc_bound_ndn ) ) { + if ( !BER_BVISNULL( bound_ndn ) ) { goto done; } @@ -1522,23 +2198,18 @@ ldap_back_proxy_authz_ctrl( goto done; } - if ( BER_BVISNULL( &li->li_idassert_authcDN ) ) { + if ( BER_BVISNULL( &si->si_bc.sb_binddn ) ) { goto done; } - } else if ( li->li_idassert_authmethod == LDAP_AUTH_SASL ) { - if ( ( li->li_idassert_flags & LDAP_BACK_AUTH_NATIVE_AUTHZ ) - /* && ( !BER_BVISNULL( &ndn ) - || LDAP_BACK_CONN_ISBOUND( lc ) ) */ ) + } 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 */ - /* NOTE: the test on lc->lc_bound is used to trap - * native authorization of anonymous users, - * since in that case ndn is NULL */ goto done; } - } else if ( li->li_idassert_authz && !be_isroot( op ) ) { + } else if ( si->si_authz && !be_isroot( op ) ) { int rc; struct berval authcDN; @@ -1547,11 +2218,10 @@ ldap_back_proxy_authz_ctrl( } else { authcDN = ndn; } - rc = slap_sasl_matches( op, li->li_idassert_authz, + rc = slap_sasl_matches( op, si->si_authz, &authcDN, & authcDN ); if ( rc != LDAP_SUCCESS ) { - if ( li->li_idassert_flags & LDAP_BACK_AUTH_PRESCRIPTIVE ) - { + if ( si->si_flags & LDAP_BACK_AUTH_PRESCRIPTIVE ) { /* ndn is not authorized * to use idassert */ rs->sr_err = rc; @@ -1586,7 +2256,7 @@ ldap_back_proxy_authz_ctrl( mode = LDAP_BACK_IDASSERT_NOASSERT; } else { - mode = li->li_idassert_mode; + mode = si->si_mode; } switch ( mode ) { @@ -1619,7 +2289,7 @@ ldap_back_proxy_authz_ctrl( case LDAP_BACK_IDASSERT_OTHERID: case LDAP_BACK_IDASSERT_OTHERDN: /* assert idassert DN */ - assertedID = li->li_idassert_authzID; + assertedID = si->si_bc.sb_authzId; break; default: @@ -1631,7 +2301,7 @@ ldap_back_proxy_authz_ctrl( } /* don't idassert the bound DN (ITS#4497) */ - if ( dn_match( &assertedID, &lc->lc_bound_ndn ) ) { + if ( dn_match( &assertedID, bound_ndn ) ) { goto done; } @@ -1647,7 +2317,7 @@ ldap_back_proxy_authz_ctrl( ctrls[ 0 ]->ldctl_oid = LDAP_CONTROL_PROXY_AUTHZ; ctrls[ 0 ]->ldctl_iscritical = 1; - switch ( li->li_idassert_mode ) { + switch ( si->si_mode ) { /* already in u:ID or dn:DN form */ case LDAP_BACK_IDASSERT_OTHERID: case LDAP_BACK_IDASSERT_OTHERDN: @@ -1665,6 +2335,88 @@ ldap_back_proxy_authz_ctrl( break; } + /* Older versions of 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 = ctrls[ 0 ]->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, &ctrls[ 0 ]->ldctl_value, 1 ) == -1 ) { + rs->sr_err = LDAP_OTHER; + goto free_ber; + } + +free_ber:; + op->o_tmpfree( authzID.bv_val, op->o_tmpmemctx ); + ber_free_buf( ber ); + + if ( rs->sr_err != LDAP_SUCCESS ) { + op->o_tmpfree( ctrls, op->o_tmpmemctx ); + ctrls = NULL; + goto done; + } + + } else if ( si->si_flags & LDAP_BACK_AUTH_OBSOLETE_PROXY_AUTHZ ) { + struct berval authzID = ctrls[ 0 ]->ldctl_value, + tmp; + BerElementBuffer berbuf; + BerElement *ber = (BerElement *)&berbuf; + ber_tag_t tag; + + if ( strncasecmp( authzID.bv_val, "dn:", STRLENOF( "dn:" ) ) != 0 ) { + op->o_tmpfree( ctrls[ 0 ]->ldctl_value.bv_val, op->o_tmpmemctx ); + op->o_tmpfree( ctrls, op->o_tmpmemctx ); + ctrls = NULL; + 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, &ctrls[ 0 ]->ldctl_value, 1 ) == -1 ) { + rs->sr_err = LDAP_OTHER; + goto free_ber2; + } + +free_ber2:; + op->o_tmpfree( authzID.bv_val, op->o_tmpmemctx ); + ber_free_buf( ber ); + + if ( rs->sr_err != LDAP_SUCCESS ) { + op->o_tmpfree( ctrls, op->o_tmpmemctx ); + ctrls = NULL; + goto done; + } + + ctrls[ 0 ]->ldctl_oid = LDAP_CONTROL_OBSOLETE_PROXY_AUTHZ; + } + if ( op->o_ctrls ) { for ( i = 0; op->o_ctrls[ i ]; i++ ) { ctrls[ i + 1 ] = op->o_ctrls[ i ]; diff --git a/servers/slapd/back-ldap/chain.c b/servers/slapd/back-ldap/chain.c index 1ee58f1418..cf3ffa3362 100644 --- a/servers/slapd/back-ldap/chain.c +++ b/servers/slapd/back-ldap/chain.c @@ -27,9 +27,9 @@ #include #include +#include "lutil.h" #include "slap.h" #include "back-ldap.h" - #include "config.h" #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR @@ -58,10 +58,11 @@ static int sc_chainingBehavior; #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */ -#define LDAP_CH_NONE ((void *)(0)) -#define LDAP_CH_RES ((void *)(1)) -#define LDAP_CH_ERR ((void *)(2)) - +typedef enum { + LDAP_CH_NONE = 0, + LDAP_CH_RES, + LDAP_CH_ERR +} ldap_chain_status_t; static BackendInfo *lback; typedef struct ldap_chain_t { @@ -87,13 +88,19 @@ typedef struct ldap_chain_t { /* 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 (0x10U) +#define LDAP_CHAIN_F_CACHE_URI (0x02U) +#define LDAP_CHAIN_F_RETURN_ERR (0x04U) -#define LDAP_CHAIN_CHAINING( lc ) ( ( (lc)->lc_flags & LDAP_CHAIN_F_CHAINING ) == LDAP_CHAIN_F_CHAINING ) -#define LDAP_CHAIN_CACHE_URI( lc ) ( ( (lc)->lc_flags & LDAP_CHAIN_F_CACHE_URI ) == LDAP_CHAIN_F_CACHE_URI ) +#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; @@ -103,10 +110,34 @@ typedef struct ldap_chain_t { static int ldap_chain_db_init_common( BackendDB *be ); static int ldap_chain_db_init_one( BackendDB *be ); -#define ldap_chain_db_open_one(be) (lback)->bi_db_open( (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) (lback)->bi_db_destroy( (be) ) +typedef struct ldap_chain_cb_t { + ldap_chain_status_t lb_status; + ldap_chain_t *lb_lc; + BI_op_func *lb_op_f; + int lb_depth; +} ldap_chain_cb_t; + +static int +ldap_chain_op( + Operation *op, + SlapReply *rs, + BI_op_func *op_f, + 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( @@ -225,10 +256,12 @@ ldap_chain_uri_dup( void *c1, void *c2 ) 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 ( op->o_callback->sc_private == LDAP_CH_ERR ) { + if ( lb->lb_status == LDAP_CH_ERR ) { return 0; } @@ -260,12 +293,15 @@ ldap_chain_cb_search_response( Operation *op, SlapReply *rs ) } 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 ( get_chaining( op ) > SLAP_CONTROL_IGNORED ) { + if ( rs->sr_err == LDAP_REFERRAL && get_chaining( op ) > SLAP_CONTROL_IGNORED ) { switch ( get_continuationBehavior( op ) ) { case SLAP_CH_RESOLVE_CHAINING_REQUIRED: - op->o_callback->sc_private = LDAP_CH_ERR; + lb->lb_status = LDAP_CH_ERR; return rs->sr_err = LDAP_X_CANNOT_CHAIN; default: @@ -276,8 +312,15 @@ ldap_chain_cb_search_response( Operation *op, SlapReply *rs ) 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_f, rs->sr_ref, lb->lb_depth ); + } + /* back-ldap tried to send result */ - op->o_callback->sc_private = LDAP_CH_RES; + lb->lb_status = LDAP_CH_RES; } return 0; @@ -290,12 +333,15 @@ ldap_chain_cb_search_response( Operation *op, SlapReply *rs ) 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 ( op->o_callback->sc_private == LDAP_CH_ERR ) { + 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: @@ -305,15 +351,20 @@ ldap_chain_cb_response( Operation *op, SlapReply *rs ) /* fallthru */ case LDAP_SUCCESS: - op->o_callback->sc_private = LDAP_CH_RES; + 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_f, 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: - op->o_callback->sc_private = LDAP_CH_ERR; + lb->lb_status = LDAP_CH_ERR; return rs->sr_err = LDAP_X_CANNOT_CHAIN; default: @@ -341,15 +392,18 @@ ldap_chain_op( Operation *op, SlapReply *rs, BI_op_func *op_f, - BerVarray ref ) + 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 } }; /* NOTE: returned if ref is empty... */ - int rc = LDAP_OTHER; + int rc = LDAP_OTHER, + first_rc; #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR LDAPControl **ctrls = NULL; @@ -358,15 +412,20 @@ ldap_chain_op( #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */ li.li_bvuri = bvuri; + first_rc = -1; for ( ; !BER_BVISNULL( ref ); ref++ ) { - LDAPURLDesc *srv; - char *save_dn; + LDAPURLDesc *srv = NULL; + struct berval save_req_dn = op->o_req_dn, + save_req_ndn = op->o_req_ndn, + dn, + pdn = BER_BVNULL, + ndn = BER_BVNULL; int temporary = 0; /* We're setting the URI of the first referral; * what if there are more? -Document: draft-ietf-ldapbis-protocol-27.txt +Document: RFC 4511 4.1.10. Referral ... @@ -388,22 +447,35 @@ Document: draft-ietf-ldapbis-protocol-27.txt continue; } - /* remove DN essentially because later on - * ldap_initialize() will parse the URL - * as a comma-separated URL list */ - save_dn = srv->lud_dn; - srv->lud_dn = ""; - srv->lud_scope = LDAP_SCOPE_DEFAULT; - li.li_uri = ldap_url_desc2str( srv ); - srv->lud_dn = save_dn; + /* normalize DN */ + 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 = ""; + srv->lud_scope = LDAP_SCOPE_DEFAULT; + li.li_uri = ldap_url_desc2str( srv ); + srv->lud_dn = dn.bv_val; + } ldap_free_urldesc( srv ); - if ( li.li_uri == NULL ) { + if ( rc != LDAP_SUCCESS ) { /* try next */ rc = LDAP_OTHER; continue; } + if ( li.li_uri == NULL ) { + /* try next */ + rc = LDAP_OTHER; + goto further_cleanup; + } + + op->o_req_dn = pdn; + op->o_req_ndn = ndn; + ber_str2bv( li.li_uri, 0, 0, &li.li_bvuri[ 0 ] ); /* Searches for a ldapinfo in the avl tree */ @@ -425,6 +497,8 @@ Document: draft-ietf-ldapbis-protocol-27.txt lip->li_bvuri = bvuri; rc = ldap_chain_db_open_one( op->o_bd ); if ( rc != 0 ) { + lip->li_uri = NULL; + lip->li_bvuri = NULL; (void)ldap_chain_db_destroy_one( op->o_bd ); goto cleanup; } @@ -446,8 +520,16 @@ Document: draft-ietf-ldapbis-protocol-27.txt } } + lb->lb_op_f = op_f; + lb->lb_depth = depth + 1; + rc = op_f( op, rs ); + /* note the first error */ + if ( first_rc == -1 ) { + first_rc = rc; + } + cleanup:; ldap_memfree( li.li_uri ); li.li_uri = NULL; @@ -458,6 +540,17 @@ cleanup:; (void)ldap_chain_db_close_one( op->o_bd ); (void)ldap_chain_db_destroy_one( op->o_bd ); } + +further_cleanup:; + if ( !BER_BVISNULL( &pdn ) ) { + op->o_tmpfree( pdn.bv_val, op->o_tmpmemctx ); + } + op->o_req_dn = save_req_dn; + + if ( !BER_BVISNULL( &ndn ) ) { + op->o_tmpfree( ndn.bv_val, op->o_tmpmemctx ); + } + op->o_req_ndn = save_req_ndn; if ( rc == LDAP_SUCCESS && rs->sr_err == LDAP_SUCCESS ) { break; @@ -468,6 +561,204 @@ cleanup:; (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; + slap_response *save_response = op->o_callback->sc_response; + + 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 */ + + rs->sr_type = REP_SEARCH; + + op->o_callback->sc_response = ldap_chain_cb_search_response; + + /* 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++ ) { + LDAPURLDesc *srv; + struct berval save_req_dn = op->o_req_dn, + save_req_ndn = op->o_req_ndn, + dn, + pdn = BER_BVNULL, + ndn = BER_BVNULL; + int temporary = 0; + + /* parse reference and use + * proto://[host][:port]/ only */ + rc = ldap_url_parse_ext( ref[0].bv_val, &srv ); + if ( rc != LDAP_URL_SUCCESS ) { + /* try next */ + rs->sr_err = LDAP_OTHER; + continue; + } + + /* normalize DN */ + 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 = ""; + srv->lud_scope = LDAP_SCOPE_DEFAULT; + li.li_uri = ldap_url_desc2str( srv ); + srv->lud_dn = dn.bv_val; + } + ldap_free_urldesc( srv ); + + if ( rc != LDAP_SUCCESS ) { + /* try next */ + rc = LDAP_OTHER; + continue; + } + + if ( li.li_uri == NULL ) { + /* try next */ + rc = LDAP_OTHER; + goto further_cleanup; + } + + op->o_req_dn = pdn; + op->o_req_ndn = ndn; + + 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 *)avl_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; + + } else { + /* if none is found, create a temporary... */ + rc = ldap_chain_db_init_one( op->o_bd ); + if ( rc != 0 ) { + 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 ) { + lip->li_uri = NULL; + lip->li_bvuri = NULL; + (void)ldap_chain_db_destroy_one( op->o_bd ); + goto cleanup; + } + + if ( LDAP_CHAIN_CACHE_URI( lc ) ) { + ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex ); + if ( avl_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; + } + } + + lb->lb_op_f = lback->bi_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, rs ); + if ( first_rc == -1 ) { + first_rc = rc; + } + +cleanup:; + ldap_memfree( li.li_uri ); + li.li_uri = NULL; + + op->o_tmpfree( op->o_req_dn.bv_val, op->o_tmpmemctx ); + op->o_tmpfree( op->o_req_ndn.bv_val, op->o_tmpmemctx ); + + 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 ); + } + +further_cleanup:; + if ( !BER_BVISNULL( &pdn ) ) { + op->o_tmpfree( pdn.bv_val, op->o_tmpmemctx ); + } + op->o_req_dn = save_req_dn; + + if ( !BER_BVISNULL( &ndn ) ) { + op->o_tmpfree( ndn.bv_val, op->o_tmpmemctx ); + } + op->o_req_ndn = save_req_ndn; + + if ( rc == LDAP_SUCCESS && rs->sr_err == LDAP_SUCCESS ) { + break; + } + + rc = rs->sr_err; + } + +#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR + (void)chaining_control_remove( op, &ctrls ); +#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */ + + op->o_req_dn = odn; + op->o_req_ndn = ondn; + op->o_callback->sc_response = save_response; + rs->sr_type = REP_SEARCHREF; + rs->sr_entry = NULL; + + 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; } @@ -475,7 +766,9 @@ static int ldap_chain_response( Operation *op, SlapReply *rs ) { slap_overinst *on = (slap_overinst *)op->o_bd->bd_info; - void *private = op->o_bd->be_private; + 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; @@ -530,12 +823,17 @@ ldap_chain_response( Operation *op, SlapReply *rs ) * e) what ssf */ + db = *op->o_bd; + op->o_bd = &db; + matched = rs->sr_matched; rs->sr_matched = NULL; ref = rs->sr_ref; rs->sr_ref = NULL; /* we need this to know if back-ldap returned any result */ + lb.lb_lc = lc; + sc2.sc_private = &lb; sc2.sc_response = ldap_chain_cb_response; op->o_callback = &sc2; @@ -556,30 +854,30 @@ ldap_chain_response( Operation *op, SlapReply *rs ) /* 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, lback->bi_op_bind, ref ); + rc = ldap_chain_op( op, rs, lback->bi_op_bind, ref, 0 ); op->o_req_ndn = rndn; op->o_conn = conn; } break; case LDAP_REQ_ADD: - rc = ldap_chain_op( op, rs, lback->bi_op_add, ref ); + rc = ldap_chain_op( op, rs, lback->bi_op_add, ref, 0 ); break; case LDAP_REQ_DELETE: - rc = ldap_chain_op( op, rs, lback->bi_op_delete, ref ); + rc = ldap_chain_op( op, rs, lback->bi_op_delete, ref, 0 ); break; case LDAP_REQ_MODRDN: - rc = ldap_chain_op( op, rs, lback->bi_op_modrdn, ref ); + rc = ldap_chain_op( op, rs, lback->bi_op_modrdn, ref, 0 ); break; case LDAP_REQ_MODIFY: - rc = ldap_chain_op( op, rs, lback->bi_op_modify, ref ); + rc = ldap_chain_op( op, rs, lback->bi_op_modify, ref, 0 ); break; case LDAP_REQ_COMPARE: - rc = ldap_chain_op( op, rs, lback->bi_op_compare, ref ); + rc = ldap_chain_op( op, rs, lback->bi_op_compare, ref, 0 ); if ( rs->sr_err == LDAP_COMPARE_TRUE || rs->sr_err == LDAP_COMPARE_FALSE ) { rc = LDAP_SUCCESS; } @@ -587,150 +885,7 @@ ldap_chain_response( Operation *op, SlapReply *rs ) case LDAP_REQ_SEARCH: if ( rs->sr_type == REP_SEARCHREF ) { - 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 *curr = ref, - odn = op->o_req_dn, - ondn = op->o_req_ndn; - -#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR - LDAPControl **ctrls = NULL; - - (void)chaining_control_add( lc, op, &ctrls ); -#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */ - - rs->sr_type = REP_SEARCH; - - sc2.sc_response = ldap_chain_cb_search_response; - - /* 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( &curr[0] ); curr++ ) { - LDAPURLDesc *srv; - char *save_dn; - int temporary = 0; - - /* parse reference and use - * proto://[host][:port]/ only */ - rc = ldap_url_parse_ext( curr[0].bv_val, &srv ); - if ( rc != LDAP_URL_SUCCESS ) { - /* try next */ - rs->sr_err = LDAP_OTHER; - continue; - } - - /* remove DN essentially because later on - * ldap_initialize() will parse the URL - * as a comma-separated URL list */ - save_dn = srv->lud_dn; - srv->lud_dn = ""; - srv->lud_scope = LDAP_SCOPE_DEFAULT; - li.li_uri = ldap_url_desc2str( srv ); - if ( li.li_uri != NULL ) { - ber_str2bv_x( save_dn, 0, 1, &op->o_req_dn, - op->o_tmpmemctx ); - ber_dupbv_x( &op->o_req_ndn, &op->o_req_dn, - op->o_tmpmemctx ); - } - - srv->lud_dn = save_dn; - ldap_free_urldesc( srv ); - - if ( li.li_uri == NULL ) { - /* try next */ - rs->sr_err = LDAP_OTHER; - continue; - } - - 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 *)avl_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; - - } else { - /* if none is found, create a temporary... */ - rc = ldap_chain_db_init_one( op->o_bd ); - if ( rc != 0 ) { - 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 ) { - (void)ldap_chain_db_destroy_one( op->o_bd ); - goto cleanup; - } - - if ( LDAP_CHAIN_CACHE_URI( lc ) ) { - ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex ); - if ( avl_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; - } - } - - /* FIXME: should we also copy filter and scope? - * according to RFC3296, no */ - rc = lback->bi_op_search( op, rs ); - -cleanup:; - ldap_memfree( li.li_uri ); - li.li_uri = NULL; - - op->o_tmpfree( op->o_req_dn.bv_val, - op->o_tmpmemctx ); - op->o_tmpfree( op->o_req_ndn.bv_val, - op->o_tmpmemctx ); - - 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 ); - } - - if ( rc == LDAP_SUCCESS && rs->sr_err == LDAP_SUCCESS ) { - break; - } - - rc = rs->sr_err; - } - -#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR - (void)chaining_control_remove( op, &ctrls ); -#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */ - - op->o_req_dn = odn; - op->o_req_ndn = ondn; - rs->sr_type = REP_SEARCHREF; - rs->sr_entry = NULL; - - if ( rc != LDAP_SUCCESS ) { - /* couldn't chase any of the referrals */ - rc = SLAP_CB_CONTINUE; - } + rc = ldap_chain_search( op, rs, ref, 0 ); } else { /* we might get here before any database actually @@ -738,7 +893,7 @@ cleanup:; * 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, lback->bi_op_search, ref ); + rc = ldap_chain_op( op, rs, lback->bi_op_search, ref, 0 ); } else { rc = SLAP_CB_CONTINUE; @@ -747,7 +902,7 @@ cleanup:; break; case LDAP_REQ_EXTENDED: - rc = ldap_chain_op( op, rs, lback->bi_extended, ref ); + rc = ldap_chain_op( op, rs, lback->bi_extended, ref, 0 ); /* FIXME: ldap_back_extended() by design * doesn't send result; frontend is expected * to send it... */ @@ -756,7 +911,7 @@ cleanup:; send_ldap_extended( op, rs ); rc = LDAP_SUCCESS; } - sc2.sc_private = LDAP_CH_RES; + lb.lb_status = LDAP_CH_RES; break; default: @@ -771,7 +926,7 @@ cleanup:; case LDAP_SUCCESS: case LDAP_REFERRAL: /* slapd-ldap sent response */ - if ( !op->o_abandon && sc2.sc_private != LDAP_CH_RES ) { + if ( !op->o_abandon && lb.lb_status != LDAP_CH_RES ) { /* FIXME: should we send response? */ Debug( LDAP_DEBUG_ANY, "%s: ldap_chain_response: " @@ -782,7 +937,7 @@ cleanup:; default: #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR - if ( sc2.sc_private == LDAP_CH_ERR && rs->sr_err == LDAP_X_CANNOT_CHAIN ) { + if ( lb.lb_status == LDAP_CH_ERR && rs->sr_err == LDAP_X_CANNOT_CHAIN ) { goto cannot_chain; } @@ -796,18 +951,24 @@ cannot_chain:; default: #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */ - rc = SLAP_CB_CONTINUE; - rs->sr_err = sr_err; - rs->sr_type = sr_type; - rs->sr_matched = matched; - rs->sr_ref = ref; + if ( LDAP_CHAIN_RETURN_ERR( lc ) ) { + rs->sr_err = rc; + rs->sr_type = sr_type; + + } else { + rc = SLAP_CB_CONTINUE; + rs->sr_err = sr_err; + rs->sr_type = sr_type; + rs->sr_matched = matched; + rs->sr_ref = ref; + } #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR break; } #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */ } - if ( sc2.sc_private == LDAP_CH_NONE && rc != SLAPD_ABANDON ) { + if ( lb.lb_status == LDAP_CH_NONE && rc != SLAPD_ABANDON ) { op->o_callback = NULL; rc = rs->sr_err = slap_map_api2result( rs ); send_ldap_result( op, rs ); @@ -818,7 +979,7 @@ dont_chain:; rs->sr_type = sr_type; rs->sr_matched = matched; rs->sr_ref = ref; - op->o_bd->be_private = private; + op->o_bd = bd; op->o_callback = sc; op->o_ndn = ndn; @@ -858,7 +1019,9 @@ str2chain( const char *s ) enum { CH_CHAINING = 1, - CH_CACHE_URI = 2, + CH_CACHE_URI, + CH_MAX_DEPTH, + CH_RETURN_ERR, CH_LAST }; @@ -877,9 +1040,21 @@ static ConfigTable chaincfg[] = { #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 'olcCacheURI' " + "( OLcfgOvAt:3.2 NAME 'olcChainCacheURI' " "DESC 'Enables caching of URIs not present in configuration' " "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' " + "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL }, { NULL, NULL, 0, 0, 0, ARG_IGNORED } }; @@ -892,7 +1067,9 @@ static ConfigOCs chainocs[] = { #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR "olcChainingBehavior $ " #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */ - "olcCacheURI " + "olcChainCacheURI $ " + "olcChainMaxReferralDepth $ " + "olcChainReturnError " ") )", Cft_Overlay, chaincfg, NULL, chain_cfadd }, { "( OLcfgOvOc:3.2 " @@ -976,14 +1153,18 @@ chain_ldadd( CfEntryInfo *p, Entry *e, ConfigArgs *ca ) if ( lc->lc_common_li == NULL ) { lc->lc_common_li = li; - } else if ( avl_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, 0, 0 ); - rc = LDAP_CONSTRAINT_VIOLATION; - goto done; + } else { + li->li_uri = ch_strdup( at->a_vals[ 0 ].bv_val ); + value_add_one( &li->li_bvuri, &at->a_vals[ 0 ] ); + if ( avl_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, 0, 0 ); + rc = LDAP_CONSTRAINT_VIOLATION; + goto done; + } } done:; @@ -1110,6 +1291,14 @@ chain_cf_gen( ConfigArgs *c ) 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; @@ -1125,6 +1314,14 @@ chain_cf_gen( ConfigArgs *c ) 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; } @@ -1257,6 +1454,26 @@ chain_cf_gen( ConfigArgs *c ) } break; + case CH_MAX_DEPTH: + if ( c->value_int < 0 ) { + snprintf( c->msg, sizeof( c->msg ), + "<%s> invalid max referral depth %d", + c->argv[0], c->value_int ); + Debug( LDAP_DEBUG_ANY, "%s: %s.\n", + c->log, c->msg, 0 ); + 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; @@ -1272,11 +1489,17 @@ ldap_chain_db_init( ldap_chain_t *lc = NULL; if ( lback == NULL ) { + static BackendInfo lback2; + lback = backend_info( "ldap" ); if ( lback == NULL ) { return 1; } + + lback2 = *lback; + lback2.bi_type = ldapchain.on_bi.bi_type; + lback = &lback2; } lc = ch_malloc( sizeof( ldap_chain_t ) ); @@ -1284,6 +1507,7 @@ ldap_chain_db_init( 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; @@ -1494,10 +1718,9 @@ ldap_chain_db_open( { slap_overinst *on = (slap_overinst *) be->bd_info; ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private; + int rc = 0; #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR - int rc = 0; - rc = overlay_register_control( be, LDAP_CONTROL_X_CHAINING_BEHAVIOR ); if ( rc != 0 ) { return rc; @@ -1511,7 +1734,10 @@ ldap_chain_db_open( be->be_private = be_private; } - return ldap_chain_db_func( be, db_open ); + /* filter out and restore monitoring */ + rc = ldap_chain_db_func( be, db_open ); + + return rc; } static int @@ -1550,14 +1776,17 @@ ldap_chain_db_init_common( BackendDB *be ) { BackendInfo *bi = be->bd_info; - int t; + ldapinfo_t *li; + int rc; be->bd_info = lback; be->be_private = NULL; - t = lback->bi_db_init( be ); - if ( t != 0 ) { - return t; + rc = lback->bi_db_init( be ); + if ( rc != 0 ) { + return rc; } + li = (ldapinfo_t *)be->be_private; + be->bd_info = bi; return 0; @@ -1581,7 +1810,7 @@ ldap_chain_db_init_one( BackendInfo *bi = be->bd_info; ldapinfo_t *li; - int t; + slap_op_t t; be->bd_info = lback; be->be_private = NULL; @@ -1595,7 +1824,7 @@ ldap_chain_db_init_one( 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 < LDAP_BACK_OP_LAST; t++ ) { + for ( t = 0; t < SLAP_OP_LAST; t++ ) { li->li_timeout[ t ] = lc->lc_common_li->li_timeout[ t ]; } be->bd_info = bi; @@ -1603,6 +1832,13 @@ ldap_chain_db_init_one( return 0; } +static int +ldap_chain_db_open_one( + BackendDB *be ) +{ + return lback->bi_db_open( be ); +} + typedef struct ldap_chain_conn_apply_t { BackendDB *be; Connection *conn; diff --git a/servers/slapd/back-ldap/compare.c b/servers/slapd/back-ldap/compare.c index 16a0e4bdc9..ee964a1183 100644 --- a/servers/slapd/back-ldap/compare.c +++ b/servers/slapd/back-ldap/compare.c @@ -36,21 +36,23 @@ ldap_back_compare( Operation *op, SlapReply *rs ) { - ldapconn_t *lc; - ber_int_t msgid; - int do_retry = 1; - LDAPControl **ctrls = NULL; - int rc = LDAP_SUCCESS; + ldapinfo_t *li = (ldapinfo_t *)op->o_bd->be_private; - lc = ldap_back_getconn( op, rs, LDAP_BACK_SENDERR ); - if ( !lc || !ldap_back_dobind( lc, op, rs, LDAP_BACK_SENDERR ) ) { + 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_proxy_authz_ctrl( lc, op, rs, &ctrls ); + rc = ldap_back_proxy_authz_ctrl( &lc->lc_bound_ndn, + li->li_version, &li->li_idassert, op, rs, &ctrls ); if ( rc != LDAP_SUCCESS ) { send_ldap_result( op, rs ); goto cleanup; @@ -60,9 +62,11 @@ retry: 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, 0, LDAP_BACK_SENDRESULT ); - if ( rc == LDAP_UNAVAILABLE && do_retry ) { - do_retry = 0; + 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_proxy_authz_ctrl_free( op, &ctrls ); diff --git a/servers/slapd/back-ldap/config.c b/servers/slapd/back-ldap/config.c index 0a3bf1828b..9fd2f1490e 100644 --- a/servers/slapd/back-ldap/config.c +++ b/servers/slapd/back-ldap/config.c @@ -64,6 +64,11 @@ enum { 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_REWRITE, LDAP_BACK_CFG_LAST @@ -250,6 +255,46 @@ static ConfigTable ldapcfg[] = { "SYNTAX OMsInteger " "SINGLE-VALUE )", NULL, NULL }, + { "single-conn", "TRUE/FALSE", 2, 0, 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' " + "SYNTAX OMsBoolean " + "SINGLE-VALUE )", + NULL, NULL }, + { "cancel", "ABANDON|ignore|exop", 2, 0, 0, + ARG_MAGIC|LDAP_BACK_CFG_CANCEL, + ldap_back_cf_gen, "( OLcfgDbAt:3.20 " + "NAME 'olcDbCancel' " + "DESC 'abandon/ignore/exop operations when appropriate' " + "SYNTAX OMsDirectoryString " + "SINGLE-VALUE )", + NULL, NULL }, + { "quarantine", "retrylist", 2, 0, 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' " + "SYNTAX OMsDirectoryString " + "SINGLE-VALUE )", + NULL, NULL }, + { "use-temporary-conn", "TRUE/FALSE", 2, 0, 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' " + "SYNTAX OMsBoolean " + "SINGLE-VALUE )", + NULL, NULL }, + { "conn-pool-max", "", 2, 0, 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' " + "SYNTAX OMsInteger " + "SINGLE-VALUE )", + NULL, NULL }, { "suffixmassage", "[virtual]> 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 ) +{ + int i; + char buf[ BUFSIZ * 2 ], + *ptr = buf; + struct berval bv = BER_BVNULL; + + assert( ri != NULL ); + assert( bvout != NULL ); + + BER_BVZERO( bvout ); + +#define WHATSLEFT ( sizeof( buf ) - ( ptr - buf ) ) + + for ( i = 0; ri->ri_num[ i ] != SLAP_RETRYNUM_TAIL; i++ ) { + if ( i > 0 ) { + if ( WHATSLEFT <= 1 ) { + return 1; + } + *ptr++ = ';'; + } + + if ( lutil_unparse_time( ptr, WHATSLEFT, (long)ri->ri_interval[i] ) ) { + return 1; + } + ptr += strlen( ptr ); + + if ( WHATSLEFT <= 1 ) { + return 1; + } + *ptr++ = ','; + + if ( ri->ri_num[i] == SLAP_RETRYNUM_FOREVER ) { + if ( WHATSLEFT <= 1 ) { + return 1; + } + *ptr++ = '+'; + + } else { + ptr += snprintf( ptr, WHATSLEFT, "%d", ri->ri_num[i] ); + if ( WHATSLEFT <= 0 ) { + return 1; + } + } + } + + 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; +} + +static int +slap_idassert_authzfrom_parse( ConfigArgs *c, slap_idassert_t *si ) +{ + struct berval bv; + + if ( strcmp( c->argv[ 1 ], "*" ) == 0 + || 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->msg, sizeof( c->msg ), + "\"idassert-authzFrom \": " + "\"%s\" conflicts with existing authz rules", + c->argv[ 1 ] ); + Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->msg, 0 ); + return 1; + } + + si->si_flags |= LDAP_BACK_AUTH_AUTHZ_ALL; + + return 0; + + } else if ( ( si->si_flags & LDAP_BACK_AUTH_AUTHZ_ALL ) ) { + snprintf( c->msg, sizeof( c->msg ), + "\"idassert-authzFrom \": " + "\"\" conflicts with \"*\"" ); + Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->msg, 0 ); + return 1; + } + +#ifdef SLAP_AUTHZ_SYNTAX + { + 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->msg, sizeof( c->msg ), + "\"idassert-authzFrom \": " + "invalid syntax" ); + Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->msg, 0 ); + return 1; + } + } +#else /* !SLAP_AUTHZ_SYNTAX */ + ber_str2bv( c->argv[ 1 ], 0, 1, &bv ); +#endif /* !SLAP_AUTHZ_SYNTAX */ + + ber_bvarray_add( &si->si_authz, &bv ); + + return 0; +} + +static int +slap_idassert_parse( ConfigArgs *c, slap_idassert_t *si ) +{ + int i; + + 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->msg, sizeof( c->msg ), + "\"idassert-bind \": " + "unknown mode \"%s\"", + argvi ); + Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->msg, 0 ); + 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->msg, sizeof( c->msg ), + "\"idassert-bind \": " + "authz=\"native\" incompatible " + "with auth method" ); + Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->msg, 0 ); + 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->msg, sizeof( c->msg ), + "\"idassert-bind \": " + "unknown authz \"%s\"", + argvi ); + Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->msg, 0 ); + 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->msg, sizeof( c->msg ), + "\"idassert-bind \": " + "unable to parse flags \"%s\"", + argvi ); + Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->msg, 0 ); + 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 ) { + Debug( LDAP_DEBUG_ANY, + "%s: \"obsolete-proxy-authz\" flag " + "in \"idassert-mode \" " + "incompatible with previously issued \"obsolete-encoding-workaround\" flag.\n", + c->log, 0, 0 ); + 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 ) { + Debug( LDAP_DEBUG_ANY, + "%s: \"obsolete-encoding-workaround\" flag " + "in \"idassert-mode \" " + "incompatible with previously issued \"obsolete-proxy-authz\" flag.\n", + c->log, 0, 0 ); + err = 1; + break; + + } else { + si->si_flags |= LDAP_BACK_AUTH_OBSOLETE_ENCODING_WORKAROUND; + } + + } else { + snprintf( c->msg, sizeof( c->msg ), + "\"idassert-bind \": " + "unknown flag \"%s\"", + flags[ j ] ); + Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->msg, 0 ); + err = 1; + break; + } + } + + ldap_charray_free( flags ); + if ( err ) { + return 1; + } + + } else if ( bindconf_parse( c->argv[ i ], &si->si_bc ) ) { + return 1; + } + } + + return 0; +} + +/* NOTE: temporary, until back-meta is ported to back-config */ +int +slap_idassert_authzfrom_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-authzFrom"; + argv[ 1 ] = (char *)arg; + argv[ 2 ] = NULL; + + return slap_idassert_authzfrom_parse( &c, si ); +} + +int +slap_idassert_parse_cf( const char *fname, int lineno, int argc, char *argv[], slap_idassert_t *si ) +{ + ConfigArgs c = { 0 }; + + snprintf( c.log, sizeof( c.log ), "%s: line %d", fname, lineno ); + c.argc = argc; + c.argv = argv; + + return slap_idassert_parse( &c, si ); +} + static int ldap_back_cf_gen( ConfigArgs *c ) { ldapinfo_t *li = ( ldapinfo_t * )c->be->be_private; - int rc; + int rc = 0; int i; if ( c->op == SLAP_CONFIG_EMIT ) { struct berval bv = BER_BVNULL; - rc = 0; if ( li == NULL ) { return 1; @@ -339,10 +771,14 @@ ldap_back_cf_gen( ConfigArgs *c ) switch( c->type ) { case LDAP_BACK_CFG_URI: if ( li->li_uri != NULL ) { - struct berval bv; + struct berval bv, bv2; ber_str2bv( li->li_uri, 0, 0, &bv ); - value_add_one( &c->rvalue_vals, &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; @@ -396,7 +832,13 @@ ldap_back_cf_gen( ConfigArgs *c ) int i; if ( li->li_idassert_authz == NULL ) { - rc = 1; + if ( ( li->li_idassert_flags & LDAP_BACK_AUTH_AUTHZ_ALL ) ) { + BER_BVSTR( &bv, "*" ); + value_add_one( &c->rvalue_vals, &bv ); + + } else { + rc = 1; + } break; } @@ -462,7 +904,7 @@ ldap_back_cf_gen( ConfigArgs *c ) (void)lutil_strcopy( ptr, "authz=native" ); } - len = bv.bv_len + STRLENOF( "flags=non-prescriptive,override" ); + len = bv.bv_len + STRLENOF( "flags=non-prescriptive,override,obsolete-encoding-workaround" ); /* flags */ if ( !BER_BVISEMPTY( &bv ) ) { len += STRLENOF( " " ); @@ -488,11 +930,18 @@ ldap_back_cf_gen( ConfigArgs *c ) 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" ); + } + bv.bv_len = ( ptr - bv.bv_val ); /* end-of-flags */ } - bindconf_unparse( &li->li_idassert, &bc ); + bindconf_unparse( &li->li_idassert.si_bc, &bc ); if ( !BER_BVISNULL( &bv ) ) { ber_len_t len = bv.bv_len + bc.bv_len; @@ -531,7 +980,7 @@ ldap_back_cf_gen( ConfigArgs *c ) break; case LDAP_BACK_CFG_T_F: - enum_to_verb( t_f_mode, (li->li_flags & LDAP_BACK_F_SUPPORT_T_F_MASK), &bv ); + 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 ); @@ -549,13 +998,13 @@ ldap_back_cf_gen( ConfigArgs *c ) case LDAP_BACK_CFG_TIMEOUT: BER_BVZERO( &bv ); - for ( i = 0; i < LDAP_BACK_OP_LAST; i++ ) { + for ( i = 0; i < SLAP_OP_LAST; i++ ) { if ( li->li_timeout[ i ] != 0 ) { break; } } - if ( i == LDAP_BACK_OP_LAST ) { + if ( i == SLAP_OP_LAST ) { return 1; } @@ -622,6 +1071,47 @@ ldap_back_cf_gen( ConfigArgs *c ) 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; + default: /* FIXME: we need to handle all... */ assert( 0 ); @@ -630,7 +1120,6 @@ ldap_back_cf_gen( ConfigArgs *c ) return rc; } else if ( c->op == LDAP_MOD_DELETE ) { - rc = 0; switch( c->type ) { case LDAP_BACK_CFG_URI: if ( li->li_uri != NULL ) { @@ -683,18 +1172,19 @@ ldap_back_cf_gen( ConfigArgs *c ) break; case LDAP_BACK_CFG_IDASSERT_BIND: - bindconf_free( &li->li_idassert ); + bindconf_free( &li->li_idassert.si_bc ); 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 < LDAP_BACK_OP_LAST; i++ ) { + for ( i = 0; i < SLAP_OP_LAST; i++ ) { li->li_timeout[ i ] = 0; } break; @@ -715,6 +1205,28 @@ ldap_back_cf_gen( ConfigArgs *c ) 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; + break; + default: /* FIXME: we need to handle all... */ assert( 0 ); @@ -988,6 +1500,28 @@ done_url:; } else if ( strcasecmp( c->argv[ i ], "non-prescriptive" ) == 0 ) { li->li_idassert_flags &= ( ~LDAP_BACK_AUTH_PRESCRIPTIVE ); + } else if ( strcasecmp( c->argv[ i ], "obsolete-proxy-authz" ) == 0 ) { + if ( li->li_idassert_flags & LDAP_BACK_AUTH_OBSOLETE_ENCODING_WORKAROUND ) { + Debug( LDAP_DEBUG_ANY, + "%s: line %d: \"obsolete-proxy-authz\" flag " + "in \"idassert-mode \" " + "incompatible with previously issued \"obsolete-encoding-workaround\" flag.\n", + c->fname, c->lineno, 0 ); + return 1; + } + li->li_idassert_flags |= LDAP_BACK_AUTH_OBSOLETE_PROXY_AUTHZ; + + } else if ( strcasecmp( c->argv[ i ], "obsolete-encoding-workaround" ) == 0 ) { + if ( li->li_idassert_flags & LDAP_BACK_AUTH_OBSOLETE_PROXY_AUTHZ ) { + Debug( LDAP_DEBUG_ANY, + "%s: line %d: \"obsolete-encoding-workaround\" flag " + "in \"idassert-mode \" " + "incompatible with previously issued \"obsolete-proxy-authz\" flag.\n", + c->fname, c->lineno, 0 ); + return 1; + } + li->li_idassert_flags |= LDAP_BACK_AUTH_OBSOLETE_ENCODING_WORKAROUND; + } else { Debug( LDAP_DEBUG_ANY, "%s: line %d: unknown flag #%d " @@ -1049,26 +1583,9 @@ done_url:; ber_str2bv( c->argv[ 1 ], 0, 1, &li->li_idassert_passwd ); break; - case LDAP_BACK_CFG_IDASSERT_AUTHZFROM: { - struct berval bv; -#ifdef SLAP_AUTHZ_SYNTAX - 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->msg, sizeof( c->msg ), - "\"idassert-authzFrom \": " - "invalid syntax" ); - Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->msg, 0 ); - return 1; - } -#else /* !SLAP_AUTHZ_SYNTAX */ - ber_str2bv( c->argv[ 1 ], 0, 1, &bv ); -#endif /* !SLAP_AUTHZ_SYNTAX */ - ber_bvarray_add( &li->li_idassert_authz, &bv ); - } break; + case LDAP_BACK_CFG_IDASSERT_AUTHZFROM: + rc = slap_idassert_authzfrom_parse( c, &li->li_idassert ); + break; case LDAP_BACK_CFG_IDASSERT_METHOD: /* no longer supported */ @@ -1079,90 +1596,7 @@ done_url:; return 1; case LDAP_BACK_CFG_IDASSERT_BIND: - 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->msg, sizeof( c->msg ), - "\"idassert-bind \": " - "unknown mode \"%s\"", - argvi ); - Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->msg, 0 ); - return 1; - } - - li->li_idassert_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 ( li->li_idassert_authmethod != LDAP_AUTH_SASL ) { - snprintf( c->msg, sizeof( c->msg ), - "\"idassert-bind \": " - "authz=\"native\" incompatible " - "with auth method" ); - Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->msg, 0 ); - return 1; - } - li->li_idassert_flags |= LDAP_BACK_AUTH_NATIVE_AUTHZ; - - } else if ( strcasecmp( argvi, "proxyAuthz" ) == 0 ) { - li->li_idassert_flags &= ~LDAP_BACK_AUTH_NATIVE_AUTHZ; - - } else { - snprintf( c->msg, sizeof( c->msg ), - "\"idassert-bind \": " - "unknown authz \"%s\"", - argvi ); - Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->msg, 0 ); - 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; - - if ( flags == NULL ) { - snprintf( c->msg, sizeof( c->msg ), - "\"idassert-bind \": " - "unable to parse flags \"%s\"", - argvi ); - Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->msg, 0 ); - return 1; - } - - for ( j = 0; flags[ j ] != NULL; j++ ) { - if ( strcasecmp( flags[ j ], "override" ) == 0 ) { - li->li_idassert_flags |= LDAP_BACK_AUTH_OVERRIDE; - - } else if ( strcasecmp( flags[ j ], "prescriptive" ) == 0 ) { - li->li_idassert_flags |= LDAP_BACK_AUTH_PRESCRIPTIVE; - - } else if ( strcasecmp( flags[ j ], "non-prescriptive" ) == 0 ) { - li->li_idassert_flags &= ( ~LDAP_BACK_AUTH_PRESCRIPTIVE ); - - } else { - snprintf( c->msg, sizeof( c->msg ), - "\"idassert-bind \": " - "unknown flag \"%s\"", - flags[ j ] ); - Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->msg, 0 ); - ldap_charray_free( flags ); - return 1; - } - } - - ldap_charray_free( flags ); - - } else if ( bindconf_parse( c->argv[ i ], &li->li_idassert ) ) { - return 1; - } - } + rc = slap_idassert_parse( c, &li->li_idassert ); break; case LDAP_BACK_CFG_REBIND: @@ -1183,14 +1617,41 @@ done_url:; } break; - case LDAP_BACK_CFG_T_F: + 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; } - li->li_flags &= ~LDAP_BACK_F_SUPPORT_T_F_MASK; - li->li_flags |= t_f_mode[i].mask; - break; + + mask = t_f_mode[i].mask; + + if ( LDAP_BACK_ISOPEN( li ) + && mask == LDAP_BACK_F_T_F_DISCOVER + && !LDAP_BACK_T_F( li ) ) + { + int rc; + + if ( li->li_uri == NULL ) { + snprintf( c->msg, sizeof( c->msg ), + "need URI to discover \"cancel\" support " + "in \"cancel exop-discover\"" ); + Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->msg, 0 ); + return 1; + } + + rc = slap_discover_feature( li->li_uri, li->li_version, + 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 ) { @@ -1210,10 +1671,14 @@ done_url:; unsigned u; if ( lutil_atoux( &u, c->argv[ i ], 0 ) != 0 ) { + snprintf( c->msg, sizeof( c->msg), + "unable to parse timeout \"%s\"", + c->argv[ i ] ); + Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->msg, 0 ); return 1; } - for ( j = 0; j < LDAP_BACK_OP_LAST; j++ ) { + for ( j = 0; j < SLAP_OP_LAST; j++ ) { li->li_timeout[ j ] = u; } @@ -1221,6 +1686,10 @@ done_url:; } if ( slap_cf_aux_table_parse( c->argv[ i ], li->li_timeout, timeout_table, "slapd-ldap timeout" ) ) { + snprintf( c->msg, sizeof( c->msg), + "unable to parse timeout \"%s\"", + c->argv[ i ] ); + Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->msg, 0 ); return 1; } } @@ -1266,16 +1735,108 @@ done_url:; } break; case LDAP_BACK_CFG_VERSION: - switch ( c->value_int ) { - case 0: - case LDAP_VERSION2: - case LDAP_VERSION3: - li->li_version = c->value_int; - break; + if ( c->value_int != 0 && ( c->value_int < LDAP_VERSION_MIN || c->value_int > LDAP_VERSION_MAX ) ) { + snprintf( c->msg, sizeof( c->msg ), + "unsupported version \"%s\" " + "in \"protocol-version \"", + c->argv[ 1 ] ); + Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->msg, 0 ); + return 1; + } - default: + 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->msg, sizeof( c->msg ), + "invalid max size " "of privileged " + "connections pool \"%s\" " + "in \"conn-pool-max " + "(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->msg, 0 ); + 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 ) ) + { + int rc; + + if ( li->li_uri == NULL ) { + snprintf( c->msg, sizeof( c->msg ), + "need URI to discover \"cancel\" support " + "in \"cancel exop-discover\"" ); + Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->msg, 0 ); + return 1; + } + + rc = slap_discover_feature( li->li_uri, li->li_version, + 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->msg, sizeof( c->msg ), + "quarantine already defined" ); + Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->msg, 0 ); return 1; } + rc = slap_retry_info_parse( c->argv[1], &li->li_quarantine, + c->msg, sizeof( c->msg ) ); + if ( rc ) { + Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->msg, 0 ); + + } 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; + } break; case LDAP_BACK_CFG_REWRITE: @@ -1293,7 +1854,7 @@ done_url:; break; } - return 0; + return rc; } int @@ -1368,7 +1929,7 @@ ldap_back_exop_whoami( && !strcmp( op->o_conn->c_authz_backend->be_type, "ldap" ) && !dn_match( &op->o_ndn, &op->o_conn->c_ndn ) ) { - ldapconn_t *lc; + ldapconn_t *lc = NULL; LDAPControl c, *ctrls[2] = {NULL, NULL}; LDAPMessage *res; Operation op2 = *op; @@ -1378,8 +1939,7 @@ ldap_back_exop_whoami( ctrls[0] = &c; op2.o_ndn = op->o_conn->c_ndn; - lc = ldap_back_getconn(&op2, rs, LDAP_BACK_SENDERR); - if ( !lc || !ldap_back_dobind( lc, op, rs, LDAP_BACK_SENDERR ) ) { + if ( !ldap_back_dobind( &lc, &op2, rs, LDAP_BACK_SENDERR ) ) { return -1; } c.ldctl_oid = LDAP_CONTROL_PROXY_AUTHZ; diff --git a/servers/slapd/back-ldap/delete.c b/servers/slapd/back-ldap/delete.c index 607108af1b..abf56c37f2 100644 --- a/servers/slapd/back-ldap/delete.c +++ b/servers/slapd/back-ldap/delete.c @@ -38,21 +38,20 @@ ldap_back_delete( { ldapinfo_t *li = (ldapinfo_t *)op->o_bd->be_private; - ldapconn_t *lc; - ber_int_t msgid; - LDAPControl **ctrls = NULL; - int do_retry = 1; - int rc = LDAP_SUCCESS; + ldapconn_t *lc = NULL; + ber_int_t msgid; + LDAPControl **ctrls = NULL; + ldap_back_send_t retrying = LDAP_BACK_RETRYING; + int rc = LDAP_SUCCESS; - lc = ldap_back_getconn( op, rs, LDAP_BACK_SENDERR ); - - if ( !lc || !ldap_back_dobind( lc, op, rs, LDAP_BACK_SENDERR ) ) { + if ( !ldap_back_dobind( &lc, op, rs, LDAP_BACK_SENDERR ) ) { return rs->sr_err; } retry: ctrls = op->o_ctrls; - rc = ldap_back_proxy_authz_ctrl( lc, op, rs, &ctrls ); + rc = ldap_back_proxy_authz_ctrl( &lc->lc_bound_ndn, + li->li_version, &li->li_idassert, op, rs, &ctrls ); if ( rc != LDAP_SUCCESS ) { send_ldap_result( op, rs ); rc = rs->sr_err; @@ -62,9 +61,10 @@ retry: 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[ LDAP_BACK_OP_DELETE], LDAP_BACK_SENDRESULT ); - if ( rs->sr_err == LDAP_SERVER_DOWN && do_retry ) { - do_retry = 0; + li->li_timeout[ SLAP_OP_DELETE ], + ( LDAP_BACK_SENDRESULT | retrying ) ); + if ( rs->sr_err == LDAP_SERVER_DOWN && 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_proxy_authz_ctrl_free( op, &ctrls ); diff --git a/servers/slapd/back-ldap/extended.c b/servers/slapd/back-ldap/extended.c index 746be96d9f..8ffb591985 100644 --- a/servers/slapd/back-ldap/extended.c +++ b/servers/slapd/back-ldap/extended.c @@ -42,7 +42,9 @@ static struct exop { static int ldap_back_extended_one( Operation *op, SlapReply *rs, BI_op_extended exop ) { - ldapconn_t *lc; + ldapinfo_t *li = (ldapinfo_t *) op->o_bd->be_private; + + ldapconn_t *lc = NULL; LDAPControl **oldctrls = NULL; int rc; @@ -50,13 +52,14 @@ ldap_back_extended_one( Operation *op, SlapReply *rs, BI_op_extended exop ) * called twice; maybe we could avoid the * ldap_back_dobind() call inside each extended() * call ... */ - lc = ldap_back_getconn( op, rs, LDAP_BACK_SENDERR ); - if ( !lc || !ldap_back_dobind( lc, op, rs, LDAP_BACK_SENDERR ) ) { + if ( !ldap_back_dobind( &lc, op, rs, LDAP_BACK_SENDERR ) ) { return -1; } oldctrls = op->o_ctrls; - if ( ldap_back_proxy_authz_ctrl( lc, op, rs, &op->o_ctrls ) ) { + if ( ldap_back_proxy_authz_ctrl( &lc->lc_bound_ndn, + li->li_version, &li->li_idassert, op, rs, &op->o_ctrls ) ) + { op->o_ctrls = oldctrls; send_ldap_extended( op, rs ); rs->sr_text = NULL; @@ -106,7 +109,9 @@ ldap_back_exop_passwd( Operation *op, SlapReply *rs ) { - ldapconn_t *lc; + ldapinfo_t *li = (ldapinfo_t *) op->o_bd->be_private; + + ldapconn_t *lc = NULL; req_pwdexop_s *qpw = &op->oq_pwdexop; LDAPMessage *res; ber_int_t msgid; @@ -114,8 +119,7 @@ ldap_back_exop_passwd( int do_retry = 1; char *text = NULL; - lc = ldap_back_getconn( op, rs, LDAP_BACK_SENDERR ); - if ( !lc || !ldap_back_dobind( lc, op, rs, LDAP_BACK_SENDERR ) ) { + if ( !ldap_back_dobind( &lc, op, rs, LDAP_BACK_SENDERR ) ) { return -1; } @@ -136,6 +140,11 @@ retry: rs->sr_err = rc; } else { + /* only touch when activity actually took place... */ + if ( li->li_idle_timeout && lc ) { + lc->lc_time = op->o_time; + } + /* sigh. parse twice, because parse_passwd * doesn't give us the err / match / msg info. */ @@ -191,10 +200,18 @@ retry: goto retry; } } + + if ( LDAP_BACK_QUARANTINE( li ) ) { + ldap_back_quarantine( op, rs ); + } + if ( text ) rs->sr_text = text; send_ldap_extended( op, rs ); /* otherwise frontend resends result */ rc = rs->sr_err = SLAPD_ABANDON; + + } else if ( LDAP_BACK_QUARANTINE( li ) ) { + ldap_back_quarantine( op, rs ); } /* these have to be freed anyway... */ @@ -220,15 +237,16 @@ ldap_back_exop_generic( Operation *op, SlapReply *rs ) { - ldapconn_t *lc; + ldapinfo_t *li = (ldapinfo_t *) op->o_bd->be_private; + + ldapconn_t *lc = NULL; LDAPMessage *res; ber_int_t msgid; int rc; int do_retry = 1; char *text = NULL; - lc = ldap_back_getconn( op, rs, LDAP_BACK_SENDERR ); - if ( !lc || !ldap_back_dobind( lc, op, rs, LDAP_BACK_SENDERR ) ) { + if ( !ldap_back_dobind( &lc, op, rs, LDAP_BACK_SENDERR ) ) { return -1; } @@ -246,6 +264,11 @@ retry: rs->sr_err = rc; } else { + /* only touch when activity actually took place... */ + if ( li->li_idle_timeout && lc ) { + lc->lc_time = op->o_time; + } + /* sigh. parse twice, because parse_passwd * doesn't give us the err / match / msg info. */ @@ -287,10 +310,18 @@ retry: goto retry; } } + + if ( LDAP_BACK_QUARANTINE( li ) ) { + ldap_back_quarantine( op, rs ); + } + if ( text ) rs->sr_text = text; send_ldap_extended( op, rs ); /* otherwise frontend resends result */ rc = rs->sr_err = SLAPD_ABANDON; + + } else if ( LDAP_BACK_QUARANTINE( li ) ) { + ldap_back_quarantine( op, rs ); } /* these have to be freed anyway... */ diff --git a/servers/slapd/back-ldap/init.c b/servers/slapd/back-ldap/init.c index 02586a230d..9198e0c8f1 100644 --- a/servers/slapd/back-ldap/init.c +++ b/servers/slapd/back-ldap/init.c @@ -81,12 +81,16 @@ int ldap_back_db_init( Backend *be ) { ldapinfo_t *li; + unsigned i; li = (ldapinfo_t *)ch_calloc( 1, sizeof( ldapinfo_t ) ); if ( li == NULL ) { return -1; } + li->li_rebind_f = ldap_back_default_rebind; + 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 ); @@ -105,7 +109,7 @@ ldap_back_db_init( Backend *be ) li->li_idassert_authmethod = LDAP_AUTH_NONE; BER_BVZERO( &li->li_idassert_sasl_mech ); - li->li_idassert.sb_tls = SB_TLS_DEFAULT; + li->li_idassert_tls = SB_TLS_DEFAULT; /* by default, use proxyAuthz control on each operation */ li->li_idassert_flags = LDAP_BACK_AUTH_PRESCRIPTIVE; @@ -120,6 +124,12 @@ ldap_back_db_init( Backend *be ) 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; + be->be_private = li; SLAP_DBFLAGS( be ) |= SLAP_DBFLAG_NOLASTMOD; @@ -150,48 +160,30 @@ ldap_back_db_open( BackendDB *be ) break; } -#if 0 && defined(SLAPD_MONITOR) - { - /* FIXME: disabled because namingContexts doesn't have - * a matching rule, and using an MRA filter doesn't work - * because the normalized assertion is compared to the - * non-normalized value, which in general differs from - * the normalized one. See ITS#3406 */ - struct berval filter, - base = BER_BVC( "cn=Databases," SLAPD_MONITOR ); - Attribute a = { 0 }; - - filter.bv_len = STRLENOF( "(&(namingContexts:distinguishedNameMatch:=)(monitoredInfo=ldap))" ) - + be->be_nsuffix[ 0 ].bv_len; - filter.bv_val = ch_malloc( filter.bv_len + 1 ); - snprintf( filter.bv_val, filter.bv_len + 1, - "(&(namingContexts:distinguishedNameMatch:=%s)(monitoredInfo=ldap))", - be->be_nsuffix[ 0 ].bv_val ); - - a.a_desc = slap_schema.si_ad_labeledURI; - a.a_vals = li->li_bvuri; - a.a_nvals = li->li_bvuri; - if ( monitor_back_register_entry_attrs( NULL, &a, NULL, &base, LDAP_SCOPE_SUBTREE, &filter ) ) { - /* error */ - } + if ( LDAP_BACK_T_F_DISCOVER( li ) && !LDAP_BACK_T_F( li ) ) { + int rc; - ch_free( filter.bv_val ); + rc = slap_discover_feature( li->li_uri, li->li_version, + 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; + } } -#endif /* SLAPD_MONITOR */ - if ( li->li_flags & LDAP_BACK_F_SUPPORT_T_F_DISCOVER ) { + if ( LDAP_BACK_CANCEL_DISCOVER( li ) && !LDAP_BACK_CANCEL( li ) ) { int rc; - li->li_flags &= ~LDAP_BACK_F_SUPPORT_T_F_DISCOVER; - rc = slap_discover_feature( li->li_uri, li->li_version, - slap_schema.si_ad_supportedFeatures->ad_cname.bv_val, - LDAP_FEATURE_ABSOLUTE_FILTERS ); + slap_schema.si_ad_supportedExtension->ad_cname.bv_val, + LDAP_EXOP_CANCEL ); if ( rc == LDAP_COMPARE_TRUE ) { - li->li_flags |= LDAP_BACK_F_SUPPORT_T_F; + li->li_flags |= LDAP_BACK_F_CANCEL_EXOP; } } + li->li_flags |= LDAP_BACK_F_ISOPEN; + return 0; } @@ -213,16 +205,17 @@ ldap_back_conn_free( void *v_lc ) 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_destroy( - Backend *be -) +ldap_back_db_destroy( Backend *be ) { if ( be->be_private ) { ldapinfo_t *li = ( ldapinfo_t * )be->be_private; + unsigned i; ldap_pvt_thread_mutex_lock( &li->li_conninfo.lai_mutex ); @@ -285,9 +278,22 @@ ldap_back_db_destroy( if ( li->li_conninfo.lai_tree ) { avl_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 ); } ch_free( be->be_private ); diff --git a/servers/slapd/back-ldap/modify.c b/servers/slapd/back-ldap/modify.c index 69914feda8..fc8b53d04a 100644 --- a/servers/slapd/back-ldap/modify.c +++ b/servers/slapd/back-ldap/modify.c @@ -36,20 +36,19 @@ ldap_back_modify( Operation *op, SlapReply *rs ) { - ldapinfo_t *li = (ldapinfo_t *)op->o_bd->be_private; - - ldapconn_t *lc; - LDAPMod **modv = NULL, - *mods = NULL; - Modifications *ml; - int i, j, rc; - ber_int_t msgid; - int isupdate; - int do_retry = 1; - LDAPControl **ctrls = NULL; - - lc = ldap_back_getconn( op, rs, LDAP_BACK_SENDERR ); - if ( !lc || !ldap_back_dobind( lc, op, rs, LDAP_BACK_SENDERR ) ) { + 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; } @@ -100,7 +99,8 @@ ldap_back_modify( retry:; ctrls = op->o_ctrls; - rc = ldap_back_proxy_authz_ctrl( lc, op, rs, &ctrls ); + rc = ldap_back_proxy_authz_ctrl( &lc->lc_bound_ndn, + li->li_version, &li->li_idassert, op, rs, &ctrls ); if ( rc != LDAP_SUCCESS ) { send_ldap_result( op, rs ); rc = -1; @@ -110,9 +110,10 @@ retry:; 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[ LDAP_BACK_OP_MODIFY], LDAP_BACK_SENDRESULT ); - if ( rs->sr_err == LDAP_UNAVAILABLE && do_retry ) { - do_retry = 0; + 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_proxy_authz_ctrl_free( op, &ctrls ); diff --git a/servers/slapd/back-ldap/modrdn.c b/servers/slapd/back-ldap/modrdn.c index e72ed98d37..802701d622 100644 --- a/servers/slapd/back-ldap/modrdn.c +++ b/servers/slapd/back-ldap/modrdn.c @@ -36,17 +36,16 @@ ldap_back_modrdn( Operation *op, SlapReply *rs ) { - ldapinfo_t *li = (ldapinfo_t *)op->o_bd->be_private; + ldapinfo_t *li = (ldapinfo_t *)op->o_bd->be_private; - ldapconn_t *lc; - ber_int_t msgid; - LDAPControl **ctrls = NULL; - int do_retry = 1; - int rc = LDAP_SUCCESS; - char *newSup = NULL; + 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; - lc = ldap_back_getconn( op, rs, LDAP_BACK_SENDERR ); - if ( !lc || !ldap_back_dobind( lc, op, rs, LDAP_BACK_SENDERR ) ) { + if ( !ldap_back_dobind( &lc, op, rs, LDAP_BACK_SENDERR ) ) { return rs->sr_err; } @@ -75,7 +74,8 @@ ldap_back_modrdn( retry: ctrls = op->o_ctrls; - rc = ldap_back_proxy_authz_ctrl( lc, op, rs, &ctrls ); + rc = ldap_back_proxy_authz_ctrl( &lc->lc_bound_ndn, + li->li_version, &li->li_idassert, op, rs, &ctrls ); if ( rc != LDAP_SUCCESS ) { send_ldap_result( op, rs ); rc = -1; @@ -86,9 +86,10 @@ retry: op->orr_newrdn.bv_val, newSup, op->orr_deleteoldrdn, ctrls, NULL, &msgid ); rc = ldap_back_op_result( lc, op, rs, msgid, - li->li_timeout[ LDAP_BACK_OP_MODRDN ], LDAP_BACK_SENDRESULT ); - if ( rs->sr_err == LDAP_SERVER_DOWN && do_retry ) { - do_retry = 0; + li->li_timeout[ SLAP_OP_MODRDN ], + ( LDAP_BACK_SENDRESULT | retrying ) ); + if ( rs->sr_err == LDAP_SERVER_DOWN && 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_proxy_authz_ctrl_free( op, &ctrls ); diff --git a/servers/slapd/back-ldap/proto-ldap.h b/servers/slapd/back-ldap/proto-ldap.h index ff791f19ba..3d2f8721d1 100644 --- a/servers/slapd/back-ldap/proto-ldap.h +++ b/servers/slapd/back-ldap/proto-ldap.h @@ -47,15 +47,14 @@ extern BI_connection_destroy ldap_back_conn_destroy; extern BI_entry_get_rw ldap_back_entry_get; -int ldap_back_freeconn( Operation *op, ldapconn_t *lc, int dolock ); -ldapconn_t *ldap_back_getconn( Operation *op, SlapReply *rs, ldap_back_send_t sendok ); -void ldap_back_release_conn_lock( Operation *op, SlapReply *rs, ldapconn_t *lc, int dolock ); -#define ldap_back_release_conn(op, rs, lc) ldap_back_release_conn_lock((op), (rs), (lc), 1) -int ldap_back_dobind( ldapconn_t *lc, Operation *op, SlapReply *rs, ldap_back_send_t sendok ); +void ldap_back_release_conn_lock( Operation *op, SlapReply *rs, ldapconn_t **lcp, int dolock ); +#define ldap_back_release_conn(op, rs, lc) ldap_back_release_conn_lock((op), (rs), &(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 ); @@ -66,7 +65,9 @@ extern void ldap_back_conn_free( void *c ); extern int ldap_back_proxy_authz_ctrl( - ldapconn_t *lc, + struct berval *bound_ndn, + int version, + slap_idassert_t *si, Operation *op, SlapReply *rs, LDAPControl ***pctrls ); @@ -76,9 +77,27 @@ ldap_back_proxy_authz_ctrl_free( Operation *op, 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_cf( const char *fname, int lineno, const char *arg, slap_idassert_t *si ); +extern int slap_idassert_parse_cf( const char *fname, int lineno, int argc, char *argv[], slap_idassert_t *si ); + extern int chain_init( void ); -extern LDAP_REBIND_PROC *ldap_back_rebind_f; +extern LDAP_REBIND_PROC ldap_back_default_rebind; LDAP_END_DECL diff --git a/servers/slapd/back-ldap/search.c b/servers/slapd/back-ldap/search.c index a7645bd926..b27d39795c 100644 --- a/servers/slapd/back-ldap/search.c +++ b/servers/slapd/back-ldap/search.c @@ -75,7 +75,7 @@ ldap_back_munge_filter( if ( strncmp( ptr, bv_true.bv_val, bv_true.bv_len ) == 0 ) { oldbv = &bv_true; - if ( li->li_flags & LDAP_BACK_F_SUPPORT_T_F ) { + if ( LDAP_BACK_T_F( li ) ) { newbv = &bv_t; } else { @@ -85,7 +85,7 @@ ldap_back_munge_filter( } else if ( strncmp( ptr, bv_false.bv_val, bv_false.bv_len ) == 0 ) { oldbv = &bv_false; - if ( li->li_flags & LDAP_BACK_F_SUPPORT_T_F ) { + if ( LDAP_BACK_T_F( li ) ) { newbv = &bv_f; } else { @@ -141,9 +141,11 @@ ldap_back_search( Operation *op, SlapReply *rs ) { - ldapconn_t *lc; + ldapinfo_t *li = (ldapinfo_t *) op->o_bd->be_private; + + ldapconn_t *lc = NULL; struct timeval tv; - time_t stoptime = (time_t)-1; + time_t stoptime = (time_t)(-1); LDAPMessage *res, *e; int rc = 0, @@ -159,8 +161,7 @@ ldap_back_search( /* FIXME: shouldn't this be null? */ const char *save_matched = rs->sr_matched; - lc = ldap_back_getconn( op, rs, LDAP_BACK_SENDERR ); - if ( !lc || !ldap_back_dobind( lc, op, rs, LDAP_BACK_SENDERR ) ) { + if ( !ldap_back_dobind( &lc, op, rs, LDAP_BACK_SENDERR ) ) { return rs->sr_err; } @@ -202,7 +203,8 @@ ldap_back_search( } ctrls = op->o_ctrls; - rc = ldap_back_proxy_authz_ctrl( lc, op, rs, &ctrls ); + rc = ldap_back_proxy_authz_ctrl( &lc->lc_bound_ndn, + li->li_version, &li->li_idassert, op, rs, &ctrls ); if ( rc != LDAP_SUCCESS ) { goto finish; } @@ -266,7 +268,7 @@ retry: if ( rc > 0 ) { ldap_msgfree( res ); } - ldap_abandon_ext( lc->lc_ld, msgid, NULL, NULL ); + (void)ldap_back_cancel( lc, op, rs, msgid, LDAP_BACK_DONTSEND ); rc = SLAPD_ABANDON; goto finish; } @@ -279,13 +281,18 @@ retry: if ( op->ors_tlimit != SLAP_NO_LIMIT && slap_get_time() > stoptime ) { - ldap_abandon_ext( lc->lc_ld, msgid, NULL, NULL ); + (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->lc_time = op->o_time; + } + /* don't retry any more */ dont_retry = 1; } @@ -322,7 +329,7 @@ retry: if ( rc == LDAP_UNAVAILABLE ) { rc = rs->sr_err = LDAP_OTHER; } else { - ldap_abandon_ext( lc->lc_ld, msgid, NULL, NULL ); + (void)ldap_back_cancel( lc, op, rs, msgid, LDAP_BACK_DONTSEND ); } goto finish; } @@ -346,7 +353,8 @@ retry: /* NO OP */ ; /* FIXME: there MUST be at least one */ - rs->sr_ref = ch_malloc( ( cnt + 1 ) * sizeof( struct berval ) ); + 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 ] ); @@ -367,7 +375,7 @@ retry: /* cleanup */ if ( references ) { ber_memvfree( (void **)references ); - ch_free( rs->sr_ref ); + op->o_tmpfree( rs->sr_ref, op->o_tmpmemctx ); rs->sr_ref = NULL; } @@ -407,7 +415,8 @@ retry: for ( cnt = 0; references[ cnt ]; cnt++ ) /* NO OP */ ; - rs->sr_ref = ch_malloc( ( cnt + 1 ) * sizeof( struct berval ) ); + rs->sr_ref = op->o_tmpalloc( ( cnt + 1 ) * sizeof( struct berval ), + op->o_tmpmemctx ); for ( cnt = 0; references[ cnt ]; cnt++ ) { /* duplicating ...*/ @@ -476,7 +485,15 @@ retry: } finish:; - if ( rc != SLAPD_ABANDON ) { + if ( LDAP_BACK_QUARANTINE( li ) ) { + ldap_back_quarantine( op, rs ); + } + +#if 0 + /* let send_ldap_result play cleanup handlers (ITS#4645) */ + if ( rc != SLAPD_ABANDON ) +#endif + { send_ldap_result( op, rs ); } @@ -509,7 +526,7 @@ finish:; } if ( rs->sr_ref ) { - ber_bvarray_free( rs->sr_ref ); + ber_bvarray_free_x( rs->sr_ref, op->o_tmpmemctx ); rs->sr_ref = NULL; } @@ -716,10 +733,11 @@ ldap_back_entry_get( ObjectClass *oc, AttributeDescription *at, int rw, - Entry **ent -) + Entry **ent ) { - ldapconn_t *lc; + ldapinfo_t *li = (ldapinfo_t *) op->o_bd->be_private; + + ldapconn_t *lc = NULL; int rc = 1, do_not_cache; struct berval bdn; @@ -736,8 +754,7 @@ ldap_back_entry_get( /* Tell getconn this is a privileged op */ do_not_cache = op->o_do_not_cache; op->o_do_not_cache = 1; - lc = ldap_back_getconn( op, &rs, LDAP_BACK_DONTSEND ); - if ( !lc || !ldap_back_dobind( lc, op, &rs, LDAP_BACK_DONTSEND ) ) { + if ( !ldap_back_dobind( &lc, op, &rs, LDAP_BACK_DONTSEND ) ) { op->o_do_not_cache = do_not_cache; return rs.sr_err; } @@ -769,7 +786,8 @@ ldap_back_entry_get( retry: ctrls = op->o_ctrls; - rc = ldap_back_proxy_authz_ctrl( lc, op, &rs, &ctrls ); + rc = ldap_back_proxy_authz_ctrl( &lc->lc_bound_ndn, + li->li_version, &li->li_idassert, op, &rs, &ctrls ); if ( rc != LDAP_SUCCESS ) { goto cleanup; } diff --git a/servers/slapd/back-ldap/unbind.c b/servers/slapd/back-ldap/unbind.c index 71941b269e..13f4887314 100644 --- a/servers/slapd/back-ldap/unbind.c +++ b/servers/slapd/back-ldap/unbind.c @@ -48,11 +48,14 @@ ldap_back_conn_destroy( 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 = avl_delete( &li->li_conninfo.lai_tree, (caddr_t)&lc_curr, ldap_back_conn_cmp ) ) != NULL ) { Debug( LDAP_DEBUG_TRACE, "=>ldap_back_conn_destroy: destroying conn %ld (refcnt=%u)\n", - LDAP_BACK_PCONN_ID( lc->lc_conn ), lc->lc_refcnt, 0 ); + LDAP_BACK_PCONN_ID( lc ), lc->lc_refcnt, 0 ); assert( lc->lc_refcnt == 0 ); @@ -63,6 +66,9 @@ ldap_back_conn_destroy( */ 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; diff --git a/servers/slapd/back-meta/add.c b/servers/slapd/back-meta/add.c index b20ed994c3..a8096eff84 100644 --- a/servers/slapd/back-meta/add.c +++ b/servers/slapd/back-meta/add.c @@ -36,6 +36,7 @@ int meta_back_add( Operation *op, SlapReply *rs ) { metainfo_t *mi = ( metainfo_t * )op->o_bd->be_private; + metatarget_t *mt; metaconn_t *mc; int i, candidate = -1; int isupdate; @@ -45,7 +46,7 @@ meta_back_add( Operation *op, SlapReply *rs ) dncookie dc; int msgid; int do_retry = 1; - int maperr = 1; + LDAPControl **ctrls = NULL; Debug(LDAP_DEBUG_ARGS, "==> meta_back_add: %s\n", op->o_req_dn.bv_val, 0, 0 ); @@ -63,7 +64,8 @@ meta_back_add( Operation *op, SlapReply *rs ) /* * Rewrite the add dn, if needed */ - dc.target = &mi->mi_targets[ candidate ]; + mt = mi->mi_targets[ candidate ]; + dc.target = mt; dc.conn = op->o_conn; dc.rs = rs; dc.ctx = "addDN"; @@ -96,7 +98,7 @@ meta_back_add( Operation *op, SlapReply *rs ) mapped = a->a_desc->ad_cname; } else { - ldap_back_map( &mi->mi_targets[ candidate ].mt_rwmap.rwm_at, + ldap_back_map( &mt->mt_rwmap.rwm_at, &a->a_desc->ad_cname, &mapped, BACKLDAP_MAP ); if ( BER_BVISNULL( &mapped ) || BER_BVISEMPTY( &mapped ) ) { continue; @@ -121,11 +123,11 @@ meta_back_add( Operation *op, SlapReply *rs ) for ( j = 0; !BER_BVISNULL( &a->a_vals[ j ] ); ) { struct ldapmapping *mapping; - ldap_back_mapping( &mi->mi_targets[ candidate ].mt_rwmap.rwm_oc, + ldap_back_mapping( &mt->mt_rwmap.rwm_oc, &a->a_vals[ j ], &mapping, BACKLDAP_MAP ); if ( mapping == NULL ) { - if ( mi->mi_targets[ candidate ].mt_rwmap.rwm_oc.drop_missing ) { + if ( mt->mt_rwmap.rwm_oc.drop_missing ) { continue; } attrs[ i ]->mod_bvalues[ j ] = &a->a_vals[ j ]; @@ -166,64 +168,30 @@ meta_back_add( Operation *op, SlapReply *rs ) attrs[ i ] = NULL; retry:; + ctrls = op->o_ctrls; + if ( ldap_back_proxy_authz_ctrl( &mc->mc_conns[ candidate ].msc_bound_ndn, + mt->mt_version, &mt->mt_idassert, op, rs, &ctrls ) != LDAP_SUCCESS ) + { + send_ldap_result( op, rs ); + goto cleanup; + } + rs->sr_err = ldap_add_ext( mc->mc_conns[ candidate ].msc_ld, mdn.bv_val, - attrs, op->o_ctrls, NULL, &msgid ); + attrs, ctrls, NULL, &msgid ); + rs->sr_err = meta_back_op_result( mc, op, rs, candidate, msgid, + mt->mt_timeout[ SLAP_OP_ADD ], LDAP_BACK_SENDRESULT ); if ( rs->sr_err == LDAP_UNAVAILABLE && do_retry ) { do_retry = 0; if ( meta_back_retry( op, rs, &mc, candidate, LDAP_BACK_SENDERR ) ) { + /* if the identity changed, there might be need to re-authz */ + (void)ldap_back_proxy_authz_ctrl_free( op, &ctrls ); goto retry; } - goto cleanup; - - } else if ( rs->sr_err == LDAP_SUCCESS ) { - struct timeval tv, *tvp = NULL; - LDAPMessage *res = NULL; - int rc; - - if ( mi->mi_targets[ candidate ].mt_timeout[ LDAP_BACK_OP_ADD ] != 0 ) { - tv.tv_sec = mi->mi_targets[ candidate ].mt_timeout[ LDAP_BACK_OP_ADD ]; - tv.tv_usec = 0; - tvp = &tv; - } - - rs->sr_err = LDAP_OTHER; - maperr = 0; - rc = ldap_result( mc->mc_conns[ candidate ].msc_ld, - msgid, LDAP_MSG_ALL, tvp, &res ); - switch ( rc ) { - case -1: - break; - - case 0: - ldap_abandon_ext( mc->mc_conns[ candidate ].msc_ld, - msgid, NULL, NULL ); - rs->sr_err = op->o_protocol >= LDAP_VERSION3 ? - LDAP_ADMINLIMIT_EXCEEDED : LDAP_OPERATIONS_ERROR; - break; - - case LDAP_RES_ADD: - rc = ldap_parse_result( mc->mc_conns[ candidate ].msc_ld, - res, &rs->sr_err, NULL, NULL, NULL, NULL, 1 ); - if ( rc != LDAP_SUCCESS ) { - rs->sr_err = rc; - } - maperr = 1; - break; - - default: - ldap_msgfree( res ); - break; - } - } - - if ( maperr ) { - rs->sr_err = meta_back_op_result( mc, op, rs, candidate ); - - } else { - send_ldap_result( op, rs ); } cleanup:; + (void)ldap_back_proxy_authz_ctrl_free( op, &ctrls ); + for ( --i; i >= 0; --i ) { free( attrs[ i ]->mod_bvalues ); free( attrs[ i ] ); diff --git a/servers/slapd/back-meta/back-meta.h b/servers/slapd/back-meta/back-meta.h index 51a56199c5..4bdc0c7635 100644 --- a/servers/slapd/back-meta/back-meta.h +++ b/servers/slapd/back-meta/back-meta.h @@ -33,6 +33,13 @@ #include "rewrite.h" LDAP_BEGIN_DECL +/* + * Set META_BACK_PRINT_CONNTREE larger than 0 to dump the connection tree (debug only) + */ +#ifndef META_BACK_PRINT_CONNTREE +#define META_BACK_PRINT_CONNTREE 0 +#endif /* !META_BACK_PRINT_CONNTREE */ + struct slap_conn; struct slap_op; @@ -153,26 +160,67 @@ ldap_dnattr_result_rewrite( /* (end of) from back-ldap.h before rwm removal */ +/* + * A metasingleconn_t can be in the following, mutually exclusive states: + * + * - none (0x0U) + * - creating META_BACK_FCONN_CREATING + * - initialized META_BACK_FCONN_INITED + * - binding LDAP_BACK_FCONN_BINDING + * - bound/anonymous LDAP_BACK_FCONN_ISBOUND/LDAP_BACK_FCONN_ISANON + * + * possible modifiers are: + * + * - privileged LDAP_BACK_FCONN_ISPRIV + * - privileged, TLS LDAP_BACK_FCONN_ISTLS + * - subjected to idassert LDAP_BACK_FCONN_ISIDASR + * - tainted LDAP_BACK_FCONN_TAINTED + */ + +#define META_BACK_FCONN_INITED (0x00100000U) +#define META_BACK_FCONN_CREATING (0x00200000U) + +#define META_BACK_CONN_INITED(lc) LDAP_BACK_CONN_ISSET((lc), META_BACK_FCONN_INITED) +#define META_BACK_CONN_INITED_SET(lc) LDAP_BACK_CONN_SET((lc), META_BACK_FCONN_INITED) +#define META_BACK_CONN_INITED_CLEAR(lc) LDAP_BACK_CONN_CLEAR((lc), META_BACK_FCONN_INITED) +#define META_BACK_CONN_INITED_CPY(lc, mlc) LDAP_BACK_CONN_CPY((lc), META_BACK_FCONN_INITED, (mlc)) +#define META_BACK_CONN_CREATING(lc) LDAP_BACK_CONN_ISSET((lc), META_BACK_FCONN_CREATING) +#define META_BACK_CONN_CREATING_SET(lc) LDAP_BACK_CONN_SET((lc), META_BACK_FCONN_CREATING) +#define META_BACK_CONN_CREATING_CLEAR(lc) LDAP_BACK_CONN_CLEAR((lc), META_BACK_FCONN_CREATING) +#define META_BACK_CONN_CREATING_CPY(lc, mlc) LDAP_BACK_CONN_CPY((lc), META_BACK_FCONN_CREATING, (mlc)) + struct metainfo_t; +#define META_NOT_CANDIDATE ((ber_tag_t)0x0) +#define META_CANDIDATE ((ber_tag_t)0x1) +#define META_BINDING ((ber_tag_t)0x2) + typedef struct metasingleconn_t { - int msc_candidate; -#define META_NOT_CANDIDATE ((ber_tag_t)0) -#define META_CANDIDATE ((ber_tag_t)1) +#define META_CND_ISSET(rs,f) ( ( (rs)->sr_tag & (f) ) == (f) ) +#define META_CND_SET(rs,f) ( (rs)->sr_tag |= (f) ) +#define META_CND_CLEAR(rs,f) ( (rs)->sr_tag &= ~(f) ) + +#define META_CANDIDATE_RESET(rs) ( (rs)->sr_tag = 0 ) +#define META_IS_CANDIDATE(rs) META_CND_ISSET( (rs), META_CANDIDATE ) +#define META_CANDIDATE_SET(rs) META_CND_SET( (rs), META_CANDIDATE ) +#define META_CANDIDATE_CLEAR(rs) META_CND_CLEAR( (rs), META_CANDIDATE ) +#define META_IS_BINDING(rs) META_CND_ISSET( (rs), META_BINDING ) +#define META_BINDING_SET(rs) META_CND_SET( (rs), META_BINDING ) +#define META_BINDING_CLEAR(rs) META_CND_CLEAR( (rs), META_BINDING ) LDAP *msc_ld; + time_t msc_time; struct berval msc_bound_ndn; struct berval msc_cred; unsigned msc_mscflags; /* NOTE: lc_lcflags is redefined to msc_mscflags to reuse the macros * defined for back-ldap */ #define lc_lcflags msc_mscflags - - struct metainfo_t *msc_info; } metasingleconn_t; typedef struct metaconn_t { struct slap_conn *mc_conn; +#define lc_conn mc_conn unsigned mc_refcnt; time_t mc_create_time; @@ -190,6 +238,11 @@ typedef struct metaconn_t { int mc_authz_target; #define META_BOUND_NONE (-1) #define META_BOUND_ALL (-2) + + struct metainfo_t *mc_info; + + LDAP_TAILQ_ENTRY(metaconn_t) mc_q; + /* supersedes the connection stuff */ metasingleconn_t mc_conns[ 1 ]; /* NOTE: mc_conns must be last, because @@ -199,6 +252,13 @@ typedef struct metaconn_t { typedef struct metatarget_t { char *mt_uri; + ldap_pvt_thread_mutex_t mt_uri_mutex; + + /* TODO: we might want to enable different strategies + * for different targets */ + LDAP_REBIND_PROC *mt_rebind_f; + void *mt_urllist_p; + BerVarray mt_subtree_exclude; int mt_scope; @@ -208,23 +268,50 @@ typedef struct metatarget_t { struct berval mt_binddn; struct berval mt_bindpw; - struct berval mt_pseudorootdn; - struct berval mt_pseudorootpw; + slap_idassert_t mt_idassert; +#define mt_idassert_mode mt_idassert.si_mode +#define mt_idassert_authcID mt_idassert.si_bc.sb_authcId +#define mt_idassert_authcDN mt_idassert.si_bc.sb_binddn +#define mt_idassert_passwd mt_idassert.si_bc.sb_cred +#define mt_idassert_authzID mt_idassert.si_bc.sb_authzId +#define mt_idassert_authmethod mt_idassert.si_bc.sb_method +#define mt_idassert_sasl_mech mt_idassert.si_bc.sb_saslmech +#define mt_idassert_sasl_realm mt_idassert.si_bc.sb_realm +#define mt_idassert_secprops mt_idassert.si_bc.sb_secprops +#define mt_idassert_tls mt_idassert.si_bc.sb_tls +#define mt_idassert_flags mt_idassert.si_flags +#define mt_idassert_authz mt_idassert.si_authz int mt_nretries; #define META_RETRY_UNDEFINED (-2) #define META_RETRY_FOREVER (-1) #define META_RETRY_NEVER (0) -#define META_RETRY_DEFAULT (3) +#define META_RETRY_DEFAULT (10) struct ldaprwmap mt_rwmap; + sig_atomic_t mt_isquarantined; + slap_retry_info_t mt_quarantine; + ldap_pvt_thread_mutex_t mt_quarantine_mutex; +#define META_BACK_TGT_QUARANTINE(mt) ( (mt)->mt_quarantine.ri_num != NULL ) + unsigned mt_flags; +#define META_BACK_TGT_ISSET(mt,f) ( ( (mt)->mt_flags & (f) ) == (f) ) +#define META_BACK_TGT_ISMASK(mt,m,f) ( ( (mt)->mt_flags & (m) ) == (f) ) + +#define META_BACK_TGT_T_F(mt) META_BACK_TGT_ISMASK( (mt), LDAP_BACK_F_T_F_MASK, LDAP_BACK_F_T_F ) +#define META_BACK_TGT_T_F_DISCOVER(mt) META_BACK_TGT_ISMASK( (mt), LDAP_BACK_F_T_F_MASK2, LDAP_BACK_F_T_F_DISCOVER ) + +#define META_BACK_TGT_ABANDON(mt) META_BACK_TGT_ISMASK( (mt), LDAP_BACK_F_CANCEL_MASK, LDAP_BACK_F_CANCEL_ABANDON ) +#define META_BACK_TGT_IGNORE(mt) META_BACK_TGT_ISMASK( (mt), LDAP_BACK_F_CANCEL_MASK, LDAP_BACK_F_CANCEL_IGNORE ) +#define META_BACK_TGT_CANCEL(mt) META_BACK_TGT_ISMASK( (mt), LDAP_BACK_F_CANCEL_MASK, LDAP_BACK_F_CANCEL_EXOP ) +#define META_BACK_TGT_CANCEL_DISCOVER(mt) META_BACK_TGT_ISMASK( (mt), LDAP_BACK_F_CANCEL_MASK2, LDAP_BACK_F_CANCEL_EXOP_DISCOVER ) + int mt_version; time_t mt_network_timeout; struct timeval mt_bind_timeout; #define META_BIND_TIMEOUT LDAP_BACK_RESULT_UTIMEOUT - time_t mt_timeout[ LDAP_BACK_OP_LAST ]; + time_t mt_timeout[ SLAP_OP_LAST ]; } metatarget_t; typedef struct metadncache_t { @@ -241,36 +328,62 @@ typedef struct metacandidates_t { SlapReply *mc_candidates; } metacandidates_t; +/* + * Hook to allow mucking with metainfo_t/metatarget_t when quarantine is over + */ +typedef int (*meta_back_quarantine_f)( struct metainfo_t *, int target, void * ); + typedef struct metainfo_t { int mi_ntargets; int mi_defaulttarget; #define META_DEFAULT_TARGET_NONE (-1) int mi_nretries; - metatarget_t *mi_targets; + metatarget_t **mi_targets; metacandidates_t *mi_candidates; + LDAP_REBIND_PROC *mi_rebind_f; + metadncache_t mi_cache; + /* cached connections; + * special conns are in tailq rather than in tree */ ldap_avl_info_t mi_conninfo; + struct { + int mic_num; + LDAP_TAILQ_HEAD(mc_conn_priv_q, metaconn_t) mic_priv; + } mi_conn_priv[ LDAP_BACK_PCONN_LAST ]; + int mi_conn_priv_max; + + /* NOTE: quarantine uses the connection mutex */ + slap_retry_info_t mi_quarantine; + +#define META_BACK_QUARANTINE(mi) ( (mi)->mi_quarantine.ri_num != NULL ) + meta_back_quarantine_f mi_quarantine_f; + void *mi_quarantine_p; unsigned mi_flags; #define li_flags mi_flags /* uses flags as defined in */ -#define META_BACK_F_ONERR_STOP 0x00010000U -#define META_BACK_F_DEFER_ROOTDN_BIND 0x00020000U +#define META_BACK_F_ONERR_STOP (0x00100000U) +#define META_BACK_F_ONERR_REPORT (0x00200000U) +#define META_BACK_F_ONERR_MASK (META_BACK_F_ONERR_STOP|META_BACK_F_ONERR_REPORT) +#define META_BACK_F_DEFER_ROOTDN_BIND (0x00400000U) +#define META_BACK_F_PROXYAUTHZ_ALWAYS (0x00800000U) #define META_BACK_ONERR_STOP(mi) ( (mi)->mi_flags & META_BACK_F_ONERR_STOP ) -#define META_BACK_ONERR_CONTINUE(mi) ( !META_BACK_ONERR_CONTINUE( (mi) ) ) +#define META_BACK_ONERR_REPORT(mi) ( (mi)->mi_flags & META_BACK_F_ONERR_REPORT ) +#define META_BACK_ONERR_CONTINUE(mi) ( !( (mi)->mi_flags & META_BACK_F_ONERR_MASK ) ) #define META_BACK_DEFER_ROOTDN_BIND(mi) ( (mi)->mi_flags & META_BACK_F_DEFER_ROOTDN_BIND ) +#define META_BACK_PROXYAUTHZ_ALWAYS(mi) ( (mi)->mi_flags & META_BACK_F_PROXYAUTHZ_ALWAYS ) int mi_version; time_t mi_network_timeout; time_t mi_conn_ttl; time_t mi_idle_timeout; struct timeval mi_bind_timeout; - time_t mi_timeout[ LDAP_BACK_OP_LAST ]; + time_t mi_timeout[ SLAP_OP_LAST ]; } metainfo_t; typedef enum meta_op_type { @@ -293,9 +406,8 @@ extern void meta_back_release_conn_lock( Operation *op, metaconn_t *mc, - int dofree, int dolock ); -#define meta_back_release_conn(op, mc) meta_back_release_conn_lock( (op), (mc), 0, 1 ) +#define meta_back_release_conn(op, mc) meta_back_release_conn_lock( (op), (mc), 1 ) extern int meta_back_retry( @@ -309,23 +421,28 @@ extern void meta_back_conn_free( void *v_mc ); +#if META_BACK_PRINT_CONNTREE > 0 +extern void +meta_back_print_conntree( + metainfo_t *mi, + char *msg ); +#endif + extern int meta_back_init_one_conn( Operation *op, SlapReply *rs, - metatarget_t *mt, metaconn_t *mc, int candidate, int ispriv, - ldap_back_send_t sendok ); + ldap_back_send_t sendok, + int dolock ); -extern int -meta_back_single_bind( +extern void +meta_back_quarantine( Operation *op, SlapReply *rs, - metaconn_t *mc, - int candidate, - int massage ); + int candidate ); extern int meta_back_dobind( @@ -344,12 +461,35 @@ meta_back_single_dobind( int retries, int dolock ); +extern int +meta_back_proxy_authz_cred( + metaconn_t *mc, + int candidate, + Operation *op, + SlapReply *rs, + ldap_back_send_t sendok, + struct berval *binddn, + struct berval *bindcred, + int *method ); + +extern int +meta_back_cancel( + metaconn_t *mc, + Operation *op, + SlapReply *rs, + ber_int_t msgid, + int candidate, + ldap_back_send_t sendok ); + extern int meta_back_op_result( metaconn_t *mc, Operation *op, SlapReply *rs, - int candidate ); + int candidate, + ber_int_t msgid, + time_t timeout, + ldap_back_send_t sendok ); extern int back_meta_LTX_init_module( @@ -376,9 +516,7 @@ meta_back_conndn_dup( */ extern int meta_back_is_candidate( - struct berval *nsuffix, - int suffixscope, - BerVarray subtree_exclude, + metatarget_t *mt, struct berval *ndn, int scope ); @@ -394,12 +532,9 @@ meta_clear_unused_candidates( extern int meta_clear_one_candidate( - metasingleconn_t *mc ); - -extern int -meta_clear_candidates( Operation *op, - metaconn_t *mc ); + metaconn_t *mc, + int candidate ); /* * Dn cache stuff (experimental) @@ -435,7 +570,7 @@ meta_dncache_delete_entry( extern void meta_dncache_free( void *entry ); -extern LDAP_REBIND_PROC *meta_back_rebind_f; +extern LDAP_REBIND_PROC meta_back_default_rebind; LDAP_END_DECL diff --git a/servers/slapd/back-meta/bind.c b/servers/slapd/back-meta/bind.c index 0cc60f027c..b6d822a6ce 100644 --- a/servers/slapd/back-meta/bind.c +++ b/servers/slapd/back-meta/bind.c @@ -33,13 +33,25 @@ #include "slap.h" #include "../back-ldap/back-ldap.h" #include "back-meta.h" +#undef ldap_debug /* silence a warning in ldap-int.h */ +#include "../../../libraries/libldap/ldap-int.h" -static LDAP_REBIND_PROC meta_back_default_rebind; +#include "lutil_ldap.h" -/* - * a module could register a replacement for this function - */ -LDAP_REBIND_PROC *meta_back_rebind_f = meta_back_default_rebind; +static int +meta_back_proxy_authz_bind( + metaconn_t *mc, + int candidate, + Operation *op, + SlapReply *rs, + ldap_back_send_t sendok ); + +static int +meta_back_single_bind( + Operation *op, + SlapReply *rs, + metaconn_t *mc, + int candidate ); int meta_back_bind( Operation *op, SlapReply *rs ) @@ -85,17 +97,19 @@ meta_back_bind( Operation *op, SlapReply *rs ) * invalidCredentials */ mc = meta_back_getconn( op, rs, NULL, LDAP_BACK_BIND_DONTSEND ); if ( !mc ) { - char buf[ SLAP_TEXT_BUFLEN ]; - - snprintf( buf, sizeof( buf ), - "meta_back_bind: no target " - "for dn \"%s\" (%d%s%s).", - op->o_req_dn.bv_val, rs->sr_err, - rs->sr_text ? ". " : "", - rs->sr_text ? rs->sr_text : "" ); - Debug( LDAP_DEBUG_ANY, - "%s %s\n", - op->o_log_prefix, buf, 0 ); + if ( StatslogTest( LDAP_DEBUG_ANY ) ) { + char buf[ SLAP_TEXT_BUFLEN ]; + + snprintf( buf, sizeof( buf ), + "meta_back_bind: no target " + "for dn \"%s\" (%d%s%s).", + op->o_req_dn.bv_val, rs->sr_err, + rs->sr_text ? ". " : "", + rs->sr_text ? rs->sr_text : "" ); + Debug( LDAP_DEBUG_ANY, + "%s %s\n", + op->o_log_prefix, buf, 0 ); + } /* FIXME: there might be cases where we don't want * to map the error onto invalidCredentials */ @@ -115,14 +129,13 @@ meta_back_bind( Operation *op, SlapReply *rs ) */ mc->mc_authz_target = META_BOUND_NONE; for ( i = 0; i < mi->mi_ntargets; i++ ) { + metatarget_t *mt = mi->mi_targets[ i ]; int lerr; - Operation op2 = *op; - int massage = 1; /* * Skip non-candidates */ - if ( candidates[ i ].sr_tag != META_CANDIDATE ) { + if ( !META_IS_CANDIDATE( &candidates[ i ] ) ) { continue; } @@ -144,7 +157,8 @@ meta_back_bind( Operation *op, SlapReply *rs ) } if ( isroot ) { - if ( BER_BVISNULL( &mi->mi_targets[ i ].mt_pseudorootdn ) ) + if ( mt->mt_idassert_authmethod == LDAP_AUTH_NONE + || BER_BVISNULL( &mt->mt_idassert_authcDN ) ) { metasingleconn_t *msc = &mc->mc_conns[ i ]; @@ -154,9 +168,7 @@ meta_back_bind( Operation *op, SlapReply *rs ) BER_BVZERO( &msc->msc_bound_ndn ); } - if ( LDAP_BACK_SAVECRED( mi ) && - !BER_BVISNULL( &msc->msc_cred ) ) - { + if ( !BER_BVISNULL( &msc->msc_cred ) ) { /* destroy sensitive data */ memset( msc->msc_cred.bv_val, 0, msc->msc_cred.bv_len ); @@ -167,15 +179,13 @@ meta_back_bind( Operation *op, SlapReply *rs ) continue; } - op2.o_req_dn = mi->mi_targets[ i ].mt_pseudorootdn; - op2.o_req_ndn = mi->mi_targets[ i ].mt_pseudorootdn; - op2.orb_cred = mi->mi_targets[ i ].mt_pseudorootpw; - op2.orb_method = LDAP_AUTH_SIMPLE; + + (void)meta_back_proxy_authz_bind( mc, i, op, rs, LDAP_BACK_DONTSEND ); + lerr = rs->sr_err; - massage = 0; + } else { + lerr = meta_back_single_bind( op, rs, mc, i ); } - - lerr = meta_back_single_bind( &op2, rs, mc, i, massage ); if ( lerr != LDAP_SUCCESS ) { rc = rs->sr_err = lerr; @@ -183,7 +193,7 @@ meta_back_bind( Operation *op, SlapReply *rs ) * do not assume it's not candidate; rather * mark this as an error to be eventually * reported to client */ - candidates[ i ].sr_tag = META_NOT_CANDIDATE; + META_CANDIDATE_CLEAR( &candidates[ i ] ); break; } } @@ -209,18 +219,50 @@ retry_lock:; } assert( mc->mc_refcnt == 1 ); +#if META_BACK_PRINT_CONNTREE > 0 + meta_back_print_conntree( mi, ">>> meta_back_bind" ); +#endif /* META_BACK_PRINT_CONNTREE */ tmpmc = avl_delete( &mi->mi_conninfo.lai_tree, (caddr_t)mc, meta_back_conndn_cmp ); assert( tmpmc == mc ); + /* delete all cached connections with the current connection */ + if ( LDAP_BACK_SINGLECONN( mi ) ) { + while ( ( tmpmc = avl_delete( &mi->mi_conninfo.lai_tree, (caddr_t)mc, meta_back_conn_cmp ) ) != NULL ) + { + Debug( LDAP_DEBUG_TRACE, + "=>meta_back_bind: destroying conn %ld (refcnt=%u)\n", + LDAP_BACK_PCONN_ID( mc ), mc->mc_refcnt, 0 ); + + if ( tmpmc->mc_refcnt != 0 ) { + /* taint it */ + LDAP_BACK_CONN_TAINTED_SET( tmpmc ); + + } else { + /* + * Needs a test because the handler may be corrupted, + * and calling ldap_unbind on a corrupted header results + * in a segmentation fault + */ + meta_back_conn_free( tmpmc ); + } + } + } + ber_bvreplace( &mc->mc_local_ndn, &op->o_req_ndn ); + if ( isroot ) { + LDAP_BACK_CONN_ISPRIV_SET( mc ); + LDAP_BACK_PCONN_SET( mc, op ); + } lerr = avl_insert( &mi->mi_conninfo.lai_tree, (caddr_t)mc, meta_back_conndn_cmp, meta_back_conndn_dup ); +#if META_BACK_PRINT_CONNTREE > 0 + meta_back_print_conntree( mi, "<<< meta_back_bind" ); +#endif /* META_BACK_PRINT_CONNTREE */ ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex ); if ( lerr == -1 ) { - meta_clear_candidates( op, mc ); - /* we can do this because mc_refcnt == 1 */ + assert( mc->mc_refcnt == 1 ); mc->mc_refcnt = 0; meta_back_conn_free( mc ); mc = NULL; @@ -260,75 +302,65 @@ retry_lock:; return LDAP_SUCCESS; } -/* - * meta_back_single_bind - * - * attempts to perform a bind with creds - */ -int -meta_back_single_bind( +static int +meta_back_bind_op_result( Operation *op, SlapReply *rs, metaconn_t *mc, int candidate, - int massage ) + int msgid, + ldap_back_send_t sendok ) { metainfo_t *mi = ( metainfo_t * )op->o_bd->be_private; - metatarget_t *mt = &mi->mi_targets[ candidate ]; - struct berval mdn = BER_BVNULL; + metatarget_t *mt = mi->mi_targets[ candidate ]; metasingleconn_t *msc = &mc->mc_conns[ candidate ]; - int msgid, - rebinding = 0; + LDAPMessage *res; + struct timeval tv; + int rc; + int nretries = mt->mt_nretries; + char buf[ SLAP_TEXT_BUFLEN ]; - - if ( !BER_BVISNULL( &msc->msc_bound_ndn ) ) { - ch_free( msc->msc_bound_ndn.bv_val ); - BER_BVZERO( &msc->msc_bound_ndn ); - } + Debug( LDAP_DEBUG_TRACE, + ">>> %s meta_back_bind_op_result[%d]\n", + op->o_log_prefix, candidate, 0 ); - if ( LDAP_BACK_SAVECRED( mi ) && !BER_BVISNULL( &msc->msc_cred ) ) { - /* destroy sensitive data */ - memset( msc->msc_cred.bv_val, 0, msc->msc_cred.bv_len ); - ch_free( msc->msc_cred.bv_val ); - BER_BVZERO( &msc->msc_cred ); - } + if ( rs->sr_err == LDAP_SUCCESS ) { + time_t stoptime = (time_t)(-1), + timeout; + int timeout_err = op->o_protocol >= LDAP_VERSION3 ? + LDAP_ADMINLIMIT_EXCEEDED : LDAP_OTHER; + const char *timeout_text = "Operation timed out"; + slap_op_t opidx = slap_req2op( op->o_tag ); + + /* since timeout is not specified, compute and use + * the one specific to the ongoing operation */ + if ( opidx == LDAP_REQ_SEARCH ) { + if ( op->ors_tlimit <= 0 ) { + timeout = 0; + + } else { + timeout = op->ors_tlimit; + timeout_err = LDAP_TIMELIMIT_EXCEEDED; + timeout_text = NULL; + } - /* - * Rewrite the bind dn if needed - */ - if ( massage ) { - dncookie dc; + } else { + timeout = mt->mt_timeout[ opidx ]; + } - dc.target = mt; - dc.conn = op->o_conn; - dc.rs = rs; - dc.ctx = "bindDN"; + /* better than nothing :) */ + if ( timeout == 0 ) { + if ( mi->mi_idle_timeout ) { + timeout = mi->mi_idle_timeout; - if ( ldap_back_dn_massage( &dc, &op->o_req_dn, &mdn ) ) { - rs->sr_text = "DN rewrite error"; - rs->sr_err = LDAP_OTHER; - return rs->sr_err; + } else if ( mi->mi_conn_ttl ) { + timeout = mi->mi_conn_ttl; + } } - } else { - mdn = op->o_req_dn; - } - - /* FIXME: this fixes the bind problem right now; we need - * to use the asynchronous version to get the "matched" - * and more in case of failure ... */ - /* FIXME: should we check if at least some of the op->o_ctrls - * can/should be passed? */ -rebind:; - rs->sr_err = ldap_sasl_bind( msc->msc_ld, mdn.bv_val, - LDAP_SASL_SIMPLE, &op->orb_cred, - op->o_ctrls, NULL, &msgid ); - if ( rs->sr_err == LDAP_SUCCESS ) { - LDAPMessage *res; - struct timeval tv; - int rc; - int nretries = mt->mt_nretries; - char buf[ SLAP_TEXT_BUFLEN ]; + if ( timeout ) { + stoptime = op->o_time + timeout; + } LDAP_BACK_TV_SET( &tv ); @@ -336,16 +368,18 @@ rebind:; * handle response!!! */ retry:; - switch ( ldap_result( msc->msc_ld, msgid, LDAP_MSG_ALL, &tv, &res ) ) { + rc = ldap_result( msc->msc_ld, msgid, LDAP_MSG_ALL, &tv, &res ); + switch ( rc ) { case 0: - snprintf( buf, sizeof( buf ), - "ldap_result=0 nretries=%d%s", - nretries, rebinding ? " rebinding" : "" ); +#if 0 Debug( LDAP_DEBUG_ANY, - "%s meta_back_single_bind[%d]: %s.\n", - op->o_log_prefix, candidate, buf ); + "%s meta_back_bind_op_result[%d]: ldap_result=0 nretries=%d.\n", + op->o_log_prefix, candidate, nretries ); +#endif - if ( nretries != META_RETRY_NEVER ) { + if ( nretries != META_RETRY_NEVER + || ( timeout && slap_get_time() <= stoptime ) ) + { ldap_pvt_thread_yield(); if ( nretries > 0 ) { nretries--; @@ -354,50 +388,43 @@ retry:; goto retry; } - rs->sr_err = LDAP_BUSY; - if ( rebinding ) { - ldap_abandon_ext( msc->msc_ld, msgid, NULL, NULL ); - break; - } + /* don't let anyone else use this handler, + * because there's a pending bind that will not + * be acknowledged */ + ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex ); + assert( LDAP_BACK_CONN_BINDING( msc ) ); - /* FIXME: some times the request times out - * while the other party is not willing to - * send a response any more. Give it a second - * chance with a freshly bound connection */ - rebinding = 1; - nretries = mt->mt_nretries; - /* fallthru */ +#ifdef DEBUG_205 + Debug( LDAP_DEBUG_ANY, "### %s meta_back_bind_op_result ldap_unbind_ext[%d] ld=%p\n", + op->o_log_prefix, candidate, (void *)msc->msc_ld ); +#endif /* DEBUG_205 */ + + meta_clear_one_candidate( op, mc, candidate ); + ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex ); + + rs->sr_err = timeout_err; + rs->sr_text = timeout_text; + break; case -1: - ldap_get_option( msc->msc_ld, LDAP_OPT_ERROR_NUMBER, + ldap_get_option( msc->msc_ld, LDAP_OPT_RESULT_CODE, &rs->sr_err ); - if ( rebinding ) { - ldap_abandon_ext( msc->msc_ld, msgid, NULL, NULL ); - } - snprintf( buf, sizeof( buf ), "err=%d (%s) nretries=%d", rs->sr_err, ldap_err2string( rs->sr_err ), nretries ); Debug( LDAP_DEBUG_ANY, - "### %s meta_back_single_bind[%d]: %s.\n", + "### %s meta_back_bind_op_result[%d]: %s.\n", op->o_log_prefix, candidate, buf ); - - rc = slap_map_api2result( rs ); - if ( rs->sr_err == LDAP_UNAVAILABLE && nretries != META_RETRY_NEVER ) { - rc = meta_back_retry( op, rs, &mc, candidate, LDAP_BACK_DONTSEND ); - if ( rc ) { - if ( nretries > 0 ) { - nretries--; - } - ldap_pvt_thread_yield(); - goto rebind; - } - goto return_results; - } break; default: + /* only touch when activity actually took place... */ + if ( mi->mi_idle_timeout != 0 && msc->msc_time < op->o_time ) { + msc->msc_time = op->o_time; + } + + /* FIXME: matched? referrals? response controls? */ rc = ldap_parse_result( msc->msc_ld, res, &rs->sr_err, NULL, NULL, NULL, NULL, 1 ); if ( rc != LDAP_SUCCESS ) { @@ -407,22 +434,102 @@ retry:; } } + rs->sr_err = slap_map_api2result( rs ); + + Debug( LDAP_DEBUG_TRACE, + "<<< %s meta_back_bind_op_result[%d] err=%d\n", + op->o_log_prefix, candidate, rs->sr_err ); + + return rs->sr_err; +} + +/* + * meta_back_single_bind + * + * attempts to perform a bind with creds + */ +static int +meta_back_single_bind( + Operation *op, + SlapReply *rs, + metaconn_t *mc, + int candidate ) +{ + metainfo_t *mi = ( metainfo_t * )op->o_bd->be_private; + metatarget_t *mt = mi->mi_targets[ candidate ]; + struct berval mdn = BER_BVNULL; + metasingleconn_t *msc = &mc->mc_conns[ candidate ]; + int msgid; + dncookie dc; + + if ( !BER_BVISNULL( &msc->msc_bound_ndn ) ) { + ch_free( msc->msc_bound_ndn.bv_val ); + BER_BVZERO( &msc->msc_bound_ndn ); + } + + if ( !BER_BVISNULL( &msc->msc_cred ) ) { + /* destroy sensitive data */ + memset( msc->msc_cred.bv_val, 0, msc->msc_cred.bv_len ); + ch_free( msc->msc_cred.bv_val ); + BER_BVZERO( &msc->msc_cred ); + } + + /* + * Rewrite the bind dn if needed + */ + dc.target = mt; + dc.conn = op->o_conn; + dc.rs = rs; + dc.ctx = "bindDN"; + + if ( ldap_back_dn_massage( &dc, &op->o_req_dn, &mdn ) ) { + rs->sr_text = "DN rewrite error"; + rs->sr_err = LDAP_OTHER; + return rs->sr_err; + } + + /* FIXME: this fixes the bind problem right now; we need + * to use the asynchronous version to get the "matched" + * and more in case of failure ... */ + /* FIXME: should we check if at least some of the op->o_ctrls + * can/should be passed? */ + rs->sr_err = ldap_sasl_bind( msc->msc_ld, mdn.bv_val, + LDAP_SASL_SIMPLE, &op->orb_cred, + op->o_ctrls, NULL, &msgid ); + meta_back_bind_op_result( op, rs, mc, candidate, msgid, LDAP_BACK_DONTSEND ); if ( rs->sr_err != LDAP_SUCCESS ) { - rs->sr_err = slap_map_api2result( rs ); goto return_results; } - ber_bvreplace( &msc->msc_bound_ndn, &op->o_req_dn ); + /* If defined, proxyAuthz will be used also when + * back-ldap is the authorizing backend; for this + * purpose, a successful bind is followed by a + * bind with the configured identity assertion */ + /* NOTE: use with care */ + if ( mt->mt_idassert_flags & LDAP_BACK_AUTH_OVERRIDE ) { + meta_back_proxy_authz_bind( mc, candidate, op, rs, LDAP_BACK_SENDERR ); + if ( !LDAP_BACK_CONN_ISBOUND( msc ) ) { + goto return_results; + } + goto cache_refresh; + } + + ber_bvreplace( &msc->msc_bound_ndn, &op->o_req_ndn ); LDAP_BACK_CONN_ISBOUND_SET( msc ); mc->mc_authz_target = candidate; if ( LDAP_BACK_SAVECRED( mi ) ) { + if ( !BER_BVISNULL( &msc->msc_cred ) ) { + memset( msc->msc_cred.bv_val, 0, + msc->msc_cred.bv_len ); + } ber_bvreplace( &msc->msc_cred, &op->orb_cred ); - ldap_set_rebind_proc( msc->msc_ld, meta_back_rebind_f, msc ); + ldap_set_rebind_proc( msc->msc_ld, mt->mt_rebind_f, msc ); } +cache_refresh:; if ( mi->mi_cache.ttl != META_DNCACHE_DISABLED - && op->o_req_ndn.bv_len != 0 ) + && !BER_BVISEMPTY( &op->o_req_ndn ) ) { ( void )meta_dncache_update_entry( &mi->mi_cache, &op->o_req_ndn, candidate ); @@ -433,6 +540,10 @@ return_results:; free( mdn.bv_val ); } + if ( META_BACK_TGT_QUARANTINE( mt ) ) { + meta_back_quarantine( op, rs, candidate ); + } + return rs->sr_err; } @@ -450,180 +561,59 @@ meta_back_single_dobind( int dolock ) { metainfo_t *mi = ( metainfo_t * )op->o_bd->be_private; - metatarget_t *mt = &mi->mi_targets[ candidate ]; + metatarget_t *mt = mi->mi_targets[ candidate ]; metaconn_t *mc = *mcp; metasingleconn_t *msc = &mc->mc_conns[ candidate ]; int rc; static struct berval cred = BER_BVC( "" ); - int msgid, - rebinding = 0, - save_nretries = nretries; + int msgid; assert( !LDAP_BACK_CONN_ISBOUND( msc ) ); - /* - * meta_back_single_dobind() calls meta_back_single_bind() - * if required. - */ - if ( be_isroot( op ) && !BER_BVISNULL( &mi->mi_targets[ candidate ].mt_pseudorootdn ) ) + /* NOTE: this obsoletes pseudorootdn */ + if ( op->o_conn != NULL && + !op->o_do_not_cache && + ( BER_BVISNULL( &msc->msc_bound_ndn ) || + BER_BVISEMPTY( &msc->msc_bound_ndn ) || + ( LDAP_BACK_CONN_ISPRIV( mc ) && dn_match( &msc->msc_bound_ndn, &mt->mt_idassert_authcDN ) ) || + ( mt->mt_idassert_flags & LDAP_BACK_AUTH_OVERRIDE ) ) ) { - Operation op2 = *op; - - op2.o_tag = LDAP_REQ_BIND; - op2.o_req_dn = mi->mi_targets[ candidate ].mt_pseudorootdn; - op2.o_req_ndn = mi->mi_targets[ candidate ].mt_pseudorootdn; - op2.orb_cred = mi->mi_targets[ candidate ].mt_pseudorootpw; - op2.orb_method = LDAP_AUTH_SIMPLE; - - rc = meta_back_single_bind( &op2, rs, *mcp, candidate, 0 ); + (void)meta_back_proxy_authz_bind( mc, candidate, op, rs, sendok ); + rc = rs->sr_err; goto done; } - /* - * Otherwise an anonymous bind is performed - * (note: if the target was already bound, the anonymous - * bind clears the previous bind). - */ - if ( !BER_BVISNULL( &msc->msc_bound_ndn ) ) { - ber_memfree( msc->msc_bound_ndn.bv_val ); - BER_BVZERO( &msc->msc_bound_ndn ); - } - - if ( LDAP_BACK_SAVECRED( mi ) && !BER_BVISNULL( &msc->msc_cred ) ) { - /* destroy sensitive data */ - memset( msc->msc_cred.bv_val, 0, msc->msc_cred.bv_len ); - ber_memfree( msc->msc_cred.bv_val ); - BER_BVZERO( &msc->msc_cred ); - } - /* FIXME: should we check if at least some of the op->o_ctrls * can/should be passed? */ -rebind:; - rc = ldap_sasl_bind( msc->msc_ld, "", LDAP_SASL_SIMPLE, &cred, + rs->sr_err = ldap_sasl_bind( msc->msc_ld, "", LDAP_SASL_SIMPLE, &cred, NULL, NULL, &msgid ); - if ( rc == LDAP_SUCCESS ) { - LDAPMessage *res; - struct timeval tv; - char buf[ SLAP_TEXT_BUFLEN ]; - - LDAP_BACK_TV_SET( &tv ); - - /* - * handle response!!! - */ -retry:; - switch ( ldap_result( msc->msc_ld, msgid, LDAP_MSG_ALL, &tv, &res ) ) { - case 0: - snprintf( buf, sizeof( buf ), - "ldap_result=0 nretries=%d%s", - nretries, rebinding ? " rebinding" : "" ); - Debug( LDAP_DEBUG_ANY, - "%s meta_back_single_dobind[%d]: %s.\n", - op->o_log_prefix, candidate, buf ); - - if ( nretries != META_RETRY_NEVER ) { - ldap_pvt_thread_yield(); - if ( nretries > 0 ) { - nretries--; - } - tv = mt->mt_bind_timeout; - goto retry; - } - - rc = LDAP_BUSY; - if ( rebinding ) { - ldap_abandon_ext( msc->msc_ld, msgid, NULL, NULL ); - break; - } - - /* FIXME: some times the request times out - * while the other party is not willing to - * send a response any more. Give it a second - * chance with a freshly bound connection */ - rebinding = 1; - nretries = save_nretries; - /* fallthru */ - - case -1: - ldap_get_option( msc->msc_ld, LDAP_OPT_ERROR_NUMBER, - &rs->sr_err ); - - if ( rebinding ) { - ldap_abandon_ext( msc->msc_ld, msgid, NULL, NULL ); - } - - snprintf( buf, sizeof( buf ), - "err=%d (%s) nretries=%d", - rs->sr_err, ldap_err2string( rs->sr_err ), nretries ); - Debug( LDAP_DEBUG_ANY, - "### %s meta_back_single_dobind[%d]: %s.\n", - op->o_log_prefix, candidate, buf ); - - rc = slap_map_api2result( rs ); - if ( rc == LDAP_UNAVAILABLE && nretries != META_RETRY_NEVER ) { - if ( dolock ) { - ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex ); - } - - if ( mc->mc_refcnt == 1 ) { - meta_clear_one_candidate( msc ); - LDAP_BACK_CONN_ISBOUND_CLEAR( msc ); - - ( void )rewrite_session_delete( mt->mt_rwmap.rwm_rw, op->o_conn ); - - /* mc here must be the regular mc, - * reset and ready for init */ - rc = meta_back_init_one_conn( op, rs, - mt, mc, candidate, - LDAP_BACK_CONN_ISPRIV( mc ), - LDAP_BACK_DONTSEND ); - LDAP_BACK_CONN_BINDING_SET( msc ); - - } else { - /* can't do anything about it */ - rc = LDAP_UNAVAILABLE; - } - - if ( dolock ) { - ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex ); - } - - if ( rc == LDAP_SUCCESS ) { - ldap_pvt_thread_yield(); - if ( nretries > 0 ) { - nretries--; - } - goto rebind; - } - } - break; - - default: - rc = ldap_parse_result( msc->msc_ld, res, &rs->sr_err, - NULL, NULL, NULL, NULL, 1 ); - if ( rc == LDAP_SUCCESS ) { - rc = slap_map_api2result( rs ); - } - break; - } - - } else { - rs->sr_err = rc; - rc = slap_map_api2result( rs ); - } + rc = meta_back_bind_op_result( op, rs, mc, candidate, msgid, sendok ); done:; rs->sr_err = rc; - if ( rc != LDAP_SUCCESS && META_BACK_ONERR_STOP( mi ) ) { + if ( rc != LDAP_SUCCESS ) { + if ( dolock ) { + ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex ); + } LDAP_BACK_CONN_BINDING_CLEAR( msc ); - meta_back_release_conn_lock( op, mc, 1, dolock ); - *mcp = NULL; + if ( META_BACK_ONERR_STOP( mi ) ) { + LDAP_BACK_CONN_TAINTED_SET( mc ); + meta_back_release_conn_lock( op, mc, 0 ); + *mcp = NULL; + } + if ( dolock ) { + ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex ); + } - if ( sendok & LDAP_BACK_SENDERR ) { + if ( META_BACK_ONERR_STOP( mi ) && ( sendok & LDAP_BACK_SENDERR ) ) { send_ldap_result( op, rs ); } } + if ( META_BACK_TGT_QUARANTINE( mt ) ) { + meta_back_quarantine( op, rs, candidate ); + } + return rc; } @@ -652,7 +642,7 @@ meta_back_dobind( Debug( LDAP_DEBUG_TRACE, "%s meta_back_dobind: conn=%ld%s\n", op->o_log_prefix, - LDAP_BACK_PCONN_ID( mc->mc_conn ), + LDAP_BACK_PCONN_ID( mc ), isroot ? " (isroot)" : "" ); /* @@ -664,15 +654,14 @@ meta_back_dobind( } for ( i = 0; i < mi->mi_ntargets; i++ ) { - metatarget_t *mt = &mi->mi_targets[ i ]; + metatarget_t *mt = mi->mi_targets[ i ]; metasingleconn_t *msc = &mc->mc_conns[ i ]; - int rc, do_retry = 1; - char *rootdn = NULL; + int rc; /* * Not a candidate */ - if ( candidates[ i ].sr_tag != META_CANDIDATE ) { + if ( !META_IS_CANDIDATE( &candidates[ i ] ) ) { continue; } @@ -684,22 +673,25 @@ meta_back_dobind( retry_binding:; ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex ); - if ( LDAP_BACK_CONN_ISBOUND( msc ) || LDAP_BACK_CONN_ISANON( msc ) ) { + if ( LDAP_BACK_CONN_ISBOUND( msc ) + || ( LDAP_BACK_CONN_ISANON( msc ) + && mt->mt_idassert_authmethod == LDAP_AUTH_NONE ) ) + { ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex ); ++bound; continue; - } else if ( LDAP_BACK_CONN_BINDING( msc ) ) { + } else if ( META_BACK_CONN_CREATING( msc ) || LDAP_BACK_CONN_BINDING( msc ) ) + { ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex ); ldap_pvt_thread_yield(); goto retry_binding; - } else { - LDAP_BACK_CONN_BINDING_SET( msc ); - ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex ); - } + } + + LDAP_BACK_CONN_BINDING_SET( msc ); + ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex ); -retry:; rc = meta_back_single_dobind( op, rs, &mc, i, LDAP_BACK_DONTSEND, mt->mt_nretries, 1 ); /* @@ -716,14 +708,19 @@ retry:; } - if ( rc == LDAP_UNAVAILABLE && do_retry ) { - do_retry = 0; + if ( rc == LDAP_UNAVAILABLE ) { + /* FIXME: meta_back_retry() already re-calls + * meta_back_single_dobind() */ if ( meta_back_retry( op, rs, &mc, i, sendok ) ) { - goto retry; + goto retry_ok; + } + + if ( mc != NULL ) { + ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex ); + LDAP_BACK_CONN_BINDING_CLEAR( msc ); + ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex ); + meta_back_release_conn( op, mc ); } - ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex ); - LDAP_BACK_CONN_BINDING_CLEAR( msc ); - ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex ); return 0; } @@ -734,7 +731,7 @@ retry:; snprintf( buf, sizeof( buf ), "meta_back_dobind[%d]: (%s) err=%d (%s).", - i, rootdn ? rootdn : "anonymous", + i, isroot ? op->o_bd->be_rootdn.bv_val : "anonymous", rc, ldap_err2string( rc ) ); Debug( LDAP_DEBUG_ANY, "%s %s\n", @@ -756,16 +753,17 @@ retry:; continue; } /* else */ - + +retry_ok:; Debug( LDAP_DEBUG_TRACE, "%s meta_back_dobind[%d]: " "(%s)\n", op->o_log_prefix, i, - rootdn ? rootdn : "anonymous" ); + isroot ? op->o_bd->be_rootdn.bv_val : "anonymous" ); ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex ); LDAP_BACK_CONN_BINDING_CLEAR( msc ); - if ( rootdn ) { + if ( isroot ) { LDAP_BACK_CONN_ISBOUND_SET( msc ); } else { LDAP_BACK_CONN_ISANON_SET( msc ); @@ -777,7 +775,7 @@ retry:; done:; Debug( LDAP_DEBUG_TRACE, "%s meta_back_dobind: conn=%ld bound=%d\n", - op->o_log_prefix, LDAP_BACK_PCONN_ID( mc->mc_conn ), bound ); + op->o_log_prefix, LDAP_BACK_PCONN_ID( mc ), bound ); if ( bound == 0 ) { meta_back_release_conn( op, mc ); @@ -802,7 +800,7 @@ send_err:; * This is a callback used for chasing referrals using the same * credentials as the original user on this session. */ -static int +int meta_back_default_rebind( LDAP *ld, LDAP_CONST char *url, @@ -817,68 +815,214 @@ meta_back_default_rebind( NULL, NULL, NULL ); } +int +meta_back_cancel( + metaconn_t *mc, + Operation *op, + SlapReply *rs, + ber_int_t msgid, + int candidate, + ldap_back_send_t sendok ) +{ + metainfo_t *mi = (metainfo_t *)op->o_bd->be_private; + + metatarget_t *mt = mi->mi_targets[ candidate ]; + metasingleconn_t *msc = &mc->mc_conns[ candidate ]; + + int rc = LDAP_OTHER; + + Debug( LDAP_DEBUG_TRACE, ">>> %s meta_back_cancel[%d] msgid=%d\n", + op->o_log_prefix, candidate, msgid ); + + /* default behavior */ + if ( META_BACK_TGT_ABANDON( mt ) ) { + rc = ldap_abandon_ext( msc->msc_ld, msgid, NULL, NULL ); + + } else if ( META_BACK_TGT_CANCEL( mt ) ) { + rc = ldap_cancel_s( msc->msc_ld, msgid, NULL, NULL ); + + } else { + assert( 0 ); + } + + Debug( LDAP_DEBUG_TRACE, "<<< %s meta_back_cancel[%d] err=%d\n", + op->o_log_prefix, candidate, rc ); + + return rc; +} + + + /* * FIXME: error return must be handled in a cleaner way ... */ int meta_back_op_result( - metaconn_t *mc, - Operation *op, - SlapReply *rs, - int candidate ) + metaconn_t *mc, + Operation *op, + SlapReply *rs, + int candidate, + ber_int_t msgid, + time_t timeout, + ldap_back_send_t sendok ) { - metainfo_t *mi = ( metainfo_t * )op->o_bd->be_private; + metainfo_t *mi = ( metainfo_t * )op->o_bd->be_private; + + const char *save_text = rs->sr_text, + *save_matched = rs->sr_matched; + BerVarray save_ref = rs->sr_ref; + LDAPControl **save_ctrls = rs->sr_ctrls; + void *matched_ctx = NULL; + + char *matched = NULL; + char *text = NULL; + char **refs = NULL; + LDAPControl **ctrls = NULL; + + assert( mc != NULL ); - int i, - rerr = LDAP_SUCCESS; - char *rmsg = NULL, - *rmatch = NULL; - const char *save_rmsg = NULL, - *save_rmatch = NULL; - void *rmatch_ctx = NULL; + rs->sr_text = NULL; + rs->sr_matched = NULL; + rs->sr_ref = NULL; + rs->sr_ctrls = NULL; if ( candidate != META_TARGET_NONE ) { + metatarget_t *mt = mi->mi_targets[ candidate ]; metasingleconn_t *msc = &mc->mc_conns[ candidate ]; - rs->sr_err = LDAP_SUCCESS; +#define ERR_OK(err) ((err) == LDAP_SUCCESS || (err) == LDAP_COMPARE_FALSE || (err) == LDAP_COMPARE_TRUE) + + if ( 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; + } - ldap_get_option( msc->msc_ld, LDAP_OPT_ERROR_NUMBER, &rs->sr_err ); - if ( rs->sr_err != LDAP_SUCCESS ) { - /* - * better check the type of error. In some cases - * (search ?) it might be better to return a - * success if at least one of the targets gave - * positive result ... - */ - ldap_get_option( msc->msc_ld, - LDAP_OPT_ERROR_STRING, &rmsg ); - if ( rmsg != NULL && rmsg[ 0 ] == '\0' ) { - ldap_memfree( rmsg ); - rmsg = NULL; + } else { + timeout = mt->mt_timeout[ opidx ]; + } + } + + /* better than nothing :) */ + if ( timeout == 0 ) { + if ( mi->mi_idle_timeout ) { + timeout = mi->mi_idle_timeout; + + } else if ( mi->mi_conn_ttl ) { + timeout = mi->mi_conn_ttl; + } } - ldap_get_option( msc->msc_ld, - LDAP_OPT_MATCHED_DN, &rmatch ); - if ( rmatch != NULL && rmatch[ 0 ] == '\0' ) { - ldap_memfree( rmatch ); - rmatch = NULL; + if ( timeout ) { + stoptime = op->o_time + timeout; + } + + LDAP_BACK_TV_SET( &tv ); + +retry:; + rc = ldap_result( msc->msc_ld, msgid, LDAP_MSG_ALL, &tv, &res ); + switch ( rc ) { + case 0: + if ( timeout && slap_get_time() > stoptime ) { + (void)meta_back_cancel( mc, op, rs, msgid, candidate, sendok ); + rs->sr_err = timeout_err; + rs->sr_text = timeout_text; + break; + } + + LDAP_BACK_TV_SET( &tv ); + ldap_pvt_thread_yield(); + goto retry; + + case -1: + ldap_get_option( msc->msc_ld, LDAP_OPT_RESULT_CODE, + &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 ( mi->mi_idle_timeout != 0 && msc->msc_time < op->o_time ) { + msc->msc_time = op->o_time; + } + + rc = ldap_parse_result( msc->msc_ld, res, &rs->sr_err, + &matched, &text, &refs, &ctrls, 1 ); + res = NULL; + rs->sr_text = text; + if ( rc != LDAP_SUCCESS ) { + rs->sr_err = rc; + } + if ( refs != NULL ) { + 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 ] ); + } + if ( ctrls != NULL ) { + rs->sr_ctrls = ctrls; + } } - rerr = rs->sr_err = slap_map_api2result( rs ); + assert( res == NULL ); + } + + /* if the error in the reply structure is not + * LDAP_SUCCESS, try to map it from client + * to server error */ + if ( !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 && matched ) { - Debug(LDAP_DEBUG_ANY, - "==> meta_back_op_result: target" - " <%d> sending msg \"%s\"" - " (matched \"%s\")\n", - candidate, ( rmsg ? rmsg : "" ), - ( rmatch ? rmatch : "" ) ); + /* record the (massaged) matched + * DN into the reply structure */ + rs->sr_matched = matched; + } + } + + if ( META_BACK_TGT_QUARANTINE( mt ) ) { + meta_back_quarantine( op, rs, candidate ); } } else { + int i, + err = rs->sr_err; + for ( i = 0; i < mi->mi_ntargets; i++ ) { metasingleconn_t *msc = &mc->mc_conns[ i ]; - char *msg = NULL; - char *match = NULL; + char *xtext = NULL; + char *xmatched = NULL; rs->sr_err = LDAP_SUCCESS; @@ -891,89 +1035,415 @@ meta_back_op_result( * positive result ... */ ldap_get_option( msc->msc_ld, - LDAP_OPT_ERROR_STRING, &msg ); - if ( msg != NULL && msg[ 0 ] == '\0' ) { - ldap_memfree( msg ); - msg = NULL; + LDAP_OPT_ERROR_STRING, &xtext ); + if ( xtext != NULL && xtext [ 0 ] == '\0' ) { + ldap_memfree( xtext ); + xtext = NULL; } ldap_get_option( msc->msc_ld, - LDAP_OPT_MATCHED_DN, &match ); - if ( match != NULL && match[ 0 ] == '\0' ) { - ldap_memfree( match ); - match = NULL; + LDAP_OPT_MATCHED_DN, &xmatched ); + if ( xmatched != NULL && xmatched[ 0 ] == '\0' ) { + ldap_memfree( xmatched ); + xmatched = NULL; } rs->sr_err = slap_map_api2result( rs ); - Debug(LDAP_DEBUG_ANY, - "==> meta_back_op_result: target" - " <%d> sending msg \"%s\"" - " (matched \"%s\")\n", - i, ( msg ? msg : "" ), - ( match ? match : "" ) ); - + if ( StatslogTest( LDAP_DEBUG_ANY ) ) { + char buf[ SLAP_TEXT_BUFLEN ]; + + snprintf( buf, sizeof( buf ), + "meta_back_op_result[%d] " + "err=%d text=\"%s\" matched=\"%s\"", + i, rs->sr_err, + ( xtext ? xtext : "" ), + ( xmatched ? xmatched : "" ) ); + Debug( LDAP_DEBUG_ANY, "%s %s.\n", + op->o_log_prefix, buf, 0 ); + } + /* * FIXME: need to rewrite "match" (need rwinfo) */ switch ( rs->sr_err ) { default: - rerr = rs->sr_err; - if ( msg != NULL ) { - if ( rmsg ) { - ldap_memfree( rmsg ); + err = rs->sr_err; + if ( xtext != NULL ) { + if ( text ) { + ldap_memfree( text ); } - rmsg = msg; - msg = NULL; + text = xtext; + xtext = NULL; } - if ( match != NULL ) { - if ( rmatch ) { - ldap_memfree( rmatch ); + if ( xmatched != NULL ) { + if ( matched ) { + ldap_memfree( matched ); } - rmatch = match; - match = NULL; + matched = xmatched; + xmatched = NULL; } break; } - if ( msg ) { - ldap_memfree( msg ); + if ( xtext ) { + ldap_memfree( xtext ); } - if ( match ) { - ldap_memfree( match ); + if ( xmatched ) { + ldap_memfree( xmatched ); } } + + if ( META_BACK_TGT_QUARANTINE( mi->mi_targets[ i ] ) ) { + meta_back_quarantine( op, rs, i ); + } + } + + if ( err != LDAP_SUCCESS ) { + rs->sr_err = err; } } - - rs->sr_err = rerr; - if ( rmsg != NULL ) { - save_rmsg = rs->sr_text; - rs->sr_text = rmsg; - } - if ( rmatch != NULL ) { + + if ( matched != NULL ) { struct berval dn, pdn; - ber_str2bv( rmatch, 0, 0, &dn ); + ber_str2bv( matched, 0, 0, &dn ); if ( dnPretty( NULL, &dn, &pdn, op->o_tmpmemctx ) == LDAP_SUCCESS ) { - ldap_memfree( rmatch ); - rmatch_ctx = op->o_tmpmemctx; - rmatch = pdn.bv_val; + ldap_memfree( matched ); + matched_ctx = op->o_tmpmemctx; + matched = pdn.bv_val; } - save_rmatch = rs->sr_matched; - rs->sr_matched = rmatch; + rs->sr_matched = matched; + } + + if ( op->o_conn && + ( ( sendok & LDAP_BACK_SENDOK ) + || ( ( sendok & LDAP_BACK_SENDERR ) && rs->sr_err != LDAP_SUCCESS ) ) ) + { + send_ldap_result( op, rs ); } - send_ldap_result( op, rs ); - if ( rmsg != NULL ) { - ber_memfree( rmsg ); - rs->sr_text = save_rmsg; + if ( matched ) { + op->o_tmpfree( (char *)rs->sr_matched, matched_ctx ); } - if ( rmatch != NULL ) { - ber_memfree_x( rmatch, rmatch_ctx ); - rs->sr_matched = save_rmatch; + if ( text ) { + ldap_memfree( text ); + } + if ( rs->sr_ref ) { + assert( refs != NULL ); + ber_memvfree( (void **)refs ); + op->o_tmpfree( rs->sr_ref, op->o_tmpmemctx ); + } + if ( ctrls ) { + assert( rs->sr_ctrls != NULL ); + ldap_controls_free( ctrls ); } - return ( ( rerr == LDAP_SUCCESS ) ? 0 : -1 ); + rs->sr_text = save_text; + rs->sr_matched = save_matched; + rs->sr_ref = save_ref; + rs->sr_ctrls = save_ctrls; + + return( ERR_OK( rs->sr_err ) ? LDAP_SUCCESS : rs->sr_err ); } +/* + * meta_back_proxy_authz_cred() + * + * prepares credentials & method for meta_back_proxy_authz_bind(); + * or, if method is SASL, performs the SASL bind directly. + */ +int +meta_back_proxy_authz_cred( + metaconn_t *mc, + int candidate, + Operation *op, + SlapReply *rs, + ldap_back_send_t sendok, + struct berval *binddn, + struct berval *bindcred, + int *method ) +{ + metainfo_t *mi = (metainfo_t *)op->o_bd->be_private; + metatarget_t *mt = mi->mi_targets[ candidate ]; + metasingleconn_t *msc = &mc->mc_conns[ candidate ]; + struct berval ndn; + int dobind = 0; + + /* don't proxyAuthz if protocol is not LDAPv3 */ + switch ( mt->mt_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 ); + } + LDAP_BACK_CONN_ISBOUND_CLEAR( msc ); + goto done; + } + + if ( op->o_tag == LDAP_REQ_BIND ) { + ndn = op->o_req_ndn; + + } else if ( !BER_BVISNULL( &op->o_conn->c_ndn ) ) { + ndn = op->o_conn->c_ndn; + + } else { + ndn = op->o_ndn; + } + + /* + * 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 "proxyauthzdn" is set, then bind as + * "proxyauthzdn" and explicitly add the proxyAuthz + * control to every operation with the dn bound + * to the connection as control value. + */ + + /* bind as proxyauthzdn only if no idassert mode + * is requested, or if the client's identity + * is authorized */ + switch ( mt->mt_idassert_mode ) { + case LDAP_BACK_IDASSERT_LEGACY: + if ( !BER_BVISNULL( &ndn ) && !BER_BVISEMPTY( &ndn ) ) { + if ( !BER_BVISNULL( &mt->mt_idassert_authcDN ) && !BER_BVISEMPTY( &mt->mt_idassert_authcDN ) ) + { + *binddn = mt->mt_idassert_authcDN; + *bindcred = mt->mt_idassert_passwd; + dobind = 1; + } + } + break; + + default: + /* NOTE: rootdn can always idassert */ + if ( BER_BVISNULL( &ndn ) && mt->mt_idassert_authz == NULL ) { + if ( mt->mt_idassert_flags & LDAP_BACK_AUTH_PRESCRIPTIVE ) { + rs->sr_err = LDAP_INAPPROPRIATE_AUTH; + if ( sendok & LDAP_BACK_SENDERR ) { + send_ldap_result( op, rs ); + } + LDAP_BACK_CONN_ISBOUND_CLEAR( msc ); + + } else { + rs->sr_err = LDAP_SUCCESS; + *binddn = slap_empty_bv; + *bindcred = slap_empty_bv; + break; + } + + goto done; + + } else if ( mt->mt_idassert_authz && !be_isroot( op ) ) { + struct berval authcDN; + + if ( BER_BVISNULL( &ndn ) ) { + authcDN = slap_empty_bv; + + } else { + authcDN = ndn; + } + rs->sr_err = slap_sasl_matches( op, mt->mt_idassert_authz, + &authcDN, &authcDN ); + if ( rs->sr_err != LDAP_SUCCESS ) { + if ( mt->mt_idassert_flags & LDAP_BACK_AUTH_PRESCRIPTIVE ) { + if ( sendok & LDAP_BACK_SENDERR ) { + send_ldap_result( op, rs ); + } + LDAP_BACK_CONN_ISBOUND_CLEAR( msc ); + + } else { + rs->sr_err = LDAP_SUCCESS; + *binddn = slap_empty_bv; + *bindcred = slap_empty_bv; + break; + } + + goto done; + } + } + + *binddn = mt->mt_idassert_authcDN; + *bindcred = mt->mt_idassert_passwd; + dobind = 1; + break; + } + + if ( dobind && mt->mt_idassert_authmethod == LDAP_AUTH_SASL ) { +#ifdef HAVE_CYRUS_SASL + void *defaults = NULL; + struct berval authzID = BER_BVNULL; + int freeauthz = 0; + + /* if SASL supports native authz, prepare for it */ + if ( ( !op->o_do_not_cache || !op->o_is_auth_check ) && + ( mt->mt_idassert_flags & LDAP_BACK_AUTH_NATIVE_AUTHZ ) ) + { + switch ( mt->mt_idassert_mode ) { + case LDAP_BACK_IDASSERT_OTHERID: + case LDAP_BACK_IDASSERT_OTHERDN: + authzID = mt->mt_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 ( mt->mt_idassert_secprops != NULL ) { + rs->sr_err = ldap_set_option( msc->msc_ld, + LDAP_OPT_X_SASL_SECPROPS, + (void *)mt->mt_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( msc ); + goto done; + } + } + + defaults = lutil_sasl_defaults( msc->msc_ld, + mt->mt_idassert_sasl_mech.bv_val, + mt->mt_idassert_sasl_realm.bv_val, + mt->mt_idassert_authcID.bv_val, + mt->mt_idassert_passwd.bv_val, + authzID.bv_val ); + + rs->sr_err = ldap_sasl_interactive_bind_s( msc->msc_ld, binddn->bv_val, + mt->mt_idassert_sasl_mech.bv_val, NULL, NULL, + LDAP_SASL_QUIET, lutil_sasl_interact, + defaults ); + + rs->sr_err = slap_map_api2result( rs ); + if ( rs->sr_err != LDAP_SUCCESS ) { + LDAP_BACK_CONN_ISBOUND_CLEAR( msc ); + if ( sendok & LDAP_BACK_SENDERR ) { + send_ldap_result( op, rs ); + } + + } else { + LDAP_BACK_CONN_ISBOUND_SET( msc ); + } + + lutil_sasl_freedefs( defaults ); + if ( freeauthz ) { + slap_sl_free( authzID.bv_val, op->o_tmpmemctx ); + } + + goto done; +#endif /* HAVE_CYRUS_SASL */ + } + + *method = mt->mt_idassert_authmethod; + switch ( mt->mt_idassert_authmethod ) { + case LDAP_AUTH_NONE: + BER_BVSTR( binddn, "" ); + BER_BVSTR( bindcred, "" ); + /* fallthru */ + + case LDAP_AUTH_SIMPLE: + break; + + default: + /* unsupported! */ + LDAP_BACK_CONN_ISBOUND_CLEAR( msc ); + rs->sr_err = LDAP_AUTH_METHOD_NOT_SUPPORTED; + if ( sendok & LDAP_BACK_SENDERR ) { + send_ldap_result( op, rs ); + } + break; + } + +done:; + return rs->sr_err; +} + +static int +meta_back_proxy_authz_bind( metaconn_t *mc, int candidate, Operation *op, SlapReply *rs, ldap_back_send_t sendok ) +{ + metainfo_t *mi = (metainfo_t *)op->o_bd->be_private; + metatarget_t *mt = mi->mi_targets[ candidate ]; + metasingleconn_t *msc = &mc->mc_conns[ candidate ]; + struct berval binddn = BER_BVC( "" ), + cred = BER_BVC( "" ); + int method = LDAP_AUTH_NONE, + rc; + + rc = meta_back_proxy_authz_cred( mc, candidate, op, rs, sendok, &binddn, &cred, &method ); + if ( rc == LDAP_SUCCESS && !LDAP_BACK_CONN_ISBOUND( msc ) ) { + int msgid; + + switch ( method ) { + case LDAP_AUTH_NONE: + case LDAP_AUTH_SIMPLE: + rs->sr_err = ldap_sasl_bind( msc->msc_ld, + binddn.bv_val, LDAP_SASL_SIMPLE, + &cred, NULL, NULL, &msgid ); + rc = meta_back_bind_op_result( op, rs, mc, candidate, msgid, sendok ); + 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( msc ); + ber_bvreplace( &msc->msc_bound_ndn, &binddn ); + + if ( LDAP_BACK_SAVECRED( mi ) ) { + if ( !BER_BVISNULL( &msc->msc_cred ) ) { + memset( msc->msc_cred.bv_val, 0, + msc->msc_cred.bv_len ); + } + ber_bvreplace( &msc->msc_cred, &cred ); + ldap_set_rebind_proc( msc->msc_ld, mt->mt_rebind_f, msc ); + } + } + break; + + default: + assert( 0 ); + break; + } + } + + return LDAP_BACK_CONN_ISBOUND( msc ); +} diff --git a/servers/slapd/back-meta/candidates.c b/servers/slapd/back-meta/candidates.c index a7b2aef2da..7d3b6b7c52 100644 --- a/servers/slapd/back-meta/candidates.c +++ b/servers/slapd/back-meta/candidates.c @@ -59,40 +59,38 @@ */ int meta_back_is_candidate( - struct berval *nsuffix, - int suffixscope, - BerVarray subtree_exclude, + metatarget_t *mt, struct berval *ndn, int scope ) { - if ( dnIsSuffix( ndn, nsuffix ) ) { - if ( subtree_exclude ) { + if ( dnIsSuffix( ndn, &mt->mt_nsuffix ) ) { + if ( mt->mt_subtree_exclude ) { int i; - for ( i = 0; !BER_BVISNULL( &subtree_exclude[ i ] ); i++ ) { - if ( dnIsSuffix( ndn, &subtree_exclude[ i ] ) ) { + for ( i = 0; !BER_BVISNULL( &mt->mt_subtree_exclude[ i ] ); i++ ) { + if ( dnIsSuffix( ndn, &mt->mt_subtree_exclude[ i ] ) ) { return META_NOT_CANDIDATE; } } } - switch ( suffixscope ) { + switch ( mt->mt_scope ) { case LDAP_SCOPE_SUBTREE: default: return META_CANDIDATE; case LDAP_SCOPE_SUBORDINATE: - if ( ndn->bv_len > nsuffix->bv_len ) { + if ( ndn->bv_len > mt->mt_nsuffix.bv_len ) { return META_CANDIDATE; } break; /* nearly useless; not allowed by config */ case LDAP_SCOPE_ONELEVEL: - if ( ndn->bv_len > nsuffix->bv_len ) { + if ( ndn->bv_len > mt->mt_nsuffix.bv_len ) { struct berval rdn = *ndn; - rdn.bv_len -= nsuffix->bv_len + rdn.bv_len -= mt->mt_nsuffix.bv_len + STRLENOF( "," ); if ( dnIsOneLevelRDN( &rdn ) ) { return META_CANDIDATE; @@ -102,7 +100,7 @@ meta_back_is_candidate( /* nearly useless; not allowed by config */ case LDAP_SCOPE_BASE: - if ( ndn->bv_len == nsuffix->bv_len ) { + if ( ndn->bv_len == mt->mt_nsuffix.bv_len ) { return META_CANDIDATE; } break; @@ -111,7 +109,7 @@ meta_back_is_candidate( return META_NOT_CANDIDATE; } - if ( scope == LDAP_SCOPE_SUBTREE && dnIsSuffix( nsuffix, ndn ) ) { + if ( scope == LDAP_SCOPE_SUBTREE && dnIsSuffix( &mt->mt_nsuffix, ndn ) ) { /* * suffix longer than dn, but common part matches */ @@ -136,12 +134,10 @@ meta_back_select_unique_candidate( { int i, candidate = META_TARGET_NONE; - for ( i = 0; i < mi->mi_ntargets; ++i ) { - if ( meta_back_is_candidate( &mi->mi_targets[ i ].mt_nsuffix, - mi->mi_targets[ i ].mt_scope, - mi->mi_targets[ i ].mt_subtree_exclude, - ndn, LDAP_SCOPE_BASE ) ) - { + for ( i = 0; i < mi->mi_ntargets; i++ ) { + metatarget_t *mt = mi->mi_targets[ i ]; + + if ( meta_back_is_candidate( mt, ndn, LDAP_SCOPE_BASE ) ) { if ( candidate == META_TARGET_NONE ) { candidate = i; @@ -172,7 +168,7 @@ meta_clear_unused_candidates( if ( i == candidate ) { continue; } - candidates[ i ].sr_tag = META_NOT_CANDIDATE; + META_CANDIDATE_RESET( &candidates[ i ] ); } return 0; @@ -185,9 +181,23 @@ meta_clear_unused_candidates( */ int meta_clear_one_candidate( - metasingleconn_t *msc ) + Operation *op, + metaconn_t *mc, + int candidate ) { - if ( msc->msc_ld ) { + metasingleconn_t *msc = &mc->mc_conns[ candidate ]; + + if ( msc->msc_ld != NULL ) { + +#ifdef DEBUG_205 + char buf[ BUFSIZ ]; + + snprintf( buf, sizeof( buf ), "meta_clear_one_candidate ldap_unbind_ext[%d] mc=%p ld=%p", + candidate, (void *)mc, (void *)msc->msc_ld ); + Debug( LDAP_DEBUG_ANY, "### %s %s\n", + op ? op->o_log_prefix : "", buf, 0 ); +#endif /* DEBUG_205 */ + ldap_unbind_ext( msc->msc_ld, NULL, NULL ); msc->msc_ld = NULL; } @@ -203,25 +213,8 @@ meta_clear_one_candidate( BER_BVZERO( &msc->msc_cred ); } - return 0; -} - -/* - * meta_clear_candidates - * - * clears all candidates - */ -int -meta_clear_candidates( Operation *op, metaconn_t *mc ) -{ - metainfo_t *mi = ( metainfo_t * )op->o_bd->be_private; - int c; - - for ( c = 0; c < mi->mi_ntargets; c++ ) { - if ( mc->mc_conns[ c ].msc_ld != NULL ) { - meta_clear_one_candidate( &mc->mc_conns[ c ] ); - } - } + msc->msc_mscflags = 0; return 0; } + diff --git a/servers/slapd/back-meta/compare.c b/servers/slapd/back-meta/compare.c index 4dbd0157ea..7cef5c040c 100644 --- a/servers/slapd/back-meta/compare.c +++ b/servers/slapd/back-meta/compare.c @@ -34,296 +34,122 @@ int meta_back_compare( Operation *op, SlapReply *rs ) { - metainfo_t *mi = ( metainfo_t * )op->o_bd->be_private; - metaconn_t *mc = NULL; - char *match = NULL, - *err = NULL; - struct berval mmatch = BER_BVNULL; - int ncandidates = 0, - last = 0, - i, - count = 0, - rc, - cres = LDAP_SUCCESS, - rres = LDAP_SUCCESS, - *msgid; - dncookie dc; - - SlapReply *candidates = meta_back_candidates_get( op ); - - mc = meta_back_getconn( op, rs, NULL, LDAP_BACK_SENDERR ); + metainfo_t *mi = ( metainfo_t * )op->o_bd->be_private; + metatarget_t *mt; + metaconn_t *mc; + int rc = 0; + int candidate = -1; + struct berval mdn = BER_BVNULL; + dncookie dc; + struct berval mapped_attr = op->orc_ava->aa_desc->ad_cname; + struct berval mapped_value = op->orc_ava->aa_value; + int msgid; + int do_retry = 1; + LDAPControl **ctrls = NULL; + + mc = meta_back_getconn( op, rs, &candidate, LDAP_BACK_SENDERR ); if ( !mc || !meta_back_dobind( op, rs, mc, LDAP_BACK_SENDERR ) ) { return rs->sr_err; } - - msgid = ch_calloc( sizeof( int ), mi->mi_ntargets ); - if ( msgid == NULL ) { - send_ldap_error( op, rs, LDAP_OTHER, NULL ); - rc = LDAP_OTHER; - goto done; - } + + assert( mc->mc_conns[ candidate ].msc_ld != NULL ); /* - * start an asynchronous compare for each candidate target + * Rewrite the modify dn, if needed */ + mt = mi->mi_targets[ candidate ]; + dc.target = mt; dc.conn = op->o_conn; dc.rs = rs; dc.ctx = "compareDN"; - for ( i = 0; i < mi->mi_ntargets; i++ ) { - struct berval mdn = BER_BVNULL; - struct berval mapped_attr = op->orc_ava->aa_desc->ad_cname; - struct berval mapped_value = op->orc_ava->aa_value; - - if ( candidates[ i ].sr_tag != META_CANDIDATE ) { - msgid[ i ] = -1; - continue; - } - - /* - * Rewrite the compare dn, if needed - */ - dc.target = &mi->mi_targets[ i ]; - - switch ( ldap_back_dn_massage( &dc, &op->o_req_dn, &mdn ) ) { - case LDAP_UNWILLING_TO_PERFORM: - rc = 1; - goto finish; - - default: - break; - } - - /* - * if attr is objectClass, try to remap the value - */ - if ( op->orc_ava->aa_desc == slap_schema.si_ad_objectClass ) { - ldap_back_map( &mi->mi_targets[ i ].mt_rwmap.rwm_oc, - &op->orc_ava->aa_value, - &mapped_value, BACKLDAP_MAP ); - - if ( BER_BVISNULL( &mapped_value ) || mapped_value.bv_val[0] == '\0' ) { - continue; - } - /* - * else try to remap the attribute - */ - } else { - ldap_back_map( &mi->mi_targets[ i ].mt_rwmap.rwm_at, - &op->orc_ava->aa_desc->ad_cname, - &mapped_attr, BACKLDAP_MAP ); - if ( BER_BVISNULL( &mapped_attr ) || mapped_attr.bv_val[0] == '\0' ) { - continue; - } + switch ( ldap_back_dn_massage( &dc, &op->o_req_dn, &mdn ) ) { + case LDAP_UNWILLING_TO_PERFORM: + rc = 1; + goto cleanup; - if ( op->orc_ava->aa_desc->ad_type->sat_syntax == slap_schema.si_syn_distinguishedName ) - { - dc.ctx = "compareAttrDN"; - - switch ( ldap_back_dn_massage( &dc, &op->orc_ava->aa_value, &mapped_value ) ) - { - case LDAP_UNWILLING_TO_PERFORM: - rc = 1; - goto finish; - - default: - break; - } - } - } - - /* - * the compare op is spawned across the targets and the first - * that returns determines the result; a constraint on unicity - * of the result ought to be enforced - */ - rc = ldap_compare_ext( mc->mc_conns[ i ].msc_ld, mdn.bv_val, - mapped_attr.bv_val, &mapped_value, - op->o_ctrls, NULL, &msgid[ i ] ); - - if ( mdn.bv_val != op->o_req_dn.bv_val ) { - free( mdn.bv_val ); - BER_BVZERO( &mdn ); - } - - if ( mapped_attr.bv_val != op->orc_ava->aa_desc->ad_cname.bv_val ) { - free( mapped_attr.bv_val ); - BER_BVZERO( &mapped_attr ); - } - - if ( mapped_value.bv_val != op->orc_ava->aa_value.bv_val ) { - free( mapped_value.bv_val ); - BER_BVZERO( &mapped_value ); - } - - if ( rc != LDAP_SUCCESS ) { - /* FIXME: what should we do with the error? */ - continue; - } - - ++ncandidates; + default: + break; } /* - * wait for replies + * if attr is objectClass, try to remap the value */ - for ( rc = 0, count = 0; ncandidates > 0; ) { - - /* - * FIXME: should we check for abandon? - */ - for ( i = 0; i < mi->mi_ntargets; i++ ) { - metasingleconn_t *msc = &mc->mc_conns[ i ]; - int lrc; - LDAPMessage *res = NULL; - struct timeval tv; - - LDAP_BACK_TV_SET( &tv ); - - if ( msgid[ i ] == -1 ) { - continue; - } - - lrc = ldap_result( msc->msc_ld, msgid[ i ], - LDAP_MSG_ALL, &tv, &res ); - - if ( lrc == 0 ) { - assert( res == NULL ); - continue; - - } else if ( lrc == -1 ) { - /* we do not retry in this case; - * only for unique operations... */ - ldap_get_option( msc->msc_ld, - LDAP_OPT_ERROR_NUMBER, &rs->sr_err ); - rres = slap_map_api2result( rs ); - rres = rc; - rc = -1; - goto finish; - - } else if ( lrc == LDAP_RES_COMPARE ) { - if ( count > 0 ) { - rres = LDAP_OTHER; - rc = -1; - goto finish; - } - - rc = ldap_parse_result( msc->msc_ld, res, - &rs->sr_err, - NULL, NULL, NULL, NULL, 1 ); - if ( rc != LDAP_SUCCESS ) { - rres = rc; - rc = -1; - goto finish; - } - - switch ( rs->sr_err ) { - case LDAP_COMPARE_TRUE: - case LDAP_COMPARE_FALSE: - - /* - * true or false, got it; - * sending to cache ... - */ - if ( mi->mi_cache.ttl != META_DNCACHE_DISABLED ) { - ( void )meta_dncache_update_entry( &mi->mi_cache, &op->o_req_ndn, i ); - } - - count++; - rc = 0; - break; - - default: - rres = slap_map_api2result( rs ); + if ( op->orc_ava->aa_desc == slap_schema.si_ad_objectClass ) { + ldap_back_map( &mt->mt_rwmap.rwm_oc, + &op->orc_ava->aa_value, + &mapped_value, BACKLDAP_MAP ); - if ( err != NULL ) { - free( err ); - } - ldap_get_option( msc->msc_ld, - LDAP_OPT_ERROR_STRING, &err ); - - if ( match != NULL ) { - free( match ); - } - ldap_get_option( msc->msc_ld, - LDAP_OPT_MATCHED_DN, &match ); - - last = i; - break; - } - msgid[ i ] = -1; - --ncandidates; - - } else { - msgid[ i ] = -1; - --ncandidates; - if ( res ) { - ldap_msgfree( res ); - } - break; - } + if ( BER_BVISNULL( &mapped_value ) || BER_BVISEMPTY( &mapped_value ) ) { + goto cleanup; } - } - -finish:; /* - * Rewrite the matched portion of the search base, if required - * - * FIXME: only the last one gets caught! + * else try to remap the attribute */ - if ( count == 1 ) { - if ( match != NULL ) { - free( match ); - match = NULL; + } else { + ldap_back_map( &mt->mt_rwmap.rwm_at, + &op->orc_ava->aa_desc->ad_cname, + &mapped_attr, BACKLDAP_MAP ); + if ( BER_BVISNULL( &mapped_attr ) || BER_BVISEMPTY( &mapped_attr ) ) { + goto cleanup; } - - /* - * the result of the compare is assigned to the res code - * that will be returned - */ - rres = cres; - - /* - * At least one compare failed with matched portion, - * and none was successful - */ - } else if ( match != NULL && match[ 0 ] != '\0' ) { - struct berval matched, pmatched; - ber_str2bv( match, 0, 0, &matched ); + if ( op->orc_ava->aa_desc->ad_type->sat_syntax == slap_schema.si_syn_distinguishedName ) + { + dc.ctx = "compareAttrDN"; - dc.ctx = "matchedDN"; - ldap_back_dn_massage( &dc, &matched, &mmatch ); - if ( dnPretty( NULL, &mmatch, &pmatched, NULL ) == LDAP_SUCCESS ) { - if ( mmatch.bv_val != match ) { - free( mmatch.bv_val ); + switch ( ldap_back_dn_massage( &dc, &op->orc_ava->aa_value, &mapped_value ) ) + { + case LDAP_UNWILLING_TO_PERFORM: + rc = 1; + goto cleanup; + + default: + break; } - mmatch = pmatched; } } - if ( rres != LDAP_SUCCESS ) { - rs->sr_err = rres; +retry:; + ctrls = op->o_ctrls; + rc = ldap_back_proxy_authz_ctrl( &mc->mc_conns[ candidate ].msc_bound_ndn, + mt->mt_version, &mt->mt_idassert, op, rs, &ctrls ); + if ( rc != LDAP_SUCCESS ) { + send_ldap_result( op, rs ); + goto cleanup; } - rs->sr_matched = mmatch.bv_val; - send_ldap_result( op, rs ); - rs->sr_matched = NULL; - if ( match != NULL ) { - if ( mmatch.bv_val != match ) { - free( mmatch.bv_val ); + rs->sr_err = ldap_compare_ext( mc->mc_conns[ candidate ].msc_ld, mdn.bv_val, + mapped_attr.bv_val, &mapped_value, + ctrls, NULL, &msgid ); + + rs->sr_err = meta_back_op_result( mc, op, rs, candidate, msgid, + mt->mt_timeout[ SLAP_OP_COMPARE ], LDAP_BACK_SENDRESULT ); + if ( rs->sr_err == LDAP_UNAVAILABLE && do_retry ) { + do_retry = 0; + if ( meta_back_retry( op, rs, &mc, candidate, LDAP_BACK_SENDERR ) ) { + /* if the identity changed, there might be need to re-authz */ + (void)ldap_back_proxy_authz_ctrl_free( op, &ctrls ); + goto retry; } - free( match ); } - if ( msgid ) { - free( msgid ); +cleanup:; + (void)ldap_back_proxy_authz_ctrl_free( op, &ctrls ); + + if ( mdn.bv_val != op->o_req_dn.bv_val ) { + free( mdn.bv_val ); + } + + if ( op->orc_ava->aa_value.bv_val != mapped_value.bv_val ) { + free( mapped_value.bv_val ); } -done:; - meta_back_release_conn( op, mc ); + if ( mc ) { + meta_back_release_conn( op, mc ); + } - return rc; + return rs->sr_err; } diff --git a/servers/slapd/back-meta/config.c b/servers/slapd/back-meta/config.c index e7e9ec3a68..56ca9dcc6f 100644 --- a/servers/slapd/back-meta/config.c +++ b/servers/slapd/back-meta/config.c @@ -36,19 +36,21 @@ static int meta_back_new_target( - metatarget_t *mt ) + metatarget_t **mtp ) { - struct ldapmapping *mapping; char *rargv[ 3 ]; + metatarget_t *mt; - memset( mt, 0, sizeof( metatarget_t ) ); + *mtp = NULL; + + mt = ch_calloc( sizeof( metatarget_t ), 1 ); mt->mt_rwmap.rwm_rw = rewrite_info_init( REWRITE_MODE_USE_DEFAULT ); if ( mt->mt_rwmap.rwm_rw == NULL ) { - return -1; + ch_free( mt ); + return -1; } - /* * the filter rewrite as a string must be disabled * by default; it can be re-enabled by adding rules; @@ -64,7 +66,16 @@ meta_back_new_target( rargv[ 2 ] = NULL; rewrite_parse( mt->mt_rwmap.rwm_rw, "", 1, 2, rargv ); - ldap_back_map_init( &mt->mt_rwmap.rwm_at, &mapping ); + ldap_pvt_thread_mutex_init( &mt->mt_uri_mutex ); + + mt->mt_idassert_mode = LDAP_BACK_IDASSERT_LEGACY; + mt->mt_idassert_authmethod = LDAP_AUTH_NONE; + mt->mt_idassert_tls = SB_TLS_DEFAULT; + + /* by default, use proxyAuthz control on each operation */ + mt->mt_idassert_flags = LDAP_BACK_AUTH_PRESCRIPTIVE; + + *mtp = mt; return 0; } @@ -107,6 +118,8 @@ meta_back_db_config( struct berval dn; int rc; int c; + + metatarget_t *mt; switch ( argc ) { case 1: @@ -136,8 +149,8 @@ meta_back_db_config( ++mi->mi_ntargets; - mi->mi_targets = ( metatarget_t * )ch_realloc( mi->mi_targets, - sizeof( metatarget_t ) * mi->mi_ntargets ); + mi->mi_targets = ( metatarget_t ** )ch_realloc( mi->mi_targets, + sizeof( metatarget_t * ) * mi->mi_ntargets ); if ( mi->mi_targets == NULL ) { Debug( LDAP_DEBUG_ANY, "%s: line %d: out of memory while storing server name" @@ -154,19 +167,29 @@ meta_back_db_config( return 1; } - mi->mi_targets[ i ].mt_nretries = mi->mi_nretries; - mi->mi_targets[ i ].mt_flags = mi->mi_flags; - mi->mi_targets[ i ].mt_version = mi->mi_version; - mi->mi_targets[ i ].mt_network_timeout = mi->mi_network_timeout; - mi->mi_targets[ i ].mt_bind_timeout = mi->mi_bind_timeout; - for ( c = 0; c < LDAP_BACK_OP_LAST; c++ ) { - mi->mi_targets[ i ].mt_timeout[ c ] = mi->mi_timeout[ c ]; + mt = mi->mi_targets[ i ]; + + mt->mt_rebind_f = mi->mi_rebind_f; + mt->mt_urllist_p = mt; + + mt->mt_nretries = mi->mi_nretries; + mt->mt_quarantine = mi->mi_quarantine; + if ( META_BACK_QUARANTINE( mi ) ) { + ldap_pvt_thread_mutex_init( &mt->mt_quarantine_mutex ); + } + mt->mt_flags = mi->mi_flags; + mt->mt_version = mi->mi_version; + mt->mt_network_timeout = mi->mi_network_timeout; + mt->mt_bind_timeout = mi->mi_bind_timeout; + for ( c = 0; c < SLAP_OP_LAST; c++ ) { + mt->mt_timeout[ c ] = mi->mi_timeout[ c ]; } /* * uri MUST be legal! */ - if ( ldap_url_parselist_ext( &ludp, argv[ 1 ], "\t" ) != LDAP_SUCCESS ) { + if ( ldap_url_parselist_ext( &ludp, argv[ 1 ], "\t" ) != LDAP_SUCCESS ) + { Debug( LDAP_DEBUG_ANY, "%s: line %d: unable to parse URI" " in \"uri ://[:port]/\" line\n", @@ -206,8 +229,8 @@ meta_back_db_config( * copies and stores uri and suffix */ ber_str2bv( ludp->lud_dn, 0, 0, &dn ); - rc = dnPrettyNormal( NULL, &dn, &mi->mi_targets[ i ].mt_psuffix, - &mi->mi_targets[ i ].mt_nsuffix, NULL ); + rc = dnPrettyNormal( NULL, &dn, &mt->mt_psuffix, + &mt->mt_nsuffix, NULL ); if( rc != LDAP_SUCCESS ) { Debug( LDAP_DEBUG_ANY, "%s: line %d: " "target \"%s\" DN is invalid\n", @@ -219,12 +242,12 @@ meta_back_db_config( switch ( ludp->lud_scope ) { case LDAP_SCOPE_DEFAULT: - mi->mi_targets[ i ].mt_scope = LDAP_SCOPE_SUBTREE; + mt->mt_scope = LDAP_SCOPE_SUBTREE; break; case LDAP_SCOPE_SUBTREE: case LDAP_SCOPE_SUBORDINATE: - mi->mi_targets[ i ].mt_scope = ludp->lud_scope; + mt->mt_scope = ludp->lud_scope; break; default: @@ -246,9 +269,9 @@ meta_back_db_config( } } - mi->mi_targets[ i ].mt_uri = ldap_url_list2urls( ludp ); + mt->mt_uri = ldap_url_list2urls( ludp ); ldap_free_urllist( ludp ); - if ( mi->mi_targets[ i ].mt_uri == NULL) { + if ( mt->mt_uri == NULL) { Debug( LDAP_DEBUG_ANY, "%s: line %d: no memory?\n", fname, lineno, 0 ); return( 1 ); @@ -258,7 +281,7 @@ meta_back_db_config( * uri MUST be a branch of suffix! */ #if 0 /* too strict a constraint */ - if ( select_backend( &mi->mi_targets[ i ].suffix, 0, 0 ) != be ) { + if ( select_backend( &mt->mt_nsuffix, 0, 0 ) != be ) { Debug( LDAP_DEBUG_ANY, "%s: line %d: of URI does not refer to current backend" " in \"uri ://[:port]/\" line\n", @@ -269,7 +292,7 @@ meta_back_db_config( /* * uri MUST be a branch of a suffix! */ - if ( select_backend( &mi->mi_targets[ i ].mt_nsuffix, 0, 0 ) == NULL ) { + if ( select_backend( &mt->mt_nsuffix, 0, 0 ) == NULL ) { Debug( LDAP_DEBUG_ANY, "%s: line %d: of URI does not resolve to a backend" " in \"uri ://[:port]/\" line\n", @@ -317,7 +340,7 @@ meta_back_db_config( return( 1 ); } - if ( !dnIsSuffix( &ndn, &mi->mi_targets[ i ].mt_nsuffix ) ) { + if ( !dnIsSuffix( &ndn, &mi->mi_targets[ i ]->mt_nsuffix ) ) { Debug( LDAP_DEBUG_ANY, "%s: line %d: " "subtree-exclude DN=\"%s\" " "must be subtree of target\n", @@ -326,12 +349,12 @@ meta_back_db_config( return( 1 ); } - if ( mi->mi_targets[ i ].mt_subtree_exclude != NULL ) { + if ( mi->mi_targets[ i ]->mt_subtree_exclude != NULL ) { int j; - for ( j = 0; !BER_BVISNULL( &mi->mi_targets[ i ].mt_subtree_exclude[ j ] ); j++ ) + for ( j = 0; !BER_BVISNULL( &mi->mi_targets[ i ]->mt_subtree_exclude[ j ] ); j++ ) { - if ( dnIsSuffix( &mi->mi_targets[ i ].mt_subtree_exclude[ j ], &ndn ) ) { + if ( dnIsSuffix( &mi->mi_targets[ i ]->mt_subtree_exclude[ j ], &ndn ) ) { Debug( LDAP_DEBUG_ANY, "%s: line %d: " "subtree-exclude DN=\"%s\" " "is suffix of another subtree-exclude\n", @@ -341,7 +364,7 @@ meta_back_db_config( ber_memfree( ndn.bv_val ); return( 1 ); - } else if ( dnIsSuffix( &ndn, &mi->mi_targets[ i ].mt_subtree_exclude[ j ] ) ) { + } else if ( dnIsSuffix( &ndn, &mi->mi_targets[ i ]->mt_subtree_exclude[ j ] ) ) { Debug( LDAP_DEBUG_ANY, "%s: line %d: " "another subtree-exclude is suffix of " "subtree-exclude DN=\"%s\"\n", @@ -352,7 +375,7 @@ meta_back_db_config( } } - ber_bvarray_add( &mi->mi_targets[ i ].mt_subtree_exclude, &ndn ); + ber_bvarray_add( &mi->mi_targets[ i ]->mt_subtree_exclude, &ndn ); /* default target directive */ } else if ( strcasecmp( argv[ 0 ], "default-target" ) == 0 ) { @@ -423,7 +446,7 @@ meta_back_db_config( } else if ( strcasecmp( argv[ 0 ], "network-timeout" ) == 0 ) { unsigned long t; time_t *tp = mi->mi_ntargets ? - &mi->mi_targets[ mi->mi_ntargets - 1 ].mt_network_timeout + &mi->mi_targets[ mi->mi_ntargets - 1 ]->mt_network_timeout : &mi->mi_network_timeout; if ( argc != 2 ) { @@ -505,7 +528,7 @@ meta_back_db_config( } else if ( strcasecmp( argv[ 0 ], "bind-timeout" ) == 0 ) { unsigned long t; struct timeval *tp = mi->mi_ntargets ? - &mi->mi_targets[ mi->mi_ntargets - 1 ].mt_bind_timeout + &mi->mi_targets[ mi->mi_ntargets - 1 ]->mt_bind_timeout : &mi->mi_bind_timeout; switch ( argc ) { @@ -564,7 +587,7 @@ meta_back_db_config( } ber_str2bv( argv[ 1 ], 0, 0, &dn ); - if ( dnNormalize( 0, NULL, NULL, &dn, &mi->mi_targets[ i ].mt_binddn, + if ( dnNormalize( 0, NULL, NULL, &dn, &mi->mi_targets[ i ]->mt_binddn, NULL ) != LDAP_SUCCESS ) { Debug( LDAP_DEBUG_ANY, "%s: line %d: " @@ -601,7 +624,7 @@ meta_back_db_config( /* FIXME: some day we'll need to throw an error */ } - ber_str2bv( argv[ 1 ], 0L, 1, &mi->mi_targets[ i ].mt_bindpw ); + ber_str2bv( argv[ 1 ], 0L, 1, &mi->mi_targets[ i ]->mt_bindpw ); /* save bind creds for referral rebinds? */ } else if ( strcasecmp( argv[ 0 ], "rebind-as-user" ) == 0 ) { @@ -638,7 +661,7 @@ meta_back_db_config( } else if ( strcasecmp( argv[ 0 ], "chase-referrals" ) == 0 ) { unsigned *flagsp = mi->mi_ntargets ? - &mi->mi_targets[ mi->mi_ntargets - 1 ].mt_flags + &mi->mi_targets[ mi->mi_ntargets - 1 ]->mt_flags : &mi->mi_flags; if ( argc != 2 ) { @@ -667,7 +690,7 @@ meta_back_db_config( } else if ( strcasecmp( argv[ 0 ], "tls" ) == 0 ) { unsigned *flagsp = mi->mi_ntargets ? - &mi->mi_targets[ mi->mi_ntargets - 1 ].mt_flags + &mi->mi_targets[ mi->mi_ntargets - 1 ]->mt_flags : &mi->mi_flags; if ( argc != 2 ) { @@ -704,7 +727,7 @@ meta_back_db_config( } else if ( strcasecmp( argv[ 0 ], "t-f-support" ) == 0 ) { unsigned *flagsp = mi->mi_ntargets ? - &mi->mi_targets[ mi->mi_ntargets - 1 ].mt_flags + &mi->mi_targets[ mi->mi_ntargets - 1 ]->mt_flags : &mi->mi_flags; if ( argc != 2 ) { @@ -716,16 +739,16 @@ meta_back_db_config( switch ( check_true_false( argv[ 1 ] ) ) { case 0: - *flagsp &= ~(LDAP_BACK_F_SUPPORT_T_F|LDAP_BACK_F_SUPPORT_T_F_DISCOVER); + *flagsp &= ~LDAP_BACK_F_T_F_MASK2; break; case 1: - *flagsp |= LDAP_BACK_F_SUPPORT_T_F; + *flagsp |= LDAP_BACK_F_T_F; break; default: if ( strcasecmp( argv[ 1 ], "discover" ) == 0 ) { - *flagsp |= LDAP_BACK_F_SUPPORT_T_F_DISCOVER; + *flagsp |= LDAP_BACK_F_T_F_DISCOVER; } else { Debug( LDAP_DEBUG_ANY, @@ -740,29 +763,34 @@ meta_back_db_config( } else if ( strcasecmp( argv[ 0 ], "onerr" ) == 0 ) { if ( argc != 2 ) { Debug( LDAP_DEBUG_ANY, - "%s: line %d: \"onerr {CONTINUE|stop}\" takes 1 argument\n", + "%s: line %d: \"onerr {CONTINUE|report|stop}\" takes 1 argument\n", fname, lineno, 0 ); return( 1 ); } if ( strcasecmp( argv[ 1 ], "continue" ) == 0 ) { - mi->mi_flags &= ~META_BACK_F_ONERR_STOP; + mi->mi_flags &= ~META_BACK_F_ONERR_MASK; } else if ( strcasecmp( argv[ 1 ], "stop" ) == 0 ) { mi->mi_flags |= META_BACK_F_ONERR_STOP; + } else if ( strcasecmp( argv[ 1 ], "report" ) == 0 ) { + mi->mi_flags |= META_BACK_F_ONERR_REPORT; + } else { Debug( LDAP_DEBUG_ANY, - "%s: line %d: \"onerr {CONTINUE|stop}\": invalid arg \"%s\".\n", + "%s: line %d: \"onerr {CONTINUE|report|stop}\": invalid arg \"%s\".\n", fname, lineno, argv[ 1 ] ); return 1; } /* bind-defer? */ - } else if ( strcasecmp( argv[ 0 ], "pseudoroot-bind-defer" ) == 0 ) { + } else if ( strcasecmp( argv[ 0 ], "pseudoroot-bind-defer" ) == 0 + || strcasecmp( argv[ 0 ], "root-bind-defer" ) == 0 ) + { if ( argc != 2 ) { Debug( LDAP_DEBUG_ANY, - "%s: line %d: \"pseudoroot-bind-defer {FALSE|true}\" takes 1 argument\n", + "%s: line %d: \"[pseudo]root-bind-defer {FALSE|true}\" takes 1 argument\n", fname, lineno, 0 ); return( 1 ); } @@ -778,21 +806,148 @@ meta_back_db_config( default: Debug( LDAP_DEBUG_ANY, - "%s: line %d: \"pseudoroot-bind-defer {FALSE|true}\": invalid arg \"%s\".\n", + "%s: line %d: \"[pseudo]root-bind-defer {FALSE|true}\": invalid arg \"%s\".\n", + fname, lineno, argv[ 1 ] ); + return 1; + } + + /* single-conn? */ + } else if ( strcasecmp( argv[ 0 ], "single-conn" ) == 0 ) { + if ( argc != 2 ) { + Debug( LDAP_DEBUG_ANY, + "%s: line %d: \"single-conn {FALSE|true}\" takes 1 argument\n", + fname, lineno, 0 ); + return( 1 ); + } + + if ( mi->mi_ntargets > 0 ) { + Debug( LDAP_DEBUG_ANY, + "%s: line %d: \"single-conn\" must appear before target definitions\n", + fname, lineno, 0 ); + return( 1 ); + } + + switch ( check_true_false( argv[ 1 ] ) ) { + case 0: + mi->mi_flags &= ~LDAP_BACK_F_SINGLECONN; + break; + + case 1: + mi->mi_flags |= LDAP_BACK_F_SINGLECONN; + break; + + default: + Debug( LDAP_DEBUG_ANY, + "%s: line %d: \"single-conn {FALSE|true}\": invalid arg \"%s\".\n", + fname, lineno, argv[ 1 ] ); + return 1; + } + + /* use-temporaries? */ + } else if ( strcasecmp( argv[ 0 ], "use-temporary-conn" ) == 0 ) { + if ( argc != 2 ) { + Debug( LDAP_DEBUG_ANY, + "%s: line %d: \"use-temporary-conn {FALSE|true}\" takes 1 argument\n", + fname, lineno, 0 ); + return( 1 ); + } + + if ( mi->mi_ntargets > 0 ) { + Debug( LDAP_DEBUG_ANY, + "%s: line %d: \"use-temporary-conn\" must appear before target definitions\n", + fname, lineno, 0 ); + return( 1 ); + } + + switch ( check_true_false( argv[ 1 ] ) ) { + case 0: + mi->mi_flags &= ~LDAP_BACK_F_USE_TEMPORARIES; + break; + + case 1: + mi->mi_flags |= LDAP_BACK_F_USE_TEMPORARIES; + break; + + default: + Debug( LDAP_DEBUG_ANY, + "%s: line %d: \"use-temporary-conn {FALSE|true}\": invalid arg \"%s\".\n", + fname, lineno, argv[ 1 ] ); + return 1; + } + + /* privileged connections pool max size ? */ + } else if ( strcasecmp( argv[ 0 ], "conn-pool-max" ) == 0 ) { + if ( argc != 2 ) { + Debug( LDAP_DEBUG_ANY, + "%s: line %d: \"conn-pool-max \" takes 1 argument\n", + fname, lineno, 0 ); + return( 1 ); + } + + if ( mi->mi_ntargets > 0 ) { + Debug( LDAP_DEBUG_ANY, + "%s: line %d: \"conn-pool-max\" must appear before target definitions\n", + fname, lineno, 0 ); + return( 1 ); + } + + if ( lutil_atoi( &mi->mi_conn_priv_max, argv[1] ) + || mi->mi_conn_priv_max < LDAP_BACK_CONN_PRIV_MIN + || mi->mi_conn_priv_max > LDAP_BACK_CONN_PRIV_MAX ) + { + Debug( LDAP_DEBUG_ANY, + "%s: line %d: \"conn-pool-max \": invalid arg \"%s\".\n", fname, lineno, argv[ 1 ] ); return 1; } + } else if ( strcasecmp( argv[ 0 ], "cancel" ) == 0 ) { + unsigned flag = 0; + unsigned *flagsp = mi->mi_ntargets ? + &mi->mi_targets[ mi->mi_ntargets - 1 ]->mt_flags + : &mi->mi_flags; + + if ( argc != 2 ) { + Debug( LDAP_DEBUG_ANY, + "%s: line %d: \"cancel {abandon|ignore|exop}\" takes 1 argument\n", + fname, lineno, 0 ); + return( 1 ); + } + + if ( strcasecmp( argv[ 1 ], "abandon" ) == 0 ) { + flag = LDAP_BACK_F_CANCEL_ABANDON; + +#if 0 /* needs ldap_int_discard(), 2.4 */ + } else if ( strcasecmp( argv[ 1 ], "ignore" ) == 0 ) { + flag = LDAP_BACK_F_CANCEL_IGNORE; +#endif + + } else if ( strcasecmp( argv[ 1 ], "exop" ) == 0 ) { + flag = LDAP_BACK_F_CANCEL_EXOP; + + } else if ( strcasecmp( argv[ 1 ], "exop-discover" ) == 0 ) { + flag = LDAP_BACK_F_CANCEL_EXOP_DISCOVER; + + } else { + Debug( LDAP_DEBUG_ANY, + "%s: line %d: \"cancel {abandon|ignore|exop[-discover]}\": unknown mode \"%s\" \n", + fname, lineno, argv[ 1 ] ); + return( 1 ); + } + + *flagsp &= ~LDAP_BACK_F_CANCEL_MASK2; + *flagsp |= flag; + } else if ( strcasecmp( argv[ 0 ], "timeout" ) == 0 ) { char *sep; time_t *tv = mi->mi_ntargets ? - mi->mi_targets[ mi->mi_ntargets - 1 ].mt_timeout + mi->mi_targets[ mi->mi_ntargets - 1 ]->mt_timeout : mi->mi_timeout; int c; if ( argc < 2 ) { Debug( LDAP_DEBUG_ANY, - "%s: line %d: \"timeout [{add|delete|modify|modrdn}=] [...]\" takes at least 1 argument\n", + "%s: line %d: \"timeout [{add|bind|delete|modify|modrdn}=] [...]\" takes at least 1 argument\n", fname, lineno, 0 ); return( 1 ); } @@ -805,19 +960,33 @@ meta_back_db_config( if ( sep != NULL ) { size_t len = sep - argv[ c ]; - if ( strncasecmp( argv[ c ], "add", len ) == 0 ) { - t = &tv[ LDAP_BACK_OP_ADD ]; + if ( strncasecmp( argv[ c ], "bind", len ) == 0 ) { + t = &tv[ SLAP_OP_BIND ]; + /* unbind makes little sense */ + } else if ( strncasecmp( argv[ c ], "add", len ) == 0 ) { + t = &tv[ SLAP_OP_ADD ]; } else if ( strncasecmp( argv[ c ], "delete", len ) == 0 ) { - t = &tv[ LDAP_BACK_OP_DELETE ]; - } else if ( strncasecmp( argv[ c ], "modify", len ) == 0 ) { - t = &tv[ LDAP_BACK_OP_MODIFY ]; + t = &tv[ SLAP_OP_DELETE ]; } else if ( strncasecmp( argv[ c ], "modrdn", len ) == 0 ) { - t = &tv[ LDAP_BACK_OP_MODRDN ]; + t = &tv[ SLAP_OP_MODRDN ]; + } else if ( strncasecmp( argv[ c ], "modify", len ) == 0 ) { + t = &tv[ SLAP_OP_MODIFY ]; + } else if ( strncasecmp( argv[ c ], "compare", len ) == 0 ) { + t = &tv[ SLAP_OP_COMPARE ]; +#if 0 /* uses timelimit instead */ + } else if ( strncasecmp( argv[ c ], "search", len ) == 0 ) { + t = &tv[ SLAP_OP_SEARCH ]; +#endif + /* abandon makes little sense */ +#if 0 /* not implemented yet */ + } else if ( strncasecmp( argv[ c ], "extended", len ) == 0 ) { + t = &tv[ SLAP_OP_EXTENDED ]; +#endif } else { char buf[ SLAP_TEXT_BUFLEN ]; snprintf( buf, sizeof( buf ), - "unknown operation \"%s\" for timeout #%d", - argv[ c ], c ); + "unknown/unhandled operation \"%s\" for timeout #%d", + argv[ c ], c - 1 ); Debug( LDAP_DEBUG_ANY, "%s: line %d: %s.\n", fname, lineno, buf ); @@ -842,7 +1011,7 @@ meta_back_db_config( } else { int i; - for ( i = 0; i < LDAP_BACK_OP_LAST; i++ ) { + for ( i = 0; i < SLAP_OP_LAST; i++ ) { tv[ i ] = (time_t)val; } } @@ -851,7 +1020,6 @@ meta_back_db_config( /* name to use as pseudo-root dn */ } else if ( strcasecmp( argv[ 0 ], "pseudorootdn" ) == 0 ) { int i = mi->mi_ntargets - 1; - struct berval dn; if ( i < 0 ) { Debug( LDAP_DEBUG_ANY, @@ -867,15 +1035,74 @@ meta_back_db_config( return 1; } - dn.bv_val = argv[ 1 ]; - dn.bv_len = strlen( argv[ 1 ] ); - if ( dnNormalize( 0, NULL, NULL, &dn, - &mi->mi_targets[ i ].mt_pseudorootdn, NULL ) != LDAP_SUCCESS ) + /* + * exact replacement: + * + +idassert-bind bindmethod=simple + binddn= + credentials= + mode=none + flags=non-prescriptive +idassert-authzFrom "dn:" + + * so that only when authc'd as the proxying occurs + * rebinding as the without proxyAuthz. + */ + + Debug( LDAP_DEBUG_ANY, + "%s: line %d: \"pseudorootdn\", \"pseudorootpw\" are no longer supported; " + "use \"idassert-bind\" and \"idassert-authzFrom\" instead.\n", + fname, lineno, 0 ); + { - Debug( LDAP_DEBUG_ANY, "%s: line %d: " - "pseudoroot DN '%s' is invalid\n", - fname, lineno, argv[ 1 ] ); - return( 1 ); + char binddn[ SLAP_TEXT_BUFLEN ]; + char *cargv[] = { + "idassert-bind", + "bindmethod=simple", + NULL, + "mode=none", + "flags=non-prescriptive", + NULL + }; + int cargc = 5; + int rc; + + if ( BER_BVISNULL( &be->be_rootndn ) ) { + Debug( LDAP_DEBUG_ANY, "%s: line %d: \"pseudorootpw\": \"rootdn\" must be defined first.\n", + fname, lineno, 0 ); + return 1; + } + + if ( snprintf( binddn, sizeof( binddn ), "binddn=%s", argv[ 1 ] ) >= sizeof( binddn ) ) { + Debug( LDAP_DEBUG_ANY, "%s: line %d: \"pseudorootdn\" too long.\n", + fname, lineno, 0 ); + return 1; + } + cargv[ 2 ] = binddn; + + rc = slap_idassert_parse_cf( fname, lineno, cargc, cargv, &mi->mi_targets[ mi->mi_ntargets - 1 ]->mt_idassert ); + if ( rc == 0 ) { + struct berval bv; + + if ( mi->mi_targets[ mi->mi_ntargets - 1 ]->mt_idassert_authz != NULL ) { + Debug( LDAP_DEBUG_ANY, "%s: line %d: \"idassert-authzFrom\" already defined (discarded).\n", + fname, lineno, 0 ); + ber_bvarray_free( mi->mi_targets[ mi->mi_ntargets - 1 ]->mt_idassert_authz ); + mi->mi_targets[ mi->mi_ntargets - 1 ]->mt_idassert_authz = NULL; + } + + assert( !BER_BVISNULL( &mi->mi_targets[ mi->mi_ntargets - 1 ]->mt_idassert_authcDN ) ); + + bv.bv_len = STRLENOF( "dn:" ) + be->be_rootndn.bv_len; + bv.bv_val = ber_memalloc( bv.bv_len + 1 ); + AC_MEMCPY( bv.bv_val, "dn:", STRLENOF( "dn:" ) ); + AC_MEMCPY( &bv.bv_val[ STRLENOF( "dn:" ) ], be->be_rootndn.bv_val, be->be_rootndn.bv_len + 1 ); + + ber_bvarray_add( &mi->mi_targets[ mi->mi_ntargets - 1 ]->mt_idassert_authz, &bv ); + } + + return rc; } /* password to use as pseudo-root */ @@ -895,7 +1122,114 @@ meta_back_db_config( fname, lineno, 0 ); return 1; } - ber_str2bv( argv[ 1 ], 0L, 1, &mi->mi_targets[ i ].mt_pseudorootpw ); + + Debug( LDAP_DEBUG_ANY, + "%s: line %d: \"pseudorootdn\", \"pseudorootpw\" are no longer supported; " + "use \"idassert-bind\" and \"idassert-authzFrom\" instead.\n", + fname, lineno, 0 ); + + if ( BER_BVISNULL( &mi->mi_targets[ i ]->mt_idassert_authcDN ) ) { + Debug( LDAP_DEBUG_ANY, "%s: line %d: \"pseudorootpw\": \"pseudorootdn\" must be defined first.\n", + fname, lineno, 0 ); + return 1; + } + + if ( !BER_BVISNULL( &mi->mi_targets[ i ]->mt_idassert_passwd ) ) { + memset( mi->mi_targets[ i ]->mt_idassert_passwd.bv_val, 0, + mi->mi_targets[ i ]->mt_idassert_passwd.bv_len ); + ber_memfree( mi->mi_targets[ i ]->mt_idassert_passwd.bv_val ); + } + ber_str2bv( argv[ 1 ], 0, 1, &mi->mi_targets[ i ]->mt_idassert_passwd ); + + /* idassert-bind */ + } else if ( strcasecmp( argv[ 0 ], "idassert-bind" ) == 0 ) { + if ( mi->mi_ntargets == 0 ) { + Debug( LDAP_DEBUG_ANY, + "%s: line %d: \"idassert-bind\" " + "must appear inside a target specification.\n", + fname, lineno, 0 ); + return 1; + } + + return slap_idassert_parse_cf( fname, lineno, argc, argv, &mi->mi_targets[ mi->mi_ntargets - 1 ]->mt_idassert ); + + /* idassert-authzFrom */ + } else if ( strcasecmp( argv[ 0 ], "idassert-authzFrom" ) == 0 ) { + if ( mi->mi_ntargets == 0 ) { + Debug( LDAP_DEBUG_ANY, + "%s: line %d: \"idassert-bind\" " + "must appear inside a target specification.\n", + fname, lineno, 0 ); + return 1; + } + + switch ( argc ) { + case 2: + break; + + case 1: + Debug( LDAP_DEBUG_ANY, + "%s: line %d: missing in \"idassert-authzFrom \".\n", + fname, lineno, 0 ); + return 1; + + default: + Debug( LDAP_DEBUG_ANY, + "%s: line %d: extra cruft after in \"idassert-authzFrom \".\n", + fname, lineno, 0 ); + return 1; + } + + return slap_idassert_authzfrom_parse_cf( fname, lineno, argv[ 1 ], &mi->mi_targets[ mi->mi_ntargets - 1 ]->mt_idassert ); + + /* quarantine */ + } else if ( strcasecmp( argv[ 0 ], "quarantine" ) == 0 ) { + char buf[ SLAP_TEXT_BUFLEN ] = { '\0' }; + slap_retry_info_t *ri = mi->mi_ntargets ? + &mi->mi_targets[ mi->mi_ntargets - 1 ]->mt_quarantine + : &mi->mi_quarantine; + + if ( ( mi->mi_ntargets == 0 && META_BACK_QUARANTINE( mi ) ) + || ( mi->mi_ntargets > 0 && META_BACK_TGT_QUARANTINE( mi->mi_targets[ mi->mi_ntargets - 1 ] ) ) ) + { + Debug( LDAP_DEBUG_ANY, + "%s: line %d: quarantine already defined.\n", + fname, lineno, 0 ); + return 1; + } + + switch ( argc ) { + case 2: + break; + + case 1: + Debug( LDAP_DEBUG_ANY, + "%s: line %d: missing arg in \"quarantine \".\n", + fname, lineno, 0 ); + return 1; + + default: + Debug( LDAP_DEBUG_ANY, + "%s: line %d: extra cruft after \"quarantine \".\n", + fname, lineno, 0 ); + return 1; + } + + if ( ri != &mi->mi_quarantine ) { + ri->ri_interval = NULL; + ri->ri_num = NULL; + } + + if ( mi->mi_ntargets > 0 && !META_BACK_QUARANTINE( mi ) ) { + ldap_pvt_thread_mutex_init( &mi->mi_targets[ mi->mi_ntargets - 1 ]->mt_quarantine_mutex ); + } + + if ( slap_retry_info_parse( argv[ 1 ], ri, buf, sizeof( buf ) ) ) { + Debug( LDAP_DEBUG_ANY, + "%s line %d: %s.\n", + fname, lineno, buf ); + return 1; + } /* dn massaging */ } else if ( strcasecmp( argv[ 0 ], "suffixmassage" ) == 0 ) { @@ -978,7 +1312,7 @@ meta_back_db_config( * FIXME: no extra rewrite capabilities should be added * to the database */ - rc = suffix_massage_config( mi->mi_targets[ i ].mt_rwmap.rwm_rw, + rc = suffix_massage_config( mi->mi_targets[ i ]->mt_rwmap.rwm_rw, &pvnc, &nvnc, &prnc, &nrnc ); free( pvnc.bv_val ); @@ -999,7 +1333,7 @@ meta_back_db_config( return 1; } - return rewrite_parse( mi->mi_targets[ i ].mt_rwmap.rwm_rw, + return rewrite_parse( mi->mi_targets[ i ]->mt_rwmap.rwm_rw, fname, lineno, argc, argv ); /* objectclass/attribute mapping */ @@ -1013,8 +1347,8 @@ meta_back_db_config( return 1; } - return ldap_back_map_config( &mi->mi_targets[ i ].mt_rwmap.rwm_oc, - &mi->mi_targets[ i ].mt_rwmap.rwm_at, + return ldap_back_map_config( &mi->mi_targets[ i ]->mt_rwmap.rwm_oc, + &mi->mi_targets[ i ]->mt_rwmap.rwm_at, fname, lineno, argc, argv ); } else if ( strcasecmp( argv[ 0 ], "nretries" ) == 0 ) { @@ -1047,12 +1381,12 @@ meta_back_db_config( mi->mi_nretries = nretries; } else { - mi->mi_targets[ i ].mt_nretries = nretries; + mi->mi_targets[ i ]->mt_nretries = nretries; } } else if ( strcasecmp( argv[ 0 ], "protocol-version" ) == 0 ) { int *version = mi->mi_ntargets ? - &mi->mi_targets[ mi->mi_ntargets - 1 ].mt_version + &mi->mi_targets[ mi->mi_ntargets - 1 ]->mt_version : &mi->mi_version; if ( argc != 2 ) { @@ -1122,7 +1456,7 @@ ldap_back_map_config( if ( strcmp( argv[ 2 ], "*" ) == 0 ) { if ( argc < 4 || strcmp( argv[ 3 ], "*" ) == 0 ) { map->drop_missing = ( argc < 4 ); - return 0; + goto success_return; } src = dst = argv[ 3 ]; @@ -1136,7 +1470,7 @@ ldap_back_map_config( } if ( ( map == at_map ) - && ( strcasecmp( src, "objectclass" ) == 0 + && ( strcasecmp( src, "objectclass" ) == 0 || strcasecmp( dst, "objectclass" ) == 0 ) ) { Debug( LDAP_DEBUG_ANY, @@ -1264,6 +1598,12 @@ ldap_back_map_config( avl_insert( &map->remap, (caddr_t)&mapping[ 1 ], mapping_cmp, mapping_dup ); +success_return:; + if ( !is_oc && map->map == NULL ) { + /* only init if required */ + ldap_back_map_init( map, &mapping ); + } + return 0; error_return:; diff --git a/servers/slapd/back-meta/conn.c b/servers/slapd/back-meta/conn.c index ffe99d8467..8227ed3d92 100644 --- a/servers/slapd/back-meta/conn.c +++ b/servers/slapd/back-meta/conn.c @@ -34,11 +34,6 @@ #include "../back-ldap/back-ldap.h" #include "back-meta.h" -/* - * Set PRINT_CONNTREE larger than 0 to dump the connection tree (debug only) - */ -#define PRINT_CONNTREE 0 - /* * meta_back_conndn_cmp * @@ -143,46 +138,79 @@ meta_back_conndn_dup( /* * Debug stuff (got it from libavl) */ -#if PRINT_CONNTREE > 0 +#if META_BACK_PRINT_CONNTREE > 0 static void -ravl_print( Avlnode *root, int depth ) +meta_back_ravl_print( Avlnode *root, int depth ) { int i; - metaconn_t *mc = (metaconn_t *)root->avl_data; + metaconn_t *mc; if ( root == 0 ) { return; } - ravl_print( root->avl_right, depth + 1 ); + meta_back_ravl_print( root->avl_right, depth + 1 ); for ( i = 0; i < depth; i++ ) { - printf( " " ); + fprintf( stderr, "-" ); } - printf( "c(%d%s%s) %d\n", - LDAP_BACK_PCONN_ID( mc->mc_conn ), - BER_BVISNULL( &mc->mc_local_ndn ) ? "" : ": ", - BER_BVISNULL( &mc->mc_local_ndn ) ? "" : mc->mc_local_ndn.bv_val, - root->avl_bf ); + mc = (metaconn_t *)root->avl_data; + fprintf( stderr, "mc=%p local=\"%s\" conn=%p %s refcnt=%d%s\n", + (void *)mc, + mc->mc_local_ndn.bv_val ? mc->mc_local_ndn.bv_val : "", + (void *)mc->mc_conn, + avl_bf2str( root->avl_bf ), mc->mc_refcnt, + LDAP_BACK_CONN_TAINTED( mc ) ? " tainted" : "" ); - ravl_print( root->avl_left, depth + 1 ); + meta_back_ravl_print( root->avl_left, depth + 1 ); } -static void -myprint( Avlnode *root ) +/* NOTE: duplicate from back-ldap/bind.c */ +static char* priv2str[] = { + "privileged", + "privileged/TLS", + "anonymous", + "anonymous/TLS", + "bind", + "bind/TLS", + NULL +}; + +void +meta_back_print_conntree( metainfo_t *mi, char *msg ) { - printf( "********\n" ); + int c; + + fprintf( stderr, "========> %s\n", msg ); - if ( root == 0 ) { - printf( "\tNULL\n" ); + for ( c = LDAP_BACK_PCONN_FIRST; c < LDAP_BACK_PCONN_LAST; c++ ) { + int i = 0; + metaconn_t *mc; + + fprintf( stderr, " %s[%d]\n", priv2str[ c ], mi->mi_conn_priv[ c ].mic_num ); + + LDAP_TAILQ_FOREACH( mc, &mi->mi_conn_priv[ c ].mic_priv, mc_q ) + { + fprintf( stderr, " [%d] mc=%p local=\"%s\" conn=%p refcnt=%d flags=0x%08x\n", + i, + (void *)mc, + mc->mc_local_ndn.bv_val ? mc->mc_local_ndn.bv_val : "", + (void *)mc->mc_conn, mc->mc_refcnt, mc->msc_mscflags ); + i++; + } + } + + if ( mi->mi_conninfo.lai_tree == NULL ) { + fprintf( stderr, "\t(empty)\n" ); + } else { - ravl_print( root, 0 ); + meta_back_ravl_print( mi->mi_conninfo.lai_tree, 0 ); } - printf( "********\n" ); + fprintf( stderr, "<======== %s\n", msg ); } -#endif /* PRINT_CONNTREE */ +#endif /* META_BACK_PRINT_CONNTREE */ /* * End of debug stuff */ @@ -198,51 +226,25 @@ metaconn_alloc( { metainfo_t *mi = ( metainfo_t * )op->o_bd->be_private; metaconn_t *mc; - int i, ntargets = mi->mi_ntargets; + int ntargets = mi->mi_ntargets; assert( ntargets > 0 ); /* malloc all in one */ - mc = ( metaconn_t * )ch_malloc( sizeof( metaconn_t ) - + sizeof( metasingleconn_t ) * ntargets ); + mc = ( metaconn_t * )ch_calloc( 1, sizeof( metaconn_t ) + + sizeof( metasingleconn_t ) * ( ntargets - 1 ) ); if ( mc == NULL ) { return NULL; } - for ( i = 0; i < ntargets; i++ ) { - mc->mc_conns[ i ].msc_ld = NULL; - BER_BVZERO( &mc->mc_conns[ i ].msc_bound_ndn ); - BER_BVZERO( &mc->mc_conns[ i ].msc_cred ); - mc->mc_conns[ i ].msc_mscflags = 0; - mc->mc_conns[ i ].msc_info = mi; - } + mc->mc_info = mi; - BER_BVZERO( &mc->mc_local_ndn ); - mc->msc_mscflags = 0; mc->mc_authz_target = META_BOUND_NONE; mc->mc_refcnt = 1; return mc; } -static void -meta_back_freeconn( - Operation *op, - metaconn_t *mc ) -{ - metainfo_t *mi = ( metainfo_t * )op->o_bd->be_private; - - assert( mc != NULL ); - - ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex ); - - if ( --mc->mc_refcnt == 0 ) { - meta_back_conn_free( mc ); - } - - ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex ); -} - /* * meta_back_init_one_conn * @@ -252,31 +254,125 @@ int meta_back_init_one_conn( Operation *op, SlapReply *rs, - metatarget_t *mt, metaconn_t *mc, int candidate, int ispriv, - ldap_back_send_t sendok ) + ldap_back_send_t sendok, + int dolock ) { metainfo_t *mi = ( metainfo_t * )op->o_bd->be_private; + metatarget_t *mt = mi->mi_targets[ candidate ]; metasingleconn_t *msc = &mc->mc_conns[ candidate ]; int version; dncookie dc; int isauthz = ( candidate == mc->mc_authz_target ); + int do_return = 0; +#ifdef HAVE_TLS + int is_ldaps = 0; +#endif /* HAVE_TLS */ + + /* 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 ( mt->mt_isquarantined ) { + slap_retry_info_t *ri = &mt->mt_quarantine; + int dont_retry = 1; + + if ( mt->mt_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 ) { + if ( StatslogTest( LDAP_DEBUG_ANY ) ) { + char buf[ SLAP_TEXT_BUFLEN ]; + + snprintf( buf, sizeof( buf ), + "meta_back_init_one_conn[%d]: quarantine " + "retry block #%d try #%d", + candidate, ri->ri_idx, ri->ri_count ); + Debug( LDAP_DEBUG_ANY, "%s %s.\n", + op->o_log_prefix, buf, 0 ); + } + + mt->mt_isquarantined = LDAP_BACK_FQ_RETRYING; + } + } + + if ( dont_retry ) { + rs->sr_err = LDAP_UNAVAILABLE; + if ( op->o_conn && ( sendok & LDAP_BACK_SENDERR ) ) { + send_ldap_result( op, rs ); + } + return rs->sr_err; + } + } + +retry_lock:; + if ( dolock ) { + ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex ); + } /* * Already init'ed */ - if ( msc->msc_ld != NULL ) { - return rs->sr_err = LDAP_SUCCESS; + if ( LDAP_BACK_CONN_ISBOUND( msc ) + || LDAP_BACK_CONN_ISANON( msc ) ) + { + assert( msc->msc_ld != NULL ); + rs->sr_err = LDAP_SUCCESS; + do_return = 1; + + } else if ( META_BACK_CONN_CREATING( msc ) + || LDAP_BACK_CONN_BINDING( msc ) ) + { + if ( !LDAP_BACK_USE_TEMPORARIES( mi ) ) { + if ( dolock ) { + ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex ); + } + + ldap_pvt_thread_yield(); + goto retry_lock; + } + + /* sounds more appropriate */ + rs->sr_err = LDAP_BUSY; + do_return = 1; + + } else if ( META_BACK_CONN_INITED( msc ) ) { + assert( msc->msc_ld != NULL ); + rs->sr_err = LDAP_SUCCESS; + do_return = 1; + + } else { + /* + * creating... + */ + META_BACK_CONN_CREATING_SET( msc ); + } + + if ( dolock ) { + ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex ); } - msc->msc_mscflags = 0; + if ( do_return ) { + if ( rs->sr_err != LDAP_SUCCESS && op->o_conn && ( sendok & LDAP_BACK_SENDERR ) ) { + send_ldap_result( op, rs ); + } + + return rs->sr_err; + } + + assert( msc->msc_ld == NULL ); /* * Attempts to initialize the connection to the target ds */ + ldap_pvt_thread_mutex_lock( &mt->mt_uri_mutex ); rs->sr_err = ldap_initialize( &msc->msc_ld, mt->mt_uri ); +#ifdef HAVE_TLS + is_ldaps = ldap_is_ldaps_url( mt->mt_uri ); +#endif /* HAVE_TLS */ + ldap_pvt_thread_mutex_unlock( &mt->mt_uri_mutex ); if ( rs->sr_err != LDAP_SUCCESS ) { goto error_return; } @@ -303,7 +399,7 @@ meta_back_init_one_conn( #ifdef HAVE_TLS /* start TLS ("tls [try-]{start|propagate}" statement) */ if ( ( LDAP_BACK_USE_TLS( mi ) || ( op->o_conn->c_is_tls && LDAP_BACK_PROPAGATE_TLS( mi ) ) ) - && !ldap_is_ldaps_url( mt->mt_uri ) ) + && !is_ldaps ) { #ifdef SLAP_STARTTLS_ASYNCHRONOUS /* @@ -322,10 +418,12 @@ meta_back_init_one_conn( retry:; rc = ldap_result( msc->msc_ld, msgid, LDAP_MSG_ALL, &tv, &res ); - if ( rc < 0 ) { + switch ( rc ) { + case -1: rs->sr_err = LDAP_OTHER; + break; - } else if ( rc == 0 ) { + case 0: if ( nretries != 0 ) { if ( nretries > 0 ) { nretries--; @@ -334,15 +432,26 @@ retry:; goto retry; } rs->sr_err = LDAP_OTHER; + break; - } else if ( rc == LDAP_RES_EXTENDED ) { + default: + /* only touch when activity actually took place... */ + if ( mi->mi_idle_timeout != 0 && msc->msc_time < op->o_time ) { + msc->msc_time = op->o_time; + } + break; + } + + if ( rc == LDAP_RES_EXTENDED ) { struct berval *data = NULL; + /* NOTE: right now, data is unused, so don't get it */ rs->sr_err = ldap_parse_extended_result( msc->msc_ld, res, - NULL, &data, 0 ); + NULL, NULL /* &data */ , 0 ); if ( rs->sr_err == LDAP_SUCCESS ) { int err; + /* FIXME: matched? referrals? response controls? */ rs->sr_err = ldap_parse_result( msc->msc_ld, res, &err, NULL, NULL, NULL, NULL, 1 ); res = NULL; @@ -359,6 +468,7 @@ retry:; ldap_install_tls( msc->msc_ld ); } else if ( rs->sr_err == LDAP_REFERRAL ) { + /* FIXME: LDAP_OPERATIONS_ERROR? */ rs->sr_err = LDAP_OTHER; rs->sr_text = "unwilling to chase referral returned by Start TLS exop"; } @@ -393,7 +503,14 @@ retry:; if ( rs->sr_err == LDAP_SERVER_DOWN || ( rs->sr_err != LDAP_SUCCESS && LDAP_BACK_TLS_CRITICAL( mi ) ) ) { - ldap_unbind_ext_s( msc->msc_ld, NULL, NULL ); + +#ifdef DEBUG_205 + Debug( LDAP_DEBUG_ANY, "### %s meta_back_init_one_conn(TLS) ldap_unbind_ext[%d] ld=%p\n", + op->o_log_prefix, candidate, (void *)msc->msc_ld ); +#endif /* DEBUG_205 */ + + /* need to trash a failed Start TLS */ + meta_clear_one_candidate( op, mc, candidate ); goto error_return; } } @@ -417,21 +534,30 @@ retry:; */ if ( ispriv ) { - if ( !BER_BVISNULL( &mt->mt_pseudorootdn ) ) { - ber_dupbv( &msc->msc_bound_ndn, &mt->mt_pseudorootdn ); - if ( !BER_BVISNULL( &mt->mt_pseudorootpw ) ) { - ber_dupbv( &msc->msc_cred, &mt->mt_pseudorootpw ); + if ( !BER_BVISNULL( &mt->mt_idassert_authcDN ) ) { + ber_bvreplace( &msc->msc_bound_ndn, &mt->mt_idassert_authcDN ); + if ( !BER_BVISNULL( &mt->mt_idassert_passwd ) ) { + if ( !BER_BVISNULL( &msc->msc_cred ) ) { + memset( msc->msc_cred.bv_val, 0, + msc->msc_cred.bv_len ); + } + ber_bvreplace( &msc->msc_cred, &mt->mt_idassert_passwd ); } } else { - ber_str2bv( "", 0, 1, &msc->msc_bound_ndn ); + ber_bvreplace( &msc->msc_bound_ndn, &slap_empty_bv ); } - LDAP_BACK_CONN_ISPRIV_SET( msc ); - } else { - BER_BVZERO( &msc->msc_cred ); - BER_BVZERO( &msc->msc_bound_ndn ); + if ( !BER_BVISNULL( &msc->msc_cred ) ) { + memset( msc->msc_cred.bv_val, 0, msc->msc_cred.bv_len ); + ber_memfree_x( msc->msc_cred.bv_val, NULL ); + BER_BVZERO( &msc->msc_cred ); + } + if ( !BER_BVISNULL( &msc->msc_bound_ndn ) ) { + ber_memfree_x( msc->msc_bound_ndn.bv_val, NULL ); + BER_BVZERO( &msc->msc_bound_ndn ); + } if ( !BER_BVISEMPTY( &op->o_ndn ) && SLAP_IS_AUTHZ_BACKEND( op ) && isauthz ) @@ -447,30 +573,48 @@ retry:; if ( ldap_back_dn_massage( &dc, &op->o_conn->c_dn, &msc->msc_bound_ndn ) ) { - ldap_unbind_ext_s( msc->msc_ld, NULL, NULL ); + +#ifdef DEBUG_205 + Debug( LDAP_DEBUG_ANY, "### %s meta_back_init_one_conn(rewrite) ldap_unbind_ext[%d] ld=%p\n", + op->o_log_prefix, candidate, (void *)msc->msc_ld ); +#endif /* DEBUG_205 */ + + /* need to trash a connection not fully established */ + meta_clear_one_candidate( op, mc, candidate ); goto error_return; } - /* copy the DN idf needed */ + /* copy the DN if needed */ if ( msc->msc_bound_ndn.bv_val == op->o_conn->c_dn.bv_val ) { ber_dupbv( &msc->msc_bound_ndn, &op->o_conn->c_dn ); } + assert( !BER_BVISNULL( &msc->msc_bound_ndn ) ); + } else { - ber_str2bv( "", 0, 1, &msc->msc_bound_ndn ); + ber_dupbv( &msc->msc_bound_ndn, (struct berval *)&slap_empty_bv ); } } assert( !BER_BVISNULL( &msc->msc_bound_ndn ) ); error_return:; + if ( dolock ) { + ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex ); + } + META_BACK_CONN_CREATING_CLEAR( msc ); if ( rs->sr_err == LDAP_SUCCESS ) { /* * Sets a cookie for the rewrite session */ ( void )rewrite_session_init( mt->mt_rwmap.rwm_rw, op->o_conn ); + META_BACK_CONN_INITED_SET( msc ); + } + if ( dolock ) { + ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex ); + } - } else { + if ( rs->sr_err != LDAP_SUCCESS ) { rs->sr_err = slap_map_api2result( rs ); if ( sendok & LDAP_BACK_SENDERR ) { send_ldap_result( op, rs ); @@ -495,35 +639,48 @@ meta_back_retry( ldap_back_send_t sendok ) { metainfo_t *mi = ( metainfo_t * )op->o_bd->be_private; - metatarget_t *mt = &mi->mi_targets[ candidate ]; + metatarget_t *mt = mi->mi_targets[ candidate ]; metaconn_t *mc = *mcp; metasingleconn_t *msc = &mc->mc_conns[ candidate ]; int rc = LDAP_UNAVAILABLE, - binding = LDAP_BACK_CONN_BINDING( msc ); + binding; ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex ); + assert( !META_BACK_CONN_CREATING( msc ) ); + binding = LDAP_BACK_CONN_BINDING( msc ); + LDAP_BACK_CONN_BINDING_CLEAR( msc ); + assert( mc->mc_refcnt > 0 ); if ( mc->mc_refcnt == 1 ) { - char buf[ SLAP_TEXT_BUFLEN ]; + if ( StatslogTest( LDAP_DEBUG_ANY ) ) { + char buf[ SLAP_TEXT_BUFLEN ]; + + /* this lock is required; however, + * it's invoked only when logging is on */ + ldap_pvt_thread_mutex_lock( &mt->mt_uri_mutex ); + snprintf( buf, sizeof( buf ), + "retrying URI=\"%s\" DN=\"%s\"", + mt->mt_uri, + BER_BVISNULL( &msc->msc_bound_ndn ) ? + "" : msc->msc_bound_ndn.bv_val ); + ldap_pvt_thread_mutex_unlock( &mt->mt_uri_mutex ); - snprintf( buf, sizeof( buf ), - "retrying URI=\"%s\" DN=\"%s\"", - mt->mt_uri, - BER_BVISNULL( &msc->msc_bound_ndn ) ? - "" : msc->msc_bound_ndn.bv_val ); - Debug( LDAP_DEBUG_ANY, - "%s meta_back_retry[%d]: %s.\n", - op->o_log_prefix, candidate, buf ); + Debug( LDAP_DEBUG_ANY, + "%s meta_back_retry[%d]: %s.\n", + op->o_log_prefix, candidate, buf ); + } - meta_clear_one_candidate( msc ); + meta_clear_one_candidate( op, mc, candidate ); LDAP_BACK_CONN_ISBOUND_CLEAR( msc ); ( void )rewrite_session_delete( mt->mt_rwmap.rwm_rw, op->o_conn ); /* mc here must be the regular mc, reset and ready for init */ - rc = meta_back_init_one_conn( op, rs, mt, mc, candidate, - LDAP_BACK_CONN_ISPRIV( mc ), sendok ); + rc = meta_back_init_one_conn( op, rs, mc, candidate, + LDAP_BACK_CONN_ISPRIV( mc ), sendok, 0 ); + + /* restore the "binding" flag, in case */ if ( binding ) { LDAP_BACK_CONN_BINDING_SET( msc ); } @@ -531,25 +688,97 @@ meta_back_retry( if ( rc == LDAP_SUCCESS ) { rc = meta_back_single_dobind( op, rs, mcp, candidate, sendok, mt->mt_nretries, 0 ); + + Debug( LDAP_DEBUG_ANY, + "%s meta_back_retry[%d]: " + "meta_back_single_dobind=%d\n", + op->o_log_prefix, candidate, rc ); + if ( rc == LDAP_SUCCESS ) { + if ( !BER_BVISNULL( &msc->msc_bound_ndn ) && + !BER_BVISEMPTY( &msc->msc_bound_ndn ) ) + { + LDAP_BACK_CONN_ISBOUND_SET( msc ); + + } else { + LDAP_BACK_CONN_ISANON_SET( msc ); + } + + /* when bound, dispose of the "binding" flag */ + if ( binding ) { + LDAP_BACK_CONN_BINDING_CLEAR( msc ); + } + } } + + /* don't send twice */ + sendok &= ~LDAP_BACK_SENDERR; } if ( rc != LDAP_SUCCESS ) { + SlapReply *candidates = meta_back_candidates_get( op ); + + candidates[ candidate ].sr_err = rc; + if ( *mcp != NULL ) { - if ( binding ) { - LDAP_BACK_CONN_BINDING_CLEAR( msc ); + if ( mc->mc_refcnt == 1 ) { + if ( binding ) { + LDAP_BACK_CONN_BINDING_CLEAR( msc ); + } + (void)meta_clear_one_candidate( op, mc, candidate ); + } + + LDAP_BACK_CONN_TAINTED_SET( mc ); + /* only release if mandatory; otherwise + * let the caller do what's best before + * releasing */ + if ( META_BACK_ONERR_STOP( mi ) ) { + meta_back_release_conn_lock( op, mc, 0 ); + *mcp = NULL; + + } else { +#if META_BACK_PRINT_CONNTREE > 0 + meta_back_print_conntree( mi, ">>> meta_back_retry" ); +#endif /* META_BACK_PRINT_CONNTREE */ + + /* FIXME: could be done better, reworking meta_back_release_conn_lock() */ + if ( LDAP_BACK_PCONN_ISPRIV( mc ) ) { + if ( mc->mc_q.tqe_prev != NULL ) { + assert( LDAP_BACK_CONN_CACHED( mc ) ); + assert( mi->mi_conn_priv[ LDAP_BACK_CONN2PRIV( mc ) ].mic_num > 0 ); + LDAP_TAILQ_REMOVE( &mi->mi_conn_priv[ LDAP_BACK_CONN2PRIV( mc ) ].mic_priv, + mc, mc_q ); + mi->mi_conn_priv[ LDAP_BACK_CONN2PRIV( mc ) ].mic_num--; + mc->mc_q.tqe_prev = NULL; + mc->mc_q.tqe_next = NULL; + + } else { + assert( !LDAP_BACK_CONN_CACHED( mc ) ); + } + + } else { + /* FIXME: check if in tree, for consistency? */ + (void)avl_delete( &mi->mi_conninfo.lai_tree, + ( caddr_t )mc, meta_back_conndnmc_cmp ); + } + LDAP_BACK_CONN_CACHED_CLEAR( mc ); + +#if META_BACK_PRINT_CONNTREE > 0 + meta_back_print_conntree( mi, "<<< meta_back_retry" ); +#endif /* META_BACK_PRINT_CONNTREE */ } - meta_back_release_conn_lock( op, mc, 1, 0 ); - *mcp = NULL; } - if ( sendok ) { - rs->sr_err = LDAP_UNAVAILABLE; + if ( sendok & LDAP_BACK_SENDERR ) { + rs->sr_err = rc; rs->sr_text = NULL; send_ldap_result( op, rs ); } } + if ( META_BACK_TGT_QUARANTINE( mt ) ) { + meta_back_quarantine( op, rs, candidate ); + } + ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex ); return rc == LDAP_SUCCESS ? 1 : 0; @@ -644,9 +873,7 @@ meta_back_get_candidate( * and a default target is defined, and it is * a candidate, try using it (FIXME: YMMV) */ if ( mi->mi_defaulttarget != META_DEFAULT_TARGET_NONE - && meta_back_is_candidate( &mi->mi_targets[ mi->mi_defaulttarget ].mt_nsuffix, - mi->mi_targets[ mi->mi_defaulttarget ].mt_scope, - mi->mi_targets[ mi->mi_defaulttarget ].mt_subtree_exclude, + && meta_back_is_candidate( mi->mi_targets[ mi->mi_defaulttarget ], ndn, op->o_tag == LDAP_REQ_SEARCH ? op->ors_scope : LDAP_SCOPE_BASE ) ) { candidate = mi->mi_defaulttarget; @@ -716,9 +943,11 @@ meta_back_candidates_get( Operation *op ) } else if ( mc->mc_ntargets < mi->mi_ntargets ) { /* NOTE: in the future, may want to allow back-config * to add/remove targets from back-meta... */ - mc->mc_ntargets = mi->mi_ntargets; mc->mc_candidates = ch_realloc( mc->mc_candidates, - sizeof( SlapReply ) * mc->mc_ntargets ); + sizeof( SlapReply ) * mi->mi_ntargets ); + memset( &mc->mc_candidates[ mc->mc_ntargets ], 0, + sizeof( SlapReply ) * ( mi->mi_ntargets - mc->mc_ntargets ) ); + mc->mc_ntargets = mi->mi_ntargets; } return mc->mc_candidates; @@ -779,18 +1008,18 @@ meta_back_getconn( META_DNTYPE_ENTRY, META_DNTYPE_PARENT, META_DNTYPE_NEWPARENT - } dn_type = META_DNTYPE_ENTRY; + } dn_type = META_DNTYPE_ENTRY; struct berval ndn = op->o_req_ndn, pndn; SlapReply *candidates = meta_back_candidates_get( op ); /* Internal searches are privileged and shared. So is root. */ - /* FIXME: there seem to be concurrency issues */ - if ( op->o_do_not_cache || be_isroot( op ) ) { + /* FIXME: there seems to be concurrency issues */ + if ( META_BACK_PROXYAUTHZ_ALWAYS( mi ) || op->o_do_not_cache || be_isroot( op ) ) { mc_curr.mc_local_ndn = op->o_bd->be_rootndn; LDAP_BACK_CONN_ISPRIV_SET( &mc_curr ); - mc_curr.mc_conn = LDAP_BACK_PCONN_SET( op ); + LDAP_BACK_PCONN_ROOTDN_SET( &mc_curr, op ); } else { mc_curr.mc_local_ndn = op->o_ndn; @@ -800,37 +1029,110 @@ meta_back_getconn( mc_curr.mc_conn = op->o_conn; } else { - mc_curr.mc_conn = LDAP_BACK_PCONN_SET( op ); + LDAP_BACK_CONN_ISANON_SET( &mc_curr ); + LDAP_BACK_PCONN_ANON_SET( &mc_curr, op ); } } /* Explicit Bind requests always get their own conn */ if ( !( sendok & LDAP_BACK_BINDING ) ) { /* Searches for a metaconn in the avl tree */ -retry_lock: +retry_lock:; ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex ); - mc = (metaconn_t *)avl_find( mi->mi_conninfo.lai_tree, - (caddr_t)&mc_curr, meta_back_conndn_cmp ); - if ( mc ) { - if ( ( mi->mi_conn_ttl != 0 && op->o_time > mc->mc_create_time + mi->mi_conn_ttl ) - || ( mi->mi_idle_timeout != 0 && op->o_time > mc->mc_time + mi->mi_idle_timeout ) ) + if ( LDAP_BACK_PCONN_ISPRIV( &mc_curr ) ) { + /* lookup a conn that's not binding */ + LDAP_TAILQ_FOREACH( mc, + &mi->mi_conn_priv[ LDAP_BACK_CONN2PRIV( &mc_curr ) ].mic_priv, + mc_q ) { - /* don't let anyone else use this expired connection */ - (void)avl_delete( &mi->mi_conninfo.lai_tree, - (caddr_t)mc, meta_back_conndnmc_cmp ); + if ( !LDAP_BACK_CONN_BINDING( mc ) && mc->mc_refcnt == 0 ) { + break; + } + } - Debug( LDAP_DEBUG_TRACE, "%s meta_back_getconn: mc=%p conn=%ld expired.\n", - op->o_log_prefix, (void *)mc, LDAP_BACK_PCONN_ID( mc->mc_conn ) ); + if ( mc != NULL ) { + if ( mc != LDAP_TAILQ_LAST( &mi->mi_conn_priv[ LDAP_BACK_CONN2PRIV( mc ) ].mic_priv, + metaconn_t, mc_q ) ) + { + LDAP_TAILQ_REMOVE( &mi->mi_conn_priv[ LDAP_BACK_CONN2PRIV( mc ) ].mic_priv, + mc, mc_q ); + mc->mc_q.tqe_prev = NULL; + mc->mc_q.tqe_next = NULL; + LDAP_TAILQ_INSERT_TAIL( &mi->mi_conn_priv[ LDAP_BACK_CONN2PRIV( mc ) ].mic_priv, + mc, mc_q ); + } + + } else if ( !LDAP_BACK_USE_TEMPORARIES( mi ) + && mi->mi_conn_priv[ LDAP_BACK_CONN2PRIV( &mc_curr ) ].mic_num == mi->mi_conn_priv_max ) + { + mc = LDAP_TAILQ_FIRST( &mi->mi_conn_priv[ LDAP_BACK_CONN2PRIV( &mc_curr ) ].mic_priv ); } + - /* Don't reuse connections while they're still binding */ + } else { + mc = (metaconn_t *)avl_find( mi->mi_conninfo.lai_tree, + (caddr_t)&mc_curr, meta_back_conndn_cmp ); + } + + if ( mc ) { + /* catch taint errors */ + assert( !LDAP_BACK_CONN_TAINTED( mc ) ); + + /* Don't reuse connections while they're still binding + * NOTE: only makes sense for binds */ if ( LDAP_BACK_CONN_BINDING( mc ) ) { - ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex ); - ldap_pvt_thread_yield(); - goto retry_lock; - } + if ( !LDAP_BACK_USE_TEMPORARIES( mi ) ) { + ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex ); + + ldap_pvt_thread_yield(); + goto retry_lock; + } + + /* release conn, and create a temporary */ + mc = NULL; + + } else { + if ( ( mi->mi_conn_ttl != 0 && op->o_time > mc->mc_create_time + mi->mi_conn_ttl ) + || ( mi->mi_idle_timeout != 0 && op->o_time > mc->mc_time + mi->mi_idle_timeout ) ) + { +#if META_BACK_PRINT_CONNTREE > 0 + meta_back_print_conntree( mi, + ">>> meta_back_getconn(expired)" ); +#endif /* META_BACK_PRINT_CONNTREE */ + + /* don't let anyone else use this expired connection */ + if ( LDAP_BACK_PCONN_ISPRIV( mc ) ) { + if ( mc->mc_q.tqe_prev != NULL ) { + assert( LDAP_BACK_CONN_CACHED( mc ) ); + assert( mi->mi_conn_priv[ LDAP_BACK_CONN2PRIV( mc ) ].mic_num > 0 ); + LDAP_TAILQ_REMOVE( &mi->mi_conn_priv[ LDAP_BACK_CONN2PRIV( mc ) ].mic_priv, + mc, mc_q ); + mi->mi_conn_priv[ LDAP_BACK_CONN2PRIV( mc ) ].mic_num--; + mc->mc_q.tqe_prev = NULL; + mc->mc_q.tqe_next = NULL; + + } else { + assert( !LDAP_BACK_CONN_CACHED( mc ) ); + } - mc->mc_refcnt++; + } else { + (void)avl_delete( &mi->mi_conninfo.lai_tree, + (caddr_t)mc, meta_back_conndnmc_cmp ); + } + +#if META_BACK_PRINT_CONNTREE > 0 + meta_back_print_conntree( mi, + "<<< meta_back_getconn(expired)" ); +#endif /* META_BACK_PRINT_CONNTREE */ + LDAP_BACK_CONN_TAINTED_SET( mc ); + LDAP_BACK_CONN_CACHED_CLEAR( mc ); + + Debug( LDAP_DEBUG_TRACE, "%s meta_back_getconn: mc=%p conn=%ld expired (tainted).\n", + op->o_log_prefix, (void *)mc, LDAP_BACK_PCONN_ID( mc ) ); + } + + mc->mc_refcnt++; + } } ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex ); } @@ -859,12 +1161,12 @@ retry_lock: } break; + case LDAP_REQ_COMPARE: case LDAP_REQ_DELETE: case LDAP_REQ_MODIFY: /* just a unique candidate */ break; - case LDAP_REQ_COMPARE: case LDAP_REQ_SEARCH: /* allow multiple candidates for the searchBase */ op_type = META_OP_ALLOW_MULTIPLE; @@ -882,6 +1184,7 @@ retry_lock: /* Looks like we didn't get a bind. Open a new session... */ if ( mc == NULL ) { + assert( new_conn == 0 ); mc = metaconn_alloc( op ); mc->mc_conn = mc_curr.mc_conn; ber_dupbv( &mc->mc_local_ndn, &mc_curr.mc_local_ndn ); @@ -889,20 +1192,28 @@ retry_lock: if ( sendok & LDAP_BACK_BINDING ) { LDAP_BACK_CONN_BINDING_SET( mc ); } + if ( LDAP_BACK_CONN_ISPRIV( &mc_curr ) ) { + LDAP_BACK_CONN_ISPRIV_SET( mc ); + + } else if ( LDAP_BACK_CONN_ISANON( &mc_curr ) ) { + LDAP_BACK_CONN_ISANON_SET( mc ); + } + + } else if ( 0 ) { + /* TODO: if any of the connections is binding, + * release mc and create a new one */ } for ( i = 0; i < mi->mi_ntargets; i++ ) { - metatarget_t *mt = &mi->mi_targets[ i ]; - /* * The target is activated; if needed, it is * also init'd */ candidates[ i ].sr_err = meta_back_init_one_conn( op, - rs, mt, mc, i, - LDAP_BACK_CONN_ISPRIV( &mc_curr ), sendok ); + rs, mc, i, LDAP_BACK_CONN_ISPRIV( &mc_curr ), + LDAP_BACK_DONTSEND, !new_conn ); if ( candidates[ i ].sr_err == LDAP_SUCCESS ) { - candidates[ i ].sr_tag = META_CANDIDATE; + META_CANDIDATE_SET( &candidates[ i ] ); ncandidates++; } else { @@ -912,7 +1223,7 @@ retry_lock: * be init'd, should the other ones * be tried? */ - candidates[ i ].sr_tag = META_NOT_CANDIDATE; + META_CANDIDATE_RESET( &candidates[ i ] ); err = candidates[ i ].sr_err; continue; } @@ -920,7 +1231,8 @@ retry_lock: if ( ncandidates == 0 ) { if ( new_conn ) { - meta_back_freeconn( op, mc ); + mc->mc_refcnt = 0; + meta_back_conn_free( mc ); } else { meta_back_release_conn( op, mc ); @@ -958,7 +1270,7 @@ retry_lock: int j; for ( j = 0; j < mi->mi_ntargets; j++ ) { - candidates[ j ].sr_tag = META_NOT_CANDIDATE; + META_CANDIDATE_RESET( &candidates[ j ] ); } /* @@ -1020,20 +1332,31 @@ retry_lock2:; mc = (metaconn_t *)avl_find( mi->mi_conninfo.lai_tree, (caddr_t)&mc_curr, meta_back_conndn_cmp ); if ( mc != NULL ) { + /* catch taint errors */ + assert( !LDAP_BACK_CONN_TAINTED( mc ) ); + /* Don't reuse connections while they're still binding */ - if ( LDAP_BACK_CONN_BINDING( mc ) ) { - ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex ); - ldap_pvt_thread_yield(); - goto retry_lock2; - } + if ( META_BACK_CONN_CREATING( &mc->mc_conns[ i ] ) + || LDAP_BACK_CONN_BINDING( &mc->mc_conns[ i ] ) ) + { + if ( !LDAP_BACK_USE_TEMPORARIES( mi ) ) { + ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex ); + ldap_pvt_thread_yield(); + goto retry_lock2; + } - mc->mc_refcnt++; + mc = NULL; + + } else { + mc->mc_refcnt++; + } } ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex ); } /* Looks like we didn't get a bind. Open a new session... */ if ( mc == NULL ) { + assert( new_conn == 0 ); mc = metaconn_alloc( op ); mc->mc_conn = mc_curr.mc_conn; ber_dupbv( &mc->mc_local_ndn, &mc_curr.mc_local_ndn ); @@ -1041,6 +1364,12 @@ retry_lock2:; if ( sendok & LDAP_BACK_BINDING ) { LDAP_BACK_CONN_BINDING_SET( mc ); } + if ( LDAP_BACK_CONN_ISPRIV( &mc_curr ) ) { + LDAP_BACK_CONN_ISPRIV_SET( mc ); + + } else if ( LDAP_BACK_CONN_ISANON( &mc_curr ) ) { + LDAP_BACK_CONN_ISANON_SET( mc ); + } } } @@ -1049,7 +1378,7 @@ retry_lock2:; */ ( void )meta_clear_unused_candidates( op, i ); - mt = &mi->mi_targets[ i ]; + mt = mi->mi_targets[ i ]; msc = &mc->mc_conns[ i ]; /* @@ -1057,18 +1386,18 @@ retry_lock2:; * also init'd. In case of error, meta_back_init_one_conn * sends the appropriate result. */ - err = meta_back_init_one_conn( op, rs, mt, mc, i, - LDAP_BACK_CONN_ISPRIV( &mc_curr ), sendok ); + err = meta_back_init_one_conn( op, rs, mc, i, + LDAP_BACK_CONN_ISPRIV( &mc_curr ), sendok, !new_conn ); if ( err != LDAP_SUCCESS ) { /* * FIXME: in case one target cannot * be init'd, should the other ones * be tried? */ - candidates[ i ].sr_tag = META_NOT_CANDIDATE; + META_CANDIDATE_RESET( &candidates[ i ] ); if ( new_conn ) { - (void)meta_clear_one_candidate( msc ); - meta_back_freeconn( op, mc ); + mc->mc_refcnt = 0; + meta_back_conn_free( mc ); } else { meta_back_release_conn( op, mc ); @@ -1077,7 +1406,7 @@ retry_lock2:; } candidates[ i ].sr_err = LDAP_SUCCESS; - candidates[ i ].sr_tag = META_CANDIDATE; + META_CANDIDATE_SET( &candidates[ i ] ); ncandidates++; if ( candidate ) { @@ -1091,43 +1420,52 @@ retry_lock2:; /* Looks like we didn't get a bind. Open a new session... */ if ( mc == NULL ) { + assert( new_conn == 0 ); mc = metaconn_alloc( op ); mc->mc_conn = mc_curr.mc_conn; ber_dupbv( &mc->mc_local_ndn, &mc_curr.mc_local_ndn ); new_conn = 1; - if ( sendok & LDAP_BACK_BINDING ) { - LDAP_BACK_CONN_BINDING_SET( mc ); + if ( LDAP_BACK_CONN_ISPRIV( &mc_curr ) ) { + LDAP_BACK_CONN_ISPRIV_SET( mc ); + + } else if ( LDAP_BACK_CONN_ISANON( &mc_curr ) ) { + LDAP_BACK_CONN_ISANON_SET( mc ); } } for ( i = 0; i < mi->mi_ntargets; i++ ) { - metatarget_t *mt = &mi->mi_targets[ i ]; - metasingleconn_t *msc = &mc->mc_conns[ i ]; + metatarget_t *mt = mi->mi_targets[ i ]; + + META_CANDIDATE_RESET( &candidates[ i ] ); if ( i == cached - || meta_back_is_candidate( &mt->mt_nsuffix, - mt->mt_scope, - mt->mt_subtree_exclude, - &op->o_req_ndn, - LDAP_SCOPE_SUBTREE ) ) + || meta_back_is_candidate( mt, &op->o_req_ndn, + LDAP_SCOPE_SUBTREE ) ) { /* * The target is activated; if needed, it is * also init'd */ - int lerr = meta_back_init_one_conn( op, rs, - mt, mc, i, - LDAP_BACK_CONN_ISPRIV( &mc_curr ), - sendok ); + int lerr = meta_back_init_one_conn( op, rs, mc, i, + LDAP_BACK_CONN_ISPRIV( &mc_curr ), + LDAP_BACK_DONTSEND, !new_conn ); if ( lerr == LDAP_SUCCESS ) { - candidates[ i ].sr_tag = META_CANDIDATE; + META_CANDIDATE_SET( &candidates[ i ] ); candidates[ i ].sr_err = LDAP_SUCCESS; ncandidates++; Debug( LDAP_DEBUG_TRACE, "%s: meta_back_getconn[%d]\n", op->o_log_prefix, i, 0 ); + } else if ( lerr == LDAP_UNAVAILABLE && !META_BACK_ONERR_STOP( mi ) ) { + META_CANDIDATE_SET( &candidates[ i ] ); + candidates[ i ].sr_err = LDAP_UNAVAILABLE; + + Debug( LDAP_DEBUG_TRACE, "%s: meta_back_getconn[%d] %s\n", + op->o_log_prefix, i, + mt->mt_isquarantined != LDAP_BACK_FQ_NO ? "quarantined" : "unavailable" ); + } else { /* @@ -1136,36 +1474,61 @@ retry_lock2:; * be tried? */ if ( new_conn ) { - ( void )meta_clear_one_candidate( msc ); + ( void )meta_clear_one_candidate( op, mc, i ); } /* leave the target candidate, but record the error for later use */ candidates[ i ].sr_err = lerr; err = lerr; - Debug( LDAP_DEBUG_ANY, "%s: meta_back_getconn[%d] failed: %d\n", - op->o_log_prefix, i, lerr ); + if ( lerr == LDAP_UNAVAILABLE && mt->mt_isquarantined != LDAP_BACK_FQ_NO ) { + Debug( LDAP_DEBUG_TRACE, "%s: meta_back_getconn[%d] quarantined err=%d\n", + op->o_log_prefix, i, lerr ); + } else { + Debug( LDAP_DEBUG_ANY, "%s: meta_back_getconn[%d] failed err=%d\n", + op->o_log_prefix, i, lerr ); + } + + if ( META_BACK_ONERR_STOP( mi ) ) { + if ( sendok & LDAP_BACK_SENDERR ) { + send_ldap_result( op, rs ); + rs->sr_text = NULL; + } + if ( new_conn ) { + mc->mc_refcnt = 0; + meta_back_conn_free( mc ); + + } else { + meta_back_release_conn( op, mc ); + } + + return NULL; + } + + rs->sr_text = NULL; continue; } } else { if ( new_conn ) { - ( void )meta_clear_one_candidate( msc ); + ( void )meta_clear_one_candidate( op, mc, i ); } - candidates[ i ].sr_tag = META_NOT_CANDIDATE; } } if ( ncandidates == 0 ) { if ( new_conn ) { - meta_back_freeconn( op, mc ); + mc->mc_refcnt = 0; + meta_back_conn_free( mc ); } else { meta_back_release_conn( op, mc ); } - rs->sr_err = LDAP_NO_SUCH_OBJECT; - rs->sr_text = "Unable to select valid candidates"; + if ( rs->sr_err == LDAP_SUCCESS ) { + rs->sr_err = LDAP_NO_SUCH_OBJECT; + rs->sr_text = "Unable to select valid candidates"; + } if ( sendok & LDAP_BACK_SENDERR ) { if ( rs->sr_err == LDAP_NO_SUCH_OBJECT ) { @@ -1191,7 +1554,6 @@ done:; } if ( new_conn ) { - if ( mi->mi_conn_ttl ) { mc->mc_create_time = op->o_time; } @@ -1200,49 +1562,86 @@ done:; * Inserts the newly created metaconn in the avl tree */ ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex ); - err = avl_insert( &mi->mi_conninfo.lai_tree, ( caddr_t )mc, +#if META_BACK_PRINT_CONNTREE > 0 + meta_back_print_conntree( mi, ">>> meta_back_getconn" ); +#endif /* META_BACK_PRINT_CONNTREE */ + + if ( LDAP_BACK_PCONN_ISPRIV( mc ) ) { + if ( mi->mi_conn_priv[ LDAP_BACK_CONN2PRIV( mc ) ].mic_num < mi->mi_conn_priv_max ) { + LDAP_TAILQ_INSERT_TAIL( &mi->mi_conn_priv[ LDAP_BACK_CONN2PRIV( mc ) ].mic_priv, mc, mc_q ); + mi->mi_conn_priv[ LDAP_BACK_CONN2PRIV( mc ) ].mic_num++; + LDAP_BACK_CONN_CACHED_SET( mc ); + + } else { + LDAP_BACK_CONN_TAINTED_SET( mc ); + } + rs->sr_err = 0; + + } else { + err = avl_insert( &mi->mi_conninfo.lai_tree, ( caddr_t )mc, meta_back_conndn_cmp, meta_back_conndn_dup ); + LDAP_BACK_CONN_CACHED_SET( mc ); + } -#if PRINT_CONNTREE > 0 - myprint( mi->mi_conninfo.lai_tree ); -#endif /* PRINT_CONNTREE */ - +#if META_BACK_PRINT_CONNTREE > 0 + meta_back_print_conntree( mi, ">>> meta_back_getconn" ); +#endif /* META_BACK_PRINT_CONNTREE */ ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex ); - /* - * Err could be -1 in case a duplicate metaconn is inserted - * - * FIXME: what if the same client issues more than one - * asynchronous operations? - */ - if ( err != 0 ) { - Debug( LDAP_DEBUG_ANY, - "%s meta_back_getconn: candidates=%d conn=%ld insert failed\n", - op->o_log_prefix, ncandidates, - LDAP_BACK_PCONN_ID( mc->mc_conn ) ); - - rs->sr_err = LDAP_OTHER; - rs->sr_text = "Internal server error"; - meta_back_freeconn( op, mc ); - if ( sendok & LDAP_BACK_SENDERR ) { - send_ldap_result( op, rs ); - rs->sr_text = NULL; + if ( !LDAP_BACK_PCONN_ISPRIV( mc ) ) { + /* + * Err could be -1 in case a duplicate metaconn is inserted + */ + switch ( err ) { + case 0: + break; + + case -1: + LDAP_BACK_CONN_CACHED_CLEAR( mc ); + /* duplicate: free and try to get the newly created one */ + if ( !( sendok & LDAP_BACK_BINDING ) && !LDAP_BACK_USE_TEMPORARIES( mi ) ) { + mc->mc_refcnt = 0; + meta_back_conn_free( mc ); + + new_conn = 0; + goto retry_lock; + } + + LDAP_BACK_CONN_TAINTED_SET( mc ); + break; + + default: + LDAP_BACK_CONN_CACHED_CLEAR( mc ); + Debug( LDAP_DEBUG_ANY, + "%s meta_back_getconn: candidates=%d conn=%ld insert failed\n", + op->o_log_prefix, ncandidates, + LDAP_BACK_PCONN_ID( mc ) ); + + mc->mc_refcnt = 0; + meta_back_conn_free( mc ); + + rs->sr_err = LDAP_OTHER; + rs->sr_text = "proxy bind collision"; + if ( sendok & LDAP_BACK_SENDERR ) { + send_ldap_result( op, rs ); + rs->sr_text = NULL; + } + return NULL; } - return NULL; } Debug( LDAP_DEBUG_TRACE, "%s meta_back_getconn: candidates=%d conn=%ld inserted\n", op->o_log_prefix, ncandidates, - LDAP_BACK_PCONN_ID( mc->mc_conn ) ); + LDAP_BACK_PCONN_ID( mc ) ); } else { Debug( LDAP_DEBUG_TRACE, "%s meta_back_getconn: candidates=%d conn=%ld fetched\n", op->o_log_prefix, ncandidates, - LDAP_BACK_PCONN_ID( mc->mc_conn ) ); + LDAP_BACK_PCONN_ID( mc ) ); } - + return mc; } @@ -1250,7 +1649,6 @@ void meta_back_release_conn_lock( Operation *op, metaconn_t *mc, - int dofree, int dolock ) { metainfo_t *mi = ( metainfo_t * )op->o_bd->be_private; @@ -1262,21 +1660,140 @@ meta_back_release_conn_lock( } assert( mc->mc_refcnt > 0 ); mc->mc_refcnt--; - LDAP_BACK_CONN_BINDING_CLEAR( mc ); - if ( dofree - || ( mi->mi_conn_ttl != 0 && op->o_time > mc->mc_create_time + mi->mi_conn_ttl ) - || ( mi->mi_idle_timeout != 0 && op->o_time > mc->mc_time + mi->mi_idle_timeout ) ) - { - Debug( LDAP_DEBUG_TRACE, "%s meta_back_release_conn: mc=%p conn=%ld expired.\n", - op->o_log_prefix, (void *)mc, LDAP_BACK_PCONN_ID( mc->mc_conn ) ); - (void)avl_delete( &mi->mi_conninfo.lai_tree, - ( caddr_t )mc, meta_back_conndnmc_cmp ); + /* NOTE: the connection is removed if either it is tainted + * or if it is shared and no one else is using it. This needs + * to occur because for intrinsic reasons cached connections + * that are not privileged would live forever and pollute + * the connection space (and eat up resources). Maybe this + * should be configurable... */ + if ( LDAP_BACK_CONN_TAINTED( mc ) ) { + Debug( LDAP_DEBUG_TRACE, "%s meta_back_release_conn: mc=%p conn=%ld tainted.\n", + op->o_log_prefix, (void *)mc, LDAP_BACK_PCONN_ID( mc ) ); +#if META_BACK_PRINT_CONNTREE > 0 + meta_back_print_conntree( mi, ">>> meta_back_release_conn" ); +#endif /* META_BACK_PRINT_CONNTREE */ + + if ( LDAP_BACK_PCONN_ISPRIV( mc ) ) { + if ( mc->mc_q.tqe_prev != NULL ) { + assert( LDAP_BACK_CONN_CACHED( mc ) ); + assert( mi->mi_conn_priv[ LDAP_BACK_CONN2PRIV( mc ) ].mic_num > 0 ); + mi->mi_conn_priv[ LDAP_BACK_CONN2PRIV( mc ) ].mic_num--; + LDAP_TAILQ_REMOVE( &mi->mi_conn_priv[ LDAP_BACK_CONN2PRIV( mc ) ].mic_priv, mc, mc_q ); + + } else { + assert( !LDAP_BACK_CONN_CACHED( mc ) ); + } + mc->mc_q.tqe_prev = NULL; + mc->mc_q.tqe_next = NULL; + + } else { + metaconn_t *tmpmc; + + tmpmc = avl_delete( &mi->mi_conninfo.lai_tree, + ( caddr_t )mc, meta_back_conndnmc_cmp ); + + if ( tmpmc == NULL ) { + Debug( LDAP_DEBUG_TRACE, "%s: meta_back_release_conn: unable to find mc=%p\n", + op->o_log_prefix, (void *)mc, 0 ); + } else { + assert( tmpmc == mc ); + } + } + + LDAP_BACK_CONN_CACHED_CLEAR( mc ); + +#if META_BACK_PRINT_CONNTREE > 0 + meta_back_print_conntree( mi, "<<< meta_back_release_conn" ); +#endif /* META_BACK_PRINT_CONNTREE */ + if ( mc->mc_refcnt == 0 ) { - meta_clear_candidates( op, mc ); meta_back_conn_free( mc ); + mc = NULL; } } + + if ( mc != NULL && LDAP_BACK_CONN_BINDING( mc ) ) { + LDAP_BACK_CONN_BINDING_CLEAR( mc ); + } + if ( dolock ) { ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex ); } } + +void +meta_back_quarantine( + Operation *op, + SlapReply *rs, + int candidate ) +{ + metainfo_t *mi = (metainfo_t *)op->o_bd->be_private; + metatarget_t *mt = mi->mi_targets[ candidate ]; + + slap_retry_info_t *ri = &mt->mt_quarantine; + + ldap_pvt_thread_mutex_lock( &mt->mt_quarantine_mutex ); + + if ( rs->sr_err == LDAP_UNAVAILABLE ) { + time_t new_last = slap_get_time(); + + switch ( mt->mt_isquarantined ) { + case LDAP_BACK_FQ_NO: + if ( ri->ri_last == new_last ) { + goto done; + } + + Debug( LDAP_DEBUG_ANY, + "%s meta_back_quarantine[%d]: enter.\n", + op->o_log_prefix, candidate, 0 ); + + ri->ri_idx = 0; + ri->ri_count = 0; + break; + + case LDAP_BACK_FQ_RETRYING: + if ( StatslogTest( LDAP_DEBUG_ANY ) ) { + char buf[ SLAP_TEXT_BUFLEN ]; + + snprintf( buf, sizeof( buf ), + "meta_back_quarantine[%d]: block #%d try #%d failed", + candidate, ri->ri_idx, ri->ri_count ); + Debug( LDAP_DEBUG_ANY, "%s %s.\n", + op->o_log_prefix, buf, 0 ); + } + + ++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: + break; + } + + mt->mt_isquarantined = LDAP_BACK_FQ_YES; + ri->ri_last = new_last; + + } else if ( mt->mt_isquarantined == LDAP_BACK_FQ_RETRYING ) { + Debug( LDAP_DEBUG_ANY, + "%s meta_back_quarantine[%d]: exit.\n", + op->o_log_prefix, candidate, 0 ); + + if ( mi->mi_quarantine_f ) { + (void)mi->mi_quarantine_f( mi, candidate, + mi->mi_quarantine_p ); + } + + ri->ri_count = 0; + ri->ri_idx = 0; + mt->mt_isquarantined = LDAP_BACK_FQ_NO; + } + +done:; + ldap_pvt_thread_mutex_unlock( &mt->mt_quarantine_mutex ); +} + diff --git a/servers/slapd/back-meta/delete.c b/servers/slapd/back-meta/delete.c index 3e188e09ab..883fac495c 100644 --- a/servers/slapd/back-meta/delete.c +++ b/servers/slapd/back-meta/delete.c @@ -35,13 +35,14 @@ int meta_back_delete( Operation *op, SlapReply *rs ) { metainfo_t *mi = ( metainfo_t * )op->o_bd->be_private; + metatarget_t *mt; metaconn_t *mc = NULL; int candidate = -1; struct berval mdn = BER_BVNULL; dncookie dc; int msgid; int do_retry = 1; - int maperr = 1; + LDAPControl **ctrls = NULL; mc = meta_back_getconn( op, rs, &candidate, LDAP_BACK_SENDERR ); if ( !mc || !meta_back_dobind( op, rs, mc, LDAP_BACK_SENDERR ) ) { @@ -53,82 +54,47 @@ meta_back_delete( Operation *op, SlapReply *rs ) /* * Rewrite the compare dn, if needed */ - dc.target = &mi->mi_targets[ candidate ]; + mt = mi->mi_targets[ candidate ]; + dc.target = mt; dc.conn = op->o_conn; dc.rs = rs; dc.ctx = "deleteDN"; if ( ldap_back_dn_massage( &dc, &op->o_req_dn, &mdn ) ) { send_ldap_result( op, rs ); - goto done; + goto cleanup; } retry:; + ctrls = op->o_ctrls; + if ( ldap_back_proxy_authz_ctrl( &mc->mc_conns[ candidate ].msc_bound_ndn, + mt->mt_version, &mt->mt_idassert, op, rs, &ctrls ) != LDAP_SUCCESS ) + { + send_ldap_result( op, rs ); + goto cleanup; + } + rs->sr_err = ldap_delete_ext( mc->mc_conns[ candidate ].msc_ld, - mdn.bv_val, op->o_ctrls, NULL, &msgid ); + mdn.bv_val, ctrls, NULL, &msgid ); + rs->sr_err = meta_back_op_result( mc, op, rs, candidate, msgid, + mt->mt_timeout[ SLAP_OP_DELETE ], LDAP_BACK_SENDRESULT ); if ( rs->sr_err == LDAP_UNAVAILABLE && do_retry ) { do_retry = 0; if ( meta_back_retry( op, rs, &mc, candidate, LDAP_BACK_SENDERR ) ) { + /* if the identity changed, there might be need to re-authz */ + (void)ldap_back_proxy_authz_ctrl_free( op, &ctrls ); goto retry; } - goto cleanup; - - } else if ( rs->sr_err == LDAP_SUCCESS ) { - struct timeval tv, *tvp = NULL; - LDAPMessage *res = NULL; - int rc; - - if ( mi->mi_targets[ candidate ].mt_timeout[ LDAP_BACK_OP_DELETE ] != 0 ) { - tv.tv_sec = mi->mi_targets[ candidate ].mt_timeout[ LDAP_BACK_OP_DELETE ]; - tv.tv_usec = 0; - tvp = &tv; - } - - rs->sr_err = LDAP_OTHER; - maperr = 0; - rc = ldap_result( mc->mc_conns[ candidate ].msc_ld, - msgid, LDAP_MSG_ALL, tvp, &res ); - switch ( rc ) { - case -1: - rs->sr_err = LDAP_OTHER; - break; - - case 0: - ldap_abandon_ext( mc->mc_conns[ candidate ].msc_ld, - msgid, NULL, NULL ); - rs->sr_err = op->o_protocol >= LDAP_VERSION3 ? - LDAP_ADMINLIMIT_EXCEEDED : LDAP_OPERATIONS_ERROR; - break; - - case LDAP_RES_DELETE: - rc = ldap_parse_result( mc->mc_conns[ candidate ].msc_ld, - res, &rs->sr_err, NULL, NULL, NULL, NULL, 1 ); - if ( rc != LDAP_SUCCESS ) { - rs->sr_err = rc; - } - maperr = 1; - break; - - default: - ldap_msgfree( res ); - break; - } - } - - if ( maperr ) { - rs->sr_err = meta_back_op_result( mc, op, rs, candidate ); - - } else { - send_ldap_result( op, rs ); } cleanup:; + (void)ldap_back_proxy_authz_ctrl_free( op, &ctrls ); + if ( mdn.bv_val != op->o_req_dn.bv_val ) { free( mdn.bv_val ); BER_BVZERO( &mdn ); } -done:; if ( mc ) { meta_back_release_conn( op, mc ); } diff --git a/servers/slapd/back-meta/init.c b/servers/slapd/back-meta/init.c index 2c625acb6d..13d59f8a73 100644 --- a/servers/slapd/back-meta/init.c +++ b/servers/slapd/back-meta/init.c @@ -76,6 +76,7 @@ meta_back_db_init( Backend *be ) { metainfo_t *mi; + int i; mi = ch_calloc( 1, sizeof( metainfo_t ) ); if ( mi == NULL ) { @@ -90,12 +91,20 @@ meta_back_db_init( mi->mi_bind_timeout.tv_sec = 0; mi->mi_bind_timeout.tv_usec = META_BIND_TIMEOUT; + mi->mi_rebind_f = meta_back_default_rebind; + ldap_pvt_thread_mutex_init( &mi->mi_conninfo.lai_mutex ); ldap_pvt_thread_mutex_init( &mi->mi_cache.mutex ); /* safe default */ mi->mi_nretries = META_RETRY_DEFAULT; mi->mi_version = LDAP_VERSION3; + + for ( i = LDAP_BACK_PCONN_FIRST; i < LDAP_BACK_PCONN_LAST; i++ ) { + mi->mi_conn_priv[ i ].mic_num = 0; + LDAP_TAILQ_INIT( &mi->mi_conn_priv[ i ].mic_priv ); + } + mi->mi_conn_priv_max = LDAP_BACK_CONN_PRIV_DEFAULT; be->be_private = mi; @@ -108,46 +117,73 @@ meta_back_db_open( { metainfo_t *mi = (metainfo_t *)be->be_private; - int i, rc; + int i, + not_always = 0, + rc; for ( i = 0; i < mi->mi_ntargets; i++ ) { - if ( mi->mi_targets[ i ].mt_flags & LDAP_BACK_F_SUPPORT_T_F_DISCOVER ) - { - mi->mi_targets[ i ].mt_flags &= ~LDAP_BACK_F_SUPPORT_T_F_DISCOVER; - rc = slap_discover_feature( mi->mi_targets[ i ].mt_uri, - mi->mi_targets[ i ].mt_version, + metatarget_t *mt = mi->mi_targets[ i ]; + + if ( META_BACK_TGT_T_F_DISCOVER( mt ) ) { + rc = slap_discover_feature( mt->mt_uri, mt->mt_version, slap_schema.si_ad_supportedFeatures->ad_cname.bv_val, LDAP_FEATURE_ABSOLUTE_FILTERS ); if ( rc == LDAP_COMPARE_TRUE ) { - mi->mi_targets[ i ].mt_flags |= LDAP_BACK_F_SUPPORT_T_F; + mt->mt_flags |= LDAP_BACK_F_T_F; + } + } + + if ( META_BACK_TGT_CANCEL_DISCOVER( mt ) ) { + rc = slap_discover_feature( mt->mt_uri, mt->mt_version, + slap_schema.si_ad_supportedExtension->ad_cname.bv_val, + LDAP_EXOP_CANCEL ); + if ( rc == LDAP_COMPARE_TRUE ) { + mt->mt_flags |= LDAP_BACK_F_CANCEL_EXOP; } } + + if ( not_always == 0 ) { + if ( !( mt->mt_idassert_flags & LDAP_BACK_AUTH_OVERRIDE ) + || !( mt->mt_idassert_flags & LDAP_BACK_AUTH_AUTHZ_ALL ) ) + { + not_always = 1; + } + } + } + + if ( not_always == 0 ) { + mi->mi_flags |= META_BACK_F_PROXYAUTHZ_ALWAYS; } return 0; } +/* + * meta_back_conn_free() + * + * actually frees a connection; the reference count must be 0, + * and it must not (or no longer) be in the cache. + */ void meta_back_conn_free( void *v_mc ) { metaconn_t *mc = v_mc; - int i, ntargets; + int ntargets; assert( mc != NULL ); assert( mc->mc_refcnt == 0 ); - if ( !BER_BVISNULL( &mc->mc_local_ndn ) ) { - free( mc->mc_local_ndn.bv_val ); - } - - assert( mc->mc_conns != NULL ); - /* at least one must be present... */ - ntargets = mc->mc_conns[ 0 ].msc_info->mi_ntargets; + ntargets = mc->mc_info->mi_ntargets; + assert( ntargets > 0 ); + + for ( ; ntargets--; ) { + (void)meta_clear_one_candidate( NULL, mc, ntargets ); + } - for ( i = 0; i < ntargets; i++ ) { - (void)meta_clear_one_candidate( &mc->mc_conns[ i ] ); + if ( !BER_BVISNULL( &mc->mc_local_ndn ) ) { + free( mc->mc_local_ndn.bv_val ); } free( mc ); @@ -180,6 +216,7 @@ target_free( { if ( mt->mt_uri ) { free( mt->mt_uri ); + ldap_pvt_thread_mutex_destroy( &mt->mt_uri_mutex ); } if ( mt->mt_subtree_exclude ) { ber_bvarray_free( mt->mt_subtree_exclude ); @@ -196,11 +233,26 @@ target_free( if ( !BER_BVISNULL( &mt->mt_bindpw ) ) { free( mt->mt_bindpw.bv_val ); } - if ( !BER_BVISNULL( &mt->mt_pseudorootdn ) ) { - free( mt->mt_pseudorootdn.bv_val ); + if ( !BER_BVISNULL( &mt->mt_idassert_authcID ) ) { + ch_free( mt->mt_idassert_authcID.bv_val ); + } + if ( !BER_BVISNULL( &mt->mt_idassert_authcDN ) ) { + ch_free( mt->mt_idassert_authcDN.bv_val ); + } + if ( !BER_BVISNULL( &mt->mt_idassert_passwd ) ) { + ch_free( mt->mt_idassert_passwd.bv_val ); + } + if ( !BER_BVISNULL( &mt->mt_idassert_authzID ) ) { + ch_free( mt->mt_idassert_authzID.bv_val ); + } + if ( !BER_BVISNULL( &mt->mt_idassert_sasl_mech ) ) { + ch_free( mt->mt_idassert_sasl_mech.bv_val ); + } + if ( !BER_BVISNULL( &mt->mt_idassert_sasl_realm ) ) { + ch_free( mt->mt_idassert_sasl_realm.bv_val ); } - if ( !BER_BVISNULL( &mt->mt_pseudorootpw ) ) { - free( mt->mt_pseudorootpw.bv_val ); + if ( mt->mt_idassert_authz != NULL ) { + ber_bvarray_free( mt->mt_idassert_authz ); } if ( mt->mt_rwmap.rwm_rw ) { rewrite_info_delete( &mt->mt_rwmap.rwm_rw ); @@ -209,6 +261,8 @@ target_free( avl_free( mt->mt_rwmap.rwm_oc.map, mapping_free ); avl_free( mt->mt_rwmap.rwm_at.remap, mapping_dst_free ); avl_free( mt->mt_rwmap.rwm_at.map, mapping_free ); + + free( mt ); } int @@ -230,6 +284,14 @@ meta_back_db_destroy( if ( mi->mi_conninfo.lai_tree ) { avl_free( mi->mi_conninfo.lai_tree, meta_back_conn_free ); } + for ( i = LDAP_BACK_PCONN_FIRST; i < LDAP_BACK_PCONN_LAST; i++ ) { + while ( !LDAP_TAILQ_EMPTY( &mi->mi_conn_priv[ i ].mic_priv ) ) { + metaconn_t *mc = LDAP_TAILQ_FIRST( &mi->mi_conn_priv[ i ].mic_priv ); + + LDAP_TAILQ_REMOVE( &mi->mi_conn_priv[ i ].mic_priv, mc, mc_q ); + meta_back_conn_free( mc ); + } + } /* * Destroy the per-target stuff (assuming there's at @@ -237,7 +299,18 @@ meta_back_db_destroy( */ if ( mi->mi_targets != NULL ) { for ( i = 0; i < mi->mi_ntargets; i++ ) { - target_free( &mi->mi_targets[ i ] ); + metatarget_t *mt = mi->mi_targets[ i ]; + + if ( META_BACK_TGT_QUARANTINE( mt ) ) { + if ( mt->mt_quarantine.ri_num != mi->mi_quarantine.ri_num ) + { + slap_retry_info_destroy( &mt->mt_quarantine ); + } + + ldap_pvt_thread_mutex_destroy( &mt->mt_quarantine_mutex ); + } + + target_free( mt ); } free( mi->mi_targets ); @@ -257,6 +330,10 @@ meta_back_db_destroy( if ( mi->mi_candidates != NULL ) { ber_memfree_x( mi->mi_candidates, NULL ); } + + if ( META_BACK_QUARANTINE( mi ) ) { + slap_retry_info_destroy( &mi->mi_quarantine ); + } } free( be->be_private ); diff --git a/servers/slapd/back-meta/map.c b/servers/slapd/back-meta/map.c index e6291cfbb8..4d6aa59b61 100644 --- a/servers/slapd/back-meta/map.c +++ b/servers/slapd/back-meta/map.c @@ -90,19 +90,19 @@ ldap_back_map_init ( struct ldapmap *lm, struct ldapmapping **m ) assert( m != NULL ); *m = NULL; - + mapping = (struct ldapmapping *)ch_calloc( 2, sizeof( struct ldapmapping ) ); if ( mapping == NULL ) { return; } - ber_str2bv( "objectclass", sizeof("objectclass")-1, 1, &mapping->src); - ber_dupbv( &mapping->dst, &mapping->src ); - mapping[1].src = mapping->src; - mapping[1].dst = mapping->dst; + ber_str2bv( "objectclass", STRLENOF("objectclass"), 1, &mapping[0].src); + ber_dupbv( &mapping[0].dst, &mapping[0].src ); + mapping[1].src = mapping[0].src; + mapping[1].dst = mapping[0].dst; - avl_insert( &lm->map, (caddr_t)mapping, + avl_insert( &lm->map, (caddr_t)&mapping[0], mapping_cmp, mapping_dup ); avl_insert( &lm->remap, (caddr_t)&mapping[1], mapping_cmp, mapping_dup ); @@ -120,6 +120,7 @@ ldap_back_mapping ( struct ldapmap *map, struct berval *s, struct ldapmapping ** if ( remap == BACKLDAP_REMAP ) { tree = map->remap; + } else { tree = map->map; } @@ -139,6 +140,13 @@ ldap_back_map ( struct ldapmap *map, struct berval *s, struct berval *bv, { struct ldapmapping *mapping; + /* map->map may be NULL when mapping is configured, + * but map->remap can't */ + if ( map->remap == NULL ) { + *bv = *s; + return; + } + BER_BVZERO( bv ); ( void )ldap_back_mapping( map, s, &mapping, remap ); if ( mapping != NULL ) { @@ -297,6 +305,9 @@ ldap_back_int_filter_map_rewrite( ber_bvnone = BER_BVC( "(?=none)" ); ber_len_t len; + assert( fstr != NULL ); + BER_BVZERO( fstr ); + if ( f == NULL ) { ber_dupbv( fstr, &ber_bvnone ); return LDAP_OTHER; @@ -513,7 +524,7 @@ ldap_back_int_filter_map_rewrite( /* FIXME: treat UNDEFINED as FALSE */ case SLAPD_COMPARE_UNDEFINED: computed:; - if ( dc->target->mt_flags & LDAP_BACK_F_SUPPORT_T_F ) { + if ( META_BACK_TGT_T_F( dc->target ) ) { tmp = &ber_bvtf_false; break; } @@ -521,7 +532,7 @@ computed:; break; case LDAP_COMPARE_TRUE: - if ( dc->target->mt_flags & LDAP_BACK_F_SUPPORT_T_F ) { + if ( META_BACK_TGT_T_F( dc->target ) ) { tmp = &ber_bvtf_true; break; } diff --git a/servers/slapd/back-meta/modify.c b/servers/slapd/back-meta/modify.c index a07b4cef40..40d67b5368 100644 --- a/servers/slapd/back-meta/modify.c +++ b/servers/slapd/back-meta/modify.c @@ -35,9 +35,9 @@ int meta_back_modify( Operation *op, SlapReply *rs ) { metainfo_t *mi = ( metainfo_t * )op->o_bd->be_private; + metatarget_t *mt; metaconn_t *mc; int rc = 0; - int maperr = 1; LDAPMod **modv = NULL; LDAPMod *mods = NULL; Modifications *ml; @@ -48,6 +48,7 @@ meta_back_modify( Operation *op, SlapReply *rs ) dncookie dc; int msgid; int do_retry = 1; + LDAPControl **ctrls = NULL; mc = meta_back_getconn( op, rs, &candidate, LDAP_BACK_SENDERR ); if ( !mc || !meta_back_dobind( op, rs, mc, LDAP_BACK_SENDERR ) ) { @@ -59,13 +60,14 @@ meta_back_modify( Operation *op, SlapReply *rs ) /* * Rewrite the modify dn, if needed */ - dc.target = &mi->mi_targets[ candidate ]; + mt = mi->mi_targets[ candidate ]; + dc.target = mt; dc.conn = op->o_conn; dc.rs = rs; dc.ctx = "modifyDN"; if ( ldap_back_dn_massage( &dc, &op->o_req_dn, &mdn ) ) { - maperr = 0; + send_ldap_result( op, rs ); goto cleanup; } @@ -74,14 +76,14 @@ meta_back_modify( Operation *op, SlapReply *rs ) mods = ch_malloc( sizeof( LDAPMod )*i ); if ( mods == NULL ) { - rs->sr_err = LDAP_NO_MEMORY; - maperr = 0; + rs->sr_err = LDAP_OTHER; + send_ldap_result( op, rs ); goto cleanup; } modv = ( LDAPMod ** )ch_malloc( ( i + 1 )*sizeof( LDAPMod * ) ); if ( modv == NULL ) { - rs->sr_err = LDAP_NO_MEMORY; - maperr = 0; + rs->sr_err = LDAP_OTHER; + send_ldap_result( op, rs ); goto cleanup; } @@ -102,7 +104,7 @@ meta_back_modify( Operation *op, SlapReply *rs ) mapped = ml->sml_desc->ad_cname; } else { - ldap_back_map( &mi->mi_targets[ candidate ].mt_rwmap.rwm_at, + ldap_back_map( &mt->mt_rwmap.rwm_at, &ml->sml_desc->ad_cname, &mapped, BACKLDAP_MAP ); if ( BER_BVISNULL( &mapped ) || BER_BVISEMPTY( &mapped ) ) { @@ -129,11 +131,11 @@ meta_back_modify( Operation *op, SlapReply *rs ) for ( j = 0; !BER_BVISNULL( &ml->sml_values[ j ] ); ) { struct ldapmapping *mapping; - ldap_back_mapping( &mi->mi_targets[ candidate ].mt_rwmap.rwm_oc, + ldap_back_mapping( &mt->mt_rwmap.rwm_oc, &ml->sml_values[ j ], &mapping, BACKLDAP_MAP ); if ( mapping == NULL ) { - if ( mi->mi_targets[ candidate ].mt_rwmap.rwm_oc.drop_missing ) { + if ( mt->mt_rwmap.rwm_oc.drop_missing ) { continue; } mods[ i ].mod_bvalues[ j ] = &ml->sml_values[ j ]; @@ -175,66 +177,30 @@ meta_back_modify( Operation *op, SlapReply *rs ) modv[ i ] = 0; retry:; + ctrls = op->o_ctrls; + rc = ldap_back_proxy_authz_ctrl( &mc->mc_conns[ candidate ].msc_bound_ndn, + mt->mt_version, &mt->mt_idassert, op, rs, &ctrls ); + if ( rc != LDAP_SUCCESS ) { + send_ldap_result( op, rs ); + goto cleanup; + } + rs->sr_err = ldap_modify_ext( mc->mc_conns[ candidate ].msc_ld, mdn.bv_val, - modv, op->o_ctrls, NULL, &msgid ); + modv, ctrls, NULL, &msgid ); + rs->sr_err = meta_back_op_result( mc, op, rs, candidate, msgid, + mt->mt_timeout[ SLAP_OP_MODIFY ], LDAP_BACK_SENDRESULT ); if ( rs->sr_err == LDAP_UNAVAILABLE && do_retry ) { do_retry = 0; if ( meta_back_retry( op, rs, &mc, candidate, LDAP_BACK_SENDERR ) ) { + /* if the identity changed, there might be need to re-authz */ + (void)ldap_back_proxy_authz_ctrl_free( op, &ctrls ); goto retry; } - goto done; - - } else if ( rs->sr_err == LDAP_SUCCESS ) { - struct timeval tv, *tvp = NULL; - LDAPMessage *res = NULL; - - if ( mi->mi_targets[ candidate ].mt_timeout[ LDAP_BACK_OP_MODIFY ] != 0 ) { - tv.tv_sec = mi->mi_targets[ candidate ].mt_timeout[ LDAP_BACK_OP_MODIFY ]; - tv.tv_usec = 0; - tvp = &tv; - } - - rs->sr_err = LDAP_OTHER; - rc = ldap_result( mc->mc_conns[ candidate ].msc_ld, - msgid, LDAP_MSG_ALL, tvp, &res ); - switch ( rc ) { - case -1: - maperr = 0; - break; - - case 0: - ldap_abandon_ext( mc->mc_conns[ candidate ].msc_ld, - msgid, NULL, NULL ); - rs->sr_err = op->o_protocol >= LDAP_VERSION3 ? - LDAP_ADMINLIMIT_EXCEEDED : LDAP_OPERATIONS_ERROR; - maperr = 0; - break; - - case LDAP_RES_MODIFY: - rc = ldap_parse_result( mc->mc_conns[ candidate ].msc_ld, - res, &rs->sr_err, NULL, NULL, NULL, NULL, 1 ); - if ( rc != LDAP_SUCCESS ) { - rs->sr_err = rc; - } - maperr = 1; - break; - - default: - maperr = 0; - ldap_msgfree( res ); - break; - } } cleanup:; - if ( maperr ) { - rc = meta_back_op_result( mc, op, rs, candidate ); - - } else { - send_ldap_result( op, rs ); - } + (void)ldap_back_proxy_authz_ctrl_free( op, &ctrls ); -done:; if ( mdn.bv_val != op->o_req_dn.bv_val ) { free( mdn.bv_val ); BER_BVZERO( &mdn ); diff --git a/servers/slapd/back-meta/modrdn.c b/servers/slapd/back-meta/modrdn.c index cb84beb77f..604257cc9d 100644 --- a/servers/slapd/back-meta/modrdn.c +++ b/servers/slapd/back-meta/modrdn.c @@ -35,6 +35,7 @@ int meta_back_modrdn( Operation *op, SlapReply *rs ) { metainfo_t *mi = ( metainfo_t * )op->o_bd->be_private; + metatarget_t *mt; metaconn_t *mc; int candidate = -1; struct berval mdn = BER_BVNULL, @@ -42,7 +43,7 @@ meta_back_modrdn( Operation *op, SlapReply *rs ) dncookie dc; int msgid; int do_retry = 1; - int maperr = 1; + LDAPControl **ctrls = NULL; mc = meta_back_getconn( op, rs, &candidate, LDAP_BACK_SENDERR ); if ( !mc || !meta_back_dobind( op, rs, mc, LDAP_BACK_SENDERR ) ) { @@ -51,6 +52,8 @@ meta_back_modrdn( Operation *op, SlapReply *rs ) assert( mc->mc_conns[ candidate ].msc_ld != NULL ); + mt = mi->mi_targets[ candidate ]; + dc.target = mt; dc.conn = op->o_conn; dc.rs = rs; @@ -76,7 +79,7 @@ meta_back_modrdn( Operation *op, SlapReply *rs ) */ /* needs LDAPv3 */ - switch ( mi->mi_targets[ candidate ].mt_version ) { + switch ( mt->mt_version ) { case LDAP_VERSION3: break; @@ -90,18 +93,17 @@ meta_back_modrdn( Operation *op, SlapReply *rs ) /* op->o_protocol cannot be anything but LDAPv3, * otherwise wouldn't be here */ rs->sr_err = LDAP_UNWILLING_TO_PERFORM; - maperr = 0; + send_ldap_result( op, rs ); goto cleanup; } /* * Rewrite the new superior, if defined and required */ - dc.target = &mi->mi_targets[ candidate ]; dc.ctx = "newSuperiorDN"; if ( ldap_back_dn_massage( &dc, op->orr_newSup, &mnewSuperior ) ) { rs->sr_err = LDAP_OTHER; - maperr = 0; + send_ldap_result( op, rs ); goto cleanup; } } @@ -109,76 +111,40 @@ meta_back_modrdn( Operation *op, SlapReply *rs ) /* * Rewrite the modrdn dn, if required */ - dc.target = &mi->mi_targets[ candidate ]; dc.ctx = "modrDN"; if ( ldap_back_dn_massage( &dc, &op->o_req_dn, &mdn ) ) { rs->sr_err = LDAP_OTHER; - maperr = 0; + send_ldap_result( op, rs ); goto cleanup; } retry:; + ctrls = op->o_ctrls; + if ( ldap_back_proxy_authz_ctrl( &mc->mc_conns[ candidate ].msc_bound_ndn, + mt->mt_version, &mt->mt_idassert, op, rs, &ctrls ) != LDAP_SUCCESS ) + { + send_ldap_result( op, rs ); + goto cleanup; + } + rs->sr_err = ldap_rename( mc->mc_conns[ candidate ].msc_ld, mdn.bv_val, op->orr_newrdn.bv_val, mnewSuperior.bv_val, op->orr_deleteoldrdn, - op->o_ctrls, NULL, &msgid ); + ctrls, NULL, &msgid ); + rs->sr_err = meta_back_op_result( mc, op, rs, candidate, msgid, + mt->mt_timeout[ SLAP_OP_MODRDN ], LDAP_BACK_SENDRESULT ); if ( rs->sr_err == LDAP_UNAVAILABLE && do_retry ) { do_retry = 0; if ( meta_back_retry( op, rs, &mc, candidate, LDAP_BACK_SENDERR ) ) { + /* if the identity changed, there might be need to re-authz */ + (void)ldap_back_proxy_authz_ctrl_free( op, &ctrls ); goto retry; } - goto done; - - } else if ( rs->sr_err == LDAP_SUCCESS ) { - struct timeval tv, *tvp = NULL; - LDAPMessage *res = NULL; - int rc; - - if ( mi->mi_targets[ candidate ].mt_timeout[ LDAP_BACK_OP_MODRDN ] != 0 ) { - tv.tv_sec = mi->mi_targets[ candidate ].mt_timeout[ LDAP_BACK_OP_MODRDN ]; - tv.tv_usec = 0; - tvp = &tv; - } - - rs->sr_err = LDAP_OTHER; - rc = ldap_result( mc->mc_conns[ candidate ].msc_ld, - msgid, LDAP_MSG_ALL, tvp, &res ); - maperr = 0; - switch ( rc ) { - case -1: - break; - - case 0: - ldap_abandon_ext( mc->mc_conns[ candidate ].msc_ld, - msgid, NULL, NULL ); - rs->sr_err = op->o_protocol >= LDAP_VERSION3 ? - LDAP_ADMINLIMIT_EXCEEDED : LDAP_OPERATIONS_ERROR; - break; - - case LDAP_RES_RENAME: - rc = ldap_parse_result( mc->mc_conns[ candidate ].msc_ld, - res, &rs->sr_err, NULL, NULL, NULL, NULL, 1 ); - if ( rc != LDAP_SUCCESS ) { - rs->sr_err = rc; - } - maperr = 1; - break; - - default: - ldap_msgfree( res ); - break; - } } cleanup:; - if ( maperr ) { - meta_back_op_result( mc, op, rs, candidate ); - - } else { - send_ldap_result( op, rs ); - } + (void)ldap_back_proxy_authz_ctrl_free( op, &ctrls ); -done:; if ( mdn.bv_val != op->o_req_dn.bv_val ) { free( mdn.bv_val ); BER_BVZERO( &mdn ); diff --git a/servers/slapd/back-meta/search.c b/servers/slapd/back-meta/search.c index c45bfbd70b..fe250bee09 100644 --- a/servers/slapd/back-meta/search.c +++ b/servers/slapd/back-meta/search.c @@ -28,6 +28,7 @@ #include #include +#include "lutil.h" #include "slap.h" #include "../back-ldap/back-ldap.h" #include "back-meta.h" @@ -35,6 +36,13 @@ #include "ldap_log.h" #include "../../../libraries/libldap/ldap-int.h" +/* IGNORE means that target does not (no longer) participate + * in the search; + * NOTREADY means the search on that target has not been initialized yet + */ +#define META_MSGID_IGNORE (-1) +#define META_MSGID_NEED_BIND (-2) + static int meta_send_entry( Operation *op, @@ -46,48 +54,356 @@ meta_send_entry( typedef enum meta_search_candidate_t { META_SEARCH_ERR = -1, META_SEARCH_NOT_CANDIDATE, - META_SEARCH_CANDIDATE + META_SEARCH_CANDIDATE, + META_SEARCH_BINDING, + META_SEARCH_NEED_BIND } meta_search_candidate_t; +/* + * meta_search_dobind_init() + * + * initiates bind for a candidate target of a search. + */ static meta_search_candidate_t -meta_back_search_start( +meta_search_dobind_init( Operation *op, SlapReply *rs, - dncookie *dc, - metasingleconn_t *msc, + metaconn_t **mcp, int candidate, - SlapReply *candidates -) + SlapReply *candidates ) { - metainfo_t *mi = ( metainfo_t * )op->o_bd->be_private; - struct berval realbase = op->o_req_dn; - int realscope = op->ors_scope; - ber_len_t suffixlen = 0; - struct berval mbase = BER_BVNULL; - struct berval mfilter = BER_BVNULL; - char **mapped_attrs = NULL; - int rc; + metaconn_t *mc = *mcp; + metainfo_t *mi = ( metainfo_t * )op->o_bd->be_private; + metatarget_t *mt = mi->mi_targets[ candidate ]; + metasingleconn_t *msc = &mc->mc_conns[ candidate ]; + + struct berval binddn = msc->msc_bound_ndn, + cred = msc->msc_cred; + int method; + + int rc; + meta_search_candidate_t retcode; - struct timeval tv, *tvp = NULL; - /* should we check return values? */ - if ( op->ors_deref != -1 ) { - ldap_set_option( msc->msc_ld, LDAP_OPT_DEREF, - ( void * )&op->ors_deref ); + Debug( LDAP_DEBUG_TRACE, "%s >>> meta_search_dobind_init[%d]\n", + op->o_log_prefix, candidate, 0 ); + + /* + * all the targets are already bound as pseudoroot + */ + if ( mc->mc_authz_target == META_BOUND_ALL ) { + return META_SEARCH_CANDIDATE; } - if ( op->ors_tlimit != SLAP_NO_LIMIT ) { - tv.tv_sec = op->ors_tlimit > 0 ? op->ors_tlimit : 1; - tvp = &tv; + retcode = META_SEARCH_BINDING; + ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex ); + if ( LDAP_BACK_CONN_ISBOUND( msc ) || LDAP_BACK_CONN_ISANON( msc ) ) { + /* already bound (or anonymous) */ + +#ifdef DEBUG_205 + char buf[ SLAP_TEXT_BUFLEN ] = { '\0' }; + int bound = 0; + + if ( LDAP_BACK_CONN_ISBOUND( msc ) ) { + bound = 1; + } + + snprintf( buf, sizeof( buf ), " mc=%p ld=%p%s DN=\"%s\"", + (void *)mc, (void *)msc->msc_ld, + bound ? " bound" : " anonymous", + bound == 0 ? "" : msc->msc_bound_ndn.bv_val ); + Debug( LDAP_DEBUG_ANY, "### %s meta_search_dobind_init[%d]%s\n", + op->o_log_prefix, candidate, buf ); +#endif /* DEBUG_205 */ + + retcode = META_SEARCH_CANDIDATE; + + } else if ( META_BACK_CONN_CREATING( msc ) || LDAP_BACK_CONN_BINDING( msc ) ) { + /* another thread is binding the target for this conn; wait */ + +#ifdef DEBUG_205 + char buf[ SLAP_TEXT_BUFLEN ] = { '\0' }; + + snprintf( buf, sizeof( buf ), " mc=%p ld=%p needbind", + (void *)mc, (void *)msc->msc_ld ); + Debug( LDAP_DEBUG_ANY, "### %s meta_search_dobind_init[%d]%s\n", + op->o_log_prefix, candidate, buf ); +#endif /* DEBUG_205 */ + + candidates[ candidate ].sr_msgid = META_MSGID_NEED_BIND; + retcode = META_SEARCH_NEED_BIND; + + } else { + /* we'll need to bind the target for this conn */ + +#ifdef DEBUG_205 + char buf[ SLAP_TEXT_BUFLEN ]; + + snprintf( buf, sizeof( buf ), " mc=%p ld=%p binding", + (void *)mc, (void *)msc->msc_ld ); + Debug( LDAP_DEBUG_ANY, "### %s meta_search_dobind_init[%d]%s\n", + op->o_log_prefix, candidate, buf ); +#endif /* DEBUG_205 */ + + if ( msc->msc_ld == NULL ) { + /* for some reason (e.g. because formerly in "binding" + * state, with eventual connection expiration or invalidation) + * it was not initialized as expected */ + + Debug( LDAP_DEBUG_ANY, "%s meta_search_dobind_init[%d] mc=%p ld=NULL\n", + op->o_log_prefix, candidate, (void *)mc ); + + rc = meta_back_init_one_conn( op, rs, *mcp, candidate, + LDAP_BACK_CONN_ISPRIV( *mcp ), LDAP_BACK_DONTSEND, 0 ); + switch ( rc ) { + case LDAP_SUCCESS: + assert( msc->msc_ld != NULL ); + break; + + case LDAP_SERVER_DOWN: + case LDAP_UNAVAILABLE: + ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex ); + goto down; + + default: + ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex ); + goto other; + } + } + + LDAP_BACK_CONN_BINDING_SET( msc ); + } + + ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex ); + + if ( retcode != META_SEARCH_BINDING ) { + return retcode; + } + + /* NOTE: this obsoletes pseudorootdn */ + if ( op->o_conn != NULL && + !op->o_do_not_cache && + ( BER_BVISNULL( &msc->msc_bound_ndn ) || + BER_BVISEMPTY( &msc->msc_bound_ndn ) || + ( mt->mt_idassert_flags & LDAP_BACK_AUTH_OVERRIDE ) ) ) + { + rc = meta_back_proxy_authz_cred( mc, candidate, op, rs, LDAP_BACK_DONTSEND, &binddn, &cred, &method ); + if ( rc != LDAP_SUCCESS ) { + goto down; + } + + /* NOTE: we copy things here, even if bind didn't succeed yet, + * because the connection is not shared until bind is over */ + if ( !BER_BVISNULL( &binddn ) ) { + ber_bvreplace( &msc->msc_bound_ndn, &binddn ); + if ( LDAP_BACK_SAVECRED( mi ) && !BER_BVISNULL( &cred ) ) { + if ( !BER_BVISNULL( &msc->msc_cred ) ) { + memset( msc->msc_cred.bv_val, 0, + msc->msc_cred.bv_len ); + } + ber_bvreplace( &msc->msc_cred, &cred ); + } + } + + if ( LDAP_BACK_CONN_ISBOUND( msc ) ) { + /* apparently, idassert was configured with SASL bind, + * so bind occurred inside meta_back_proxy_authz_cred() */ + ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex ); + LDAP_BACK_CONN_BINDING_CLEAR( msc ); + ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex ); + return META_SEARCH_CANDIDATE; + } + + /* paranoid */ + switch ( method ) { + case LDAP_AUTH_NONE: + case LDAP_AUTH_SIMPLE: + /* do a simple bind with binddn, cred */ + break; + + default: + assert( 0 ); + break; + } + } + + assert( msc->msc_ld != NULL ); + + rc = ldap_sasl_bind( msc->msc_ld, binddn.bv_val, LDAP_SASL_SIMPLE, &cred, + NULL, NULL, &candidates[ candidate ].sr_msgid ); + +#ifdef DEBUG_205 + { + char buf[ SLAP_TEXT_BUFLEN ]; + + snprintf( buf, sizeof( buf ), "meta_search_dobind_init[%d] mc=%p ld=%p rc=%d", + candidate, (void *)mc, (void *)mc->mc_conns[ candidate ].msc_ld, rc ); + Debug( LDAP_DEBUG_ANY, "### %s %s\n", + op->o_log_prefix, buf, 0 ); + } +#endif /* DEBUG_205 */ + + switch ( rc ) { + case LDAP_SUCCESS: + assert( candidates[ candidate ].sr_msgid >= 0 ); + META_BINDING_SET( &candidates[ candidate ] ); + return META_SEARCH_BINDING; + + case LDAP_SERVER_DOWN: +down:; + /* This is the worst thing that could happen: + * the search will wait until the retry is over. */ + if ( meta_back_retry( op, rs, mcp, candidate, LDAP_BACK_DONTSEND ) ) { + candidates[ candidate ].sr_msgid = META_MSGID_IGNORE; + return META_SEARCH_CANDIDATE; + } + + if ( *mcp == NULL ) { + retcode = META_SEARCH_ERR; + rs->sr_err = LDAP_UNAVAILABLE; + break; + } + /* fall thru */ + + default: +other:; + rs->sr_err = rc; + rc = slap_map_api2result( rs ); + + ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex ); + meta_clear_one_candidate( op, mc, candidate ); + if ( META_BACK_ONERR_STOP( mi ) ) { + LDAP_BACK_CONN_TAINTED_SET( mc ); + meta_back_release_conn_lock( op, mc, 0 ); + *mcp = NULL; + + retcode = META_SEARCH_ERR; + + } else { + if ( META_BACK_ONERR_REPORT( mi ) ) { + candidates[ candidate ].sr_err = rc; + } + + retcode = META_SEARCH_NOT_CANDIDATE; + } + candidates[ candidate ].sr_msgid = META_MSGID_IGNORE; + ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex ); + break; + } + + return retcode; +} + +static meta_search_candidate_t +meta_search_dobind_result( + Operation *op, + SlapReply *rs, + metaconn_t **mcp, + int candidate, + SlapReply *candidates, + LDAPMessage *res ) +{ + metainfo_t *mi = ( metainfo_t * )op->o_bd->be_private; + metaconn_t *mc = *mcp; + metasingleconn_t *msc = &mc->mc_conns[ candidate ]; + + meta_search_candidate_t retcode = META_SEARCH_NOT_CANDIDATE; + int rc; + + assert( msc->msc_ld != NULL ); + + /* FIXME: matched? referrals? response controls? */ + rc = ldap_parse_result( msc->msc_ld, res, + &candidates[ candidate ].sr_err, + NULL, NULL, NULL, NULL, 0 ); + if ( rc != LDAP_SUCCESS ) { + candidates[ candidate ].sr_err = rc; + + } else { + rc = slap_map_api2result( &candidates[ candidate ] ); + } + + ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex ); + LDAP_BACK_CONN_BINDING_CLEAR( msc ); + if ( rc != LDAP_SUCCESS ) { + if ( META_BACK_ONERR_STOP( mi ) ) { + LDAP_BACK_CONN_TAINTED_SET( mc ); + meta_clear_one_candidate( op, mc, candidate ); + meta_back_release_conn_lock( op, mc, 0 ); + *mcp = NULL; + retcode = META_SEARCH_ERR; + rs->sr_err = rc; + + } else if ( META_BACK_ONERR_REPORT( mi ) ) { + candidates[ candidate ].sr_err = rc; + } + + } else { + /* FIXME: check if bound as idassert authcDN! */ + if ( BER_BVISNULL( &msc->msc_bound_ndn ) + || BER_BVISEMPTY( &msc->msc_bound_ndn ) ) + { + LDAP_BACK_CONN_ISANON_SET( msc ); + + } else { + LDAP_BACK_CONN_ISBOUND_SET( msc ); + } + retcode = META_SEARCH_CANDIDATE; + } + + candidates[ candidate ].sr_msgid = META_MSGID_IGNORE; + META_BINDING_CLEAR( &candidates[ candidate ] ); + + ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex ); + + return retcode; +} + +static meta_search_candidate_t +meta_back_search_start( + Operation *op, + SlapReply *rs, + dncookie *dc, + metaconn_t **mcp, + int candidate, + SlapReply *candidates ) +{ + metainfo_t *mi = ( metainfo_t * )op->o_bd->be_private; + metatarget_t *mt = mi->mi_targets[ candidate ]; + metasingleconn_t *msc = &(*mcp)->mc_conns[ candidate ]; + struct berval realbase = op->o_req_dn; + int realscope = op->ors_scope; + struct berval mbase = BER_BVNULL; + struct berval mfilter = BER_BVNULL; + char **mapped_attrs = NULL; + int rc; + meta_search_candidate_t retcode; + struct timeval tv, *tvp = NULL; + int nretries = 1; + LDAPControl **ctrls = NULL; + + /* this should not happen; just in case... */ + if ( msc->msc_ld == NULL ) { + Debug( LDAP_DEBUG_ANY, + "%s: meta_back_search_start candidate=%d ld=NULL%s.\n", + op->o_log_prefix, candidate, + META_BACK_ONERR_STOP( mi ) ? "" : " (ignored)" ); + if ( META_BACK_ONERR_STOP( mi ) ) { + return META_SEARCH_ERR; + } + if ( META_BACK_ONERR_REPORT( mi ) ) { + candidates[ candidate ].sr_err = LDAP_OTHER; + } + candidates[ candidate ].sr_msgid = META_MSGID_IGNORE; + return META_SEARCH_NOT_CANDIDATE; } - dc->target = &mi->mi_targets[ candidate ]; + Debug( LDAP_DEBUG_TRACE, "%s >>> meta_back_search_start[%d]\n", op->o_log_prefix, candidate, 0 ); /* * modifies the base according to the scope, if required */ - suffixlen = mi->mi_targets[ candidate ].mt_nsuffix.bv_len; - if ( suffixlen > op->o_req_ndn.bv_len ) { + if ( mt->mt_nsuffix.bv_len > op->o_req_ndn.bv_len ) { switch ( op->ors_scope ) { case LDAP_SCOPE_SUBTREE: /* @@ -98,11 +414,9 @@ meta_back_search_start( * the requested searchBase already passed * thru the candidate analyzer... */ - if ( dnIsSuffix( &mi->mi_targets[ candidate ].mt_nsuffix, - &op->o_req_ndn ) ) - { - realbase = mi->mi_targets[ candidate ].mt_nsuffix; - if ( mi->mi_targets[ candidate ].mt_scope == LDAP_SCOPE_SUBORDINATE ) { + if ( dnIsSuffix( &mt->mt_nsuffix, &op->o_req_ndn ) ) { + realbase = mt->mt_nsuffix; + if ( mt->mt_scope == LDAP_SCOPE_SUBORDINATE ) { realscope = LDAP_SCOPE_SUBORDINATE; } @@ -110,26 +424,27 @@ meta_back_search_start( /* * this target is no longer candidate */ - return META_SEARCH_NOT_CANDIDATE; + retcode = META_SEARCH_NOT_CANDIDATE; + goto doreturn; } break; case LDAP_SCOPE_SUBORDINATE: case LDAP_SCOPE_ONELEVEL: { - struct berval rdn = mi->mi_targets[ candidate ].mt_nsuffix; + struct berval rdn = mt->mt_nsuffix; rdn.bv_len -= op->o_req_ndn.bv_len + STRLENOF( "," ); if ( dnIsOneLevelRDN( &rdn ) - && dnIsSuffix( &mi->mi_targets[ candidate ].mt_nsuffix, &op->o_req_ndn ) ) + && dnIsSuffix( &mt->mt_nsuffix, &op->o_req_ndn ) ) { /* * if there is exactly one level, * make the target suffix the new * base, and make scope "base" */ - realbase = mi->mi_targets[ candidate ].mt_nsuffix; + realbase = mt->mt_nsuffix; if ( op->ors_scope == LDAP_SCOPE_SUBORDINATE ) { - if ( mi->mi_targets[ candidate ].mt_scope == LDAP_SCOPE_SUBORDINATE ) { + if ( mt->mt_scope == LDAP_SCOPE_SUBORDINATE ) { realscope = LDAP_SCOPE_SUBORDINATE; } else { realscope = LDAP_SCOPE_SUBTREE; @@ -145,13 +460,24 @@ meta_back_search_start( /* * this target is no longer candidate */ - return META_SEARCH_NOT_CANDIDATE; + retcode = META_SEARCH_NOT_CANDIDATE; + goto doreturn; } } + /* initiate dobind */ + retcode = meta_search_dobind_init( op, rs, mcp, candidate, candidates ); + + Debug( LDAP_DEBUG_TRACE, "%s <<< meta_search_dobind_init[%d]=%d\n", op->o_log_prefix, candidate, retcode ); + + if ( retcode != META_SEARCH_CANDIDATE ) { + goto doreturn; + } + /* * Rewrite the search base, if required */ + dc->target = mt; dc->ctx = "searchBase"; switch ( ldap_back_dn_massage( dc, &realbase, &mbase ) ) { case LDAP_SUCCESS: @@ -161,14 +487,16 @@ meta_back_search_start( rs->sr_err = LDAP_UNWILLING_TO_PERFORM; rs->sr_text = "Operation not allowed"; send_ldap_result( op, rs ); - return META_SEARCH_ERR; + retcode = META_SEARCH_ERR; + goto doreturn; default: /* * this target is no longer candidate */ - return META_SEARCH_NOT_CANDIDATE; + retcode = META_SEARCH_NOT_CANDIDATE; + goto doreturn; } /* @@ -192,7 +520,7 @@ meta_back_search_start( /* * Maps required attributes */ - rc = ldap_back_map_attrs( &mi->mi_targets[ candidate ].mt_rwmap.rwm_at, + rc = ldap_back_map_attrs( &mt->mt_rwmap.rwm_at, op->ors_attrs, BACKLDAP_MAP, &mapped_attrs ); if ( rc != LDAP_SUCCESS ) { /* @@ -202,23 +530,66 @@ meta_back_search_start( goto done; } + /* should we check return values? */ + if ( op->ors_deref != -1 ) { + assert( msc->msc_ld != NULL ); + (void)ldap_set_option( msc->msc_ld, LDAP_OPT_DEREF, + ( void * )&op->ors_deref ); + } + + if ( op->ors_tlimit != SLAP_NO_LIMIT ) { + tv.tv_sec = op->ors_tlimit > 0 ? op->ors_tlimit : 1; + tv.tv_usec = 0; + tvp = &tv; + } + +retry:; + ctrls = op->o_ctrls; + if ( ldap_back_proxy_authz_ctrl( &msc->msc_bound_ndn, + mt->mt_version, &mt->mt_idassert, op, rs, &ctrls ) + != LDAP_SUCCESS ) + { + candidates[ candidate ].sr_msgid = META_MSGID_IGNORE; + retcode = META_SEARCH_NOT_CANDIDATE; + goto done; + } + /* * Starts the search */ + assert( msc->msc_ld != NULL ); rc = ldap_search_ext( msc->msc_ld, mbase.bv_val, realscope, mfilter.bv_val, mapped_attrs, op->ors_attrsonly, - op->o_ctrls, NULL, tvp, op->ors_slimit, + ctrls, NULL, tvp, op->ors_slimit, &candidates[ candidate ].sr_msgid ); - if ( rc == LDAP_SUCCESS ) { + switch ( rc ) { + case LDAP_SUCCESS: retcode = META_SEARCH_CANDIDATE; + break; + + case LDAP_SERVER_DOWN: + if ( nretries && meta_back_retry( op, rs, mcp, candidate, LDAP_BACK_DONTSEND ) ) { + nretries = 0; + /* if the identity changed, there might be need to re-authz */ + (void)ldap_back_proxy_authz_ctrl_free( op, &ctrls ); + goto retry; + } - } else { - candidates[ candidate ].sr_msgid = -1; + if ( *mcp == NULL ) { + retcode = META_SEARCH_ERR; + break; + } + /* fall thru */ + + default: + candidates[ candidate ].sr_msgid = META_MSGID_IGNORE; retcode = META_SEARCH_NOT_CANDIDATE; } done:; + (void)ldap_back_proxy_authz_ctrl_free( op, &ctrls ); + if ( mapped_attrs ) { free( mapped_attrs ); } @@ -229,6 +600,9 @@ done:; free( mbase.bv_val ); } +doreturn:; + Debug( LDAP_DEBUG_TRACE, "%s <<< meta_back_search_start[%d]=%d\n", op->o_log_prefix, candidate, retcode ); + return retcode; } @@ -237,13 +611,15 @@ meta_back_search( Operation *op, SlapReply *rs ) { metainfo_t *mi = ( metainfo_t * )op->o_bd->be_private; metaconn_t *mc; - struct timeval tv = { 0, 0 }; + struct timeval save_tv = { 0, 0 }, + tv; time_t stoptime = (time_t)-1; - LDAPMessage *res = NULL, *e; int rc = 0, sres = LDAP_SUCCESS; char *matched = NULL; int last = 0, ncandidates = 0, - initial_candidates = 0, candidate_match = 0; + initial_candidates = 0, candidate_match = 0, + needbind = 0; + ldap_back_send_t sendok = LDAP_BACK_SENDERR; long i; dncookie dc; int is_ok = 0; @@ -256,8 +632,9 @@ meta_back_search( Operation *op, SlapReply *rs ) * FIXME: in case of values return filter, we might want * to map attrs and maybe rewrite value */ - mc = meta_back_getconn( op, rs, NULL, LDAP_BACK_SENDERR ); - if ( !mc || !meta_back_dobind( op, rs, mc, LDAP_BACK_SENDERR ) ) { +getconn:; + mc = meta_back_getconn( op, rs, NULL, sendok ); + if ( !mc ) { return rs->sr_err; } @@ -268,56 +645,114 @@ meta_back_search( Operation *op, SlapReply *rs ) * Inits searches */ for ( i = 0; i < mi->mi_ntargets; i++ ) { - metasingleconn_t *msc = &mc->mc_conns[ i ]; + /* reset sr_msgid; it is used in most loops + * to check if that target is still to be considered */ + candidates[ i ].sr_msgid = META_MSGID_IGNORE; + + /* a target is marked as candidate by meta_back_getconn(); + * if for any reason (an error, it's over or so) it is + * no longer active, sr_msgid is set to META_MSGID_IGNORE + * but it remains candidate, which means it has been active + * at some point during the operation. This allows to + * use its response code and more to compute the final + * response */ + if ( !META_IS_CANDIDATE( &candidates[ i ] ) ) { + continue; + } - candidates[ i ].sr_msgid = -1; candidates[ i ].sr_matched = NULL; candidates[ i ].sr_text = NULL; candidates[ i ].sr_ref = NULL; candidates[ i ].sr_ctrls = NULL; + } - if ( candidates[ i ].sr_tag != META_CANDIDATE + for ( i = 0; i < mi->mi_ntargets; i++ ) { + if ( !META_IS_CANDIDATE( &candidates[ i ] ) || candidates[ i ].sr_err != LDAP_SUCCESS ) { continue; } - switch ( meta_back_search_start( op, rs, &dc, msc, i, candidates ) ) + switch ( meta_back_search_start( op, rs, &dc, &mc, i, candidates ) ) { case META_SEARCH_NOT_CANDIDATE: + candidates[ i ].sr_msgid = META_MSGID_IGNORE; break; + case META_SEARCH_NEED_BIND: + ++needbind; + /* fallthru */ + case META_SEARCH_CANDIDATE: + case META_SEARCH_BINDING: candidates[ i ].sr_type = REP_INTERMEDIATE; ++ncandidates; break; case META_SEARCH_ERR: + savepriv = op->o_private; + op->o_private = (void *)i; + send_ldap_result( op, rs ); + op->o_private = savepriv; rc = -1; goto finish; } } + if ( ncandidates > 0 && needbind == ncandidates ) { + /* + * give up the second time... + * + * NOTE: this should not occur the second time, since a fresh + * connection has ben created; however, targets may also + * need bind because the bind timed out or so. + */ + if ( sendok & LDAP_BACK_BINDING ) { + Debug( LDAP_DEBUG_ANY, + "%s meta_back_search: unable to initialize conn\n", + op->o_log_prefix, 0, 0 ); + rs->sr_err = LDAP_UNAVAILABLE; + rs->sr_text = "unable to initialize connection to remote targets"; + send_ldap_result( op, rs ); + rc = -1; + goto finish; + } + + /* FIXME: better create a separate connection? */ + sendok |= LDAP_BACK_BINDING; + +#ifdef DEBUG_205 + Debug( LDAP_DEBUG_ANY, "*** %s drop mc=%p create new connection\n", + op->o_log_prefix, (void *)mc, 0 ); +#endif /* DEBUG_205 */ + + meta_back_release_conn( op, mc ); + mc = NULL; + + needbind = 0; + ncandidates = 0; + + goto getconn; + } + initial_candidates = ncandidates; -#if 0 - { - char cnd[BUFSIZ]; - int i; + if ( StatslogTest( LDAP_DEBUG_TRACE ) ) { + char cnd[ SLAP_TEXT_BUFLEN ]; + int c; - for ( i = 0; i < mi->mi_ntargets; i++ ) { - if ( candidates[ i ].sr_tag == META_CANDIDATE ) { - cnd[ i ] = '*'; + for ( c = 0; c < mi->mi_ntargets; c++ ) { + if ( META_IS_CANDIDATE( &candidates[ c ] ) ) { + cnd[ c ] = '*'; } else { - cnd[ i ] = ' '; + cnd[ c ] = ' '; } } - cnd[ i ] = '\0'; + cnd[ c ] = '\0'; - Debug( LDAP_DEBUG_ANY, "%s meta_back_search: ncandidates=%d " + Debug( LDAP_DEBUG_TRACE, "%s meta_back_search: ncandidates=%d " "cnd=\"%s\"\n", op->o_log_prefix, ncandidates, cnd ); } -#endif if ( initial_candidates == 0 ) { /* NOTE: here we are not sending any matchedDN; @@ -337,7 +772,7 @@ meta_back_search( Operation *op, SlapReply *rs ) * maybe we should pick the worst... */ rc = LDAP_NO_SUCH_OBJECT; for ( i = 0; i < mi->mi_ntargets; i++ ) { - if ( candidates[ i ].sr_tag == META_CANDIDATE + if ( META_IS_CANDIDATE( &candidates[ i ] ) && candidates[ i ].sr_err != LDAP_SUCCESS ) { rc = candidates[ i ].sr_err; @@ -366,12 +801,114 @@ meta_back_search( Operation *op, SlapReply *rs ) * among the candidates */ for ( rc = 0; ncandidates > 0; ) { - int gotit = 0, doabandon = 0; + int gotit = 0, + doabandon = 0, + alreadybound = ncandidates; + + /* check time limit */ + if ( op->ors_tlimit != SLAP_NO_LIMIT + && slap_get_time() > stoptime ) + { + doabandon = 1; + rc = rs->sr_err = LDAP_TIMELIMIT_EXCEEDED; + savepriv = op->o_private; + op->o_private = (void *)i; + send_ldap_result( op, rs ); + op->o_private = savepriv; + goto finish; + } for ( i = 0; i < mi->mi_ntargets; i++ ) { metasingleconn_t *msc = &mc->mc_conns[ i ]; + LDAPMessage *res = NULL, *msg; + + /* if msgid is invalid, don't ldap_result() */ + if ( candidates[ i ].sr_msgid == META_MSGID_IGNORE ) { + continue; + } + + /* if target still needs bind, retry */ + if ( candidates[ i ].sr_msgid == META_MSGID_NEED_BIND ) { + meta_search_candidate_t retcode; + + /* initiate dobind */ + retcode = meta_search_dobind_init( op, rs, &mc, i, candidates ); + + Debug( LDAP_DEBUG_TRACE, "%s <<< meta_search_dobind_init[%ld]=%d\n", + op->o_log_prefix, i, retcode ); + + switch ( retcode ) { + case META_SEARCH_NEED_BIND: + alreadybound--; + /* fallthru */ + + case META_SEARCH_BINDING: + break; + + case META_SEARCH_ERR: + if ( META_BACK_ONERR_STOP( mi ) ) { + savepriv = op->o_private; + op->o_private = (void *)i; + send_ldap_result( op, rs ); + op->o_private = savepriv; + goto finish; + } + if ( META_BACK_ONERR_REPORT( mi ) ) { + candidates[ i ].sr_err = rs->sr_err; + } + /* fallthru */ + + case META_SEARCH_NOT_CANDIDATE: + /* + * When no candidates are left, + * the outer cycle finishes + */ + candidates[ i ].sr_msgid = META_MSGID_IGNORE; + assert( ncandidates > 0 ); + --ncandidates; + break; + + case META_SEARCH_CANDIDATE: + candidates[ i ].sr_msgid = META_MSGID_IGNORE; + switch ( meta_back_search_start( op, rs, &dc, &mc, i, candidates ) ) + { + case META_SEARCH_CANDIDATE: + assert( candidates[ i ].sr_msgid >= 0 ); + break; + + case META_SEARCH_ERR: + if ( META_BACK_ONERR_STOP( mi ) ) { + savepriv = op->o_private; + op->o_private = (void *)i; + send_ldap_result( op, rs ); + op->o_private = savepriv; + goto finish; + } + if ( META_BACK_ONERR_REPORT( mi ) ) { + candidates[ i ].sr_err = rs->sr_err; + } + /* fallthru */ + + case META_SEARCH_NOT_CANDIDATE: + /* means that meta_back_search_start() + * failed but onerr == continue */ + candidates[ i ].sr_msgid = META_MSGID_IGNORE; + assert( ncandidates > 0 ); + --ncandidates; + break; + + default: + /* impossible */ + assert( 0 ); + break; + } + break; - if ( candidates[ i ].sr_msgid == -1 ) { + default: + /* impossible */ + assert( 0 ); + break; + } continue; } @@ -379,6 +916,24 @@ meta_back_search( Operation *op, SlapReply *rs ) if ( op->o_abandon ) { break; } + +#ifdef DEBUG_205 + if ( msc->msc_ld == NULL ) { + char buf[ SLAP_TEXT_BUFLEN ]; + + ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex ); + snprintf( buf, sizeof( buf ), + "%s meta_back_search[%ld] mc=%p msgid=%d%s%s%s\n", + op->o_log_prefix, (long)i, (void *)mc, + candidates[ i ].sr_msgid, + META_IS_BINDING( &candidates[ i ] ) ? " binding" : "", + LDAP_BACK_CONN_BINDING( &mc->mc_conns[ i ] ) ? " connbinding" : "", + META_BACK_CONN_CREATING( &mc->mc_conns[ i ] ) ? " conncreating" : "" ); + ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex ); + + Debug( LDAP_DEBUG_ANY, "!!! %s\n", buf, 0, 0 ); + } +#endif /* DEBUG_205 */ /* * FIXME: handle time limit as well? @@ -387,230 +942,235 @@ meta_back_search( Operation *op, SlapReply *rs ) * get a LDAP_TIMELIMIT_EXCEEDED from * one of them ... */ -get_result:; + tv = save_tv; rc = ldap_result( msc->msc_ld, candidates[ i ].sr_msgid, - LDAP_MSG_ONE, &tv, &res ); - - if ( rc == 0 ) { + LDAP_MSG_RECEIVED, &tv, &res ); + switch ( rc ) { + case 0: /* FIXME: res should not need to be freed */ assert( res == NULL ); - - /* check time limit */ - if ( op->ors_tlimit != SLAP_NO_LIMIT - && slap_get_time() > stoptime ) - { - doabandon = 1; - rc = rs->sr_err = LDAP_TIMELIMIT_EXCEEDED; - savepriv = op->o_private; - op->o_private = (void *)i; - send_ldap_result( op, rs ); - op->o_private = savepriv; - goto finish; - } - continue; - } else if ( rc == -1 ) { + case -1: really_bad:; /* something REALLY bad happened! */ if ( candidates[ i ].sr_type == REP_INTERMEDIATE ) { candidates[ i ].sr_type = REP_RESULT; if ( meta_back_retry( op, rs, &mc, i, LDAP_BACK_DONTSEND ) ) { - switch ( meta_back_search_start( op, rs, &dc, msc, i, candidates ) ) + candidates[ i ].sr_msgid = META_MSGID_IGNORE; + switch ( meta_back_search_start( op, rs, &dc, &mc, i, candidates ) ) { case META_SEARCH_CANDIDATE: - goto get_result; + /* get back into business... */ + continue; + + /* means that failed but onerr == continue */ + case META_SEARCH_NOT_CANDIDATE: + candidates[ i ].sr_msgid = META_MSGID_IGNORE; + --ncandidates; + + if ( META_BACK_ONERR_STOP( mi ) ) { + savepriv = op->o_private; + op->o_private = (void *)i; + send_ldap_result( op, rs ); + op->o_private = savepriv; + goto finish; + } + if ( META_BACK_ONERR_REPORT( mi ) ) { + candidates[ i ].sr_err = rs->sr_err; + } + break; + + case META_SEARCH_BINDING: + case META_SEARCH_NEED_BIND: + assert( 0 ); default: + /* unrecoverable error */ + candidates[ i ].sr_msgid = META_MSGID_IGNORE; rc = rs->sr_err = LDAP_OTHER; goto finish; } } - savepriv = op->o_private; - op->o_private = (void *)i; - send_ldap_result( op, rs ); - op->o_private = savepriv; - goto finish; + if ( META_BACK_ONERR_STOP( mi ) ) { + savepriv = op->o_private; + op->o_private = (void *)i; + send_ldap_result( op, rs ); + op->o_private = savepriv; + goto finish; + } + if ( META_BACK_ONERR_REPORT( mi ) ) { + candidates[ i ].sr_err = rs->sr_err; + } } /* * When no candidates are left, * the outer cycle finishes */ - candidates[ i ].sr_msgid = -1; + candidates[ i ].sr_msgid = META_MSGID_IGNORE; + assert( ncandidates > 0 ); --ncandidates; - rs->sr_err = candidates[ i ].sr_err = LDAP_OTHER; - rs->sr_text = "remote server unavailable"; + rs->sr_err = candidates[ i ].sr_err; + continue; - } else if ( rc == LDAP_RES_SEARCH_ENTRY ) { - if ( candidates[ i ].sr_type == REP_INTERMEDIATE ) { - /* don't retry any more... */ - candidates[ i ].sr_type = REP_RESULT; + default: + /* only touch when activity actually took place... */ + if ( mi->mi_idle_timeout != 0 && msc->msc_time < op->o_time ) { + msc->msc_time = op->o_time; } + break; + } + + for ( msg = ldap_first_message( msc->msc_ld, res ); + msg != NULL; + msg = ldap_next_message( msc->msc_ld, msg ) ) + { + rc = ldap_msgtype( msg ); + if ( rc == LDAP_RES_SEARCH_ENTRY ) { + LDAPMessage *e; - is_ok++; + if ( candidates[ i ].sr_type == REP_INTERMEDIATE ) { + /* don't retry any more... */ + candidates[ i ].sr_type = REP_RESULT; + } - e = ldap_first_entry( msc->msc_ld, res ); - savepriv = op->o_private; - op->o_private = (void *)i; - rs->sr_err = meta_send_entry( op, rs, mc, i, e ); - ldap_msgfree( res ); - res = NULL; + is_ok++; - switch ( rs->sr_err ) { - case LDAP_SIZELIMIT_EXCEEDED: + e = ldap_first_entry( msc->msc_ld, msg ); savepriv = op->o_private; op->o_private = (void *)i; - send_ldap_result( op, rs ); - op->o_private = savepriv; - rs->sr_err = LDAP_SUCCESS; - goto finish; + rs->sr_err = meta_send_entry( op, rs, mc, i, e ); - case LDAP_UNAVAILABLE: - rs->sr_err = LDAP_OTHER; - goto finish; - } - op->o_private = savepriv; - - /* don't wait any longer... */ - gotit = 1; - tv.tv_sec = 0; - tv.tv_usec = 0; - -#if 0 - /* - * If scope is BASE, we need to jump out - * as soon as one entry is found; if - * the target pool is properly crafted, - * this should correspond to the sole - * entry that has the base DN - */ - /* FIXME: this defeats the purpose of - * doing a search with scope == base and - * sizelimit = 1 to determine if a - * candidate is actually unique */ - if ( op->ors_scope == LDAP_SCOPE_BASE - && rs->sr_nentries > 0 ) - { - doabandon = 1; - ncandidates = 0; - sres = LDAP_SUCCESS; - break; - } -#endif - - } else if ( rc == LDAP_RES_SEARCH_REFERENCE ) { - char **references = NULL; - int cnt; - - if ( candidates[ i ].sr_type == REP_INTERMEDIATE ) { - /* don't retry any more... */ - candidates[ i ].sr_type = REP_RESULT; - } + switch ( rs->sr_err ) { + case LDAP_SIZELIMIT_EXCEEDED: + savepriv = op->o_private; + op->o_private = (void *)i; + send_ldap_result( op, rs ); + op->o_private = savepriv; + rs->sr_err = LDAP_SUCCESS; + ldap_msgfree( res ); + res = NULL; + goto finish; - is_ok++; + case LDAP_UNAVAILABLE: + rs->sr_err = LDAP_OTHER; + ldap_msgfree( res ); + res = NULL; + goto finish; + } + op->o_private = savepriv; - rc = ldap_parse_reference( msc->msc_ld, res, - &references, &rs->sr_ctrls, 1 ); - res = NULL; + /* don't wait any longer... */ + gotit = 1; + save_tv.tv_sec = 0; + save_tv.tv_usec = 0; - if ( rc != LDAP_SUCCESS ) { - continue; - } + } else if ( rc == LDAP_RES_SEARCH_REFERENCE ) { + char **references = NULL; + int cnt; - if ( references == NULL ) { - continue; - } + if ( candidates[ i ].sr_type == REP_INTERMEDIATE ) { + /* don't retry any more... */ + candidates[ i ].sr_type = REP_RESULT; + } + + is_ok++; + + rc = ldap_parse_reference( msc->msc_ld, msg, + &references, &rs->sr_ctrls, 0 ); + + if ( rc != LDAP_SUCCESS ) { + continue; + } + + if ( references == NULL ) { + continue; + } #ifdef ENABLE_REWRITE - dc.ctx = "referralDN"; + dc.ctx = "referralDN"; #else /* ! ENABLE_REWRITE */ - dc.tofrom = 0; - dc.normalized = 0; + dc.tofrom = 0; + dc.normalized = 0; #endif /* ! ENABLE_REWRITE */ - /* FIXME: merge all and return at the end */ - - for ( cnt = 0; references[ cnt ]; cnt++ ) - ; - - rs->sr_ref = ch_calloc( sizeof( struct berval ), cnt + 1 ); - - for ( cnt = 0; references[ cnt ]; cnt++ ) { - ber_str2bv( references[ cnt ], 0, 1, &rs->sr_ref[ cnt ] ); - } - BER_BVZERO( &rs->sr_ref[ cnt ] ); - - ( void )ldap_back_referral_result_rewrite( &dc, rs->sr_ref ); - - if ( rs->sr_ref != NULL && !BER_BVISNULL( &rs->sr_ref[ 0 ] ) ) { - /* ignore return value by now */ - savepriv = op->o_private; - op->o_private = (void *)i; - ( void )send_search_reference( op, rs ); - op->o_private = savepriv; - - ber_bvarray_free( rs->sr_ref ); - rs->sr_ref = NULL; - } + /* FIXME: merge all and return at the end */ + + for ( cnt = 0; references[ cnt ]; cnt++ ) + ; + + rs->sr_ref = ch_calloc( sizeof( struct berval ), cnt + 1 ); + + for ( cnt = 0; references[ cnt ]; cnt++ ) { + ber_str2bv( references[ cnt ], 0, 1, &rs->sr_ref[ cnt ] ); + } + BER_BVZERO( &rs->sr_ref[ cnt ] ); + + ( void )ldap_back_referral_result_rewrite( &dc, rs->sr_ref ); - /* cleanup */ - if ( references ) { - ber_memvfree( (void **)references ); - } + if ( rs->sr_ref != NULL && !BER_BVISNULL( &rs->sr_ref[ 0 ] ) ) { + /* ignore return value by now */ + savepriv = op->o_private; + op->o_private = (void *)i; + ( void )send_search_reference( op, rs ); + op->o_private = savepriv; + + ber_bvarray_free( rs->sr_ref ); + rs->sr_ref = NULL; + } - if ( rs->sr_ctrls ) { - ldap_controls_free( rs->sr_ctrls ); - rs->sr_ctrls = NULL; - } + /* cleanup */ + if ( references ) { + ber_memvfree( (void **)references ); + } - } else if ( rc == LDAP_RES_SEARCH_RESULT ) { - char buf[ SLAP_TEXT_BUFLEN ]; - char **references = NULL; + if ( rs->sr_ctrls ) { + ldap_controls_free( rs->sr_ctrls ); + rs->sr_ctrls = NULL; + } - if ( candidates[ i ].sr_type == REP_INTERMEDIATE ) { - /* don't retry any more... */ - candidates[ i ].sr_type = REP_RESULT; - } + } else if ( rc == LDAP_RES_SEARCH_RESULT ) { + char buf[ SLAP_TEXT_BUFLEN ]; + char **references = NULL; - /* NOTE: ignores response controls - * (and intermediate response controls - * as well, except for those with search - * references); this may not be correct, - * but if they're not ignored then - * back-meta would need to merge them - * consistently (think of pagedResults...) - */ - rs->sr_err = ldap_parse_result( msc->msc_ld, - res, - &candidates[ i ].sr_err, - (char **)&candidates[ i ].sr_matched, - NULL /* (char **)&candidates[ i ].sr_text */ , - &references, - NULL /* &candidates[ i ].sr_ctrls (unused) */ , - 1 ); - res = NULL; - if ( rs->sr_err != LDAP_SUCCESS ) { - ldap_get_option( msc->msc_ld, + if ( candidates[ i ].sr_type == REP_INTERMEDIATE ) { + /* don't retry any more... */ + candidates[ i ].sr_type = REP_RESULT; + } + + /* NOTE: ignores response controls + * (and intermediate response controls + * as well, except for those with search + * references); this may not be correct, + * but if they're not ignored then + * back-meta would need to merge them + * consistently (think of pagedResults...) + */ + /* FIXME: response controls? */ + rs->sr_err = ldap_parse_result( msc->msc_ld, + msg, + &candidates[ i ].sr_err, + (char **)&candidates[ i ].sr_matched, + NULL /* (char **)&candidates[ i ].sr_text */ , + &references, + NULL /* &candidates[ i ].sr_ctrls (unused) */ , + 0 ); + if ( rs->sr_err != LDAP_SUCCESS ) { + ldap_get_option( msc->msc_ld, LDAP_OPT_ERROR_NUMBER, &rs->sr_err ); - sres = slap_map_api2result( rs ); - candidates[ i ].sr_type = REP_RESULT; - goto really_bad; - } - - /* massage matchedDN if need be */ - if ( candidates[ i ].sr_matched != NULL ) { -#ifndef LDAP_NULL_IS_NULL - if ( candidates[ i ].sr_matched[ 0 ] == '\0' ) { - ldap_memfree( (char *)candidates[ i ].sr_matched ); - candidates[ i ].sr_matched = NULL; + sres = slap_map_api2result( rs ); + candidates[ i ].sr_type = REP_RESULT; + ldap_msgfree( res ); + res = NULL; + goto really_bad; + } - } else -#endif /* LDAP_NULL_IS_NULL */ - { + /* massage matchedDN if need be */ + if ( candidates[ i ].sr_matched != NULL ) { struct berval match, mmatch; ber_str2bv( candidates[ i ].sr_matched, @@ -618,10 +1178,11 @@ really_bad:; candidates[ i ].sr_matched = NULL; dc.ctx = "matchedDN"; - dc.target = &mi->mi_targets[ i ]; + dc.target = mi->mi_targets[ i ]; if ( !ldap_back_dn_massage( &dc, &match, &mmatch ) ) { if ( mmatch.bv_val == match.bv_val ) { - candidates[ i ].sr_matched = ch_strdup( mmatch.bv_val ); + candidates[ i ].sr_matched + = ch_strdup( mmatch.bv_val ); } else { candidates[ i ].sr_matched = mmatch.bv_val; @@ -631,156 +1192,268 @@ really_bad:; } ldap_memfree( match.bv_val ); } - } - -#ifndef LDAP_NULL_IS_NULL - /* just get rid of the error message, if any */ - if ( candidates[ i ].sr_text && candidates[ i ].sr_text[ 0 ] == '\0' ) - { - ldap_memfree( (char *)candidates[ i ].sr_text ); - candidates[ i ].sr_text = NULL; - } -#endif /* LDAP_NULL_IS_NULL */ - - /* add references to array */ - if ( references ) { - BerVarray sr_ref; - int cnt; - - for ( cnt = 0; references[ cnt ]; cnt++ ) - ; - - sr_ref = ch_calloc( sizeof( struct berval ), cnt + 1 ); - - for ( cnt = 0; references[ cnt ]; cnt++ ) { - ber_str2bv( references[ cnt ], 0, 1, &sr_ref[ cnt ] ); - } - BER_BVZERO( &sr_ref[ cnt ] ); - ( void )ldap_back_referral_result_rewrite( &dc, sr_ref ); - - /* cleanup */ - ber_memvfree( (void **)references ); - - if ( rs->sr_v2ref == NULL ) { - rs->sr_v2ref = sr_ref; + /* add references to array */ + if ( references ) { + BerVarray sr_ref; + int cnt; + + for ( cnt = 0; references[ cnt ]; cnt++ ) + ; + + sr_ref = ch_calloc( sizeof( struct berval ), cnt + 1 ); + + for ( cnt = 0; references[ cnt ]; cnt++ ) { + ber_str2bv( references[ cnt ], 0, 1, &sr_ref[ cnt ] ); + } + BER_BVZERO( &sr_ref[ cnt ] ); + + ( void )ldap_back_referral_result_rewrite( &dc, sr_ref ); + + /* cleanup */ + ber_memvfree( (void **)references ); + + if ( rs->sr_v2ref == NULL ) { + rs->sr_v2ref = sr_ref; - } else { - for ( cnt = 0; !BER_BVISNULL( &sr_ref[ cnt ] ); cnt++ ) { - ber_bvarray_add( &rs->sr_v2ref, &sr_ref[ cnt ] ); + } else { + for ( cnt = 0; !BER_BVISNULL( &sr_ref[ cnt ] ); cnt++ ) { + ber_bvarray_add( &rs->sr_v2ref, &sr_ref[ cnt ] ); + } + ber_memfree( sr_ref ); } - ber_memfree( sr_ref ); } - } - - rs->sr_err = candidates[ i ].sr_err; - sres = slap_map_api2result( rs ); - - if ( StatslogTest( LDAP_DEBUG_TRACE | LDAP_DEBUG_ANY ) ) { - snprintf( buf, sizeof( buf ), - "%s meta_back_search[%ld] " - "match=\"%s\" err=%ld", - op->o_log_prefix, i, - candidates[ i ].sr_matched ? candidates[ i ].sr_matched : "", - (long) candidates[ i ].sr_err ); - if ( candidates[ i ].sr_err == LDAP_SUCCESS ) { - Debug( LDAP_DEBUG_TRACE, "%s.\n", buf, 0, 0 ); - - } else { - Debug( LDAP_DEBUG_ANY, "%s (%s).\n", - buf, ldap_err2string( candidates[ i ].sr_err ), 0 ); + + rs->sr_err = candidates[ i ].sr_err; + sres = slap_map_api2result( rs ); + + if ( StatslogTest( LDAP_DEBUG_TRACE | LDAP_DEBUG_ANY ) ) { + snprintf( buf, sizeof( buf ), + "%s meta_back_search[%ld] " + "match=\"%s\" err=%ld", + op->o_log_prefix, i, + candidates[ i ].sr_matched ? candidates[ i ].sr_matched : "", + (long) candidates[ i ].sr_err ); + if ( candidates[ i ].sr_err == LDAP_SUCCESS ) { + Debug( LDAP_DEBUG_TRACE, "%s.\n", buf, 0, 0 ); + + } else { + Debug( LDAP_DEBUG_ANY, "%s (%s).\n", + buf, ldap_err2string( candidates[ i ].sr_err ), 0 ); + } } - } - - switch ( sres ) { - case LDAP_NO_SUCH_OBJECT: - /* is_ok is touched any time a valid - * (even intermediate) result is - * returned; as a consequence, if - * a candidate returns noSuchObject - * it is ignored and the candidate - * is simply demoted. */ - if ( is_ok ) { - sres = LDAP_SUCCESS; + + switch ( sres ) { + case LDAP_NO_SUCH_OBJECT: + /* is_ok is touched any time a valid + * (even intermediate) result is + * returned; as a consequence, if + * a candidate returns noSuchObject + * it is ignored and the candidate + * is simply demoted. */ + if ( is_ok ) { + sres = LDAP_SUCCESS; + } + break; + + case LDAP_SUCCESS: + case LDAP_REFERRAL: + is_ok++; + break; + + case LDAP_SIZELIMIT_EXCEEDED: + /* if a target returned sizelimitExceeded + * and the entry count is equal to the + * proxy's limit, the target would have + * returned more, and the error must be + * propagated to the client; otherwise, + * the target enforced a limit lower + * than what requested by the proxy; + * ignore it */ + if ( rs->sr_nentries == op->ors_slimit + || META_BACK_ONERR_STOP( mi ) ) + { + savepriv = op->o_private; + op->o_private = (void *)i; + send_ldap_result( op, rs ); + op->o_private = savepriv; + ldap_msgfree( res ); + res = NULL; + goto finish; + } + if ( META_BACK_ONERR_REPORT( mi ) ) { + candidates[ i ].sr_err = rs->sr_err; + } + break; + + default: + if ( META_BACK_ONERR_STOP( mi ) ) { + savepriv = op->o_private; + op->o_private = (void *)i; + send_ldap_result( op, rs ); + op->o_private = savepriv; + ldap_msgfree( res ); + res = NULL; + goto finish; + } + if ( META_BACK_ONERR_REPORT( mi ) ) { + candidates[ i ].sr_err = rs->sr_err; + } + break; } - break; - - case LDAP_SUCCESS: - case LDAP_REFERRAL: - is_ok++; - break; - - case LDAP_SIZELIMIT_EXCEEDED: - /* if a target returned sizelimitExceeded - * and the entry count is equal to the - * proxy's limit, the target would have - * returned more, and the error must be - * propagated to the client; otherwise, - * the target enforced a limit lower - * than what requested by the proxy; - * ignore it */ - if ( rs->sr_nentries == op->ors_slimit - || META_BACK_ONERR_STOP( mi ) ) - { - savepriv = op->o_private; - op->o_private = (void *)i; - send_ldap_result( op, rs ); - op->o_private = savepriv; - goto finish; + + last = i; + rc = 0; + + /* + * When no candidates are left, + * the outer cycle finishes + */ + candidates[ i ].sr_msgid = META_MSGID_IGNORE; + --ncandidates; + + } else if ( rc == LDAP_RES_BIND ) { + meta_search_candidate_t retcode; + + retcode = meta_search_dobind_result( op, rs, &mc, i, candidates, msg ); + if ( retcode == META_SEARCH_CANDIDATE ) { + candidates[ i ].sr_msgid = META_MSGID_IGNORE; + retcode = meta_back_search_start( op, rs, &dc, &mc, i, candidates ); } - break; - - default: - if ( META_BACK_ONERR_STOP( mi ) ) { - savepriv = op->o_private; - op->o_private = (void *)i; - send_ldap_result( op, rs ); - op->o_private = savepriv; - goto finish; + + switch ( retcode ) { + case META_SEARCH_CANDIDATE: + break; + + /* means that failed but onerr == continue */ + case META_SEARCH_NOT_CANDIDATE: + case META_SEARCH_ERR: + candidates[ i ].sr_msgid = META_MSGID_IGNORE; + --ncandidates; + + if ( META_BACK_ONERR_STOP( mi ) ) { + savepriv = op->o_private; + op->o_private = (void *)i; + send_ldap_result( op, rs ); + op->o_private = savepriv; + ldap_msgfree( res ); + res = NULL; + goto finish; + } + if ( META_BACK_ONERR_REPORT( mi ) ) { + candidates[ i ].sr_err = rs->sr_err; + } + break; + + default: + assert( 0 ); + break; } - break; + + } else { + assert( 0 ); + ldap_msgfree( res ); + res = NULL; + goto really_bad; } - - last = i; - rc = 0; - - /* - * When no candidates are left, - * the outer cycle finishes - */ - candidates[ i ].sr_msgid = -1; - --ncandidates; - - } else { - assert( 0 ); - goto really_bad; } + + ldap_msgfree( res ); + res = NULL; } /* check for abandon */ if ( op->o_abandon || doabandon ) { for ( i = 0; i < mi->mi_ntargets; i++ ) { - metasingleconn_t *msc = &mc->mc_conns[ i ]; + if ( candidates[ i ].sr_msgid >= 0 ) { + if ( META_IS_BINDING( &candidates[ i ] ) ) { + ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex ); + if ( LDAP_BACK_CONN_BINDING( &mc->mc_conns[ i ] ) ) { + /* if still binding, destroy */ - if ( candidates[ i ].sr_msgid != -1 ) { - ldap_abandon_ext( msc->msc_ld, - candidates[ i ].sr_msgid, - NULL, NULL ); - candidates[ i ].sr_msgid = -1; +#ifdef DEBUG_205 + char buf[ SLAP_TEXT_BUFLEN ]; + + snprintf( buf, sizeof( buf), "%s meta_back_search(abandon) " + "ldap_unbind_ext[%ld] mc=%p ld=%p", + op->o_log_prefix, i, (void *)mc, + (void *)mc->mc_conns[i].msc_ld ); + + Debug( LDAP_DEBUG_ANY, "### %s\n", buf, 0, 0 ); +#endif /* DEBUG_205 */ + + meta_clear_one_candidate( op, mc, i ); + } + ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex ); + META_BINDING_CLEAR( &candidates[ i ] ); + + } else { + (void)meta_back_cancel( mc, op, rs, + candidates[ i ].sr_msgid, i, + LDAP_BACK_DONTSEND ); + } + + candidates[ i ].sr_msgid = META_MSGID_IGNORE; + assert( ncandidates > 0 ); + --ncandidates; } } if ( op->o_abandon ) { rc = SLAPD_ABANDON; - goto finish; + /* let send_ldap_result play cleanup handlers (ITS#4645) */ + break; } } /* if no entry was found during this loop, * set a minimal timeout */ - if ( gotit == 0 ) { - LDAP_BACK_TV_SET( &tv ); - ldap_pvt_thread_yield(); + if ( ncandidates > 0 && gotit == 0 ) { + if ( save_tv.tv_sec == 0 && save_tv.tv_usec == 0 ) { + save_tv.tv_usec = LDAP_BACK_RESULT_UTIMEOUT/initial_candidates; + + /* arbitrarily limit to something between 1 and 2 minutes */ + } else if ( ( stoptime == -1 && save_tv.tv_sec < 60 ) + || save_tv.tv_sec < ( stoptime - slap_get_time() ) / ( 2 * ncandidates ) ) + { + /* double the timeout */ + lutil_timermul( &save_tv, 2, &save_tv ); + } + +#if 0 + if ( StatslogTest( LDAP_DEBUG_TRACE ) ) { + char buf[ SLAP_TEXT_BUFLEN ]; + + snprintf( buf, sizeof( buf ), "%s %ld.%06ld %d/%d mc=%p", + op->o_log_prefix, save_tv.tv_sec, save_tv.tv_usec, + ncandidates, initial_candidates, mc ); + Debug( LDAP_DEBUG_TRACE, "### %s\n", buf, 0, 0 ); + for ( i = 0; i < mi->mi_ntargets; i++ ) { + if ( candidates[ i ].sr_msgid == META_MSGID_IGNORE ) { + continue; + } + + snprintf( buf, sizeof( buf ), "[%ld] ld=%p%s%s\n", + i, + mc->mc_conns[ i ].msc_ld, + ( candidates[ i ].sr_msgid == META_MSGID_NEED_BIND ) ? " needbind" : "", + META_IS_BINDING( &candidates[ i ] ) ? " binding" : "" ); + Debug( LDAP_DEBUG_TRACE, "### %s\n", buf, 0, 0 ); + } + } +#endif + + if ( alreadybound == 0 ) { +#if 0 + Debug( LDAP_DEBUG_TRACE, "### %s select(%ld.%06ld)\n", + op->o_log_prefix, save_tv.tv_sec, save_tv.tv_usec ); +#endif + tv = save_tv; + (void)select( 0, NULL, NULL, NULL, &tv ); + + } else { + ldap_pvt_thread_yield(); + } } } @@ -788,7 +1461,13 @@ really_bad:; /* * FIXME: need a better strategy to handle errors */ - rc = meta_back_op_result( mc, op, rs, META_TARGET_NONE ); + if ( mc ) { + rc = meta_back_op_result( mc, op, rs, META_TARGET_NONE, + -1, stoptime != -1 ? (stoptime - slap_get_time()) : 0, + LDAP_BACK_SENDERR ); + } else { + rc = rs->sr_err; + } goto finish; } @@ -804,7 +1483,7 @@ really_bad:; /* we use the first one */ for ( i = 0; i < mi->mi_ntargets; i++ ) { - if ( candidates[ i ].sr_tag == META_CANDIDATE + if ( META_IS_CANDIDATE( &candidates[ i ] ) && candidates[ i ].sr_matched != NULL ) { struct berval bv, pbv; @@ -816,7 +1495,7 @@ really_bad:; * ignore the matchedDN */ if ( sres == LDAP_SUCCESS && candidates[ i ].sr_err == LDAP_NO_SUCH_OBJECT - && op->o_req_ndn.bv_len > mi->mi_targets[ i ].mt_nsuffix.bv_len ) + && op->o_req_ndn.bv_len > mi->mi_targets[ i ]->mt_nsuffix.bv_len ) { free( (char *)candidates[ i ].sr_matched ); candidates[ i ].sr_matched = NULL; @@ -860,12 +1539,12 @@ really_bad:; #if 0 { - char buf[BUFSIZ]; - char cnd[BUFSIZ]; + char buf[ SLAP_TEXT_BUFLEN ]; + char cnd[ SLAP_TEXT_BUFLEN ]; int i; for ( i = 0; i < mi->mi_ntargets; i++ ) { - if ( candidates[ i ].sr_tag == META_CANDIDATE ) { + if ( META_IS_CANDIDATE( &candidates[ i ] ) ) { cnd[ i ] = '*'; } else { cnd[ i ] = ' '; @@ -883,14 +1562,35 @@ really_bad:; /* * In case we returned at least one entry, we return LDAP_SUCCESS * otherwise, the latter error code we got - * - * FIXME: we should handle error codes and return the more - * important/reasonable */ - if ( sres == LDAP_SUCCESS && rs->sr_v2ref ) { - sres = LDAP_REFERRAL; + if ( sres == LDAP_SUCCESS ) { + if ( rs->sr_v2ref ) { + sres = LDAP_REFERRAL; + } + + if ( META_BACK_ONERR_REPORT( mi ) ) { + /* + * Report errors, if any + * + * FIXME: we should handle error codes and return the more + * important/reasonable + */ + for ( i = 0; i < mi->mi_ntargets; i++ ) { + if ( !META_IS_CANDIDATE( &candidates[ i ] ) ) { + continue; + } + + if ( candidates[ i ].sr_err != LDAP_SUCCESS + && candidates[ i ].sr_err != LDAP_NO_SUCH_OBJECT ) + { + sres = candidates[ i ].sr_err; + break; + } + } + } } + rs->sr_err = sres; rs->sr_matched = matched; rs->sr_ref = ( sres == LDAP_REFERRAL ? rs->sr_v2ref : NULL ); @@ -909,10 +1609,29 @@ finish:; } for ( i = 0; i < mi->mi_ntargets; i++ ) { - if ( candidates[ i ].sr_tag != META_CANDIDATE ) { + if ( !META_IS_CANDIDATE( &candidates[ i ] ) ) { continue; } + if ( mc && META_IS_BINDING( &candidates[ i ] ) ) { + ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex ); + if ( LDAP_BACK_CONN_BINDING( &mc->mc_conns[ i ] ) ) { + assert( candidates[ i ].sr_msgid >= 0 ); + assert( mc->mc_conns[ i ].msc_ld != NULL ); + +#ifdef DEBUG_205 + Debug( LDAP_DEBUG_ANY, "### %s meta_back_search(cleanup) " + "ldap_unbind_ext[%ld] ld=%p\n", + op->o_log_prefix, i, (void *)mc->mc_conns[i].msc_ld ); +#endif /* DEBUG_205 */ + + /* if still binding, destroy */ + meta_clear_one_candidate( op, mc, i ); + } + ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex ); + META_BINDING_CLEAR( &candidates[ i ] ); + } + if ( candidates[ i ].sr_matched ) { free( (char *)candidates[ i ].sr_matched ); candidates[ i ].sr_matched = NULL; @@ -932,6 +1651,23 @@ finish:; ldap_controls_free( candidates[ i ].sr_ctrls ); candidates[ i ].sr_ctrls = NULL; } + + if ( META_BACK_TGT_QUARANTINE( mi->mi_targets[ i ] ) ) { + meta_back_quarantine( op, &candidates[ i ], i ); + } + + /* only in case of timelimit exceeded, if the timelimit exceeded because + * one contacted target never responded, invalidate the connection + * NOTE: should we quarantine the target as well? right now, the connection + * is invalidated; the next time it will be recreated and the target + * will be quarantined if it cannot be contacted */ + if ( mi->mi_idle_timeout != 0 + && rs->sr_err == LDAP_TIMELIMIT_EXCEEDED + && op->o_time > mc->mc_conns[ i ].msc_time ) + { + /* don't let anyone else use this expired connection */ + LDAP_BACK_CONN_TAINTED_SET( mc ); + } } if ( mc ) { @@ -967,7 +1703,7 @@ meta_send_entry( /* * Rewrite the dn of the result, if needed */ - dc.target = &mi->mi_targets[ target ]; + dc.target = mi->mi_targets[ target ]; dc.conn = op->o_conn; dc.rs = rs; dc.ctx = "searchResult"; @@ -1011,7 +1747,7 @@ meta_send_entry( slap_syntax_validate_func *validate; slap_syntax_transform_func *pretty; - ldap_back_map( &mi->mi_targets[ target ].mt_rwmap.rwm_at, + ldap_back_map( &mi->mi_targets[ target ]->mt_rwmap.rwm_at, &a, &mapped, BACKLDAP_REMAP ); if ( BER_BVISNULL( &mapped ) || mapped.bv_val[0] == '\0' ) { ( void )ber_scanf( &ber, "x" /* [W] */ ); @@ -1086,7 +1822,7 @@ meta_send_entry( struct berval *bv; for ( bv = attr->a_vals; !BER_BVISNULL( bv ); bv++ ) { - ldap_back_map( &mi->mi_targets[ target ].mt_rwmap.rwm_oc, + ldap_back_map( &mi->mi_targets[ target ]->mt_rwmap.rwm_oc, bv, &mapped, BACKLDAP_REMAP ); if ( BER_BVISNULL( &mapped ) || mapped.bv_val[0] == '\0') { free( bv->bv_val ); diff --git a/servers/slapd/back-meta/unbind.c b/servers/slapd/back-meta/unbind.c index 671dc840e0..b63d485b24 100644 --- a/servers/slapd/back-meta/unbind.c +++ b/servers/slapd/back-meta/unbind.c @@ -51,23 +51,29 @@ meta_back_conn_destroy( mc_curr.mc_conn = conn; ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex ); +#if META_BACK_PRINT_CONNTREE > 0 + meta_back_print_conntree( mi, ">>> meta_back_conn_destroy" ); +#endif /* META_BACK_PRINT_CONNTREE */ while ( ( mc = avl_delete( &mi->mi_conninfo.lai_tree, ( caddr_t )&mc_curr, meta_back_conn_cmp ) ) != NULL ) { Debug( LDAP_DEBUG_TRACE, "=>meta_back_conn_destroy: destroying conn %ld\n", - LDAP_BACK_PCONN_ID( mc->mc_conn ), 0, 0 ); + LDAP_BACK_PCONN_ID( mc ), 0, 0 ); assert( mc->mc_refcnt == 0 ); meta_back_conn_free( mc ); } +#if META_BACK_PRINT_CONNTREE > 0 + meta_back_print_conntree( mi, "<<< meta_back_conn_destroy" ); +#endif /* META_BACK_PRINT_CONNTREE */ ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex ); /* * Cleanup rewrite session */ for ( i = 0; i < mi->mi_ntargets; ++i ) { - rewrite_session_delete( mi->mi_targets[ i ].mt_rwmap.rwm_rw, conn ); + rewrite_session_delete( mi->mi_targets[ i ]->mt_rwmap.rwm_rw, conn ); } return 0; diff --git a/servers/slapd/back-monitor/database.c b/servers/slapd/back-monitor/database.c index 2cf9e42e86..8418df57ac 100644 --- a/servers/slapd/back-monitor/database.c +++ b/servers/slapd/back-monitor/database.c @@ -383,7 +383,7 @@ monitor_subsys_database_init( int t; for ( t = 0; t < mi->mi_ntargets; t++ ) { - char **urls = ldap_str2charray( mi->mi_targets[ t ].mt_uri, " " ); + char **urls = ldap_str2charray( mi->mi_targets[ t ]->mt_uri, " " ); int u; for ( u = 0; urls[ u ] != NULL; u++ ) { diff --git a/servers/slapd/overlays/rwm.c b/servers/slapd/overlays/rwm.c index 533ec2f719..bbd83b1865 100644 --- a/servers/slapd/overlays/rwm.c +++ b/servers/slapd/overlays/rwm.c @@ -121,6 +121,7 @@ rwm_callback_get( Operation *op, SlapReply *rs ) return roc; } + static int rwm_op_dn_massage( Operation *op, SlapReply *rs, void *cookie, rwm_op_state *ros ) @@ -1642,7 +1643,6 @@ rwm_db_init( BackendDB *be ) { slap_overinst *on = (slap_overinst *) be->bd_info; - struct ldapmapping *mapping = NULL; struct ldaprwmap *rwmap; #ifdef ENABLE_REWRITE char *rargv[ 3 ]; @@ -1671,13 +1671,6 @@ rwm_db_init( rewrite_parse( rwmap->rwm_rw, "", 2, 2, rargv ); #endif /* ENABLE_REWRITE */ - if ( rwm_map_init( &rwmap->rwm_oc, &mapping ) != LDAP_SUCCESS || - rwm_map_init( &rwmap->rwm_at, &mapping ) != LDAP_SUCCESS ) - { - rc = 1; - goto error_return; - } - error_return:; on->on_bi.bi_private = (void *)rwmap; diff --git a/servers/slapd/overlays/rwmconf.c b/servers/slapd/overlays/rwmconf.c index a37ffa8a13..13ef727f1e 100644 --- a/servers/slapd/overlays/rwmconf.c +++ b/servers/slapd/overlays/rwmconf.c @@ -47,6 +47,7 @@ rwm_map_config( struct ldapmapping *mapping; char *src, *dst; int is_oc = 0; + int rc = 0; if ( argc < 3 || argc > 4 ) { fprintf( stderr, @@ -73,7 +74,7 @@ rwm_map_config( if ( strcmp( argv[2], "*" ) == 0 ) { if ( argc < 4 || strcmp( argv[3], "*" ) == 0 ) { map->drop_missing = ( argc < 4 ); - return 0; + goto success_return; } src = dst = argv[3]; @@ -230,7 +231,13 @@ rwm_map_config( avl_insert( &map->remap, (caddr_t)&mapping[1], rwm_mapping_cmp, rwm_mapping_dup ); - return 0; +success_return:; + if ( !is_oc && map->map == NULL ) { + /* only init if required */ + rc = rwm_map_init( map, &mapping ) != LDAP_SUCCESS; + } + + return rc; error_return:; if ( mapping ) { diff --git a/servers/slapd/overlays/rwmmap.c b/servers/slapd/overlays/rwmmap.c index 337795a707..8ba1d1d9fa 100644 --- a/servers/slapd/overlays/rwmmap.c +++ b/servers/slapd/overlays/rwmmap.c @@ -84,6 +84,7 @@ rwm_map_init( struct ldapmap *lm, struct ldapmapping **m ) /* FIXME: I don't think this is needed any more... */ rc = slap_str2ad( "objectClass", &mapping[0].m_src_ad, &text ); if ( rc != LDAP_SUCCESS ) { + ch_free( mapping ); return rc; } @@ -112,6 +113,10 @@ rwm_mapping( struct ldapmap *map, struct berval *s, struct ldapmapping **m, int Avlnode *tree; struct ldapmapping fmapping; + if ( map == NULL ) { + return 0; + } + assert( m != NULL ); if ( remap == RWM_REMAP ) { @@ -137,6 +142,13 @@ rwm_map( struct ldapmap *map, struct berval *s, struct berval *bv, int remap ) { struct ldapmapping *mapping; + /* map->map may be NULL when mapping is configured, + * but map->remap can't */ + if ( map->remap == NULL ) { + *bv = *s; + return; + } + BER_BVZERO( bv ); ( void )rwm_mapping( map, s, &mapping, remap ); if ( mapping != NULL ) {