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