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