]> git.sur5r.net Git - openldap/blob - servers/slapd/back-ldap/bind.c
f8ee616724e6a5afcf49263ec224aeba07fd6f1e
[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
36 #include <lutil_ldap.h>
37
38 #ifndef PRINT_CONNTREE
39 #define PRINT_CONNTREE 0
40 #endif /* !PRINT_CONNTREE */
41
42 #define LDAP_CONTROL_OBSOLETE_PROXY_AUTHZ       "2.16.840.1.113730.3.4.12"
43
44 static LDAP_REBIND_PROC ldap_back_default_rebind;
45
46 LDAP_REBIND_PROC        *ldap_back_rebind_f = ldap_back_default_rebind;
47
48 static int
49 ldap_back_proxy_authz_bind( ldapconn_t *lc, Operation *op, SlapReply *rs, ldap_back_send_t sendok );
50
51 static int
52 ldap_back_prepare_conn( ldapconn_t **lcp, Operation *op, SlapReply *rs, ldap_back_send_t sendok );
53
54 int
55 ldap_back_bind( Operation *op, SlapReply *rs )
56 {
57         ldapinfo_t      *li = (ldapinfo_t *) op->o_bd->be_private;
58         ldapconn_t      *lc;
59
60         int rc = 0;
61         ber_int_t msgid;
62
63         lc = ldap_back_getconn( op, rs, LDAP_BACK_BIND_SERR );
64         if ( !lc ) {
65                 return rs->sr_err;
66         }
67
68         if ( !BER_BVISNULL( &lc->lc_bound_ndn ) ) {
69                 ch_free( lc->lc_bound_ndn.bv_val );
70                 BER_BVZERO( &lc->lc_bound_ndn );
71         }
72         LDAP_BACK_CONN_ISBOUND_CLEAR( lc );
73
74         /* method is always LDAP_AUTH_SIMPLE if we got here */
75         rs->sr_err = ldap_sasl_bind( lc->lc_ld, op->o_req_dn.bv_val,
76                         LDAP_SASL_SIMPLE,
77                         &op->orb_cred, op->o_ctrls, NULL, &msgid );
78         rc = ldap_back_op_result( lc, op, rs, msgid, 0, LDAP_BACK_SENDERR );
79
80         if ( rc == LDAP_SUCCESS ) {
81                 /* If defined, proxyAuthz will be used also when
82                  * back-ldap is the authorizing backend; for this
83                  * purpose, a successful bind is followed by a
84                  * bind with the configured identity assertion */
85                 /* NOTE: use with care */
86                 if ( li->li_idassert_flags & LDAP_BACK_AUTH_OVERRIDE ) {
87                         ldap_back_proxy_authz_bind( lc, op, rs, LDAP_BACK_SENDERR );
88                         if ( !LDAP_BACK_CONN_ISBOUND( lc ) ) {
89                                 rc = 1;
90                                 goto done;
91                         }
92                 }
93
94                 LDAP_BACK_CONN_ISBOUND_SET( lc );
95                 ber_dupbv( &lc->lc_bound_ndn, &op->o_req_ndn );
96
97                 if ( LDAP_BACK_SAVECRED( li ) ) {
98                         if ( !BER_BVISNULL( &lc->lc_cred ) ) {
99                                 memset( lc->lc_cred.bv_val, 0,
100                                                 lc->lc_cred.bv_len );
101                         }
102                         ber_bvreplace( &lc->lc_cred, &op->orb_cred );
103                         ldap_set_rebind_proc( lc->lc_ld, ldap_back_rebind_f, lc );
104                 }
105         }
106 done:;
107
108         assert( lc->lc_binding == 1 );
109         lc->lc_binding = 0;
110
111         /* must re-insert if local DN changed as result of bind */
112         if ( !LDAP_BACK_CONN_ISBOUND( lc )
113                 || ( LDAP_BACK_CONN_ISBOUND( lc )
114                         && !dn_match( &op->o_req_ndn, &lc->lc_local_ndn ) ) )
115         {
116                 int             lerr = -1;
117
118                 /* wait for all other ops to release the connection */
119 retry_lock:;
120                 ldap_pvt_thread_mutex_lock( &li->li_conninfo.lai_mutex );
121                 if ( lc->lc_refcnt > 1 ) {
122                         ldap_pvt_thread_mutex_unlock( &li->li_conninfo.lai_mutex );
123                         ldap_pvt_thread_yield();
124                         goto retry_lock;
125                 }
126
127                 assert( lc->lc_refcnt == 1 );
128                 lc = avl_delete( &li->li_conninfo.lai_tree, (caddr_t)lc,
129                                 ldap_back_conndn_cmp );
130                 assert( lc != NULL );
131
132                 if ( LDAP_BACK_CONN_ISBOUND( lc ) ) {
133                         ber_bvreplace( &lc->lc_local_ndn, &op->o_req_ndn );
134                         lerr = avl_insert( &li->li_conninfo.lai_tree, (caddr_t)lc,
135                                 ldap_back_conndn_cmp, ldap_back_conndn_dup );
136                 }
137
138                 ldap_pvt_thread_mutex_unlock( &li->li_conninfo.lai_mutex );
139                 if ( lerr == -1 ) {
140                         /* we can do this because lc_refcnt == 1 */
141                         ldap_back_conn_free( lc );
142                         lc = NULL;
143                 }
144         }
145
146         if ( lc != NULL ) {
147                 ldap_back_release_conn( op, rs, lc );
148         }
149
150         return( rc );
151 }
152
153 /*
154  * ldap_back_conndn_cmp
155  *
156  * compares two ldapconn_t based on the value of the conn pointer
157  * and of the local DN; used by avl stuff for insert, lookup
158  * and direct delete
159  */
160 int
161 ldap_back_conndn_cmp( const void *c1, const void *c2 )
162 {
163         const ldapconn_t        *lc1 = (const ldapconn_t *)c1;
164         const ldapconn_t        *lc2 = (const ldapconn_t *)c2;
165         int rc;
166
167         /* If local DNs don't match, it is definitely not a match */
168         /* For shared sessions, conn is NULL. Only explicitly
169          * bound sessions will have non-NULL conn.
170          */
171         rc = SLAP_PTRCMP( lc1->lc_conn, lc2->lc_conn );
172         if ( rc == 0 ) {
173                 rc = ber_bvcmp( &lc1->lc_local_ndn, &lc2->lc_local_ndn );
174         }
175
176         return rc;
177 }
178
179 /*
180  * ldap_back_conn_cmp
181  *
182  * compares two ldapconn_t based on the value of the conn pointer;
183  * used by avl stuff for delete of all conns with the same connid
184  */
185 int
186 ldap_back_conn_cmp( const void *c1, const void *c2 )
187 {
188         const ldapconn_t        *lc1 = (const ldapconn_t *)c1;
189         const ldapconn_t        *lc2 = (const ldapconn_t *)c2;
190
191         /* For shared sessions, conn is NULL. Only explicitly
192          * bound sessions will have non-NULL conn.
193          */
194         return SLAP_PTRCMP( lc1->lc_conn, lc2->lc_conn );
195 }
196
197 /*
198  * ldap_back_conndn_dup
199  *
200  * returns -1 in case a duplicate ldapconn_t has been inserted;
201  * used by avl stuff
202  */
203 int
204 ldap_back_conndn_dup( void *c1, void *c2 )
205 {
206         ldapconn_t      *lc1 = (ldapconn_t *)c1;
207         ldapconn_t      *lc2 = (ldapconn_t *)c2;
208
209         /* Cannot have more than one shared session with same DN */
210         if ( lc1->lc_conn == lc2->lc_conn &&
211                 dn_match( &lc1->lc_local_ndn, &lc2->lc_local_ndn ) )
212         {
213                 return -1;
214         }
215                 
216         return 0;
217 }
218
219 #if PRINT_CONNTREE > 0
220 static void
221 ravl_print( Avlnode *root, int depth )
222 {
223         int             i;
224         ldapconn_t      *lc;
225         
226         if ( root == 0 ) {
227                 return;
228         }
229         
230         ravl_print( root->avl_right, depth+1 );
231         
232         for ( i = 0; i < depth; i++ ) {
233                 fprintf( stderr, "-" );
234         }
235
236         lc = root->avl_data;
237         fprintf( stderr, "lc=%p local=\"%s\" conn=%p %s refcnt=%d\n",
238                 (void *)lc, lc->lc_local_ndn.bv_val, (void *)lc->lc_conn,
239                 avl_bf2str( root->avl_bf ), lc->lc_refcnt );
240         
241         ravl_print( root->avl_left, depth+1 );
242 }
243
244 static void
245 myprint( Avlnode *root )
246 {
247         fprintf( stderr, "========>\n" );
248         
249         if ( root == 0 ) {
250                 fprintf( stderr, "\tNULL\n" );
251
252         } else {
253                 ravl_print( root, 0 );
254         }
255         
256         fprintf( stderr, "<========\n" );
257 }
258 #endif /* PRINT_CONNTREE */
259
260 int
261 ldap_back_freeconn( Operation *op, ldapconn_t *lc, int dolock )
262 {
263         ldapinfo_t      *li = (ldapinfo_t *) op->o_bd->be_private;
264
265         if ( dolock ) {
266                 ldap_pvt_thread_mutex_lock( &li->li_conninfo.lai_mutex );
267         }
268
269         assert( lc->lc_refcnt > 0 );
270         if ( --lc->lc_refcnt == 0 ) {
271                 lc = avl_delete( &li->li_conninfo.lai_tree, (caddr_t)lc,
272                                 ldap_back_conndn_cmp );
273                 assert( lc != NULL );
274
275                 ldap_back_conn_free( (void *)lc );
276         }
277
278         if ( dolock ) {
279                 ldap_pvt_thread_mutex_unlock( &li->li_conninfo.lai_mutex );
280         }
281
282         return 0;
283 }
284
285 #ifdef HAVE_TLS
286 static int
287 ldap_back_start_tls(
288         LDAP            *ld,
289         int             protocol,
290         int             *is_tls,
291         const char      *url,
292         unsigned        flags,
293         int             retries,
294         const char      **text )
295 {
296         int             rc = LDAP_SUCCESS;
297         ldapinfo_t      dummy;
298
299         /* this is ridiculous... */
300         dummy.li_flags = flags;
301
302         /* start TLS ("tls-[try-]{start,propagate}" statements) */
303         if ( ( LDAP_BACK_USE_TLS( &dummy ) || ( *is_tls && LDAP_BACK_PROPAGATE_TLS( &dummy ) ) )
304                                 && !ldap_is_ldaps_url( url ) )
305         {
306 #ifdef SLAP_STARTTLS_ASYNCHRONOUS
307                 /*
308                  * use asynchronous StartTLS
309                  * in case, chase referral (not implemented yet)
310                  */
311                 int             msgid;
312
313                 if ( protocol == 0 ) {
314                         ldap_get_option( ld, LDAP_OPT_PROTOCOL_VERSION,
315                                         (void *)&protocol );
316                 }
317
318                 if ( protocol < LDAP_VERSION3 ) {
319                         protocol = LDAP_VERSION3;
320                         /* Set LDAP version */
321                         ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION,
322                                         (const void *)&protocol );
323                 }
324
325                 rc = ldap_start_tls( ld, NULL, NULL, &msgid );
326                 if ( rc == LDAP_SUCCESS ) {
327                         LDAPMessage     *res = NULL;
328                         struct timeval  tv;
329
330                         LDAP_BACK_TV_SET( &tv );
331
332 retry:;
333                         rc = ldap_result( ld, msgid, LDAP_MSG_ALL, &tv, &res );
334                         if ( rc < 0 ) {
335                                 rc = LDAP_UNAVAILABLE;
336
337                         } else if ( rc == 0 ) {
338                                 if ( retries != LDAP_BACK_RETRY_NEVER ) {
339                                         ldap_pvt_thread_yield();
340                                         if ( retries > 0 ) {
341                                                 retries--;
342                                         }
343                                         LDAP_BACK_TV_SET( &tv );
344                                         goto retry;
345                                 }
346                                 rc = LDAP_UNAVAILABLE;
347
348                         } else if ( rc == LDAP_RES_EXTENDED ) {
349                                 struct berval   *data = NULL;
350
351                                 rc = ldap_parse_extended_result( ld, res,
352                                                 NULL, &data, 0 );
353                                 if ( rc == LDAP_SUCCESS ) {
354                                         int err;
355                                         rc = ldap_parse_result( ld, res, &err,
356                                                 NULL, NULL, NULL, NULL, 1 );
357                                         if ( rc == LDAP_SUCCESS ) {
358                                                 rc = err;
359                                         }
360                                         res = NULL;
361                                         
362                                         /* FIXME: in case a referral 
363                                          * is returned, should we try
364                                          * using it instead of the 
365                                          * configured URI? */
366                                         if ( rc == LDAP_SUCCESS ) {
367                                                 rc = ldap_install_tls( ld );
368
369                                         } else if ( rc == LDAP_REFERRAL ) {
370                                                 rc = LDAP_UNWILLING_TO_PERFORM;
371                                                 *text = "unwilling to chase referral returned by Start TLS exop";
372                                         }
373
374                                         if ( data ) {
375                                                 if ( data->bv_val ) {
376                                                         ber_memfree( data->bv_val );
377                                                 }
378                                                 ber_memfree( data );
379                                         }
380                                 }
381
382                         } else {
383                                 rc = LDAP_OTHER;
384                         }
385
386                         if ( res != NULL ) {
387                                 ldap_msgfree( res );
388                         }
389                 }
390 #else /* ! SLAP_STARTTLS_ASYNCHRONOUS */
391                 /*
392                  * use synchronous StartTLS
393                  */
394                 rc = ldap_start_tls_s( ld, NULL, NULL );
395 #endif /* ! SLAP_STARTTLS_ASYNCHRONOUS */
396
397                 /* if StartTLS is requested, only attempt it if the URL
398                  * is not "ldaps://"; this may occur not only in case
399                  * of misconfiguration, but also when used in the chain 
400                  * overlay, where the "uri" can be parsed out of a referral */
401                 switch ( rc ) {
402                 case LDAP_SUCCESS:
403                         *is_tls = 1;
404                         break;
405
406                 case LDAP_SERVER_DOWN:
407                         break;
408
409                 default:
410                         if ( LDAP_BACK_TLS_CRITICAL( &dummy ) ) {
411                                 *text = "could not start TLS";
412                                 break;
413                         }
414
415                         /* in case Start TLS is not critical */
416                         *is_tls = 0;
417                         rc = LDAP_SUCCESS;
418                         break;
419                 }
420
421         } else {
422                 *is_tls = 0;
423         }
424
425         return rc;
426 }
427 #endif /* HAVE_TLS */
428
429 static int
430 ldap_back_prepare_conn( ldapconn_t **lcp, Operation *op, SlapReply *rs, ldap_back_send_t sendok )
431 {
432         ldapinfo_t      *li = (ldapinfo_t *)op->o_bd->be_private;
433         int             vers = op->o_protocol;
434         LDAP            *ld = NULL;
435 #ifdef HAVE_TLS
436         int             is_tls = op->o_conn->c_is_tls;
437 #endif /* HAVE_TLS */
438
439         assert( lcp != NULL );
440
441         rs->sr_err = ldap_initialize( &ld, li->li_uri );
442         if ( rs->sr_err != LDAP_SUCCESS ) {
443                 goto error_return;
444         }
445
446         /* Set LDAP version. This will always succeed: If the client
447          * bound with a particular version, then so can we.
448          */
449         if ( vers == 0 ) {
450                 /* assume it's an internal op; set to LDAPv3 */
451                 vers = LDAP_VERSION3;
452         }
453         ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION, (const void *)&vers );
454
455         /* automatically chase referrals ("chase-referrals [{yes|no}]" statement) */
456         ldap_set_option( ld, LDAP_OPT_REFERRALS,
457                 LDAP_BACK_CHASE_REFERRALS( li ) ? LDAP_OPT_ON : LDAP_OPT_OFF );
458
459 #ifdef HAVE_TLS
460         rs->sr_err = ldap_back_start_tls( ld, op->o_protocol, &is_tls,
461                         li->li_uri, li->li_flags, li->li_nretries, &rs->sr_text );
462         if ( rs->sr_err != LDAP_SUCCESS ) {
463                 ldap_unbind_ext( ld, NULL, NULL );
464                 goto error_return;
465         }
466 #endif /* HAVE_TLS */
467
468         if ( *lcp == NULL ) {
469                 *lcp = (ldapconn_t *)ch_calloc( 1, sizeof( ldapconn_t ) );
470                 (*lcp)->lc_flags= li->li_flags;
471         }
472         (*lcp)->lc_ld = ld;
473         (*lcp)->lc_refcnt = 1;
474         (*lcp)->lc_binding = 1;
475 #ifdef HAVE_TLS
476         if ( is_tls ) {
477                 LDAP_BACK_CONN_ISTLS_SET( *lcp );
478         } else {
479                 LDAP_BACK_CONN_ISTLS_CLEAR( *lcp );
480         }
481 #endif /* HAVE_TLS */
482
483 error_return:;
484         if ( rs->sr_err != LDAP_SUCCESS ) {
485                 rs->sr_err = slap_map_api2result( rs );
486                 if ( sendok & LDAP_BACK_SENDERR ) {
487                         if ( rs->sr_text == NULL ) {
488                                 rs->sr_text = "ldap_initialize() failed";
489                         }
490                         send_ldap_result( op, rs );
491                         rs->sr_text = NULL;
492                 }
493
494         } else {
495                 if ( li->li_conn_ttl > 0 ) {
496                         (*lcp)->lc_create_time = op->o_time;
497                 }
498         }
499
500         return rs->sr_err;
501 }
502
503 ldapconn_t *
504 ldap_back_getconn( Operation *op, SlapReply *rs, ldap_back_send_t sendok )
505 {
506         ldapinfo_t      *li = (ldapinfo_t *)op->o_bd->be_private;
507         ldapconn_t      *lc = NULL,
508                         lc_curr = { 0 };
509         int             refcnt = 1, binding = 1;
510
511         /* Internal searches are privileged and shared. So is root. */
512         if ( op->o_do_not_cache || be_isroot( op ) ) {
513                 LDAP_BACK_CONN_ISPRIV_SET( &lc_curr );
514                 lc_curr.lc_local_ndn = op->o_bd->be_rootndn;
515                 lc_curr.lc_conn = LDAP_BACK_PCONN_SET( op );
516
517         } else {
518                 lc_curr.lc_local_ndn = op->o_ndn;
519                 /* Explicit binds must not be shared */
520                 if ( op->o_tag == LDAP_REQ_BIND || SLAP_IS_AUTHZ_BACKEND( op ) ) {
521                         lc_curr.lc_conn = op->o_conn;
522         
523                 } else {
524                         lc_curr.lc_conn = LDAP_BACK_PCONN_SET( op );
525                 }
526         }
527
528         /* Explicit Bind requests always get their own conn */
529         if ( !( sendok & LDAP_BACK_BINDING ) ) {
530                 /* Searches for a ldapconn in the avl tree */
531 retry_lock:
532                 ldap_pvt_thread_mutex_lock( &li->li_conninfo.lai_mutex );
533
534                 lc = (ldapconn_t *)avl_find( li->li_conninfo.lai_tree, 
535                                 (caddr_t)&lc_curr, ldap_back_conndn_cmp );
536                 if ( lc != NULL ) {
537                         /* Don't reuse connections while they're still binding */
538                         if ( LDAP_BACK_CONN_BINDING( lc ) ) {
539                                 ldap_pvt_thread_mutex_unlock( &li->li_conninfo.lai_mutex );
540                                 ldap_pvt_thread_yield();
541                                 goto retry_lock;
542                         }
543                         refcnt = ++lc->lc_refcnt;
544                         binding = ++lc->lc_binding;
545                 }
546                 ldap_pvt_thread_mutex_unlock( &li->li_conninfo.lai_mutex );
547         }
548
549         /* Looks like we didn't get a bind. Open a new session... */
550         if ( lc == NULL ) {
551                 if ( ldap_back_prepare_conn( &lc, op, rs, sendok ) != LDAP_SUCCESS ) {
552                         return NULL;
553                 }
554                 if ( sendok & LDAP_BACK_BINDING ) {
555                         LDAP_BACK_CONN_BINDING_SET( lc );
556                 }
557                 lc->lc_conn = lc_curr.lc_conn;
558                 ber_dupbv( &lc->lc_local_ndn, &lc_curr.lc_local_ndn );
559
560                 if ( LDAP_BACK_CONN_ISPRIV( &lc_curr ) ) {
561                         ber_dupbv( &lc->lc_cred, &li->li_acl_passwd );
562                         ber_dupbv( &lc->lc_bound_ndn, &li->li_acl_authcDN );
563                         LDAP_BACK_CONN_ISPRIV_SET( lc );
564
565                 } else {
566                         BER_BVZERO( &lc->lc_cred );
567                         BER_BVZERO( &lc->lc_bound_ndn );
568 #if 0
569                         /* FIXME: if we set lc_bound_ndn = o_ndn
570                          * we end up with a bind with DN but no password! */
571                         if ( !BER_BVISEMPTY( &op->o_ndn )
572                                 && SLAP_IS_AUTHZ_BACKEND( op ) )
573                         {
574                                 ber_dupbv( &lc->lc_bound_ndn, &op->o_ndn );
575                         }
576 #endif
577                 }
578
579 #ifdef HAVE_TLS
580                 /* if start TLS failed but it was not mandatory,
581                  * check if the non-TLS connection was already
582                  * in cache; in case, destroy the newly created
583                  * connection and use the existing one */
584                 if ( lc->lc_conn == LDAP_BACK_PCONN_TLS
585                                 && !ldap_tls_inplace( lc->lc_ld ) )
586                 {
587                         ldapconn_t *tmplc;
588                         
589                         lc_curr.lc_conn = LDAP_BACK_PCONN;
590                         ldap_pvt_thread_mutex_lock( &li->li_conninfo.lai_mutex );
591                         tmplc = (ldapconn_t *)avl_find( li->li_conninfo.lai_tree, 
592                                         (caddr_t)&lc_curr, ldap_back_conndn_cmp );
593                         if ( tmplc != NULL ) {
594                                 refcnt = ++tmplc->lc_refcnt;
595                                 binding = ++tmplc->lc_binding;
596                                 ldap_back_conn_free( lc );
597                                 lc = tmplc;
598                         }
599                         ldap_pvt_thread_mutex_unlock( &li->li_conninfo.lai_mutex );
600
601                         if ( tmplc != NULL ) {
602                                 goto done;
603                         }
604                 }
605 #endif /* HAVE_TLS */
606
607                 LDAP_BACK_CONN_ISBOUND_CLEAR( lc );
608
609                 /* Inserts the newly created ldapconn in the avl tree */
610                 ldap_pvt_thread_mutex_lock( &li->li_conninfo.lai_mutex );
611
612                 assert( lc->lc_refcnt == 1 );
613                 assert( lc->lc_binding == 1 );
614                 rs->sr_err = avl_insert( &li->li_conninfo.lai_tree, (caddr_t)lc,
615                         ldap_back_conndn_cmp, ldap_back_conndn_dup );
616
617 #if PRINT_CONNTREE > 0
618                 myprint( li->li_conninfo.lai_tree );
619 #endif /* PRINT_CONNTREE */
620         
621                 ldap_pvt_thread_mutex_unlock( &li->li_conninfo.lai_mutex );
622
623                 Debug( LDAP_DEBUG_TRACE,
624                         "=>ldap_back_getconn: conn %p inserted refcnt=%u binding=%u\n",
625                         (void *)lc, refcnt, binding );
626         
627                 /* Err could be -1 in case a duplicate ldapconn is inserted */
628                 if ( rs->sr_err != 0 ) {
629                         ldap_back_conn_free( lc );
630                         rs->sr_err = LDAP_OTHER;
631                         if ( op->o_conn && ( sendok & LDAP_BACK_SENDERR ) ) {
632                                 send_ldap_error( op, rs, LDAP_OTHER,
633                                         "internal server error" );
634                         }
635                         return NULL;
636                 }
637
638         } else {
639                 if ( ( li->li_idle_timeout != 0 && op->o_time > lc->lc_time + li->li_idle_timeout )
640                         || ( li->li_conn_ttl != 0 && op->o_time > lc->lc_create_time + li->li_conn_ttl ) )
641                 {
642                         /* in case of failure, it frees/taints lc and sets it to NULL */
643                         if ( !ldap_back_retry( &lc, op, rs, sendok ) ) {
644                                 lc = NULL;
645                         }
646                 }
647
648                 if ( lc ) {
649                         Debug( LDAP_DEBUG_TRACE,
650                                 "=>ldap_back_getconn: conn %p fetched refcnt=%u binding=%u\n",
651                                 (void *)lc, refcnt, binding );
652                 }
653         }
654
655         if ( li->li_idle_timeout && lc ) {
656                 lc->lc_time = op->o_time;
657         }
658
659 done:;
660         return lc;
661 }
662
663 void
664 ldap_back_release_conn_lock(
665         Operation               *op,
666         SlapReply               *rs,
667         ldapconn_t              *lc,
668         int                     dolock )
669 {
670         ldapinfo_t      *li = (ldapinfo_t *)op->o_bd->be_private;
671
672         if ( dolock ) {
673                 ldap_pvt_thread_mutex_lock( &li->li_conninfo.lai_mutex );
674         }
675         assert( lc->lc_refcnt > 0 );
676         lc->lc_refcnt--;
677         LDAP_BACK_CONN_BINDING_CLEAR( lc );
678         if ( dolock ) {
679                 ldap_pvt_thread_mutex_unlock( &li->li_conninfo.lai_mutex );
680         }
681 }
682
683 /*
684  * ldap_back_dobind
685  *
686  * Note: as the check for the value of lc->lc_bound was already here, I removed
687  * it from all the callers, and I made the function return the flag, so
688  * it can be used to simplify the check.
689  *
690  * Note: dolock indicates whether li->li_conninfo.lai_mutex must be locked or not
691  */
692 static int
693 ldap_back_dobind_int(
694         ldapconn_t              *lc,
695         Operation               *op,
696         SlapReply               *rs,
697         ldap_back_send_t        sendok,
698         int                     retries,
699         int                     dolock )
700 {       
701         ldapinfo_t      *li = (ldapinfo_t *)op->o_bd->be_private;
702
703         int             rc, binding = 0;
704         ber_int_t       msgid;
705
706         assert( retries >= 0 );
707
708 retry_lock:;
709         if ( dolock ) {
710                 ldap_pvt_thread_mutex_lock( &li->li_conninfo.lai_mutex );
711         }
712
713         if ( binding == 0 ) {
714                 /* check if already bound */
715                 rc = LDAP_BACK_CONN_ISBOUND( lc );
716                 if ( rc ) {
717                         lc->lc_binding--;
718                         if ( dolock ) {
719                                 ldap_pvt_thread_mutex_unlock( &li->li_conninfo.lai_mutex );
720                         }
721                         return rc;
722                 }
723
724                 if ( LDAP_BACK_CONN_BINDING( lc ) ) {
725                         /* if someone else is about to bind it, give up and retry */
726                         if ( dolock ) {
727                                 ldap_pvt_thread_mutex_unlock( &li->li_conninfo.lai_mutex );
728                         }
729                         ldap_pvt_thread_yield();
730                         goto retry_lock;
731
732                 } else {
733                         /* otherwise this thread will bind it */
734                         LDAP_BACK_CONN_BINDING_SET( lc );
735                         binding = 1;
736                 }
737         }
738
739         /* wait for pending operations to finish */
740         /* FIXME: may become a bottleneck! */
741         if ( lc->lc_refcnt != lc->lc_binding ) {
742                 if ( dolock ) {
743                         ldap_pvt_thread_mutex_unlock( &li->li_conninfo.lai_mutex );
744                 }
745                 ldap_pvt_thread_yield();
746                 goto retry_lock;
747         }
748
749         if ( dolock ) {
750                 ldap_pvt_thread_mutex_unlock( &li->li_conninfo.lai_mutex );
751         }
752
753 #if 0
754         while ( lc->lc_refcnt > 1 ) {
755                 ldap_pvt_thread_yield();
756                 rc = LDAP_BACK_CONN_ISBOUND( lc );
757                 if ( rc ) {
758                         return rc;
759                 }
760         }
761
762         if ( dolock ) {
763                 ldap_pvt_thread_mutex_lock( &li->li_conninfo.lai_mutex );
764         }
765         LDAP_BACK_CONN_BINDING_SET( lc );
766         if ( dolock ) {
767                 ldap_pvt_thread_mutex_unlock( &li->li_conninfo.lai_mutex );
768         }
769 #endif
770
771         /*
772          * FIXME: we need to let clients use proxyAuthz
773          * otherwise we cannot do symmetric pools of servers;
774          * we have to live with the fact that a user can
775          * authorize itself as any ID that is allowed
776          * by the authzTo directive of the "proxyauthzdn".
777          */
778         /*
779          * NOTE: current Proxy Authorization specification
780          * and implementation do not allow proxy authorization
781          * control to be provided with Bind requests
782          */
783         /*
784          * if no bind took place yet, but the connection is bound
785          * and the "idassert-authcDN" (or other ID) is set, 
786          * then bind as the asserting identity and explicitly 
787          * add the proxyAuthz control to every operation with the
788          * dn bound to the connection as control value.
789          * This is done also if this is the authrizing backend,
790          * but the "override" flag is given to idassert.
791          * It allows to use SASL bind and yet proxyAuthz users
792          */
793         if ( op->o_conn != NULL &&
794                         !op->o_do_not_cache &&
795                         ( BER_BVISNULL( &lc->lc_bound_ndn ) ||
796                           ( li->li_idassert_flags & LDAP_BACK_AUTH_OVERRIDE ) ) )
797         {
798                 (void)ldap_back_proxy_authz_bind( lc, op, rs, sendok );
799                 goto done;
800         }
801
802 #ifdef HAVE_CYRUS_SASL
803         if ( LDAP_BACK_CONN_ISPRIV( lc )
804                 && li->li_acl_authmethod == LDAP_AUTH_SASL )
805         {
806                 void            *defaults = NULL;
807
808                 if ( li->li_acl_secprops != NULL ) {
809                         rc = ldap_set_option( lc->lc_ld,
810                                 LDAP_OPT_X_SASL_SECPROPS, li->li_acl_secprops);
811
812                         if ( rc != LDAP_OPT_SUCCESS ) {
813                                 Debug( LDAP_DEBUG_ANY, "Error: ldap_set_option "
814                                         "(%s,SECPROPS,\"%s\") failed!\n",
815                                         li->li_uri, li->li_acl_secprops, 0 );
816                                 goto done;
817                         }
818                 }
819
820                 defaults = lutil_sasl_defaults( lc->lc_ld,
821                                 li->li_acl_sasl_mech.bv_val,
822                                 li->li_acl_sasl_realm.bv_val,
823                                 li->li_acl_authcID.bv_val,
824                                 li->li_acl_passwd.bv_val,
825                                 NULL );
826
827                 rs->sr_err = ldap_sasl_interactive_bind_s( lc->lc_ld,
828                                 li->li_acl_authcDN.bv_val,
829                                 li->li_acl_sasl_mech.bv_val, NULL, NULL,
830                                 LDAP_SASL_QUIET, lutil_sasl_interact,
831                                 defaults );
832
833                 lutil_sasl_freedefs( defaults );
834
835                 rs->sr_err = slap_map_api2result( rs );
836                 if ( rs->sr_err != LDAP_SUCCESS ) {
837                         LDAP_BACK_CONN_ISBOUND_CLEAR( lc );
838                         send_ldap_result( op, rs );
839
840                 } else {
841                         LDAP_BACK_CONN_ISBOUND_SET( lc );
842                 }
843                 goto done;
844         }
845 #endif /* HAVE_CYRUS_SASL */
846
847 retry:;
848         rs->sr_err = ldap_sasl_bind( lc->lc_ld,
849                         lc->lc_bound_ndn.bv_val,
850                         LDAP_SASL_SIMPLE, &lc->lc_cred,
851                         NULL, NULL, &msgid );
852
853         if ( rs->sr_err == LDAP_SERVER_DOWN ) {
854                 if ( retries != LDAP_BACK_RETRY_NEVER ) {
855                         if ( dolock ) {
856                                 ldap_pvt_thread_mutex_lock( &li->li_conninfo.lai_mutex );
857                         }
858
859                         assert( lc->lc_refcnt > 0 );
860                         if ( lc->lc_refcnt == 1 ) {
861                                 ldap_unbind_ext( lc->lc_ld, NULL, NULL );
862                                 lc->lc_ld = NULL;
863
864                                 /* lc here must be the regular lc, reset and ready for init */
865                                 rs->sr_err = ldap_back_prepare_conn( &lc, op, rs, sendok );
866                         }
867                         if ( dolock ) {
868                                 ldap_pvt_thread_mutex_unlock( &li->li_conninfo.lai_mutex );
869                         }
870                         if ( rs->sr_err == LDAP_SUCCESS ) {
871                                 if ( retries > 0 ) {
872                                         retries--;
873                                 }
874                                 goto retry;
875                         }
876                 }
877
878                 lc->lc_binding--;
879                 ldap_back_freeconn( op, lc, dolock );
880                 rs->sr_err = slap_map_api2result( rs );
881
882                 return 0;
883         }
884
885         rc = ldap_back_op_result( lc, op, rs, msgid, 0, sendok );
886         if ( rc == LDAP_SUCCESS ) {
887                 LDAP_BACK_CONN_ISBOUND_SET( lc );
888         }
889
890 done:;
891         lc->lc_binding--;
892         LDAP_BACK_CONN_BINDING_CLEAR( lc );
893         rc = LDAP_BACK_CONN_ISBOUND( lc );
894         if ( !rc ) {
895                 ldap_back_release_conn_lock( op, rs, lc, dolock );
896         }
897
898         return rc;
899 }
900
901 int
902 ldap_back_dobind( ldapconn_t *lc, Operation *op, SlapReply *rs, ldap_back_send_t sendok )
903 {
904         ldapinfo_t      *li = (ldapinfo_t *)op->o_bd->be_private;
905
906         return ldap_back_dobind_int( lc, op, rs, sendok, li->li_nretries, 1 );
907 }
908
909 /*
910  * ldap_back_default_rebind
911  *
912  * This is a callback used for chasing referrals using the same
913  * credentials as the original user on this session.
914  */
915 static int 
916 ldap_back_default_rebind( LDAP *ld, LDAP_CONST char *url, ber_tag_t request,
917         ber_int_t msgid, void *params )
918 {
919         ldapconn_t      *lc = (ldapconn_t *)params;
920
921 #ifdef HAVE_TLS
922         /* ... otherwise we couldn't get here */
923         assert( lc != NULL );
924
925         if ( !ldap_tls_inplace( ld ) ) {
926                 int             is_tls = LDAP_BACK_CONN_ISTLS( lc ),
927                                 rc;
928                 const char      *text = NULL;
929
930                 rc = ldap_back_start_tls( ld, 0, &is_tls, url, lc->lc_flags,
931                         LDAP_BACK_RETRY_DEFAULT, &text );
932                 if ( rc != LDAP_SUCCESS ) {
933                         return rc;
934                 }
935         }
936 #endif /* HAVE_TLS */
937
938         /* FIXME: add checks on the URL/identity? */
939
940         return ldap_sasl_bind_s( ld, lc->lc_bound_ndn.bv_val,
941                         LDAP_SASL_SIMPLE, &lc->lc_cred, NULL, NULL, NULL );
942 }
943
944 int
945 ldap_back_op_result(
946                 ldapconn_t              *lc,
947                 Operation               *op,
948                 SlapReply               *rs,
949                 ber_int_t               msgid,
950                 time_t                  timeout,
951                 ldap_back_send_t        sendok )
952 {
953         char            *match = NULL;
954         LDAPMessage     *res = NULL;
955         char            *text = NULL;
956
957 #define ERR_OK(err) ((err) == LDAP_SUCCESS || (err) == LDAP_COMPARE_FALSE || (err) == LDAP_COMPARE_TRUE)
958
959         rs->sr_text = NULL;
960         rs->sr_matched = NULL;
961
962         /* if the error recorded in the reply corresponds
963          * to a successful state, get the error from the
964          * remote server response */
965         if ( ERR_OK( rs->sr_err ) ) {
966                 int             rc;
967                 struct timeval  tv;
968
969                 if ( timeout ) {
970                         tv.tv_sec = timeout;
971                         tv.tv_usec = 0;
972
973                 } else {
974                         LDAP_BACK_TV_SET( &tv );
975                 }
976
977 retry:;
978                 /* if result parsing fails, note the failure reason */
979                 rc = ldap_result( lc->lc_ld, msgid, LDAP_MSG_ALL, &tv, &res );
980                 switch ( rc ) {
981                 case 0:
982                         if ( timeout ) {
983                                 (void)ldap_abandon_ext( lc->lc_ld, msgid, NULL, NULL );
984                                 rs->sr_err = op->o_protocol >= LDAP_VERSION3 ?
985                                         LDAP_ADMINLIMIT_EXCEEDED : LDAP_OPERATIONS_ERROR;
986                                 rs->sr_text = "Operation timed out";
987                                 break;
988                         }
989
990                         LDAP_BACK_TV_SET( &tv );
991                         ldap_pvt_thread_yield();
992                         goto retry;
993
994                 case -1:
995                         ldap_get_option( lc->lc_ld, LDAP_OPT_ERROR_NUMBER,
996                                         &rs->sr_err );
997                         break;
998
999
1000                 /* otherwise get the result; if it is not
1001                  * LDAP_SUCCESS, record it in the reply
1002                  * structure (this includes 
1003                  * LDAP_COMPARE_{TRUE|FALSE}) */
1004                 default:
1005                         rc = ldap_parse_result( lc->lc_ld, res, &rs->sr_err,
1006                                         &match, &text, NULL, NULL, 1 );
1007                         rs->sr_text = text;
1008                         if ( rc != LDAP_SUCCESS ) {
1009                                 rs->sr_err = rc;
1010                         }
1011                 }
1012         }
1013
1014         /* if the error in the reply structure is not
1015          * LDAP_SUCCESS, try to map it from client 
1016          * to server error */
1017         if ( !ERR_OK( rs->sr_err ) ) {
1018                 rs->sr_err = slap_map_api2result( rs );
1019
1020                 /* internal ops ( op->o_conn == NULL ) 
1021                  * must not reply to client */
1022                 if ( op->o_conn && !op->o_do_not_cache && match ) {
1023
1024                         /* record the (massaged) matched
1025                          * DN into the reply structure */
1026                         rs->sr_matched = match;
1027                 }
1028         }
1029         if ( op->o_conn &&
1030                         ( ( sendok & LDAP_BACK_SENDOK ) 
1031                           || ( ( sendok & LDAP_BACK_SENDERR ) && rs->sr_err != LDAP_SUCCESS ) ) )
1032         {
1033                 send_ldap_result( op, rs );
1034         }
1035         if ( match ) {
1036                 if ( rs->sr_matched != match ) {
1037                         free( (char *)rs->sr_matched );
1038                 }
1039                 rs->sr_matched = NULL;
1040                 ldap_memfree( match );
1041         }
1042         if ( text ) {
1043                 ldap_memfree( text );
1044         }
1045         rs->sr_text = NULL;
1046         return( ERR_OK( rs->sr_err ) ? LDAP_SUCCESS : rs->sr_err );
1047 }
1048
1049 /* return true if bound, false if failed */
1050 int
1051 ldap_back_retry( ldapconn_t **lcp, Operation *op, SlapReply *rs, ldap_back_send_t sendok )
1052 {
1053         int             rc = 0;
1054         ldapinfo_t      *li = (ldapinfo_t *)op->o_bd->be_private;
1055
1056         assert( lcp != NULL );
1057         assert( *lcp != NULL );
1058
1059         ldap_pvt_thread_mutex_lock( &li->li_conninfo.lai_mutex );
1060
1061         if ( (*lcp)->lc_refcnt == 1 ) {
1062                 Debug( LDAP_DEBUG_ANY,
1063                         "%s ldap_back_retry: retrying URI=\"%s\" DN=\"%s\"\n",
1064                         op->o_log_prefix, li->li_uri,
1065                         BER_BVISNULL( &(*lcp)->lc_bound_ndn ) ?
1066                                 "" : (*lcp)->lc_bound_ndn.bv_val );
1067
1068                 ldap_unbind_ext( (*lcp)->lc_ld, NULL, NULL );
1069                 (*lcp)->lc_ld = NULL;
1070                 LDAP_BACK_CONN_ISBOUND_CLEAR( (*lcp) );
1071
1072                 /* lc here must be the regular lc, reset and ready for init */
1073                 rc = ldap_back_prepare_conn( lcp, op, rs, sendok );
1074                 if ( rc != LDAP_SUCCESS ) {
1075                         rc = 0;
1076                         *lcp = NULL;
1077
1078                 } else {
1079                         rc = ldap_back_dobind_int( *lcp, op, rs, sendok, 0, 0 );
1080                         if ( rc == 0 ) {
1081                                 *lcp = NULL;
1082                         }
1083                 }
1084         }
1085
1086         ldap_pvt_thread_mutex_unlock( &li->li_conninfo.lai_mutex );
1087
1088         return rc;
1089 }
1090
1091 static int
1092 ldap_back_proxy_authz_bind( ldapconn_t *lc, Operation *op, SlapReply *rs, ldap_back_send_t sendok )
1093 {
1094         ldapinfo_t      *li = (ldapinfo_t *)op->o_bd->be_private;
1095         struct berval   binddn = slap_empty_bv;
1096         struct berval   bindcred = slap_empty_bv;
1097         struct berval   ndn;
1098         int             dobind = 0;
1099         int             msgid;
1100         int             rc;
1101
1102         if ( !BER_BVISNULL( &op->o_conn->c_ndn ) ) {
1103                 ndn = op->o_conn->c_ndn;
1104
1105         } else {
1106                 ndn = op->o_ndn;
1107         }
1108
1109         /*
1110          * FIXME: we need to let clients use proxyAuthz
1111          * otherwise we cannot do symmetric pools of servers;
1112          * we have to live with the fact that a user can
1113          * authorize itself as any ID that is allowed
1114          * by the authzTo directive of the "proxyauthzdn".
1115          */
1116         /*
1117          * NOTE: current Proxy Authorization specification
1118          * and implementation do not allow proxy authorization
1119          * control to be provided with Bind requests
1120          */
1121         /*
1122          * if no bind took place yet, but the connection is bound
1123          * and the "proxyauthzdn" is set, then bind as 
1124          * "proxyauthzdn" and explicitly add the proxyAuthz 
1125          * control to every operation with the dn bound 
1126          * to the connection as control value.
1127          */
1128
1129         /* bind as proxyauthzdn only if no idassert mode
1130          * is requested, or if the client's identity
1131          * is authorized */
1132         switch ( li->li_idassert_mode ) {
1133         case LDAP_BACK_IDASSERT_LEGACY:
1134                 if ( !BER_BVISNULL( &ndn ) && !BER_BVISEMPTY( &ndn ) ) {
1135                         if ( !BER_BVISNULL( &li->li_idassert_authcDN ) && !BER_BVISEMPTY( &li->li_idassert_authcDN ) )
1136                         {
1137                                 binddn = li->li_idassert_authcDN;
1138                                 bindcred = li->li_idassert_passwd;
1139                                 dobind = 1;
1140                         }
1141                 }
1142                 break;
1143
1144         default:
1145                 /* NOTE: rootdn can always idassert */
1146                 if ( BER_BVISNULL( &ndn ) && li->li_idassert_authz == NULL ) {
1147                         if ( li->li_idassert_flags & LDAP_BACK_AUTH_PRESCRIPTIVE ) {
1148                                 rs->sr_err = LDAP_INAPPROPRIATE_AUTH;
1149                                 if ( sendok & LDAP_BACK_SENDERR ) {
1150                                         send_ldap_result( op, rs );
1151                                 }
1152                                 LDAP_BACK_CONN_ISBOUND_CLEAR( lc );
1153
1154                         } else {
1155                                 rs->sr_err = LDAP_SUCCESS;
1156                                 binddn = slap_empty_bv;
1157                                 bindcred = slap_empty_bv;
1158                                 break;
1159                         }
1160
1161                         goto done;
1162
1163                 } else if ( li->li_idassert_authz && !be_isroot( op ) ) {
1164                         struct berval authcDN;
1165
1166                         if ( BER_BVISNULL( &ndn ) ) {
1167                                 authcDN = slap_empty_bv;
1168
1169                         } else {
1170                                 authcDN = ndn;
1171                         }       
1172                         rs->sr_err = slap_sasl_matches( op, li->li_idassert_authz,
1173                                         &authcDN, &authcDN );
1174                         if ( rs->sr_err != LDAP_SUCCESS ) {
1175                                 if ( li->li_idassert_flags & LDAP_BACK_AUTH_PRESCRIPTIVE ) {
1176                                         if ( sendok & LDAP_BACK_SENDERR ) {
1177                                                 send_ldap_result( op, rs );
1178                                         }
1179                                         LDAP_BACK_CONN_ISBOUND_CLEAR( lc );
1180
1181                                 } else {
1182                                         rs->sr_err = LDAP_SUCCESS;
1183                                         binddn = slap_empty_bv;
1184                                         bindcred = slap_empty_bv;
1185                                         break;
1186                                 }
1187
1188                                 goto done;
1189                         }
1190                 }
1191
1192                 binddn = li->li_idassert_authcDN;
1193                 bindcred = li->li_idassert_passwd;
1194                 dobind = 1;
1195                 break;
1196         }
1197
1198         if ( dobind && li->li_idassert_authmethod == LDAP_AUTH_SASL ) {
1199 #ifdef HAVE_CYRUS_SASL
1200                 void            *defaults = NULL;
1201                 struct berval   authzID = BER_BVNULL;
1202                 int             freeauthz = 0;
1203
1204                 /* if SASL supports native authz, prepare for it */
1205                 if ( ( !op->o_do_not_cache || !op->o_is_auth_check ) &&
1206                                 ( li->li_idassert_flags & LDAP_BACK_AUTH_NATIVE_AUTHZ ) )
1207                 {
1208                         switch ( li->li_idassert_mode ) {
1209                         case LDAP_BACK_IDASSERT_OTHERID:
1210                         case LDAP_BACK_IDASSERT_OTHERDN:
1211                                 authzID = li->li_idassert_authzID;
1212                                 break;
1213
1214                         case LDAP_BACK_IDASSERT_ANONYMOUS:
1215                                 BER_BVSTR( &authzID, "dn:" );
1216                                 break;
1217
1218                         case LDAP_BACK_IDASSERT_SELF:
1219                                 if ( BER_BVISNULL( &ndn ) ) {
1220                                         /* connection is not authc'd, so don't idassert */
1221                                         BER_BVSTR( &authzID, "dn:" );
1222                                         break;
1223                                 }
1224                                 authzID.bv_len = STRLENOF( "dn:" ) + ndn.bv_len;
1225                                 authzID.bv_val = slap_sl_malloc( authzID.bv_len + 1, op->o_tmpmemctx );
1226                                 AC_MEMCPY( authzID.bv_val, "dn:", STRLENOF( "dn:" ) );
1227                                 AC_MEMCPY( authzID.bv_val + STRLENOF( "dn:" ),
1228                                                 ndn.bv_val, ndn.bv_len + 1 );
1229                                 freeauthz = 1;
1230                                 break;
1231
1232                         default:
1233                                 break;
1234                         }
1235                 }
1236
1237                 if ( li->li_idassert_secprops != NULL ) {
1238                         rs->sr_err = ldap_set_option( lc->lc_ld,
1239                                 LDAP_OPT_X_SASL_SECPROPS,
1240                                 (void *)li->li_idassert_secprops );
1241
1242                         if ( rs->sr_err != LDAP_OPT_SUCCESS ) {
1243                                 rs->sr_err = LDAP_OTHER;
1244                                 if ( sendok & LDAP_BACK_SENDERR ) {
1245                                         send_ldap_result( op, rs );
1246                                 }
1247                                 LDAP_BACK_CONN_ISBOUND_CLEAR( lc );
1248                                 goto done;
1249                         }
1250                 }
1251
1252                 defaults = lutil_sasl_defaults( lc->lc_ld,
1253                                 li->li_idassert_sasl_mech.bv_val,
1254                                 li->li_idassert_sasl_realm.bv_val,
1255                                 li->li_idassert_authcID.bv_val,
1256                                 li->li_idassert_passwd.bv_val,
1257                                 authzID.bv_val );
1258
1259                 rs->sr_err = ldap_sasl_interactive_bind_s( lc->lc_ld, binddn.bv_val,
1260                                 li->li_idassert_sasl_mech.bv_val, NULL, NULL,
1261                                 LDAP_SASL_QUIET, lutil_sasl_interact,
1262                                 defaults );
1263
1264                 rs->sr_err = slap_map_api2result( rs );
1265                 if ( rs->sr_err != LDAP_SUCCESS ) {
1266                         LDAP_BACK_CONN_ISBOUND_CLEAR( lc );
1267                         if ( sendok & LDAP_BACK_SENDERR ) {
1268                                 send_ldap_result( op, rs );
1269                         }
1270
1271                 } else {
1272                         LDAP_BACK_CONN_ISBOUND_SET( lc );
1273                 }
1274
1275                 lutil_sasl_freedefs( defaults );
1276                 if ( freeauthz ) {
1277                         slap_sl_free( authzID.bv_val, op->o_tmpmemctx );
1278                 }
1279
1280                 goto done;
1281 #endif /* HAVE_CYRUS_SASL */
1282         }
1283
1284         switch ( li->li_idassert_authmethod ) {
1285         case LDAP_AUTH_NONE:
1286                 LDAP_BACK_CONN_ISBOUND_SET( lc );
1287                 goto done;
1288
1289         case LDAP_AUTH_SIMPLE:
1290                 rs->sr_err = ldap_sasl_bind( lc->lc_ld,
1291                                 binddn.bv_val, LDAP_SASL_SIMPLE,
1292                                 &bindcred, NULL, NULL, &msgid );
1293                 break;
1294
1295         default:
1296                 /* unsupported! */
1297                 LDAP_BACK_CONN_ISBOUND_CLEAR( lc );
1298                 rs->sr_err = LDAP_AUTH_METHOD_NOT_SUPPORTED;
1299                 if ( sendok & LDAP_BACK_SENDERR ) {
1300                         send_ldap_result( op, rs );
1301                 }
1302                 goto done;
1303         }
1304
1305         rc = ldap_back_op_result( lc, op, rs, msgid, 0, sendok );
1306         if ( rc == LDAP_SUCCESS ) {
1307                 LDAP_BACK_CONN_ISBOUND_SET( lc );
1308         }
1309 done:;
1310         return LDAP_BACK_CONN_ISBOUND( lc );
1311 }
1312
1313 /*
1314  * ldap_back_proxy_authz_ctrl() prepends a proxyAuthz control
1315  * to existing server-side controls if required; if not,
1316  * the existing server-side controls are placed in *pctrls.
1317  * The caller, after using the controls in client API 
1318  * operations, if ( *pctrls != op->o_ctrls ), should
1319  * free( (*pctrls)[ 0 ] ) and free( *pctrls ).
1320  * The function returns success if the control could
1321  * be added if required, or if it did nothing; in the future,
1322  * it might return some error if it failed.
1323  * 
1324  * if no bind took place yet, but the connection is bound
1325  * and the "proxyauthzdn" is set, then bind as "proxyauthzdn" 
1326  * and explicitly add proxyAuthz the control to every operation
1327  * with the dn bound to the connection as control value.
1328  *
1329  * If no server-side controls are defined for the operation,
1330  * simply add the proxyAuthz control; otherwise, if the
1331  * proxyAuthz control is not already set, add it as
1332  * the first one
1333  *
1334  * FIXME: is controls order significant for security?
1335  * ANSWER: controls ordering and interoperability
1336  * must be indicated by the specs of each control; if none
1337  * is specified, the order is irrelevant.
1338  */
1339 int
1340 ldap_back_proxy_authz_ctrl(
1341                 ldapconn_t      *lc,
1342                 Operation       *op,
1343                 SlapReply       *rs,
1344                 LDAPControl     ***pctrls )
1345 {
1346         ldapinfo_t      *li = (ldapinfo_t *) op->o_bd->be_private;
1347         LDAPControl     **ctrls = NULL;
1348         int             i = 0,
1349                         mode;
1350         struct berval   assertedID,
1351                         ndn;
1352
1353         *pctrls = NULL;
1354
1355         rs->sr_err = LDAP_SUCCESS;
1356
1357         /* FIXME: SASL/EXTERNAL over ldapi:// doesn't honor the authcID,
1358          * but if it is not set this test fails.  We need a different
1359          * means to detect if idassert is enabled */
1360         if ( ( BER_BVISNULL( &li->li_idassert_authcID ) || BER_BVISEMPTY( &li->li_idassert_authcID ) )
1361                         && ( BER_BVISNULL( &li->li_idassert_authcDN ) || BER_BVISEMPTY( &li->li_idassert_authcDN ) ) )
1362         {
1363                 goto done;
1364         }
1365
1366         if ( !op->o_conn || op->o_do_not_cache || be_isroot( op ) ) {
1367                 goto done;
1368         }
1369
1370         if ( !BER_BVISNULL( &op->o_conn->c_ndn ) ) {
1371                 ndn = op->o_conn->c_ndn;
1372
1373         } else {
1374                 ndn = op->o_ndn;
1375         }
1376
1377         if ( li->li_idassert_mode == LDAP_BACK_IDASSERT_LEGACY ) {
1378                 if ( op->o_proxy_authz ) {
1379                         /*
1380                          * FIXME: we do not want to perform proxyAuthz
1381                          * on behalf of the client, because this would
1382                          * be performed with "proxyauthzdn" privileges.
1383                          *
1384                          * This might actually be too strict, since
1385                          * the "proxyauthzdn" authzTo, and each entry's
1386                          * authzFrom attributes may be crafted
1387                          * to avoid unwanted proxyAuthz to take place.
1388                          */
1389 #if 0
1390                         rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
1391                         rs->sr_text = "proxyAuthz not allowed within namingContext";
1392 #endif
1393                         goto done;
1394                 }
1395
1396                 if ( !BER_BVISNULL( &lc->lc_bound_ndn ) ) {
1397                         goto done;
1398                 }
1399
1400                 if ( BER_BVISNULL( &ndn ) ) {
1401                         goto done;
1402                 }
1403
1404                 if ( BER_BVISNULL( &li->li_idassert_authcDN ) ) {
1405                         goto done;
1406                 }
1407
1408         } else if ( li->li_idassert_authmethod == LDAP_AUTH_SASL ) {
1409                 if ( ( li->li_idassert_flags & LDAP_BACK_AUTH_NATIVE_AUTHZ )
1410                                 /* && ( !BER_BVISNULL( &ndn )
1411                                         || LDAP_BACK_CONN_ISBOUND( lc ) ) */ )
1412                 {
1413                         /* already asserted in SASL via native authz */
1414                         /* NOTE: the test on lc->lc_bound is used to trap
1415                          * native authorization of anonymous users,
1416                          * since in that case ndn is NULL */
1417                         goto done;
1418                 }
1419
1420         } else if ( li->li_idassert_authz && !be_isroot( op ) ) {
1421                 int             rc;
1422                 struct berval authcDN;
1423
1424                 if ( BER_BVISNULL( &ndn ) ) {
1425                         authcDN = slap_empty_bv;
1426                 } else {
1427                         authcDN = ndn;
1428                 }
1429                 rc = slap_sasl_matches( op, li->li_idassert_authz,
1430                                 &authcDN, & authcDN );
1431                 if ( rc != LDAP_SUCCESS ) {
1432                         if ( li->li_idassert_flags & LDAP_BACK_AUTH_PRESCRIPTIVE )
1433                         {
1434                                 /* ndn is not authorized
1435                                  * to use idassert */
1436                                 return rc;
1437                         }
1438                         return rs->sr_err;
1439                 }
1440         }
1441
1442         if ( op->o_proxy_authz ) {
1443                 /*
1444                  * FIXME: we can:
1445                  * 1) ignore the already set proxyAuthz control
1446                  * 2) leave it in place, and don't set ours
1447                  * 3) add both
1448                  * 4) reject the operation
1449                  *
1450                  * option (4) is very drastic
1451                  * option (3) will make the remote server reject
1452                  * the operation, thus being equivalent to (4)
1453                  * option (2) will likely break the idassert
1454                  * assumptions, so we cannot accept it;
1455                  * option (1) means that we are contradicting
1456                  * the client's reques.
1457                  *
1458                  * I think (4) is the only correct choice.
1459                  */
1460                 rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
1461                 rs->sr_text = "proxyAuthz not allowed within namingContext";
1462         }
1463
1464         if ( op->o_is_auth_check ) {
1465                 mode = LDAP_BACK_IDASSERT_NOASSERT;
1466
1467         } else {
1468                 mode = li->li_idassert_mode;
1469         }
1470
1471         switch ( mode ) {
1472         case LDAP_BACK_IDASSERT_SELF:
1473                 if ( BER_BVISNULL( &ndn ) ) {
1474                         goto done;
1475                 }
1476                 assertedID = ndn;
1477                 break;
1478
1479         case LDAP_BACK_IDASSERT_LEGACY:
1480                 /* original behavior:
1481                  * assert the client's identity */
1482                 if ( BER_BVISNULL( &ndn ) ) {
1483                         assertedID = slap_empty_bv;
1484                 } else {
1485                         assertedID = ndn;
1486                 }
1487                 break;
1488
1489         case LDAP_BACK_IDASSERT_ANONYMOUS:
1490                 /* assert "anonymous" */
1491                 assertedID = slap_empty_bv;
1492                 break;
1493
1494         case LDAP_BACK_IDASSERT_NOASSERT:
1495                 /* don't assert; bind as proxyauthzdn */
1496                 goto done;
1497
1498         case LDAP_BACK_IDASSERT_OTHERID:
1499         case LDAP_BACK_IDASSERT_OTHERDN:
1500                 /* assert idassert DN */
1501                 assertedID = li->li_idassert_authzID;
1502                 break;
1503
1504         default:
1505                 assert( 0 );
1506         }
1507
1508         if ( BER_BVISNULL( &assertedID ) ) {
1509                 assertedID = slap_empty_bv;
1510         }
1511
1512         if ( op->o_ctrls ) {
1513                 for ( i = 0; op->o_ctrls[ i ]; i++ )
1514                         /* just count ctrls */ ;
1515         }
1516
1517         ctrls = op->o_tmpalloc( sizeof( LDAPControl * ) * (i + 2) + sizeof( LDAPControl ),
1518                         op->o_tmpmemctx );
1519         ctrls[ 0 ] = (LDAPControl *)&ctrls[ i + 2 ];
1520         
1521         ctrls[ 0 ]->ldctl_oid = LDAP_CONTROL_PROXY_AUTHZ;
1522         ctrls[ 0 ]->ldctl_iscritical = 1;
1523
1524         switch ( li->li_idassert_mode ) {
1525         /* already in u:ID or dn:DN form */
1526         case LDAP_BACK_IDASSERT_OTHERID:
1527         case LDAP_BACK_IDASSERT_OTHERDN:
1528                 ber_dupbv_x( &ctrls[ 0 ]->ldctl_value, &assertedID, op->o_tmpmemctx );
1529                 break;
1530
1531         /* needs the dn: prefix */
1532         default:
1533                 ctrls[ 0 ]->ldctl_value.bv_len = assertedID.bv_len + STRLENOF( "dn:" );
1534                 ctrls[ 0 ]->ldctl_value.bv_val = op->o_tmpalloc( ctrls[ 0 ]->ldctl_value.bv_len + 1,
1535                                 op->o_tmpmemctx );
1536                 AC_MEMCPY( ctrls[ 0 ]->ldctl_value.bv_val, "dn:", STRLENOF( "dn:" ) );
1537                 AC_MEMCPY( &ctrls[ 0 ]->ldctl_value.bv_val[ STRLENOF( "dn:" ) ],
1538                                 assertedID.bv_val, assertedID.bv_len + 1 );
1539                 break;
1540         }
1541
1542         if ( op->o_ctrls ) {
1543                 for ( i = 0; op->o_ctrls[ i ]; i++ ) {
1544                         ctrls[ i + 1 ] = op->o_ctrls[ i ];
1545                 }
1546         }
1547         ctrls[ i + 1 ] = NULL;
1548
1549 done:;
1550         if ( ctrls == NULL ) {
1551                 ctrls = op->o_ctrls;
1552         }
1553
1554         *pctrls = ctrls;
1555         
1556         return rs->sr_err;
1557 }
1558
1559 int
1560 ldap_back_proxy_authz_ctrl_free( Operation *op, LDAPControl ***pctrls )
1561 {
1562         LDAPControl     **ctrls = *pctrls;
1563
1564         /* we assume that the first control is the proxyAuthz
1565          * added by back-ldap, so it's the only one we explicitly 
1566          * free */
1567         if ( ctrls && ctrls != op->o_ctrls ) {
1568                 assert( ctrls[ 0 ] != NULL );
1569
1570                 if ( !BER_BVISNULL( &ctrls[ 0 ]->ldctl_value ) ) {
1571                         op->o_tmpfree( ctrls[ 0 ]->ldctl_value.bv_val, op->o_tmpmemctx );
1572                 }
1573
1574                 op->o_tmpfree( ctrls, op->o_tmpmemctx );
1575         } 
1576
1577         *pctrls = NULL;
1578
1579         return 0;
1580 }