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