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