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