]> git.sur5r.net Git - openldap/blob - servers/slapd/back-ldap/bind.c
ITS#4780 plug leak
[openldap] / servers / slapd / back-ldap / bind.c
1 /* bind.c - ldap backend bind function */
2 /* $OpenLDAP$ */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4  *
5  * Copyright 1999-2006 The OpenLDAP Foundation.
6  * Portions Copyright 2000-2003 Pierangelo Masarati.
7  * Portions Copyright 1999-2003 Howard Chu.
8  * All rights reserved.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted only as authorized by the OpenLDAP
12  * Public License.
13  *
14  * A copy of this license is available in the file LICENSE in the
15  * top-level directory of the distribution or, alternatively, at
16  * <http://www.OpenLDAP.org/license.html>.
17  */
18 /* ACKNOWLEDGEMENTS:
19  * This work was initially developed by Howard Chu for inclusion
20  * in OpenLDAP Software and subsequently enhanced by Pierangelo
21  * Masarati.
22  */
23
24 #include "portable.h"
25
26 #include <stdio.h>
27
28 #include <ac/errno.h>
29 #include <ac/socket.h>
30 #include <ac/string.h>
31
32 #define AVL_INTERNAL
33 #include "slap.h"
34 #include "back-ldap.h"
35 #undef ldap_debug       /* silence a warning in ldap-int.h */
36 #include "../../../libraries/libldap/ldap-int.h"
37
38 #include "lutil_ldap.h"
39
40 #define LDAP_CONTROL_OBSOLETE_PROXY_AUTHZ       "2.16.840.1.113730.3.4.12"
41
42 #if LDAP_BACK_PRINT_CONNTREE > 0
43 static void
44 ravl_print( Avlnode *root, int depth )
45 {
46         int             i;
47         ldapconn_t      *lc;
48         
49         if ( root == 0 ) {
50                 return;
51         }
52         
53         ravl_print( root->avl_right, depth+1 );
54         
55         for ( i = 0; i < depth; i++ ) {
56                 fprintf( stderr, "-" );
57         }
58
59         lc = root->avl_data;
60         fprintf( stderr, "lc=%p local=\"%s\" conn=%p %s refcnt=%d\n",
61                 (void *)lc,
62                 lc->lc_local_ndn.bv_val ? lc->lc_local_ndn.bv_val : "",
63                 (void *)lc->lc_conn,
64                 avl_bf2str( root->avl_bf ), lc->lc_refcnt );
65         
66         ravl_print( root->avl_left, depth+1 );
67 }
68
69 void
70 ldap_back_print_conntree( Avlnode *root, char *msg )
71 {
72         fprintf( stderr, "========> %s\n", msg );
73         
74         if ( root == 0 ) {
75                 fprintf( stderr, "\t(empty)\n" );
76
77         } else {
78                 ravl_print( root, 0 );
79         }
80         
81         fprintf( stderr, "<======== %s\n", msg );
82 }
83 #endif /* LDAP_BACK_PRINT_CONNTREE */
84
85 static ldapconn_t *
86 ldap_back_getconn( Operation *op, SlapReply *rs, ldap_back_send_t sendok,
87         struct berval *binddn, struct berval *bindcred );
88
89 static int
90 ldap_back_is_proxy_authz( Operation *op, SlapReply *rs, ldap_back_send_t sendok,
91         struct berval *binddn, struct berval *bindcred );
92
93 static int
94 ldap_back_proxy_authz_bind( ldapconn_t *lc, Operation *op, SlapReply *rs,
95         ldap_back_send_t sendok, struct berval *binddn, struct berval *bindcred );
96
97 static int
98 ldap_back_prepare_conn( ldapconn_t **lcp, Operation *op, SlapReply *rs,
99         ldap_back_send_t sendok );
100
101 static int
102 ldap_back_conndnlc_cmp( const void *c1, const void *c2 );
103
104 int
105 ldap_back_bind( Operation *op, SlapReply *rs )
106 {
107         ldapinfo_t              *li = (ldapinfo_t *) op->o_bd->be_private;
108         ldapconn_t              *lc;
109
110         int                     rc = 0;
111         ber_int_t               msgid;
112         ldap_back_send_t        retrying = LDAP_BACK_RETRYING;
113
114         lc = ldap_back_getconn( op, rs, LDAP_BACK_BIND_SERR, NULL, NULL );
115         if ( !lc ) {
116                 return rs->sr_err;
117         }
118
119         if ( !BER_BVISNULL( &lc->lc_bound_ndn ) ) {
120                 ch_free( lc->lc_bound_ndn.bv_val );
121                 BER_BVZERO( &lc->lc_bound_ndn );
122         }
123         LDAP_BACK_CONN_ISBOUND_CLEAR( lc );
124
125 retry:;
126         /* method is always LDAP_AUTH_SIMPLE if we got here */
127         rs->sr_err = ldap_sasl_bind( lc->lc_ld, op->o_req_dn.bv_val,
128                         LDAP_SASL_SIMPLE,
129                         &op->orb_cred, op->o_ctrls, NULL, &msgid );
130         /* FIXME: should we always retry, or only when piping the bind
131          * in the "override" connection pool? */
132         rc = ldap_back_op_result( lc, op, rs, msgid,
133                 li->li_timeout[ SLAP_OP_BIND ],
134                 LDAP_BACK_BIND_SERR | retrying );
135         if ( rc == LDAP_UNAVAILABLE && retrying ) {
136                 retrying &= ~LDAP_BACK_RETRYING;
137                 if ( ldap_back_retry( &lc, op, rs, LDAP_BACK_BIND_SERR ) ) {
138                         goto retry;
139                 }
140         }
141
142         if ( rc == LDAP_SUCCESS ) {
143                 /* If defined, proxyAuthz will be used also when
144                  * back-ldap is the authorizing backend; for this
145                  * purpose, after a successful bind the connection
146                  * is left for further binds, and further operations 
147                  * on this client connection will use a default
148                  * connection with identity assertion */
149                 /* NOTE: use with care */
150                 if ( li->li_idassert_flags & LDAP_BACK_AUTH_OVERRIDE ) {
151                         assert( lc->lc_binding == 1 );
152                         lc->lc_binding = 0;
153                         ldap_back_release_conn( op, rs, lc );
154                         return( rc );
155                 }
156
157                 /* rebind is now done inside ldap_back_proxy_authz_bind()
158                  * in case of success */
159                 LDAP_BACK_CONN_ISBOUND_SET( lc );
160                 ber_dupbv( &lc->lc_bound_ndn, &op->o_req_ndn );
161
162                 if ( LDAP_BACK_SAVECRED( li ) ) {
163                         if ( !BER_BVISNULL( &lc->lc_cred ) ) {
164                                 memset( lc->lc_cred.bv_val, 0,
165                                                 lc->lc_cred.bv_len );
166                         }
167                         ber_bvreplace( &lc->lc_cred, &op->orb_cred );
168                         ldap_set_rebind_proc( lc->lc_ld, li->li_rebind_f, lc );
169                 }
170         }
171
172         assert( lc->lc_binding == 1 );
173         lc->lc_binding = 0;
174
175         /* must re-insert if local DN changed as result of bind */
176         if ( !LDAP_BACK_CONN_ISBOUND( lc )
177                 || ( LDAP_BACK_CONN_ISBOUND( lc )
178                         && !dn_match( &op->o_req_ndn, &lc->lc_local_ndn ) ) )
179         {
180                 int             lerr = -1;
181                 ldapconn_t      *tmplc;
182
183                 /* wait for all other ops to release the connection */
184 retry_lock:;
185                 ldap_pvt_thread_mutex_lock( &li->li_conninfo.lai_mutex );
186                 if ( lc->lc_refcnt > 1 ) {
187                         ldap_pvt_thread_mutex_unlock( &li->li_conninfo.lai_mutex );
188                         ldap_pvt_thread_yield();
189                         goto retry_lock;
190                 }
191
192 #if LDAP_BACK_PRINT_CONNTREE > 0
193                 ldap_back_print_conntree( li->li_conninfo.lai_tree, ">>> ldap_back_bind" );
194 #endif /* LDAP_BACK_PRINT_CONNTREE */
195         
196                 assert( lc->lc_refcnt == 1 );
197                 tmplc = avl_delete( &li->li_conninfo.lai_tree, (caddr_t)lc,
198                                 ldap_back_conndnlc_cmp );
199                 assert( tmplc == NULL || lc == tmplc );
200
201                 /* delete all cached connections with the current connection */
202                 if ( LDAP_BACK_SINGLECONN( li ) ) {
203                         while ( ( tmplc = avl_delete( &li->li_conninfo.lai_tree, (caddr_t)lc, ldap_back_conn_cmp ) ) != NULL )
204                         {
205                                 Debug( LDAP_DEBUG_TRACE,
206                                         "=>ldap_back_bind: destroying conn %ld (refcnt=%u)\n",
207                                         LDAP_BACK_PCONN_ID( lc ), lc->lc_refcnt, 0 );
208
209                                 if ( tmplc->lc_refcnt != 0 ) {
210                                         /* taint it */
211                                         LDAP_BACK_CONN_TAINTED_SET( tmplc );
212
213                                 } else {
214                                         /*
215                                          * Needs a test because the handler may be corrupted,
216                                          * and calling ldap_unbind on a corrupted header results
217                                          * in a segmentation fault
218                                          */
219                                         ldap_back_conn_free( tmplc );
220                                 }
221                         }
222                 }
223
224                 if ( LDAP_BACK_CONN_ISBOUND( lc ) ) {
225                         ber_bvreplace( &lc->lc_local_ndn, &op->o_req_ndn );
226                         if ( be_isroot_dn( op->o_bd, &op->o_req_ndn ) ) {
227                                 lc->lc_conn = LDAP_BACK_PCONN_SET( op );
228                         }
229                         lerr = avl_insert( &li->li_conninfo.lai_tree, (caddr_t)lc,
230                                 ldap_back_conndn_cmp, ldap_back_conndn_dup );
231                 }
232
233 #if LDAP_BACK_PRINT_CONNTREE > 0
234                 ldap_back_print_conntree( li->li_conninfo.lai_tree, "<<< ldap_back_bind" );
235 #endif /* LDAP_BACK_PRINT_CONNTREE */
236         
237                 ldap_pvt_thread_mutex_unlock( &li->li_conninfo.lai_mutex );
238                 switch ( lerr ) {
239                 case 0:
240                         break;
241
242                 case -1:
243                         /* duplicate; someone else successfully bound
244                          * on the same connection with the same identity;
245                          * we can do this because lc_refcnt == 1 */
246                         ldap_back_conn_free( lc );
247                         lc = NULL;
248                 }
249         }
250
251         if ( lc != NULL ) {
252                 ldap_back_release_conn( op, rs, lc );
253         }
254
255         return( rc );
256 }
257
258 /*
259  * ldap_back_conndn_cmp
260  *
261  * compares two ldapconn_t based on the value of the conn pointer
262  * and of the local DN; used by avl stuff for insert, lookup
263  * and direct delete
264  */
265 int
266 ldap_back_conndn_cmp( const void *c1, const void *c2 )
267 {
268         const ldapconn_t        *lc1 = (const ldapconn_t *)c1;
269         const ldapconn_t        *lc2 = (const ldapconn_t *)c2;
270         int rc;
271
272         /* If local DNs don't match, it is definitely not a match */
273         /* For shared sessions, conn is NULL. Only explicitly
274          * bound sessions will have non-NULL conn.
275          */
276         rc = SLAP_PTRCMP( lc1->lc_conn, lc2->lc_conn );
277         if ( rc == 0 ) {
278                 rc = ber_bvcmp( &lc1->lc_local_ndn, &lc2->lc_local_ndn );
279         }
280
281         return rc;
282 }
283
284 /*
285  * ldap_back_conndnlc_cmp
286  *
287  * compares two ldapconn_t based on the value of the conn pointer,
288  * the local DN and the lc pointer; used by avl stuff for insert, lookup
289  * and direct delete
290  */
291 static int
292 ldap_back_conndnlc_cmp( const void *c1, const void *c2 )
293 {
294         const ldapconn_t        *lc1 = (const ldapconn_t *)c1;
295         const ldapconn_t        *lc2 = (const ldapconn_t *)c2;
296         int rc;
297
298         /* If local DNs don't match, it is definitely not a match */
299         /* For shared sessions, conn is NULL. Only explicitly
300          * bound sessions will have non-NULL conn.
301          */
302         rc = SLAP_PTRCMP( lc1->lc_conn, lc2->lc_conn );
303         if ( rc == 0 ) {
304                 rc = ber_bvcmp( &lc1->lc_local_ndn, &lc2->lc_local_ndn );
305                 if ( rc == 0 ) {
306                         rc = SLAP_PTRCMP( lc1, lc2 );
307                 }
308         }
309
310         return rc;
311 }
312
313 /*
314  * ldap_back_conn_cmp
315  *
316  * compares two ldapconn_t based on the value of the conn pointer;
317  * used by avl stuff for delete of all conns with the same connid
318  */
319 int
320 ldap_back_conn_cmp( const void *c1, const void *c2 )
321 {
322         const ldapconn_t        *lc1 = (const ldapconn_t *)c1;
323         const ldapconn_t        *lc2 = (const ldapconn_t *)c2;
324
325         /* For shared sessions, conn is NULL. Only explicitly
326          * bound sessions will have non-NULL conn.
327          */
328         return SLAP_PTRCMP( lc1->lc_conn, lc2->lc_conn );
329 }
330
331 /*
332  * ldap_back_conndn_dup
333  *
334  * returns -1 in case a duplicate ldapconn_t has been inserted;
335  * used by avl stuff
336  */
337 int
338 ldap_back_conndn_dup( void *c1, void *c2 )
339 {
340         ldapconn_t      *lc1 = (ldapconn_t *)c1;
341         ldapconn_t      *lc2 = (ldapconn_t *)c2;
342
343         /* Cannot have more than one shared session with same DN */
344         if ( lc1->lc_conn == lc2->lc_conn &&
345                 dn_match( &lc1->lc_local_ndn, &lc2->lc_local_ndn ) )
346         {
347                 return -1;
348         }
349                 
350         return 0;
351 }
352
353 int
354 ldap_back_freeconn( Operation *op, ldapconn_t *lc, int dolock )
355 {
356         ldapinfo_t      *li = (ldapinfo_t *) op->o_bd->be_private;
357         ldapconn_t      *tmplc;
358
359         if ( dolock ) {
360                 ldap_pvt_thread_mutex_lock( &li->li_conninfo.lai_mutex );
361         }
362
363 #if LDAP_BACK_PRINT_CONNTREE > 0
364         ldap_back_print_conntree( li->li_conninfo.lai_tree, ">>> ldap_back_freeconn" );
365 #endif /* LDAP_BACK_PRINT_CONNTREE */
366
367         tmplc = avl_delete( &li->li_conninfo.lai_tree, (caddr_t)lc,
368                         ldap_back_conndnlc_cmp );
369         assert( LDAP_BACK_CONN_TAINTED( lc ) || tmplc == lc );
370         if ( lc->lc_refcnt == 0 ) {
371                 ldap_back_conn_free( (void *)lc );
372         }
373
374 #if LDAP_BACK_PRINT_CONNTREE > 0
375         ldap_back_print_conntree( li->li_conninfo.lai_tree, "<<< ldap_back_freeconn" );
376 #endif /* LDAP_BACK_PRINT_CONNTREE */
377
378         if ( dolock ) {
379                 ldap_pvt_thread_mutex_unlock( &li->li_conninfo.lai_mutex );
380         }
381
382         return 0;
383 }
384
385 #ifdef HAVE_TLS
386 static int
387 ldap_back_start_tls(
388         LDAP            *ld,
389         int             protocol,
390         int             *is_tls,
391         const char      *url,
392         unsigned        flags,
393         int             retries,
394         const char      **text )
395 {
396         int             rc = LDAP_SUCCESS;
397         ldapinfo_t      dummy;
398
399         /* this is ridiculous... */
400         dummy.li_flags = flags;
401
402         /* start TLS ("tls-[try-]{start,propagate}" statements) */
403         if ( ( LDAP_BACK_USE_TLS( &dummy ) || ( *is_tls && LDAP_BACK_PROPAGATE_TLS( &dummy ) ) )
404                                 && !ldap_is_ldaps_url( url ) )
405         {
406 #ifdef SLAP_STARTTLS_ASYNCHRONOUS
407                 /*
408                  * use asynchronous StartTLS
409                  * in case, chase referral (not implemented yet)
410                  */
411                 int             msgid;
412
413                 if ( protocol == 0 ) {
414                         ldap_get_option( ld, LDAP_OPT_PROTOCOL_VERSION,
415                                         (void *)&protocol );
416                 }
417
418                 if ( protocol < LDAP_VERSION3 ) {
419                         /* we should rather bail out... */
420                         rc = LDAP_UNWILLING_TO_PERFORM;
421                         *text = "invalid protocol version";
422                 }
423
424                 if ( rc == LDAP_SUCCESS ) {
425                         rc = ldap_start_tls( ld, NULL, NULL, &msgid );
426                 }
427
428                 if ( rc == LDAP_SUCCESS ) {
429                         LDAPMessage     *res = NULL;
430                         struct timeval  tv;
431
432                         LDAP_BACK_TV_SET( &tv );
433
434 retry:;
435                         rc = ldap_result( ld, msgid, LDAP_MSG_ALL, &tv, &res );
436                         if ( rc < 0 ) {
437                                 rc = LDAP_UNAVAILABLE;
438
439                         } else if ( rc == 0 ) {
440                                 if ( retries != LDAP_BACK_RETRY_NEVER ) {
441                                         ldap_pvt_thread_yield();
442                                         if ( retries > 0 ) {
443                                                 retries--;
444                                         }
445                                         LDAP_BACK_TV_SET( &tv );
446                                         goto retry;
447                                 }
448                                 rc = LDAP_UNAVAILABLE;
449
450                         } else if ( rc == LDAP_RES_EXTENDED ) {
451                                 struct berval   *data = NULL;
452
453                                 rc = ldap_parse_extended_result( ld, res,
454                                                 NULL, &data, 0 );
455                                 if ( rc == LDAP_SUCCESS ) {
456                                         int err;
457                                         rc = ldap_parse_result( ld, res, &err,
458                                                 NULL, NULL, NULL, NULL, 1 );
459                                         if ( rc == LDAP_SUCCESS ) {
460                                                 rc = err;
461                                         }
462                                         res = NULL;
463                                         
464                                         /* FIXME: in case a referral 
465                                          * is returned, should we try
466                                          * using it instead of the 
467                                          * configured URI? */
468                                         if ( rc == LDAP_SUCCESS ) {
469                                                 rc = ldap_install_tls( ld );
470
471                                         } else if ( rc == LDAP_REFERRAL ) {
472                                                 rc = LDAP_UNWILLING_TO_PERFORM;
473                                                 *text = "unwilling to chase referral returned by Start TLS exop";
474                                         }
475
476                                         if ( data ) {
477                                                 if ( data->bv_val ) {
478                                                         ber_memfree( data->bv_val );
479                                                 }
480                                                 ber_memfree( data );
481                                         }
482                                 }
483
484                         } else {
485                                 rc = LDAP_OTHER;
486                         }
487
488                         if ( res != NULL ) {
489                                 ldap_msgfree( res );
490                         }
491                 }
492 #else /* ! SLAP_STARTTLS_ASYNCHRONOUS */
493                 /*
494                  * use synchronous StartTLS
495                  */
496                 rc = ldap_start_tls_s( ld, NULL, NULL );
497 #endif /* ! SLAP_STARTTLS_ASYNCHRONOUS */
498
499                 /* if StartTLS is requested, only attempt it if the URL
500                  * is not "ldaps://"; this may occur not only in case
501                  * of misconfiguration, but also when used in the chain 
502                  * overlay, where the "uri" can be parsed out of a referral */
503                 switch ( rc ) {
504                 case LDAP_SUCCESS:
505                         *is_tls = 1;
506                         break;
507
508                 case LDAP_SERVER_DOWN:
509                         break;
510
511                 default:
512                         if ( LDAP_BACK_TLS_CRITICAL( &dummy ) ) {
513                                 *text = "could not start TLS";
514                                 break;
515                         }
516
517                         /* in case Start TLS is not critical */
518                         *is_tls = 0;
519                         rc = LDAP_SUCCESS;
520                         break;
521                 }
522
523         } else {
524                 *is_tls = 0;
525         }
526
527         return rc;
528 }
529 #endif /* HAVE_TLS */
530
531 static int
532 ldap_back_prepare_conn( ldapconn_t **lcp, Operation *op, SlapReply *rs, ldap_back_send_t sendok )
533 {
534         ldapinfo_t      *li = (ldapinfo_t *)op->o_bd->be_private;
535         int             version;
536         LDAP            *ld = NULL;
537 #ifdef HAVE_TLS
538         int             is_tls = op->o_conn->c_is_tls;
539         time_t          lc_time = (time_t)(-1);
540 #endif /* HAVE_TLS */
541
542         assert( lcp != NULL );
543
544         ldap_pvt_thread_mutex_lock( &li->li_uri_mutex );
545         rs->sr_err = ldap_initialize( &ld, li->li_uri );
546         ldap_pvt_thread_mutex_unlock( &li->li_uri_mutex );
547         if ( rs->sr_err != LDAP_SUCCESS ) {
548                 goto error_return;
549         }
550
551         if ( li->li_urllist_f ) {
552                 ldap_set_urllist_proc( ld, li->li_urllist_f, li->li_urllist_p );
553         }
554
555         /* Set LDAP version. This will always succeed: If the client
556          * bound with a particular version, then so can we.
557          */
558         if ( li->li_version != 0 ) {
559                 version = li->li_version;
560
561         } else if ( op->o_protocol != 0 ) {
562                 version = op->o_protocol;
563
564         } else {
565                 /* assume it's an internal op; set to LDAPv3 */
566                 version = LDAP_VERSION3;
567         }
568         ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION, (const void *)&version );
569
570         /* automatically chase referrals ("chase-referrals [{yes|no}]" statement) */
571         ldap_set_option( ld, LDAP_OPT_REFERRALS,
572                 LDAP_BACK_CHASE_REFERRALS( li ) ? LDAP_OPT_ON : LDAP_OPT_OFF );
573
574         if ( li->li_network_timeout > 0 ) {
575                 struct timeval          tv;
576
577                 tv.tv_sec = li->li_network_timeout;
578                 tv.tv_usec = 0;
579                 ldap_set_option( ld, LDAP_OPT_NETWORK_TIMEOUT, (const void *)&tv );
580         }
581
582 #ifdef HAVE_TLS
583         ldap_pvt_thread_mutex_lock( &li->li_uri_mutex );
584         rs->sr_err = ldap_back_start_tls( ld, op->o_protocol, &is_tls,
585                         li->li_uri, li->li_flags, li->li_nretries, &rs->sr_text );
586         ldap_pvt_thread_mutex_unlock( &li->li_uri_mutex );
587         if ( rs->sr_err != LDAP_SUCCESS ) {
588                 ldap_unbind_ext( ld, NULL, NULL );
589                 goto error_return;
590
591         } else if ( li->li_idle_timeout ) {
592                 /* only touch when activity actually took place... */
593                 lc_time = op->o_time;
594         }
595 #endif /* HAVE_TLS */
596
597         if ( *lcp == NULL ) {
598                 *lcp = (ldapconn_t *)ch_calloc( 1, sizeof( ldapconn_t ) );
599                 (*lcp)->lc_flags = li->li_flags;
600         }
601         (*lcp)->lc_ld = ld;
602         (*lcp)->lc_refcnt = 1;
603         (*lcp)->lc_binding = 1;
604 #ifdef HAVE_TLS
605         if ( is_tls ) {
606                 LDAP_BACK_CONN_ISTLS_SET( *lcp );
607         } else {
608                 LDAP_BACK_CONN_ISTLS_CLEAR( *lcp );
609         }
610         if ( lc_time != (time_t)(-1) ) {
611                 (*lcp)->lc_time = lc_time;
612         }
613 #endif /* HAVE_TLS */
614
615 error_return:;
616         if ( rs->sr_err != LDAP_SUCCESS ) {
617                 rs->sr_err = slap_map_api2result( rs );
618                 if ( sendok & LDAP_BACK_SENDERR ) {
619                         if ( rs->sr_text == NULL ) {
620                                 rs->sr_text = "ldap_initialize() failed";
621                         }
622                         send_ldap_result( op, rs );
623                         rs->sr_text = NULL;
624                 }
625
626         } else {
627                 if ( li->li_conn_ttl > 0 ) {
628                         (*lcp)->lc_create_time = op->o_time;
629                 }
630         }
631
632         return rs->sr_err;
633 }
634
635 static ldapconn_t *
636 ldap_back_getconn(
637         Operation               *op,
638         SlapReply               *rs,
639         ldap_back_send_t        sendok,
640         struct berval           *binddn,
641         struct berval           *bindcred )
642 {
643         ldapinfo_t      *li = (ldapinfo_t *)op->o_bd->be_private;
644         ldapconn_t      *lc = NULL,
645                         lc_curr = { 0 };
646         int             refcnt = 1,
647                         binding = 1,
648                         lookupconn = !( sendok & LDAP_BACK_BINDING );
649
650         /* if the server is quarantined, and
651          * - the current interval did not expire yet, or
652          * - no more retries should occur,
653          * don't return the connection */
654         if ( li->li_isquarantined ) {
655                 slap_retry_info_t       *ri = &li->li_quarantine;
656                 int                     dont_retry = 1;
657
658                 ldap_pvt_thread_mutex_lock( &li->li_quarantine_mutex );
659                 if ( li->li_isquarantined == LDAP_BACK_FQ_YES ) {
660                         dont_retry = ( ri->ri_num[ ri->ri_idx ] == SLAP_RETRYNUM_TAIL
661                                 || slap_get_time() < ri->ri_last + ri->ri_interval[ ri->ri_idx ] );
662                         if ( !dont_retry ) {
663                                 Debug( LDAP_DEBUG_ANY,
664                                         "%s: ldap_back_getconn quarantine "
665                                         "retry block #%d try #%d.\n",
666                                         op->o_log_prefix, ri->ri_idx, ri->ri_count );
667                                 li->li_isquarantined = LDAP_BACK_FQ_RETRYING;
668                         }
669                 }
670                 ldap_pvt_thread_mutex_unlock( &li->li_quarantine_mutex );
671
672                 if ( dont_retry ) {
673                         rs->sr_err = LDAP_UNAVAILABLE;
674                         if ( op->o_conn && ( sendok & LDAP_BACK_SENDERR ) ) {
675                                 send_ldap_result( op, rs );
676                         }
677                         return NULL;
678                 }
679         }
680
681         /* Internal searches are privileged and shared. So is root. */
682         if ( op->o_do_not_cache || be_isroot( op ) ) {
683                 LDAP_BACK_CONN_ISPRIV_SET( &lc_curr );
684                 lc_curr.lc_local_ndn = op->o_bd->be_rootndn;
685                 lc_curr.lc_conn = LDAP_BACK_PCONN_SET( op );
686
687         } else {
688                 struct berval   tmpbinddn,
689                                 tmpbindcred,
690                                 save_o_dn,
691                                 save_o_ndn;
692                 int             isproxyauthz;
693
694                 /* need cleanup */
695                 if ( binddn == NULL ) {
696                         binddn = &tmpbinddn;
697                 }       
698                 if ( bindcred == NULL ) {
699                         bindcred = &tmpbindcred;
700                 }
701                 if ( op->o_tag == LDAP_REQ_BIND ) {
702                         save_o_dn = op->o_dn;
703                         save_o_ndn = op->o_ndn;
704                         op->o_dn = op->o_req_dn;
705                         op->o_ndn = op->o_req_ndn;
706                 }
707                 isproxyauthz = ldap_back_is_proxy_authz( op, rs, sendok, binddn, bindcred );
708                 if ( isproxyauthz == -1 ) {
709                         return NULL;
710                 }
711                 if ( op->o_tag == LDAP_REQ_BIND ) {
712                         op->o_dn = save_o_dn;
713                         op->o_ndn = save_o_ndn;
714                 }
715
716                 lc_curr.lc_local_ndn = op->o_ndn;
717                 /* Explicit binds must not be shared;
718                  * however, explicit binds are piped in a special connection
719                  * when idassert is to occur with "override" set */
720                 if ( op->o_tag == LDAP_REQ_BIND && !isproxyauthz ) {
721                         lc_curr.lc_conn = op->o_conn;
722
723                 } else {
724                         if ( isproxyauthz && !( sendok & LDAP_BACK_BINDING ) ) {
725                                 lc_curr.lc_local_ndn = *binddn;
726                                 lc_curr.lc_conn = LDAP_BACK_PCONN_SET( op );
727                                 LDAP_BACK_CONN_ISIDASSERT_SET( &lc_curr );
728
729                         } else if ( isproxyauthz && ( li->li_idassert_flags & LDAP_BACK_AUTH_OVERRIDE ) ) {
730                                 lc_curr.lc_local_ndn = slap_empty_bv;
731                                 lc_curr.lc_conn = LDAP_BACK_PCONN_BIND_SET( op );
732                                 LDAP_BACK_CONN_ISIDASSERT_SET( &lc_curr );
733                                 lookupconn = 1;
734
735                         } else if ( SLAP_IS_AUTHZ_BACKEND( op ) ) {
736                                 lc_curr.lc_conn = op->o_conn;
737
738                         } else {
739                                 lc_curr.lc_conn = LDAP_BACK_PCONN_SET( op );
740                         }
741                 }
742         }
743
744         /* Explicit Bind requests always get their own conn */
745         if ( lookupconn ) {
746                 /* Searches for a ldapconn in the avl tree */
747 retry_lock:
748                 ldap_pvt_thread_mutex_lock( &li->li_conninfo.lai_mutex );
749
750                 lc = (ldapconn_t *)avl_find( li->li_conninfo.lai_tree, 
751                                 (caddr_t)&lc_curr, ldap_back_conndn_cmp );
752                 if ( lc != NULL ) {
753                         /* Don't reuse connections while they're still binding */
754                         if ( LDAP_BACK_CONN_BINDING( lc ) ) {
755                                 if ( !LDAP_BACK_USE_TEMPORARIES( li ) ) {
756                                         ldap_pvt_thread_mutex_unlock( &li->li_conninfo.lai_mutex );
757
758                                         ldap_pvt_thread_yield();
759                                         goto retry_lock;
760                                 }
761
762                                 lc = NULL;
763
764                         } else {
765
766                                 if ( op->o_tag == LDAP_REQ_BIND ) {
767                                         /* right now, this is the only possible case */
768                                         assert( ( li->li_idassert_flags & LDAP_BACK_AUTH_OVERRIDE ) );
769                                         LDAP_BACK_CONN_BINDING_SET( lc );
770                                 }
771
772                                 refcnt = ++lc->lc_refcnt;
773                                 binding = ++lc->lc_binding;
774                         }
775                 }
776                 ldap_pvt_thread_mutex_unlock( &li->li_conninfo.lai_mutex );
777         }
778
779         /* Looks like we didn't get a bind. Open a new session... */
780         if ( lc == NULL ) {
781                 if ( ldap_back_prepare_conn( &lc, op, rs, sendok ) != LDAP_SUCCESS ) {
782                         return NULL;
783                 }
784
785                 if ( sendok & LDAP_BACK_BINDING ) {
786                         LDAP_BACK_CONN_BINDING_SET( lc );
787                 }
788
789                 lc->lc_conn = lc_curr.lc_conn;
790                 ber_dupbv( &lc->lc_local_ndn, &lc_curr.lc_local_ndn );
791
792                 /*
793                  * the rationale is: connections as the rootdn are privileged,
794                  * so acl_authcDN is to be used; however, in some cases
795                  * one already configured identity assertion with a highly
796                  * privileged idassert_authcDN, so if acl_authcDN is NULL
797                  * and idassert_authcDN is not, use the second instead.
798                  *
799                  * might change in the future, because it's preferable
800                  * to make clear what identity is being used, since
801                  * the only drawback is that one risks to configure
802                  * the same identity twice...
803                  */
804                 if ( LDAP_BACK_CONN_ISPRIV( &lc_curr ) ) {
805                         if ( BER_BVISNULL( &li->li_acl_authcDN ) && !BER_BVISNULL( &li->li_idassert_authcDN ) ) {
806                                 ber_dupbv( &lc->lc_bound_ndn, &li->li_idassert_authcDN );
807                                 ber_dupbv( &lc->lc_cred, &li->li_idassert_passwd );
808
809                         } else {
810                                 ber_dupbv( &lc->lc_bound_ndn, &li->li_acl_authcDN );
811                                 ber_dupbv( &lc->lc_cred, &li->li_acl_passwd );
812                         }
813                         LDAP_BACK_CONN_ISPRIV_SET( lc );
814
815                 } else if ( LDAP_BACK_CONN_ISIDASSERT( &lc_curr ) ) {
816                         ber_dupbv( &lc->lc_bound_ndn, &li->li_idassert_authcDN );
817                         ber_dupbv( &lc->lc_cred, &li->li_idassert_passwd );
818                         LDAP_BACK_CONN_ISIDASSERT_SET( lc );
819
820                 } else {
821                         BER_BVZERO( &lc->lc_cred );
822                         BER_BVZERO( &lc->lc_bound_ndn );
823                         if ( !BER_BVISEMPTY( &op->o_ndn )
824                                 && SLAP_IS_AUTHZ_BACKEND( op ) )
825                         {
826                                 ber_dupbv( &lc->lc_bound_ndn, &op->o_ndn );
827                         }
828                 }
829
830 #ifdef HAVE_TLS
831                 /* if start TLS failed but it was not mandatory,
832                  * check if the non-TLS connection was already
833                  * in cache; in case, destroy the newly created
834                  * connection and use the existing one */
835                 if ( lc->lc_conn == LDAP_BACK_PCONN_TLS
836                                 && !ldap_tls_inplace( lc->lc_ld ) )
837                 {
838                         ldapconn_t *tmplc;
839                         
840                         lc_curr.lc_conn = LDAP_BACK_PCONN;
841                         ldap_pvt_thread_mutex_lock( &li->li_conninfo.lai_mutex );
842                         tmplc = (ldapconn_t *)avl_find( li->li_conninfo.lai_tree, 
843                                         (caddr_t)&lc_curr, ldap_back_conndn_cmp );
844                         if ( tmplc != NULL ) {
845                                 refcnt = ++tmplc->lc_refcnt;
846                                 binding = ++tmplc->lc_binding;
847                                 ldap_back_conn_free( lc );
848                                 lc = tmplc;
849                         }
850                         ldap_pvt_thread_mutex_unlock( &li->li_conninfo.lai_mutex );
851
852                         if ( tmplc != NULL ) {
853                                 goto done;
854                         }
855                 }
856 #endif /* HAVE_TLS */
857
858                 LDAP_BACK_CONN_ISBOUND_CLEAR( lc );
859
860                 /* Inserts the newly created ldapconn in the avl tree */
861                 ldap_pvt_thread_mutex_lock( &li->li_conninfo.lai_mutex );
862
863 #if LDAP_BACK_PRINT_CONNTREE > 0
864                 ldap_back_print_conntree( li->li_conninfo.lai_tree, ">>> ldap_back_getconn(insert)" );
865 #endif /* LDAP_BACK_PRINT_CONNTREE */
866         
867                 assert( lc->lc_refcnt == 1 );
868                 assert( lc->lc_binding == 1 );
869                 rs->sr_err = avl_insert( &li->li_conninfo.lai_tree, (caddr_t)lc,
870                         ldap_back_conndn_cmp, ldap_back_conndn_dup );
871
872 #if LDAP_BACK_PRINT_CONNTREE > 0
873                 ldap_back_print_conntree( li->li_conninfo.lai_tree, "<<< ldap_back_getconn(insert)" );
874 #endif /* LDAP_BACK_PRINT_CONNTREE */
875         
876                 ldap_pvt_thread_mutex_unlock( &li->li_conninfo.lai_mutex );
877
878                 Debug( LDAP_DEBUG_TRACE,
879                         "=>ldap_back_getconn: conn %p inserted refcnt=%u binding=%u\n",
880                         (void *)lc, refcnt, binding );
881         
882                 /* Err could be -1 in case a duplicate ldapconn is inserted */
883                 switch ( rs->sr_err ) {
884                 case 0:
885                         break;
886
887                 case -1:
888                         if ( !( sendok & LDAP_BACK_BINDING ) && !LDAP_BACK_USE_TEMPORARIES( li ) ) {
889                                 /* duplicate: free and try to get the newly created one */
890                                 ldap_back_conn_free( lc );
891                                 lc = NULL;
892                                 goto retry_lock;
893                         }
894
895                         /* taint connection, so that it'll be freed when released */
896                         LDAP_BACK_CONN_TAINTED_SET( lc );
897                         break;
898
899                 default:
900                         ldap_back_conn_free( lc );
901                         rs->sr_err = LDAP_OTHER;
902                         rs->sr_text = "proxy bind collision";
903                         if ( op->o_conn && ( sendok & LDAP_BACK_SENDERR ) ) {
904                                 send_ldap_result( op, rs );
905                                 rs->sr_text = NULL;
906                         }
907                         return NULL;
908                 }
909
910         } else {
911                 int     expiring = 0;
912
913                 if ( ( li->li_idle_timeout != 0 && op->o_time > lc->lc_time + li->li_idle_timeout )
914                         || ( li->li_conn_ttl != 0 && op->o_time > lc->lc_create_time + li->li_conn_ttl ) )
915                 {
916                         expiring = 1;
917
918                         /* let it be used, but taint/delete it so that 
919                          * no-one else can look it up any further */
920                         ldap_pvt_thread_mutex_lock( &li->li_conninfo.lai_mutex );
921 #if LDAP_BACK_PRINT_CONNTREE > 0
922                         ldap_back_print_conntree( li->li_conninfo.lai_tree, ">>> ldap_back_getconn(timeout)" );
923 #endif /* LDAP_BACK_PRINT_CONNTREE */
924                         (void *)avl_delete( &li->li_conninfo.lai_tree, (caddr_t)lc,
925                                         ldap_back_conndnlc_cmp );
926 #if LDAP_BACK_PRINT_CONNTREE > 0
927                         ldap_back_print_conntree( li->li_conninfo.lai_tree, "<<< ldap_back_getconn(timeout)" );
928 #endif /* LDAP_BACK_PRINT_CONNTREE */
929                         ldap_pvt_thread_mutex_unlock( &li->li_conninfo.lai_mutex );
930                         LDAP_BACK_CONN_TAINTED_SET( lc );
931                 }
932
933                 if ( LogTest( LDAP_DEBUG_TRACE ) ) {
934                         char    buf[ SLAP_TEXT_BUFLEN ];
935
936                         snprintf( buf, sizeof( buf ),
937                                 "conn %p fetched refcnt=%u binding=%u%s",
938                                 (void *)lc, refcnt, binding, expiring ? " expiring" : "" );
939                         Debug( LDAP_DEBUG_TRACE,
940                                 "=>ldap_back_getconn: %s.\n", buf, 0, 0 );
941                 }
942         }
943
944 #ifdef HAVE_TLS
945 done:;
946 #endif /* HAVE_TLS */
947
948         return lc;
949 }
950
951 void
952 ldap_back_release_conn_lock(
953         Operation               *op,
954         SlapReply               *rs,
955         ldapconn_t              **lcp,
956         int                     dolock )
957 {
958         ldapinfo_t      *li = (ldapinfo_t *)op->o_bd->be_private;
959
960         ldapconn_t      *lc = *lcp;
961
962         if ( dolock ) {
963                 ldap_pvt_thread_mutex_lock( &li->li_conninfo.lai_mutex );
964         }
965         assert( lc->lc_refcnt > 0 );
966         LDAP_BACK_CONN_BINDING_CLEAR( lc );
967         lc->lc_refcnt--;
968         if ( LDAP_BACK_CONN_TAINTED( lc ) ) {
969                 ldap_back_freeconn( op, lc, 0 );
970                 *lcp = NULL;
971         }
972         if ( dolock ) {
973                 ldap_pvt_thread_mutex_unlock( &li->li_conninfo.lai_mutex );
974         }
975 }
976
977 void
978 ldap_back_quarantine(
979         Operation       *op,
980         SlapReply       *rs )
981 {
982         ldapinfo_t              *li = (ldapinfo_t *)op->o_bd->be_private;
983
984         slap_retry_info_t       *ri = &li->li_quarantine;
985
986         ldap_pvt_thread_mutex_lock( &li->li_quarantine_mutex );
987
988         if ( rs->sr_err == LDAP_UNAVAILABLE ) {
989                 time_t          new_last = slap_get_time();
990
991                 switch ( li->li_isquarantined ) {
992                 case LDAP_BACK_FQ_NO:
993                         if ( ri->ri_last == new_last ) {
994                                 goto done;
995                         }
996
997                         Debug( LDAP_DEBUG_ANY,
998                                 "%s: ldap_back_quarantine enter.\n",
999                                 op->o_log_prefix, 0, 0 );
1000
1001                         ri->ri_idx = 0;
1002                         ri->ri_count = 0;
1003                         break;
1004
1005                 case LDAP_BACK_FQ_RETRYING:
1006                         Debug( LDAP_DEBUG_ANY,
1007                                 "%s: ldap_back_quarantine block #%d try #%d failed.\n",
1008                                 op->o_log_prefix, ri->ri_idx, ri->ri_count );
1009
1010                         ++ri->ri_count;
1011                         if ( ri->ri_num[ ri->ri_idx ] != SLAP_RETRYNUM_FOREVER
1012                                 && ri->ri_count == ri->ri_num[ ri->ri_idx ] )
1013                         {
1014                                 ri->ri_count = 0;
1015                                 ++ri->ri_idx;
1016                         }
1017                         break;
1018
1019                 default:
1020                         break;
1021                 }
1022
1023                 li->li_isquarantined = LDAP_BACK_FQ_YES;
1024                 ri->ri_last = new_last;
1025
1026         } else if ( li->li_isquarantined != LDAP_BACK_FQ_NO ) {
1027                 if ( ri->ri_last == slap_get_time() ) {
1028                         goto done;
1029                 }
1030
1031                 Debug( LDAP_DEBUG_ANY,
1032                         "%s: ldap_back_quarantine exit (%d) err=%d.\n",
1033                         op->o_log_prefix, li->li_isquarantined, rs->sr_err );
1034
1035                 if ( li->li_quarantine_f ) {
1036                         (void)li->li_quarantine_f( li, li->li_quarantine_p );
1037                 }
1038
1039                 ri->ri_count = 0;
1040                 ri->ri_idx = 0;
1041                 li->li_isquarantined = LDAP_BACK_FQ_NO;
1042         }
1043
1044 done:;
1045         ldap_pvt_thread_mutex_unlock( &li->li_quarantine_mutex );
1046 }
1047
1048 /*
1049  * ldap_back_dobind_int
1050  *
1051  * Note: dolock indicates whether li->li_conninfo.lai_mutex must be locked or not
1052  */
1053 static int
1054 ldap_back_dobind_int(
1055         ldapconn_t              **lcp,
1056         Operation               *op,
1057         SlapReply               *rs,
1058         ldap_back_send_t        sendok,
1059         int                     retries,
1060         int                     dolock )
1061 {       
1062         ldapinfo_t      *li = (ldapinfo_t *)op->o_bd->be_private;
1063
1064         ldapconn_t      *lc;
1065         struct berval   binddn = slap_empty_bv,
1066                         bindcred = slap_empty_bv;
1067
1068         int             rc = 0,
1069                         isbound,
1070                         binding = 0;
1071         ber_int_t       msgid;
1072
1073         assert( lcp != NULL );
1074         assert( retries >= 0 );
1075
1076         if ( sendok & LDAP_BACK_GETCONN ) {
1077                 assert( *lcp == NULL );
1078
1079                 lc = ldap_back_getconn( op, rs, sendok, &binddn, &bindcred );
1080                 if ( lc == NULL ) {
1081                         return 0;
1082                 }
1083                 *lcp = lc;
1084
1085         } else {
1086                 lc = *lcp;
1087         }
1088
1089         assert( lc != NULL );
1090
1091 retry_lock:;
1092         if ( dolock ) {
1093                 ldap_pvt_thread_mutex_lock( &li->li_conninfo.lai_mutex );
1094         }
1095
1096         if ( binding == 0 ) {
1097                 /* check if already bound */
1098                 rc = isbound = LDAP_BACK_CONN_ISBOUND( lc );
1099                 if ( isbound ) {
1100                         lc->lc_binding--;
1101                         if ( dolock ) {
1102                                 ldap_pvt_thread_mutex_unlock( &li->li_conninfo.lai_mutex );
1103                         }
1104                         return rc;
1105                 }
1106
1107                 if ( LDAP_BACK_CONN_BINDING( lc ) ) {
1108                         /* if someone else is about to bind it, give up and retry */
1109                         if ( dolock ) {
1110                                 ldap_pvt_thread_mutex_unlock( &li->li_conninfo.lai_mutex );
1111                         }
1112                         ldap_pvt_thread_yield();
1113                         goto retry_lock;
1114
1115                 } else {
1116                         /* otherwise this thread will bind it */
1117                         LDAP_BACK_CONN_BINDING_SET( lc );
1118                         binding = 1;
1119                 }
1120         }
1121
1122         /* wait for pending operations to finish */
1123         /* FIXME: may become a bottleneck! */
1124         if ( lc->lc_refcnt != lc->lc_binding ) {
1125                 if ( dolock ) {
1126                         ldap_pvt_thread_mutex_unlock( &li->li_conninfo.lai_mutex );
1127                 }
1128                 ldap_pvt_thread_yield();
1129                 goto retry_lock;
1130         }
1131
1132         if ( dolock ) {
1133                 ldap_pvt_thread_mutex_unlock( &li->li_conninfo.lai_mutex );
1134         }
1135
1136         /*
1137          * FIXME: we need to let clients use proxyAuthz
1138          * otherwise we cannot do symmetric pools of servers;
1139          * we have to live with the fact that a user can
1140          * authorize itself as any ID that is allowed
1141          * by the authzTo directive of the "proxyauthzdn".
1142          */
1143         /*
1144          * NOTE: current Proxy Authorization specification
1145          * and implementation do not allow proxy authorization
1146          * control to be provided with Bind requests
1147          */
1148         /*
1149          * if no bind took place yet, but the connection is bound
1150          * and the "idassert-authcDN" (or other ID) is set, 
1151          * then bind as the asserting identity and explicitly 
1152          * add the proxyAuthz control to every operation with the
1153          * dn bound to the connection as control value.
1154          * This is done also if this is the authrizing backend,
1155          * but the "override" flag is given to idassert.
1156          * It allows to use SASL bind and yet proxyAuthz users
1157          */
1158         if ( LDAP_BACK_CONN_ISIDASSERT( lc ) ) {
1159                 if ( BER_BVISEMPTY( &binddn ) && BER_BVISEMPTY( &bindcred ) ) {
1160                         /* if we got here, it shouldn't return result */
1161                         rc = ldap_back_is_proxy_authz( op, rs,
1162                                 LDAP_BACK_DONTSEND, &binddn, &bindcred );
1163                         assert( rc == 1 );
1164                 }
1165                 rc = ldap_back_proxy_authz_bind( lc, op, rs, sendok, &binddn, &bindcred );
1166                 goto done;
1167         }
1168
1169 #ifdef HAVE_CYRUS_SASL
1170         if ( LDAP_BACK_CONN_ISPRIV( lc )
1171                 && li->li_acl_authmethod == LDAP_AUTH_SASL )
1172         {
1173                 void            *defaults = NULL;
1174
1175                 if ( li->li_acl_secprops != NULL ) {
1176                         rc = ldap_set_option( lc->lc_ld,
1177                                 LDAP_OPT_X_SASL_SECPROPS, li->li_acl_secprops );
1178
1179                         if ( rc != LDAP_OPT_SUCCESS ) {
1180                                 Debug( LDAP_DEBUG_ANY, "Error: ldap_set_option "
1181                                         "(SECPROPS,\"%s\") failed!\n",
1182                                         li->li_acl_secprops, 0, 0 );
1183                                 goto done;
1184                         }
1185                 }
1186
1187                 defaults = lutil_sasl_defaults( lc->lc_ld,
1188                                 li->li_acl_sasl_mech.bv_val,
1189                                 li->li_acl_sasl_realm.bv_val,
1190                                 li->li_acl_authcID.bv_val,
1191                                 li->li_acl_passwd.bv_val,
1192                                 NULL );
1193
1194                 rs->sr_err = ldap_sasl_interactive_bind_s( lc->lc_ld,
1195                                 li->li_acl_authcDN.bv_val,
1196                                 li->li_acl_sasl_mech.bv_val, NULL, NULL,
1197                                 LDAP_SASL_QUIET, lutil_sasl_interact,
1198                                 defaults );
1199
1200                 lutil_sasl_freedefs( defaults );
1201
1202                 rs->sr_err = slap_map_api2result( rs );
1203                 if ( rs->sr_err != LDAP_SUCCESS ) {
1204                         LDAP_BACK_CONN_ISBOUND_CLEAR( lc );
1205                         if ( sendok & LDAP_BACK_SENDERR ) {
1206                                 send_ldap_result( op, rs );
1207                         }
1208
1209                 } else {
1210                         LDAP_BACK_CONN_ISBOUND_SET( lc );
1211                 }
1212
1213                 if ( LDAP_BACK_QUARANTINE( li ) ) {
1214                         ldap_back_quarantine( op, rs );
1215                 }
1216
1217                 goto done;
1218         }
1219 #endif /* HAVE_CYRUS_SASL */
1220
1221 retry:;
1222         rs->sr_err = ldap_sasl_bind( lc->lc_ld,
1223                         BER_BVISNULL( &lc->lc_cred ) ? "" : lc->lc_bound_ndn.bv_val,
1224                         LDAP_SASL_SIMPLE, &lc->lc_cred,
1225                         NULL, NULL, &msgid );
1226
1227         if ( rs->sr_err == LDAP_SERVER_DOWN ) {
1228                 if ( retries != LDAP_BACK_RETRY_NEVER ) {
1229                         if ( dolock ) {
1230                                 ldap_pvt_thread_mutex_lock( &li->li_conninfo.lai_mutex );
1231                         }
1232
1233                         assert( lc->lc_refcnt > 0 );
1234                         if ( lc->lc_refcnt == 1 ) {
1235                                 ldap_unbind_ext( lc->lc_ld, NULL, NULL );
1236                                 lc->lc_ld = NULL;
1237
1238                                 /* lc here must be the regular lc, reset and ready for init */
1239                                 rs->sr_err = ldap_back_prepare_conn( &lc, op, rs, sendok );
1240                                 if ( rs->sr_err != LDAP_SUCCESS ) {
1241                                         lc->lc_binding--;
1242                                         lc->lc_refcnt = 0;
1243                                 }
1244                         }
1245
1246                         if ( dolock ) {
1247                                 ldap_pvt_thread_mutex_unlock( &li->li_conninfo.lai_mutex );
1248                         }
1249
1250                         if ( rs->sr_err == LDAP_SUCCESS ) {
1251                                 if ( retries > 0 ) {
1252                                         retries--;
1253                                 }
1254                                 goto retry;
1255                         }
1256
1257                 } else {
1258                         if ( dolock ) {
1259                                 ldap_pvt_thread_mutex_lock( &li->li_conninfo.lai_mutex );
1260                         }
1261                         lc->lc_binding--;
1262                         if ( dolock ) {
1263                                 ldap_pvt_thread_mutex_unlock( &li->li_conninfo.lai_mutex );
1264                         }
1265                 }
1266
1267                 /* FIXME: one binding-- too many? */
1268                 lc->lc_binding--;
1269                 assert( lc->lc_refcnt == 1 );
1270                 lc->lc_refcnt = 0;
1271                 ldap_back_freeconn( op, lc, dolock );
1272                 *lcp = NULL;
1273                 rs->sr_err = slap_map_api2result( rs );
1274
1275                 if ( LDAP_BACK_QUARANTINE( li ) ) {
1276                         ldap_back_quarantine( op, rs );
1277                 }
1278
1279                 if ( rs->sr_err != LDAP_SUCCESS &&
1280                         ( sendok & LDAP_BACK_SENDERR ) )
1281                 {
1282                         send_ldap_result( op, rs );
1283                 }
1284
1285                 return 0;
1286         }
1287
1288         rc = ldap_back_op_result( lc, op, rs, msgid,
1289                 -1, (sendok|LDAP_BACK_BINDING) );
1290         if ( rc == LDAP_SUCCESS ) {
1291                 LDAP_BACK_CONN_ISBOUND_SET( lc );
1292         }
1293
1294 done:;
1295         lc->lc_binding--;
1296         LDAP_BACK_CONN_BINDING_CLEAR( lc );
1297         rc = LDAP_BACK_CONN_ISBOUND( lc );
1298         if ( !rc ) {
1299                 ldap_back_release_conn_lock( op, rs, lcp, dolock );
1300
1301         } else if ( LDAP_BACK_SAVECRED( li ) ) {
1302                 ldap_set_rebind_proc( lc->lc_ld, li->li_rebind_f, lc );
1303         }
1304
1305         return rc;
1306 }
1307
1308 /*
1309  * ldap_back_dobind
1310  *
1311  * Note: dolock indicates whether li->li_conninfo.lai_mutex must be locked or not
1312  */
1313 int
1314 ldap_back_dobind( ldapconn_t **lcp, Operation *op, SlapReply *rs, ldap_back_send_t sendok )
1315 {
1316         ldapinfo_t      *li = (ldapinfo_t *)op->o_bd->be_private;
1317
1318         return ldap_back_dobind_int( lcp, op, rs,
1319                 ( sendok | LDAP_BACK_GETCONN ), li->li_nretries, 1 );
1320 }
1321
1322 /*
1323  * ldap_back_default_rebind
1324  *
1325  * This is a callback used for chasing referrals using the same
1326  * credentials as the original user on this session.
1327  */
1328 int 
1329 ldap_back_default_rebind( LDAP *ld, LDAP_CONST char *url, ber_tag_t request,
1330         ber_int_t msgid, void *params )
1331 {
1332         ldapconn_t      *lc = (ldapconn_t *)params;
1333
1334 #ifdef HAVE_TLS
1335         /* ... otherwise we couldn't get here */
1336         assert( lc != NULL );
1337
1338         if ( !ldap_tls_inplace( ld ) ) {
1339                 int             is_tls = LDAP_BACK_CONN_ISTLS( lc ),
1340                                 rc;
1341                 const char      *text = NULL;
1342
1343                 rc = ldap_back_start_tls( ld, 0, &is_tls, url, lc->lc_flags,
1344                         LDAP_BACK_RETRY_DEFAULT, &text );
1345                 if ( rc != LDAP_SUCCESS ) {
1346                         return rc;
1347                 }
1348         }
1349 #endif /* HAVE_TLS */
1350
1351         /* FIXME: add checks on the URL/identity? */
1352
1353         return ldap_sasl_bind_s( ld,
1354                         BER_BVISNULL( &lc->lc_cred ) ? "" : lc->lc_bound_ndn.bv_val,
1355                         LDAP_SASL_SIMPLE, &lc->lc_cred, NULL, NULL, NULL );
1356 }
1357
1358 /*
1359  * ldap_back_default_urllist
1360  */
1361 int 
1362 ldap_back_default_urllist(
1363         LDAP            *ld,
1364         LDAPURLDesc     **urllist,
1365         LDAPURLDesc     **url,
1366         void            *params )
1367 {
1368         ldapinfo_t      *li = (ldapinfo_t *)params;
1369         LDAPURLDesc     **urltail;
1370
1371         if ( urllist == url ) {
1372                 return LDAP_SUCCESS;
1373         }
1374
1375         for ( urltail = &(*url)->lud_next; *urltail; urltail = &(*urltail)->lud_next )
1376                 /* count */ ;
1377
1378         *urltail = *urllist;
1379         *urllist = *url;
1380         *url = NULL;
1381
1382         ldap_pvt_thread_mutex_lock( &li->li_uri_mutex );
1383         if ( li->li_uri ) {
1384                 ch_free( li->li_uri );
1385         }
1386
1387         ldap_get_option( ld, LDAP_OPT_URI, (void *)&li->li_uri );
1388         ldap_pvt_thread_mutex_unlock( &li->li_uri_mutex );
1389
1390         return LDAP_SUCCESS;
1391 }
1392
1393 int
1394 ldap_back_cancel(
1395                 ldapconn_t              *lc,
1396                 Operation               *op,
1397                 SlapReply               *rs,
1398                 ber_int_t               msgid,
1399                 ldap_back_send_t        sendok )
1400 {
1401         ldapinfo_t      *li = (ldapinfo_t *)op->o_bd->be_private;
1402
1403         /* default behavior */
1404         if ( LDAP_BACK_ABANDON( li ) ) {
1405                 return ldap_abandon_ext( lc->lc_ld, msgid, NULL, NULL );
1406         }
1407
1408         if ( LDAP_BACK_IGNORE( li ) ) {
1409                 return ldap_pvt_discard( lc->lc_ld, msgid );
1410         }
1411
1412         if ( LDAP_BACK_CANCEL( li ) ) {
1413                 /* FIXME: asynchronous? */
1414                 return ldap_cancel_s( lc->lc_ld, msgid, NULL, NULL );
1415         }
1416
1417         assert( 0 );
1418
1419         return LDAP_OTHER;
1420 }
1421
1422 int
1423 ldap_back_op_result(
1424                 ldapconn_t              *lc,
1425                 Operation               *op,
1426                 SlapReply               *rs,
1427                 ber_int_t               msgid,
1428                 time_t                  timeout,
1429                 ldap_back_send_t        sendok )
1430 {
1431         ldapinfo_t      *li = (ldapinfo_t *)op->o_bd->be_private;
1432
1433         char            *match = NULL;
1434         char            *text = NULL;
1435         char            **refs = NULL;
1436         LDAPControl     **ctrls = NULL;
1437
1438 #define ERR_OK(err) ((err) == LDAP_SUCCESS || (err) == LDAP_COMPARE_FALSE || (err) == LDAP_COMPARE_TRUE)
1439
1440         rs->sr_text = NULL;
1441         rs->sr_matched = NULL;
1442         rs->sr_ref = NULL;
1443         rs->sr_ctrls = NULL;
1444
1445         /* if the error recorded in the reply corresponds
1446          * to a successful state, get the error from the
1447          * remote server response */
1448         if ( ERR_OK( rs->sr_err ) ) {
1449                 int             rc;
1450                 struct timeval  tv;
1451                 LDAPMessage     *res = NULL;
1452                 time_t          stoptime = (time_t)(-1);
1453                 int             timeout_err = op->o_protocol >= LDAP_VERSION3 ?
1454                                         LDAP_ADMINLIMIT_EXCEEDED : LDAP_OTHER;
1455                 const char      *timeout_text = "Operation timed out";
1456
1457                 /* if timeout is not specified, compute and use
1458                  * the one specific to the ongoing operation */
1459                 if ( timeout == (time_t)(-1) ) {
1460                         slap_op_t       opidx = slap_req2op( op->o_tag );
1461
1462                         if ( opidx == SLAP_OP_SEARCH ) {
1463                                 if ( op->ors_tlimit <= 0 ) {
1464                                         timeout = 0;
1465
1466                                 } else {
1467                                         timeout = op->ors_tlimit;
1468                                         timeout_err = LDAP_TIMELIMIT_EXCEEDED;
1469                                         timeout_text = NULL;
1470                                 }
1471
1472                         } else {
1473                                 timeout = li->li_timeout[ opidx ];
1474                         }
1475                 }
1476
1477                 /* better than nothing :) */
1478                 if ( timeout == 0 ) {
1479                         if ( li->li_idle_timeout ) {
1480                                 timeout = li->li_idle_timeout;
1481
1482                         } else if ( li->li_conn_ttl ) {
1483                                 timeout = li->li_conn_ttl;
1484                         }
1485                 }
1486
1487                 if ( timeout ) {
1488                         stoptime = op->o_time + timeout;
1489                 }
1490
1491                 LDAP_BACK_TV_SET( &tv );
1492
1493 retry:;
1494                 /* if result parsing fails, note the failure reason */
1495                 rc = ldap_result( lc->lc_ld, msgid, LDAP_MSG_ALL, &tv, &res );
1496                 switch ( rc ) {
1497                 case 0:
1498                         if ( timeout && slap_get_time() > stoptime ) {
1499                                 if ( sendok & LDAP_BACK_BINDING ) {
1500                                         ldap_unbind_ext( lc->lc_ld, NULL, NULL );
1501                                         lc->lc_ld = NULL;
1502                                         LDAP_BACK_CONN_TAINTED_SET( lc );
1503
1504                                 } else {
1505                                         (void)ldap_back_cancel( lc, op, rs, msgid, sendok );
1506                                 }
1507                                 rs->sr_err = timeout_err;
1508                                 rs->sr_text = timeout_text;
1509                                 break;
1510                         }
1511
1512                         /* timeout == 0 */
1513                         LDAP_BACK_TV_SET( &tv );
1514                         ldap_pvt_thread_yield();
1515                         goto retry;
1516
1517                 case -1:
1518                         ldap_get_option( lc->lc_ld, LDAP_OPT_ERROR_NUMBER,
1519                                         &rs->sr_err );
1520                         break;
1521
1522
1523                 /* otherwise get the result; if it is not
1524                  * LDAP_SUCCESS, record it in the reply
1525                  * structure (this includes 
1526                  * LDAP_COMPARE_{TRUE|FALSE}) */
1527                 default:
1528                         /* only touch when activity actually took place... */
1529                         if ( li->li_idle_timeout && lc ) {
1530                                 lc->lc_time = op->o_time;
1531                         }
1532
1533                         rc = ldap_parse_result( lc->lc_ld, res, &rs->sr_err,
1534                                         &match, &text, &refs, &ctrls, 1 );
1535                         rs->sr_text = text;
1536                         if ( rc != LDAP_SUCCESS ) {
1537                                 rs->sr_err = rc;
1538                         }
1539                         if ( refs != NULL ) {
1540                                 int     i;
1541
1542                                 for ( i = 0; refs[ i ] != NULL; i++ )
1543                                         /* count */ ;
1544                                 rs->sr_ref = op->o_tmpalloc( sizeof( struct berval ) * ( i + 1 ),
1545                                         op->o_tmpmemctx );
1546                                 for ( i = 0; refs[ i ] != NULL; i++ ) {
1547                                         ber_str2bv( refs[ i ], 0, 0, &rs->sr_ref[ i ] );
1548                                 }
1549                                 BER_BVZERO( &rs->sr_ref[ i ] );
1550                         }
1551                         if ( ctrls != NULL ) {
1552                                 rs->sr_ctrls = ctrls;
1553                         }
1554                 }
1555         }
1556
1557         /* if the error in the reply structure is not
1558          * LDAP_SUCCESS, try to map it from client 
1559          * to server error */
1560         if ( !ERR_OK( rs->sr_err ) ) {
1561                 rs->sr_err = slap_map_api2result( rs );
1562
1563                 /* internal ops ( op->o_conn == NULL ) 
1564                  * must not reply to client */
1565                 if ( op->o_conn && !op->o_do_not_cache && match ) {
1566
1567                         /* record the (massaged) matched
1568                          * DN into the reply structure */
1569                         rs->sr_matched = match;
1570                 }
1571         }
1572
1573         if ( rs->sr_err == LDAP_UNAVAILABLE ) {
1574                 if ( !( sendok & LDAP_BACK_RETRYING ) ) {
1575                         if ( LDAP_BACK_QUARANTINE( li ) ) {
1576                                 ldap_back_quarantine( op, rs );
1577                         }
1578                         if ( op->o_conn && ( sendok & LDAP_BACK_SENDERR ) ) {
1579                                 send_ldap_result( op, rs );
1580                         }
1581                 }
1582
1583         } else if ( op->o_conn &&
1584                 ( ( ( sendok & LDAP_BACK_SENDOK ) && ERR_OK( rs->sr_err ) )
1585                         || ( ( sendok & LDAP_BACK_SENDERR ) && rs->sr_err != LDAP_SUCCESS ) ) )
1586         {
1587                 send_ldap_result( op, rs );
1588         }
1589
1590         if ( match ) {
1591                 if ( rs->sr_matched != match ) {
1592                         free( (char *)rs->sr_matched );
1593                 }
1594                 rs->sr_matched = NULL;
1595                 ldap_memfree( match );
1596         }
1597
1598         if ( text ) {
1599                 ldap_memfree( text );
1600         }
1601         rs->sr_text = NULL;
1602
1603         if ( rs->sr_ref ) {
1604                 assert( refs != NULL );
1605                 ber_memvfree( (void **)refs );
1606                 op->o_tmpfree( rs->sr_ref, op->o_tmpmemctx );
1607                 rs->sr_ref = NULL;
1608         }
1609
1610         if ( ctrls ) {
1611                 assert( rs->sr_ctrls != NULL );
1612                 ldap_controls_free( ctrls );
1613                 rs->sr_ctrls = NULL;
1614         }
1615
1616         return( ERR_OK( rs->sr_err ) ? LDAP_SUCCESS : rs->sr_err );
1617 }
1618
1619 /* return true if bound, false if failed */
1620 int
1621 ldap_back_retry( ldapconn_t **lcp, Operation *op, SlapReply *rs, ldap_back_send_t sendok )
1622 {
1623         int             rc = 0;
1624         ldapinfo_t      *li = (ldapinfo_t *)op->o_bd->be_private;
1625
1626         assert( lcp != NULL );
1627         assert( *lcp != NULL );
1628
1629         ldap_pvt_thread_mutex_lock( &li->li_conninfo.lai_mutex );
1630
1631         if ( (*lcp)->lc_refcnt == 1 ) {
1632                 ldap_pvt_thread_mutex_lock( &li->li_uri_mutex );
1633                 Debug( LDAP_DEBUG_ANY,
1634                         "%s ldap_back_retry: retrying URI=\"%s\" DN=\"%s\"\n",
1635                         op->o_log_prefix, li->li_uri,
1636                         BER_BVISNULL( &(*lcp)->lc_bound_ndn ) ?
1637                                 "" : (*lcp)->lc_bound_ndn.bv_val );
1638                 ldap_pvt_thread_mutex_unlock( &li->li_uri_mutex );
1639
1640                 ldap_unbind_ext( (*lcp)->lc_ld, NULL, NULL );
1641                 (*lcp)->lc_ld = NULL;
1642                 LDAP_BACK_CONN_ISBOUND_CLEAR( (*lcp) );
1643
1644                 /* lc here must be the regular lc, reset and ready for init */
1645                 rc = ldap_back_prepare_conn( lcp, op, rs, sendok );
1646                 if ( rc != LDAP_SUCCESS ) {
1647                         /* freeit, because lc_refcnt == 1 */
1648                         (*lcp)->lc_refcnt = 0;
1649                         (void)ldap_back_freeconn( op, *lcp, 0 );
1650                         *lcp = NULL;
1651                         rc = 0;
1652
1653                 } else if ( ( sendok & LDAP_BACK_BINDING ) ) {
1654                         rc = 1;
1655
1656                 } else {
1657                         rc = ldap_back_dobind_int( lcp, op, rs, sendok, 0, 0 );
1658                         if ( rc == 0 && *lcp != NULL ) {
1659                                 /* freeit, because lc_refcnt == 1 */
1660                                 (*lcp)->lc_refcnt = 0;
1661                                 LDAP_BACK_CONN_TAINTED_SET( *lcp );
1662                                 (void)ldap_back_freeconn( op, *lcp, 0 );
1663                                 *lcp = NULL;
1664                         }
1665                 }
1666
1667         } else {
1668                 Debug( LDAP_DEBUG_TRACE,
1669                         "ldap_back_retry: conn %p refcnt=%u unable to retry.\n",
1670                         (void *)(*lcp), (*lcp)->lc_refcnt, 0 );
1671
1672                 LDAP_BACK_CONN_TAINTED_SET( *lcp );
1673                 ldap_back_release_conn_lock( op, rs, lcp, 0 );
1674                 assert( *lcp == NULL );
1675
1676                 if ( sendok ) {
1677                         rs->sr_err = LDAP_UNAVAILABLE;
1678                         rs->sr_text = "unable to retry";
1679                         send_ldap_result( op, rs );
1680                 }
1681         }
1682
1683         ldap_pvt_thread_mutex_unlock( &li->li_conninfo.lai_mutex );
1684
1685         return rc;
1686 }
1687
1688 static int
1689 ldap_back_is_proxy_authz( Operation *op, SlapReply *rs, ldap_back_send_t sendok,
1690         struct berval *binddn, struct berval *bindcred )
1691 {
1692         ldapinfo_t      *li = (ldapinfo_t *)op->o_bd->be_private;
1693         struct berval   ndn;
1694         int             dobind = 0;
1695
1696         if ( op->o_conn == NULL || op->o_do_not_cache ) {
1697                 goto done;
1698         }
1699
1700         /* don't proxyAuthz if protocol is not LDAPv3 */
1701         switch ( li->li_version ) {
1702         case LDAP_VERSION3:
1703                 break;
1704
1705         case 0:
1706                 if ( op->o_protocol == 0 || op->o_protocol == LDAP_VERSION3 ) {
1707                         break;
1708                 }
1709                 /* fall thru */
1710
1711         default:
1712                 rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
1713                 if ( sendok & LDAP_BACK_SENDERR ) {
1714                         send_ldap_result( op, rs );
1715                         dobind = -1;
1716                 }
1717                 goto done;
1718         }
1719
1720         /* safe default */
1721         *binddn = slap_empty_bv;
1722         *bindcred = slap_empty_bv;
1723
1724         if ( !BER_BVISNULL( &op->o_conn->c_ndn ) ) {
1725                 ndn = op->o_conn->c_ndn;
1726
1727         } else {
1728                 ndn = op->o_ndn;
1729         }
1730
1731         switch ( li->li_idassert_mode ) {
1732         case LDAP_BACK_IDASSERT_LEGACY:
1733                 if ( !BER_BVISNULL( &ndn ) && !BER_BVISEMPTY( &ndn ) ) {
1734                         if ( !BER_BVISNULL( &li->li_idassert_authcDN ) && !BER_BVISEMPTY( &li->li_idassert_authcDN ) )
1735                         {
1736                                 *binddn = li->li_idassert_authcDN;
1737                                 *bindcred = li->li_idassert_passwd;
1738                                 dobind = 1;
1739                         }
1740                 }
1741                 break;
1742
1743         default:
1744                 /* NOTE: rootdn can always idassert */
1745                 if ( BER_BVISNULL( &ndn ) && li->li_idassert_authz == NULL ) {
1746                         if ( li->li_idassert_flags & LDAP_BACK_AUTH_PRESCRIPTIVE ) {
1747                                 rs->sr_err = LDAP_INAPPROPRIATE_AUTH;
1748                                 if ( sendok & LDAP_BACK_SENDERR ) {
1749                                         send_ldap_result( op, rs );
1750                                         dobind = -1;
1751                                 }
1752
1753                         } else {
1754                                 rs->sr_err = LDAP_SUCCESS;
1755                                 *binddn = slap_empty_bv;
1756                                 *bindcred = slap_empty_bv;
1757                                 break;
1758                         }
1759
1760                         goto done;
1761
1762                 } else if ( li->li_idassert_authz && !be_isroot( op ) ) {
1763                         struct berval authcDN;
1764
1765                         if ( BER_BVISNULL( &ndn ) ) {
1766                                 authcDN = slap_empty_bv;
1767
1768                         } else {
1769                                 authcDN = ndn;
1770                         }       
1771                         rs->sr_err = slap_sasl_matches( op, li->li_idassert_authz,
1772                                         &authcDN, &authcDN );
1773                         if ( rs->sr_err != LDAP_SUCCESS ) {
1774                                 if ( li->li_idassert_flags & LDAP_BACK_AUTH_PRESCRIPTIVE ) {
1775                                         if ( sendok & LDAP_BACK_SENDERR ) {
1776                                                 send_ldap_result( op, rs );
1777                                                 dobind = -1;
1778                                         }
1779
1780                                 } else {
1781                                         rs->sr_err = LDAP_SUCCESS;
1782                                         *binddn = slap_empty_bv;
1783                                         *bindcred = slap_empty_bv;
1784                                         break;
1785                                 }
1786
1787                                 goto done;
1788                         }
1789                 }
1790
1791                 *binddn = li->li_idassert_authcDN;
1792                 *bindcred = li->li_idassert_passwd;
1793                 dobind = 1;
1794                 break;
1795         }
1796
1797 done:;
1798         return dobind;
1799 }
1800
1801 static int
1802 ldap_back_proxy_authz_bind(
1803         ldapconn_t              *lc,
1804         Operation               *op,
1805         SlapReply               *rs,
1806         ldap_back_send_t        sendok,
1807         struct berval           *binddn,
1808         struct berval           *bindcred )
1809 {
1810         ldapinfo_t      *li = (ldapinfo_t *)op->o_bd->be_private;
1811         struct berval   ndn;
1812         int             msgid;
1813         int             rc;
1814
1815         if ( !BER_BVISNULL( &op->o_conn->c_ndn ) ) {
1816                 ndn = op->o_conn->c_ndn;
1817
1818         } else {
1819                 ndn = op->o_ndn;
1820         }
1821
1822         if ( li->li_idassert_authmethod == LDAP_AUTH_SASL ) {
1823 #ifdef HAVE_CYRUS_SASL
1824                 void            *defaults = NULL;
1825                 struct berval   authzID = BER_BVNULL;
1826                 int             freeauthz = 0;
1827
1828                 /* if SASL supports native authz, prepare for it */
1829                 if ( ( !op->o_do_not_cache || !op->o_is_auth_check ) &&
1830                                 ( li->li_idassert_flags & LDAP_BACK_AUTH_NATIVE_AUTHZ ) )
1831                 {
1832                         switch ( li->li_idassert_mode ) {
1833                         case LDAP_BACK_IDASSERT_OTHERID:
1834                         case LDAP_BACK_IDASSERT_OTHERDN:
1835                                 authzID = li->li_idassert_authzID;
1836                                 break;
1837
1838                         case LDAP_BACK_IDASSERT_ANONYMOUS:
1839                                 BER_BVSTR( &authzID, "dn:" );
1840                                 break;
1841
1842                         case LDAP_BACK_IDASSERT_SELF:
1843                                 if ( BER_BVISNULL( &ndn ) ) {
1844                                         /* connection is not authc'd, so don't idassert */
1845                                         BER_BVSTR( &authzID, "dn:" );
1846                                         break;
1847                                 }
1848                                 authzID.bv_len = STRLENOF( "dn:" ) + ndn.bv_len;
1849                                 authzID.bv_val = slap_sl_malloc( authzID.bv_len + 1, op->o_tmpmemctx );
1850                                 AC_MEMCPY( authzID.bv_val, "dn:", STRLENOF( "dn:" ) );
1851                                 AC_MEMCPY( authzID.bv_val + STRLENOF( "dn:" ),
1852                                                 ndn.bv_val, ndn.bv_len + 1 );
1853                                 freeauthz = 1;
1854                                 break;
1855
1856                         default:
1857                                 break;
1858                         }
1859                 }
1860
1861                 if ( li->li_idassert_secprops != NULL ) {
1862                         rs->sr_err = ldap_set_option( lc->lc_ld,
1863                                 LDAP_OPT_X_SASL_SECPROPS,
1864                                 (void *)li->li_idassert_secprops );
1865
1866                         if ( rs->sr_err != LDAP_OPT_SUCCESS ) {
1867                                 rs->sr_err = LDAP_OTHER;
1868                                 if ( sendok & LDAP_BACK_SENDERR ) {
1869                                         send_ldap_result( op, rs );
1870                                 }
1871                                 LDAP_BACK_CONN_ISBOUND_CLEAR( lc );
1872                                 goto done;
1873                         }
1874                 }
1875
1876                 defaults = lutil_sasl_defaults( lc->lc_ld,
1877                                 li->li_idassert_sasl_mech.bv_val,
1878                                 li->li_idassert_sasl_realm.bv_val,
1879                                 li->li_idassert_authcID.bv_val,
1880                                 li->li_idassert_passwd.bv_val,
1881                                 authzID.bv_val );
1882
1883                 rs->sr_err = ldap_sasl_interactive_bind_s( lc->lc_ld, binddn->bv_val,
1884                                 li->li_idassert_sasl_mech.bv_val, NULL, NULL,
1885                                 LDAP_SASL_QUIET, lutil_sasl_interact,
1886                                 defaults );
1887
1888                 rs->sr_err = slap_map_api2result( rs );
1889                 if ( rs->sr_err != LDAP_SUCCESS ) {
1890                         LDAP_BACK_CONN_ISBOUND_CLEAR( lc );
1891                         if ( sendok & LDAP_BACK_SENDERR ) {
1892                                 send_ldap_result( op, rs );
1893                         }
1894
1895                 } else {
1896                         LDAP_BACK_CONN_ISBOUND_SET( lc );
1897                 }
1898
1899                 lutil_sasl_freedefs( defaults );
1900                 if ( freeauthz ) {
1901                         slap_sl_free( authzID.bv_val, op->o_tmpmemctx );
1902                 }
1903
1904                 goto done;
1905 #endif /* HAVE_CYRUS_SASL */
1906         }
1907
1908         switch ( li->li_idassert_authmethod ) {
1909         case LDAP_AUTH_NONE:
1910                 /* FIXME: do we really need this? */
1911                 BER_BVSTR( binddn, "" );
1912                 BER_BVSTR( bindcred, "" );
1913                 /* fallthru */
1914
1915         case LDAP_AUTH_SIMPLE:
1916                 rs->sr_err = ldap_sasl_bind( lc->lc_ld,
1917                                 binddn->bv_val, LDAP_SASL_SIMPLE,
1918                                 bindcred, NULL, NULL, &msgid );
1919                 rc = ldap_back_op_result( lc, op, rs, msgid,
1920                         -1, (sendok|LDAP_BACK_BINDING) );
1921                 break;
1922
1923         default:
1924                 /* unsupported! */
1925                 LDAP_BACK_CONN_ISBOUND_CLEAR( lc );
1926                 rs->sr_err = LDAP_AUTH_METHOD_NOT_SUPPORTED;
1927                 if ( sendok & LDAP_BACK_SENDERR ) {
1928                         send_ldap_result( op, rs );
1929                 }
1930                 goto done;
1931         }
1932
1933         if ( rc == LDAP_SUCCESS ) {
1934                 /* set rebind stuff in case of successful proxyAuthz bind,
1935                  * so that referral chasing is attempted using the right
1936                  * identity */
1937                 LDAP_BACK_CONN_ISBOUND_SET( lc );
1938                 ber_bvreplace( &lc->lc_bound_ndn, binddn );
1939
1940                 if ( LDAP_BACK_SAVECRED( li ) ) {
1941                         if ( !BER_BVISNULL( &lc->lc_cred ) ) {
1942                                 memset( lc->lc_cred.bv_val, 0,
1943                                                 lc->lc_cred.bv_len );
1944                         }
1945                         ber_bvreplace( &lc->lc_cred, bindcred );
1946                         ldap_set_rebind_proc( lc->lc_ld, li->li_rebind_f, lc );
1947                 }
1948         }
1949 done:;
1950         return LDAP_BACK_CONN_ISBOUND( lc );
1951 }
1952
1953 /*
1954  * ldap_back_proxy_authz_ctrl() prepends a proxyAuthz control
1955  * to existing server-side controls if required; if not,
1956  * the existing server-side controls are placed in *pctrls.
1957  * The caller, after using the controls in client API 
1958  * operations, if ( *pctrls != op->o_ctrls ), should
1959  * free( (*pctrls)[ 0 ] ) and free( *pctrls ).
1960  * The function returns success if the control could
1961  * be added if required, or if it did nothing; in the future,
1962  * it might return some error if it failed.
1963  * 
1964  * if no bind took place yet, but the connection is bound
1965  * and the "proxyauthzdn" is set, then bind as "proxyauthzdn" 
1966  * and explicitly add proxyAuthz the control to every operation
1967  * with the dn bound to the connection as control value.
1968  *
1969  * If no server-side controls are defined for the operation,
1970  * simply add the proxyAuthz control; otherwise, if the
1971  * proxyAuthz control is not already set, add it as
1972  * the first one
1973  *
1974  * FIXME: is controls order significant for security?
1975  * ANSWER: controls ordering and interoperability
1976  * must be indicated by the specs of each control; if none
1977  * is specified, the order is irrelevant.
1978  */
1979 int
1980 ldap_back_proxy_authz_ctrl(
1981                 struct berval   *bound_ndn,
1982                 int             version,
1983                 slap_idassert_t *si,
1984                 Operation       *op,
1985                 SlapReply       *rs,
1986                 LDAPControl     ***pctrls )
1987 {
1988         LDAPControl             **ctrls = NULL;
1989         int                     i = 0;
1990         slap_idassert_mode_t    mode;
1991         struct berval           assertedID,
1992                                 ndn;
1993
1994         *pctrls = NULL;
1995
1996         rs->sr_err = LDAP_SUCCESS;
1997
1998         /* don't proxyAuthz if protocol is not LDAPv3 */
1999         switch ( version ) {
2000         case LDAP_VERSION3:
2001                 break;
2002
2003         case 0:
2004                 if ( op->o_protocol == 0 || op->o_protocol == LDAP_VERSION3 ) {
2005                         break;
2006                 }
2007                 /* fall thru */
2008
2009         default:
2010                 goto done;
2011         }
2012
2013         /* FIXME: SASL/EXTERNAL over ldapi:// doesn't honor the authcID,
2014          * but if it is not set this test fails.  We need a different
2015          * means to detect if idassert is enabled */
2016         if ( ( BER_BVISNULL( &si->si_bc.sb_authcId ) || BER_BVISEMPTY( &si->si_bc.sb_authcId ) )
2017                         && ( BER_BVISNULL( &si->si_bc.sb_binddn ) || BER_BVISEMPTY( &si->si_bc.sb_binddn ) ) )
2018         {
2019                 goto done;
2020         }
2021
2022         if ( !op->o_conn || op->o_do_not_cache || be_isroot( op ) ) {
2023                 goto done;
2024         }
2025
2026         if ( op->o_tag == LDAP_REQ_BIND ) {
2027                 ndn = op->o_req_ndn;
2028
2029         } else if ( !BER_BVISNULL( &op->o_conn->c_ndn ) ) {
2030                 ndn = op->o_conn->c_ndn;
2031
2032         } else {
2033                 ndn = op->o_ndn;
2034         }
2035
2036         if ( si->si_mode == LDAP_BACK_IDASSERT_LEGACY ) {
2037                 if ( op->o_proxy_authz ) {
2038                         /*
2039                          * FIXME: we do not want to perform proxyAuthz
2040                          * on behalf of the client, because this would
2041                          * be performed with "proxyauthzdn" privileges.
2042                          *
2043                          * This might actually be too strict, since
2044                          * the "proxyauthzdn" authzTo, and each entry's
2045                          * authzFrom attributes may be crafted
2046                          * to avoid unwanted proxyAuthz to take place.
2047                          */
2048 #if 0
2049                         rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
2050                         rs->sr_text = "proxyAuthz not allowed within namingContext";
2051 #endif
2052                         goto done;
2053                 }
2054
2055                 if ( !BER_BVISNULL( bound_ndn ) ) {
2056                         goto done;
2057                 }
2058
2059                 if ( BER_BVISNULL( &ndn ) ) {
2060                         goto done;
2061                 }
2062
2063                 if ( BER_BVISNULL( &si->si_bc.sb_binddn ) ) {
2064                         goto done;
2065                 }
2066
2067         } else if ( si->si_bc.sb_method == LDAP_AUTH_SASL ) {
2068                 if ( ( si->si_flags & LDAP_BACK_AUTH_NATIVE_AUTHZ ) )
2069                 {
2070                         /* already asserted in SASL via native authz */
2071                         goto done;
2072                 }
2073
2074         } else if ( si->si_authz && !be_isroot( op ) ) {
2075                 int             rc;
2076                 struct berval authcDN;
2077
2078                 if ( BER_BVISNULL( &ndn ) ) {
2079                         authcDN = slap_empty_bv;
2080                 } else {
2081                         authcDN = ndn;
2082                 }
2083                 rc = slap_sasl_matches( op, si->si_authz,
2084                                 &authcDN, & authcDN );
2085                 if ( rc != LDAP_SUCCESS ) {
2086                         if ( si->si_flags & LDAP_BACK_AUTH_PRESCRIPTIVE ) {
2087                                 /* ndn is not authorized
2088                                  * to use idassert */
2089                                 rs->sr_err = rc;
2090                         }
2091                         goto done;
2092                 }
2093         }
2094
2095         if ( op->o_proxy_authz ) {
2096                 /*
2097                  * FIXME: we can:
2098                  * 1) ignore the already set proxyAuthz control
2099                  * 2) leave it in place, and don't set ours
2100                  * 3) add both
2101                  * 4) reject the operation
2102                  *
2103                  * option (4) is very drastic
2104                  * option (3) will make the remote server reject
2105                  * the operation, thus being equivalent to (4)
2106                  * option (2) will likely break the idassert
2107                  * assumptions, so we cannot accept it;
2108                  * option (1) means that we are contradicting
2109                  * the client's reques.
2110                  *
2111                  * I think (4) is the only correct choice.
2112                  */
2113                 rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
2114                 rs->sr_text = "proxyAuthz not allowed within namingContext";
2115         }
2116
2117         if ( op->o_is_auth_check ) {
2118                 mode = LDAP_BACK_IDASSERT_NOASSERT;
2119
2120         } else {
2121                 mode = si->si_mode;
2122         }
2123
2124         switch ( mode ) {
2125         case LDAP_BACK_IDASSERT_SELF:
2126                 if ( BER_BVISNULL( &ndn ) ) {
2127                         goto done;
2128                 }
2129                 assertedID = ndn;
2130                 break;
2131
2132         case LDAP_BACK_IDASSERT_LEGACY:
2133                 /* original behavior:
2134                  * assert the client's identity */
2135                 if ( BER_BVISNULL( &ndn ) ) {
2136                         assertedID = slap_empty_bv;
2137                 } else {
2138                         assertedID = ndn;
2139                 }
2140                 break;
2141
2142         case LDAP_BACK_IDASSERT_ANONYMOUS:
2143                 /* assert "anonymous" */
2144                 assertedID = slap_empty_bv;
2145                 break;
2146
2147         case LDAP_BACK_IDASSERT_NOASSERT:
2148                 /* don't assert; bind as proxyauthzdn */
2149                 goto done;
2150
2151         case LDAP_BACK_IDASSERT_OTHERID:
2152         case LDAP_BACK_IDASSERT_OTHERDN:
2153                 /* assert idassert DN */
2154                 assertedID = si->si_bc.sb_authzId;
2155                 break;
2156
2157         default:
2158                 assert( 0 );
2159         }
2160
2161         if ( BER_BVISNULL( &assertedID ) ) {
2162                 assertedID = slap_empty_bv;
2163         }
2164
2165         /* don't idassert the bound DN (ITS#4497) */
2166         if ( dn_match( &assertedID, bound_ndn ) ) {
2167                 goto done;
2168         }
2169
2170         if ( op->o_ctrls ) {
2171                 for ( i = 0; op->o_ctrls[ i ]; i++ )
2172                         /* just count ctrls */ ;
2173         }
2174
2175         ctrls = op->o_tmpalloc( sizeof( LDAPControl * ) * (i + 2) + sizeof( LDAPControl ),
2176                         op->o_tmpmemctx );
2177         ctrls[ 0 ] = (LDAPControl *)&ctrls[ i + 2 ];
2178         
2179         ctrls[ 0 ]->ldctl_oid = LDAP_CONTROL_PROXY_AUTHZ;
2180         ctrls[ 0 ]->ldctl_iscritical = 1;
2181
2182         switch ( si->si_mode ) {
2183         /* already in u:ID or dn:DN form */
2184         case LDAP_BACK_IDASSERT_OTHERID:
2185         case LDAP_BACK_IDASSERT_OTHERDN:
2186                 ber_dupbv_x( &ctrls[ 0 ]->ldctl_value, &assertedID, op->o_tmpmemctx );
2187                 break;
2188
2189         /* needs the dn: prefix */
2190         default:
2191                 ctrls[ 0 ]->ldctl_value.bv_len = assertedID.bv_len + STRLENOF( "dn:" );
2192                 ctrls[ 0 ]->ldctl_value.bv_val = op->o_tmpalloc( ctrls[ 0 ]->ldctl_value.bv_len + 1,
2193                                 op->o_tmpmemctx );
2194                 AC_MEMCPY( ctrls[ 0 ]->ldctl_value.bv_val, "dn:", STRLENOF( "dn:" ) );
2195                 AC_MEMCPY( &ctrls[ 0 ]->ldctl_value.bv_val[ STRLENOF( "dn:" ) ],
2196                                 assertedID.bv_val, assertedID.bv_len + 1 );
2197                 break;
2198         }
2199
2200         /* Older versions of <draft-weltman-ldapv3-proxy> required
2201          * to encode the value of the authzID (and called it proxyDN);
2202          * this hack provides compatibility with those DSAs that
2203          * implement it this way */
2204         if ( si->si_flags & LDAP_BACK_AUTH_OBSOLETE_ENCODING_WORKAROUND ) {
2205                 struct berval           authzID = ctrls[ 0 ]->ldctl_value;
2206                 BerElementBuffer        berbuf;
2207                 BerElement              *ber = (BerElement *)&berbuf;
2208                 ber_tag_t               tag;
2209
2210                 ber_init2( ber, 0, LBER_USE_DER );
2211                 ber_set_option( ber, LBER_OPT_BER_MEMCTX, &op->o_tmpmemctx );
2212
2213                 tag = ber_printf( ber, "O", &authzID );
2214                 if ( tag == LBER_ERROR ) {
2215                         rs->sr_err = LDAP_OTHER;
2216                         goto free_ber;
2217                 }
2218
2219                 if ( ber_flatten2( ber, &ctrls[ 0 ]->ldctl_value, 1 ) == -1 ) {
2220                         rs->sr_err = LDAP_OTHER;
2221                         goto free_ber;
2222                 }
2223
2224 free_ber:;
2225                 op->o_tmpfree( authzID.bv_val, op->o_tmpmemctx );
2226                 ber_free_buf( ber );
2227
2228                 if ( rs->sr_err != LDAP_SUCCESS ) {
2229                         op->o_tmpfree( ctrls, op->o_tmpmemctx );
2230                         ctrls = NULL;
2231                         goto done;
2232                 }
2233
2234         } else if ( si->si_flags & LDAP_BACK_AUTH_OBSOLETE_PROXY_AUTHZ ) {
2235                 struct berval           authzID = ctrls[ 0 ]->ldctl_value,
2236                                         tmp;
2237                 BerElementBuffer        berbuf;
2238                 BerElement              *ber = (BerElement *)&berbuf;
2239                 ber_tag_t               tag;
2240
2241                 if ( strncasecmp( authzID.bv_val, "dn:", STRLENOF( "dn:" ) ) != 0 ) {
2242                         op->o_tmpfree( ctrls[ 0 ]->ldctl_value.bv_val, op->o_tmpmemctx );
2243                         op->o_tmpfree( ctrls, op->o_tmpmemctx );
2244                         ctrls = NULL;
2245                         rs->sr_err = LDAP_PROTOCOL_ERROR;
2246                         goto done;
2247                 }
2248
2249                 tmp = authzID;
2250                 tmp.bv_val += STRLENOF( "dn:" );
2251                 tmp.bv_len -= STRLENOF( "dn:" );
2252
2253                 ber_init2( ber, 0, LBER_USE_DER );
2254                 ber_set_option( ber, LBER_OPT_BER_MEMCTX, &op->o_tmpmemctx );
2255
2256                 /* apparently, Mozilla API encodes this
2257                  * as "SEQUENCE { LDAPDN }" */
2258                 tag = ber_printf( ber, "{O}", &tmp );
2259                 if ( tag == LBER_ERROR ) {
2260                         rs->sr_err = LDAP_OTHER;
2261                         goto free_ber2;
2262                 }
2263
2264                 if ( ber_flatten2( ber, &ctrls[ 0 ]->ldctl_value, 1 ) == -1 ) {
2265                         rs->sr_err = LDAP_OTHER;
2266                         goto free_ber2;
2267                 }
2268
2269 free_ber2:;
2270                 op->o_tmpfree( authzID.bv_val, op->o_tmpmemctx );
2271                 ber_free_buf( ber );
2272
2273                 if ( rs->sr_err != LDAP_SUCCESS ) {
2274                         op->o_tmpfree( ctrls, op->o_tmpmemctx );
2275                         ctrls = NULL;
2276                         goto done;
2277                 }
2278
2279                 ctrls[ 0 ]->ldctl_oid = LDAP_CONTROL_OBSOLETE_PROXY_AUTHZ;
2280         }
2281
2282         if ( op->o_ctrls ) {
2283                 for ( i = 0; op->o_ctrls[ i ]; i++ ) {
2284                         ctrls[ i + 1 ] = op->o_ctrls[ i ];
2285                 }
2286         }
2287         ctrls[ i + 1 ] = NULL;
2288
2289 done:;
2290         if ( ctrls == NULL ) {
2291                 ctrls = op->o_ctrls;
2292         }
2293
2294         *pctrls = ctrls;
2295         
2296         return rs->sr_err;
2297 }
2298
2299 int
2300 ldap_back_proxy_authz_ctrl_free( Operation *op, LDAPControl ***pctrls )
2301 {
2302         LDAPControl     **ctrls = *pctrls;
2303
2304         /* we assume that the first control is the proxyAuthz
2305          * added by back-ldap, so it's the only one we explicitly 
2306          * free */
2307         if ( ctrls && ctrls != op->o_ctrls ) {
2308                 assert( ctrls[ 0 ] != NULL );
2309
2310                 if ( !BER_BVISNULL( &ctrls[ 0 ]->ldctl_value ) ) {
2311                         op->o_tmpfree( ctrls[ 0 ]->ldctl_value.bv_val, op->o_tmpmemctx );
2312                 }
2313
2314                 op->o_tmpfree( ctrls, op->o_tmpmemctx );
2315         } 
2316
2317         *pctrls = NULL;
2318
2319         return 0;
2320 }