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