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