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