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