]> git.sur5r.net Git - openldap/blob - servers/slapd/back-ldap/bind.c
c1c9791fd7350387dad4a049be8f43012197215f
[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-2005 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/socket.h>
29 #include <ac/string.h>
30
31 #define AVL_INTERNAL
32 #include "slap.h"
33 #include "back-ldap.h"
34
35 #include <lutil_ldap.h>
36
37 #define PRINT_CONNTREE 0
38
39 static LDAP_REBIND_PROC ldap_back_rebind;
40
41 static int
42 ldap_back_proxy_authz_bind( struct ldapconn *lc, Operation *op, SlapReply *rs );
43
44 static int
45 ldap_back_prepare_conn( struct ldapconn **lcp, Operation *op, SlapReply *rs, ldap_back_send_t sendok );
46
47 int
48 ldap_back_bind( Operation *op, SlapReply *rs )
49 {
50         struct ldapinfo *li = (struct ldapinfo *) op->o_bd->be_private;
51         struct ldapconn *lc;
52
53         int rc = 0;
54         ber_int_t msgid;
55
56         lc = ldap_back_getconn( op, rs, LDAP_BACK_SENDERR );
57         if ( !lc ) {
58                 return rs->sr_err;
59         }
60
61         if ( !BER_BVISNULL( &lc->lc_bound_ndn ) ) {
62                 ch_free( lc->lc_bound_ndn.bv_val );
63                 BER_BVZERO( &lc->lc_bound_ndn );
64         }
65         lc->lc_bound = 0;
66
67         /* method is always LDAP_AUTH_SIMPLE if we got here */
68         rs->sr_err = ldap_sasl_bind( lc->lc_ld, op->o_req_dn.bv_val,
69                         LDAP_SASL_SIMPLE,
70                         &op->orb_cred, op->o_ctrls, NULL, &msgid );
71         rc = ldap_back_op_result( lc, op, rs, msgid, LDAP_BACK_SENDERR );
72
73         if ( rc == LDAP_SUCCESS ) {
74                 /* If defined, proxyAuthz will be used also when
75                  * back-ldap is the authorizing backend; for this
76                  * purpose, a successful bind is followed by a
77                  * bind with the configured identity assertion */
78                 /* NOTE: use with care */
79                 if ( li->idassert_flags & LDAP_BACK_AUTH_OVERRIDE ) {
80                         ldap_back_proxy_authz_bind( lc, op, rs );
81                         if ( lc->lc_bound == 0 ) {
82                                 rc = 1;
83                                 goto done;
84                         }
85                 }
86
87                 lc->lc_bound = 1;
88                 ber_dupbv( &lc->lc_bound_ndn, &op->o_req_ndn );
89
90                 if ( LDAP_BACK_SAVECRED( li ) ) {
91                         if ( !BER_BVISNULL( &lc->lc_cred ) ) {
92                                 memset( lc->lc_cred.bv_val, 0,
93                                                 lc->lc_cred.bv_len );
94                                 ch_free( lc->lc_cred.bv_val );
95                         }
96                         ber_dupbv( &lc->lc_cred, &op->orb_cred );
97                         ldap_set_rebind_proc( lc->lc_ld, ldap_back_rebind, lc );
98                 }
99         }
100 done:;
101
102         /* must re-insert if local DN changed as result of bind */
103         if ( lc->lc_bound && !dn_match( &op->o_req_ndn, &lc->lc_local_ndn ) ) {
104                 int     lerr;
105
106                 ldap_pvt_thread_mutex_lock( &li->conn_mutex );
107                 lc = avl_delete( &li->conntree, (caddr_t)lc,
108                                 ldap_back_conn_cmp );
109                 if ( !BER_BVISNULL( &lc->lc_local_ndn ) ) {
110                         ch_free( lc->lc_local_ndn.bv_val );
111                 }
112                 ber_dupbv( &lc->lc_local_ndn, &op->o_req_ndn );
113                 lerr = avl_insert( &li->conntree, (caddr_t)lc,
114                         ldap_back_conn_cmp, ldap_back_conn_dup );
115                 ldap_pvt_thread_mutex_unlock( &li->conn_mutex );
116                 if ( lerr == -1 ) {
117                         ldap_back_conn_free( lc );
118                 }
119         }
120
121         return( rc );
122 }
123
124 /*
125  * ldap_back_conn_cmp
126  *
127  * compares two struct ldapconn based on the value of the conn pointer;
128  * used by avl stuff
129  */
130 int
131 ldap_back_conn_cmp( const void *c1, const void *c2 )
132 {
133         const struct ldapconn *lc1 = (const struct ldapconn *)c1;
134         const struct ldapconn *lc2 = (const struct ldapconn *)c2;
135         int rc;
136
137         /* If local DNs don't match, it is definitely not a match */
138         rc = ber_bvcmp( &lc1->lc_local_ndn, &lc2->lc_local_ndn );
139         if ( rc ) {
140                 return rc;
141         }
142
143         /* For shared sessions, conn is NULL. Only explicitly
144          * bound sessions will have non-NULL conn.
145          */
146         return SLAP_PTRCMP( lc1->lc_conn, lc2->lc_conn );
147 }
148
149 /*
150  * ldap_back_conn_dup
151  *
152  * returns -1 in case a duplicate struct ldapconn has been inserted;
153  * used by avl stuff
154  */
155 int
156 ldap_back_conn_dup( void *c1, void *c2 )
157 {
158         struct ldapconn *lc1 = (struct ldapconn *)c1;
159         struct ldapconn *lc2 = (struct ldapconn *)c2;
160
161         /* Cannot have more than one shared session with same DN */
162         if ( dn_match( &lc1->lc_local_ndn, &lc2->lc_local_ndn ) &&
163                         lc1->lc_conn == lc2->lc_conn )
164         {
165                 return -1;
166         }
167                 
168         return 0;
169 }
170
171 #if PRINT_CONNTREE > 0
172 static void
173 ravl_print( Avlnode *root, int depth )
174 {
175         int     i;
176         struct ldapconn *lc;
177         
178         if ( root == 0 ) {
179                 return;
180         }
181         
182         ravl_print( root->avl_right, depth+1 );
183         
184         for ( i = 0; i < depth; i++ ) {
185                 printf( "   " );
186         }
187
188         lc = root->avl_data;
189         printf( "lc(%lx) local(%s) conn(%lx) %d\n",
190                         lc, lc->lc_local_ndn.bv_val, lc->lc_conn, root->avl_bf );
191         
192         ravl_print( root->avl_left, depth+1 );
193 }
194
195 static void
196 myprint( Avlnode *root )
197 {
198         printf( "********\n" );
199         
200         if ( root == 0 ) {
201                 printf( "\tNULL\n" );
202
203         } else {
204                 ravl_print( root, 0 );
205         }
206         
207         printf( "********\n" );
208 }
209 #endif /* PRINT_CONNTREE */
210
211 int
212 ldap_back_freeconn( Operation *op, struct ldapconn *lc )
213 {
214         struct ldapinfo *li = (struct ldapinfo *) op->o_bd->be_private;
215
216         ldap_pvt_thread_mutex_lock( &li->conn_mutex );
217         lc = avl_delete( &li->conntree, (caddr_t)lc,
218                         ldap_back_conn_cmp );
219         ldap_back_conn_free( (void *)lc );
220         ldap_pvt_thread_mutex_unlock( &li->conn_mutex );
221
222         return 0;
223 }
224
225 static int
226 ldap_back_prepare_conn( struct ldapconn **lcp, Operation *op, SlapReply *rs, ldap_back_send_t sendok )
227 {
228         struct ldapinfo *li = (struct ldapinfo *)op->o_bd->be_private;
229         int             vers = op->o_protocol;
230         LDAP            *ld = NULL;
231
232         assert( lcp != NULL );
233
234         rs->sr_err = ldap_initialize( &ld, li->url );
235         if ( rs->sr_err != LDAP_SUCCESS ) {
236                 goto error_return;
237         }
238
239         /* Set LDAP version. This will always succeed: If the client
240          * bound with a particular version, then so can we.
241          */
242         ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION, (const void *)&vers );
243
244         /* automatically chase referrals ("chase-referrals"/"dont-chase-referrals" statement) */
245         if ( LDAP_BACK_CHASE_REFERRALS( li ) ) {
246                 ldap_set_option( ld, LDAP_OPT_REFERRALS, LDAP_OPT_ON );
247         }
248
249         /* start TLS ("start-tls"/"try-start-tls" statements) */
250         if ( ( LDAP_BACK_USE_TLS( li ) || ( op->o_conn->c_is_tls && LDAP_BACK_PROPAGATE_TLS( li ) ) )
251                                 && !ldap_is_ldaps_url( li->url ) ) {
252                 int             rc, msgid;
253                 LDAPMessage     *res;
254                 int             retries = 1;
255
256 retry:;
257                 rc = ldap_start_tls( ld, NULL, NULL, &msgid );
258                 if ( rc == LDAP_SUCCESS ) {
259                         struct timeval  tv = { 0, 0 };
260
261                         rc = ldap_result( ld, msgid, LDAP_MSG_ALL, &tv, &res );
262                         if ( rc < 0 ) {
263                                 rs->sr_err = LDAP_OTHER;
264
265                         } else if ( rc == 0 ) {
266                                 if ( retries ) {
267                                         retries--;
268                                         tv.tv_sec = 0;
269                                         tv.tv_usec = 100000;
270                                         goto retry;
271                                 }
272                                 rs->sr_err = LDAP_OTHER;
273
274                         } else {
275                                 if ( rc == LDAP_RES_EXTENDED ) {
276                                         rc = ldap_parse_result( ld, res,
277                                                 &rs->sr_err, NULL, NULL, NULL, NULL, 1 );
278                                         if ( rc != LDAP_SUCCESS ) {
279                                                 rs->sr_err = rc;
280
281                                         /* FIXME: in case a referral 
282                                          * is returned, should we try
283                                          * using it instead of the 
284                                          * configured URI? */
285                                         } else if ( rs->sr_err == LDAP_REFERRAL ) {
286                                                 rs->sr_err = LDAP_OTHER;
287                                                 rs->sr_text = "unwilling to chase referral returned by Start TLS exop";
288                                         }
289
290                                 } else {
291                                         ldap_msgfree( res );
292                                         rs->sr_err = LDAP_OTHER;
293                                 }
294                         }
295                 }
296
297                 /* if StartTLS is requested, only attempt it if the URL
298                  * is not "ldaps://"; this may occur not only in case
299                  * of misconfiguration, but also when used in the chain 
300                  * overlay, where the "uri" can be parsed out of a referral */
301                 if ( rs->sr_err == LDAP_SERVER_DOWN
302                                 || ( rs->sr_err != LDAP_SUCCESS && LDAP_BACK_TLS_CRITICAL( li ) ) )
303                 {
304                         ldap_unbind_ext_s( ld, NULL, NULL );
305                         goto error_return;
306                 }
307         }
308
309         if ( *lcp == NULL ) {
310                 *lcp = (struct ldapconn *)ch_malloc( sizeof( struct ldapconn ) );
311                 memset( *lcp, 0, sizeof( struct ldapconn ) );
312         }
313         (*lcp)->lc_ld = ld;
314
315 error_return:;
316         if ( rs->sr_err != LDAP_SUCCESS ) {
317                 rs->sr_err = slap_map_api2result( rs );
318                 if ( sendok & LDAP_BACK_SENDERR ) {
319                         if ( rs->sr_text == NULL ) {
320                                 rs->sr_text = "ldap_initialize() failed";
321                         }
322                         send_ldap_result( op, rs );
323                         rs->sr_text = NULL;
324                 }
325         }
326
327         return rs->sr_err;
328 }
329
330 struct ldapconn *
331 ldap_back_getconn( Operation *op, SlapReply *rs, ldap_back_send_t sendok )
332 {
333         struct ldapinfo *li = (struct ldapinfo *)op->o_bd->be_private;
334         struct ldapconn *lc, lc_curr;
335         int             is_priv = 0;
336
337         /* Searches for a ldapconn in the avl tree */
338
339         /* Explicit binds must not be shared */
340         if ( op->o_tag == LDAP_REQ_BIND
341                 || ( op->o_conn
342                         && op->o_conn->c_authz_backend
343                         && op->o_bd->be_private == op->o_conn->c_authz_backend->be_private ) )
344         {
345                 lc_curr.lc_conn = op->o_conn;
346
347         } else {
348                 lc_curr.lc_conn = NULL;
349         }
350         
351         /* Internal searches are privileged and shared. So is root. */
352         if ( op->o_do_not_cache || be_isroot( op ) ) {
353                 lc_curr.lc_local_ndn = op->o_bd->be_rootndn;
354                 lc_curr.lc_conn = NULL;
355                 is_priv = 1;
356
357         } else {
358                 lc_curr.lc_local_ndn = op->o_ndn;
359         }
360
361         ldap_pvt_thread_mutex_lock( &li->conn_mutex );
362         lc = (struct ldapconn *)avl_find( li->conntree, 
363                         (caddr_t)&lc_curr, ldap_back_conn_cmp );
364         ldap_pvt_thread_mutex_unlock( &li->conn_mutex );
365
366         /* Looks like we didn't get a bind. Open a new session... */
367         if ( !lc ) {
368                 /* lc here must be NULL */
369                 if ( ldap_back_prepare_conn( &lc, op, rs, sendok ) != LDAP_SUCCESS ) {
370                         return NULL;
371                 }
372
373                 lc->lc_conn = lc_curr.lc_conn;
374                 ber_dupbv( &lc->lc_local_ndn, &lc_curr.lc_local_ndn );
375
376                 ldap_pvt_thread_mutex_init( &lc->lc_mutex );
377
378                 if ( is_priv ) {
379                         ber_dupbv( &lc->lc_cred, &li->acl_passwd );
380                         ber_dupbv( &lc->lc_bound_ndn, &li->acl_authcDN );
381
382                 } else {
383                         BER_BVZERO( &lc->lc_cred );
384                         BER_BVZERO( &lc->lc_bound_ndn );
385                         if ( op->o_conn && !BER_BVISEMPTY( &op->o_ndn )
386                                         && op->o_bd == op->o_conn->c_authz_backend )
387                         {
388                                 ber_dupbv( &lc->lc_bound_ndn, &op->o_ndn );
389                         }
390                 }
391
392                 lc->lc_bound = 0;
393
394                 /* Inserts the newly created ldapconn in the avl tree */
395                 ldap_pvt_thread_mutex_lock( &li->conn_mutex );
396                 rs->sr_err = avl_insert( &li->conntree, (caddr_t)lc,
397                         ldap_back_conn_cmp, ldap_back_conn_dup );
398
399 #if PRINT_CONNTREE > 0
400                 myprint( li->conntree );
401 #endif /* PRINT_CONNTREE */
402         
403                 ldap_pvt_thread_mutex_unlock( &li->conn_mutex );
404
405                 Debug( LDAP_DEBUG_TRACE,
406                         "=>ldap_back_getconn: conn %p inserted\n", (void *) lc, 0, 0 );
407         
408                 /* Err could be -1 in case a duplicate ldapconn is inserted */
409                 if ( rs->sr_err != 0 ) {
410                         ldap_back_conn_free( lc );
411                         if ( op->o_conn && ( sendok & LDAP_BACK_SENDERR ) ) {
412                                 send_ldap_error( op, rs, LDAP_OTHER,
413                                 "internal server error" );
414                         }
415                         return NULL;
416                 }
417         } else {
418                 Debug( LDAP_DEBUG_TRACE,
419                         "=>ldap_back_getconn: conn %p fetched\n", (void *) lc, 0, 0 );
420         }
421         
422         return lc;
423 }
424
425 /*
426  * ldap_back_dobind
427  *
428  * Note: as the check for the value of lc->lc_bound was already here, I removed
429  * it from all the callers, and I made the function return the flag, so
430  * it can be used to simplify the check.
431  */
432 static int
433 ldap_back_dobind_int(
434         struct ldapconn         *lc,
435         Operation               *op,
436         SlapReply               *rs,
437         ldap_back_send_t        sendok,
438         int                     retries )
439 {       
440         int             rc;
441         ber_int_t       msgid;
442
443         assert( retries >= 0 );
444
445         ldap_pvt_thread_mutex_lock( &lc->lc_mutex );
446         if ( !lc->lc_bound ) {
447                 struct ldapinfo *li = (struct ldapinfo *)op->o_bd->be_private;
448
449                 /*
450                  * FIXME: we need to let clients use proxyAuthz
451                  * otherwise we cannot do symmetric pools of servers;
452                  * we have to live with the fact that a user can
453                  * authorize itself as any ID that is allowed
454                  * by the authzTo directive of the "proxyauthzdn".
455                  */
456                 /*
457                  * NOTE: current Proxy Authorization specification
458                  * and implementation do not allow proxy authorization
459                  * control to be provided with Bind requests
460                  */
461                 /*
462                  * if no bind took place yet, but the connection is bound
463                  * and the "idassert-authcDN" (or other ID) is set, 
464                  * then bind as the asserting identity and explicitly 
465                  * add the proxyAuthz control to every operation with the
466                  * dn bound to the connection as control value.
467                  * This is done also if this is the authrizing backend,
468                  * but the "override" flag is given to idassert.
469                  * It allows to use SASL bind and yet proxyAuthz users
470                  */
471                 if ( op->o_conn != NULL &&
472                                 ( BER_BVISNULL( &lc->lc_bound_ndn ) ||
473                                   ( li->idassert_flags & LDAP_BACK_AUTH_OVERRIDE ) ) )
474                 {
475                         (void)ldap_back_proxy_authz_bind( lc, op, rs );
476                         goto done;
477                 }
478
479 retry:;
480                 rs->sr_err = ldap_sasl_bind( lc->lc_ld,
481                                 lc->lc_bound_ndn.bv_val,
482                                 LDAP_SASL_SIMPLE, &lc->lc_cred,
483                                 NULL, NULL, &msgid );
484
485                 if ( rs->sr_err == LDAP_SERVER_DOWN ) {
486                         if ( retries > 0 ) {
487                                 ldap_unbind_ext_s( lc->lc_ld, NULL, NULL );
488                                 lc->lc_ld = NULL;
489
490                                 /* lc here must be the regular lc, reset and ready for init */
491                                 if ( ldap_back_prepare_conn( &lc, op, rs, sendok ) != LDAP_SUCCESS ) {
492                                         return 0;
493                                 }
494
495                                 retries--;
496                                 goto retry;
497                         }
498
499                         ldap_back_freeconn( op, lc );
500                         rs->sr_err = slap_map_api2result( rs );
501
502                         return 0;
503                 }
504
505                 rc = ldap_back_op_result( lc, op, rs, msgid, sendok );
506                 if ( rc == LDAP_SUCCESS ) {
507                         lc->lc_bound = 1;
508                 }
509         }
510
511 done:;
512         rc = lc->lc_bound;
513         ldap_pvt_thread_mutex_unlock( &lc->lc_mutex );
514         return rc;
515 }
516
517 int
518 ldap_back_dobind( struct ldapconn *lc, Operation *op, SlapReply *rs, ldap_back_send_t sendok )
519 {
520         return ldap_back_dobind_int( lc, op, rs, sendok, 1 );
521 }
522
523 /*
524  * ldap_back_rebind
525  *
526  * This is a callback used for chasing referrals using the same
527  * credentials as the original user on this session.
528  */
529 static int 
530 ldap_back_rebind( LDAP *ld, LDAP_CONST char *url, ber_tag_t request,
531         ber_int_t msgid, void *params )
532 {
533         struct ldapconn *lc = (struct ldapconn *)params;
534
535         /* FIXME: add checks on the URL/identity? */
536
537         return ldap_sasl_bind_s( ld, lc->lc_bound_ndn.bv_val,
538                         LDAP_SASL_SIMPLE, &lc->lc_cred, NULL, NULL, NULL );
539 }
540
541 int
542 ldap_back_op_result(
543                 struct ldapconn         *lc,
544                 Operation               *op,
545                 SlapReply               *rs,
546                 ber_int_t               msgid,
547                 ldap_back_send_t        sendok )
548 {
549         char            *match = NULL;
550         LDAPMessage     *res = NULL;
551         char            *text = NULL;
552
553 #define ERR_OK(err) ((err) == LDAP_SUCCESS || (err) == LDAP_COMPARE_FALSE || (err) == LDAP_COMPARE_TRUE)
554
555         rs->sr_text = NULL;
556         rs->sr_matched = NULL;
557
558         /* if the error recorded in the reply corresponds
559          * to a successful state, get the error from the
560          * remote server response */
561         if ( ERR_OK( rs->sr_err ) ) {
562                 int             rc;
563                 struct timeval  tv = { 0, 0 };
564
565 retry:;
566                 /* if result parsing fails, note the failure reason */
567                 switch ( ldap_result( lc->lc_ld, msgid, 1, &tv, &res ) ) {
568                 case 0:
569                         tv.tv_sec = 0;
570                         tv.tv_usec = 100000;    /* 0.1 s */
571                         ldap_pvt_thread_yield();
572                         goto retry;
573
574                 case -1:
575                         ldap_get_option( lc->lc_ld, LDAP_OPT_ERROR_NUMBER,
576                                         &rs->sr_err );
577                         break;
578
579
580                 /* otherwise get the result; if it is not
581                  * LDAP_SUCCESS, record it in the reply
582                  * structure (this includes 
583                  * LDAP_COMPARE_{TRUE|FALSE}) */
584                 default:
585                         rc = ldap_parse_result( lc->lc_ld, res, &rs->sr_err,
586                                         &match, &text, NULL, NULL, 1 );
587                         rs->sr_text = text;
588                         if ( rc != LDAP_SUCCESS ) {
589                                 rs->sr_err = rc;
590                         }
591                 }
592         }
593
594         /* if the error in the reply structure is not
595          * LDAP_SUCCESS, try to map it from client 
596          * to server error */
597         if ( !ERR_OK( rs->sr_err ) ) {
598                 rs->sr_err = slap_map_api2result( rs );
599
600                 /* internal ops ( op->o_conn == NULL ) 
601                  * must not reply to client */
602                 if ( op->o_conn && !op->o_do_not_cache && match ) {
603
604                         /* record the (massaged) matched
605                          * DN into the reply structure */
606                         rs->sr_matched = match;
607                 }
608         }
609         if ( op->o_conn &&
610                         ( ( sendok & LDAP_BACK_SENDOK ) 
611                           || ( ( sendok & LDAP_BACK_SENDERR ) && rs->sr_err != LDAP_SUCCESS ) ) )
612         {
613                 send_ldap_result( op, rs );
614         }
615         if ( match ) {
616                 if ( rs->sr_matched != match ) {
617                         free( (char *)rs->sr_matched );
618                 }
619                 rs->sr_matched = NULL;
620                 ldap_memfree( match );
621         }
622         if ( text ) {
623                 ldap_memfree( text );
624         }
625         rs->sr_text = NULL;
626         return( ERR_OK( rs->sr_err ) ? 0 : -1 );
627 }
628
629 /* return true if bound, false if failed */
630 int
631 ldap_back_retry( struct ldapconn *lc, Operation *op, SlapReply *rs, ldap_back_send_t sendok )
632 {
633         ldap_pvt_thread_mutex_lock( &lc->lc_mutex );
634         ldap_unbind_ext_s( lc->lc_ld, NULL, NULL );
635         lc->lc_ld = NULL;
636         lc->lc_bound = 0;
637
638         /* lc here must be the regular lc, reset and ready for init */
639         if ( ldap_back_prepare_conn( &lc, op, rs, sendok ) != LDAP_SUCCESS ) {
640                 return 0;
641         }
642
643         ldap_pvt_thread_mutex_unlock( &lc->lc_mutex );
644         return ldap_back_dobind_int( lc, op, rs, sendok, 0 );
645 }
646
647 static int
648 ldap_back_proxy_authz_bind( struct ldapconn *lc, Operation *op, SlapReply *rs )
649 {
650         struct ldapinfo *li = (struct ldapinfo *)op->o_bd->be_private;
651         struct berval   binddn = slap_empty_bv;
652         struct berval   bindcred = slap_empty_bv;
653         int             dobind = 0;
654         int             msgid;
655         int             rc;
656
657         /*
658          * FIXME: we need to let clients use proxyAuthz
659          * otherwise we cannot do symmetric pools of servers;
660          * we have to live with the fact that a user can
661          * authorize itself as any ID that is allowed
662          * by the authzTo directive of the "proxyauthzdn".
663          */
664         /*
665          * NOTE: current Proxy Authorization specification
666          * and implementation do not allow proxy authorization
667          * control to be provided with Bind requests
668          */
669         /*
670          * if no bind took place yet, but the connection is bound
671          * and the "proxyauthzdn" is set, then bind as 
672          * "proxyauthzdn" and explicitly add the proxyAuthz 
673          * control to every operation with the dn bound 
674          * to the connection as control value.
675          */
676
677         /* bind as proxyauthzdn only if no idassert mode
678          * is requested, or if the client's identity
679          * is authorized */
680         switch ( li->idassert_mode ) {
681         case LDAP_BACK_IDASSERT_LEGACY:
682                 if ( !BER_BVISNULL( &op->o_conn->c_ndn ) && !BER_BVISEMPTY( &op->o_conn->c_ndn ) ) {
683                         if ( !BER_BVISNULL( &li->idassert_authcDN ) && !BER_BVISEMPTY( &li->idassert_authcDN ) )
684                         {
685                                 binddn = li->idassert_authcDN;
686                                 bindcred = li->idassert_passwd;
687                                 dobind = 1;
688                         }
689                 }
690                 break;
691
692         default:
693                 if ( li->idassert_authz ) {
694                         struct berval authcDN;
695
696                         if ( BER_BVISNULL( &op->o_conn->c_ndn ) ) {
697                                 authcDN = slap_empty_bv;
698                         } else {
699                                 authcDN = op->o_conn->c_ndn;
700                         }       
701                         rs->sr_err = slap_sasl_matches( op, li->idassert_authz,
702                                         &authcDN, &authcDN );
703                         if ( rs->sr_err != LDAP_SUCCESS ) {
704                                 send_ldap_result( op, rs );
705                                 lc->lc_bound = 0;
706                                 goto done;
707                         }
708                 }
709
710                 binddn = li->idassert_authcDN;
711                 bindcred = li->idassert_passwd;
712                 dobind = 1;
713                 break;
714         }
715
716         if ( dobind && li->idassert_authmethod == LDAP_AUTH_SASL ) {
717 #ifdef HAVE_CYRUS_SASL
718                 void            *defaults = NULL;
719                 struct berval   authzID = BER_BVNULL;
720                 int             freeauthz = 0;
721
722                 /* if SASL supports native authz, prepare for it */
723                 if ( ( !op->o_do_not_cache || !op->o_is_auth_check ) &&
724                                 ( li->idassert_flags & LDAP_BACK_AUTH_NATIVE_AUTHZ ) )
725                 {
726                         switch ( li->idassert_mode ) {
727                         case LDAP_BACK_IDASSERT_OTHERID:
728                         case LDAP_BACK_IDASSERT_OTHERDN:
729                                 authzID = li->idassert_authzID;
730                                 break;
731
732                         case LDAP_BACK_IDASSERT_ANONYMOUS:
733                                 BER_BVSTR( &authzID, "dn:" );
734                                 break;
735
736                         case LDAP_BACK_IDASSERT_SELF:
737                                 if ( BER_BVISNULL( &op->o_conn->c_ndn ) ) {
738                                         /* connection is not authc'd, so don't idassert */
739                                         BER_BVSTR( &authzID, "dn:" );
740                                         break;
741                                 }
742                                 authzID.bv_len = STRLENOF( "dn:" ) + op->o_conn->c_ndn.bv_len;
743                                 authzID.bv_val = slap_sl_malloc( authzID.bv_len + 1, op->o_tmpmemctx );
744                                 AC_MEMCPY( authzID.bv_val, "dn:", STRLENOF( "dn:" ) );
745                                 AC_MEMCPY( authzID.bv_val + STRLENOF( "dn:" ),
746                                                 op->o_conn->c_ndn.bv_val, op->o_conn->c_ndn.bv_len + 1 );
747                                 freeauthz = 1;
748                                 break;
749
750                         default:
751                                 break;
752                         }
753                 }
754
755 #if 0   /* will deal with this later... */
756                 if ( sasl_secprops != NULL ) {
757                         rs->sr_err = ldap_set_option( lc->lc_ld, LDAP_OPT_X_SASL_SECPROPS,
758                                 (void *) sasl_secprops );
759
760                         if ( rs->sr_err != LDAP_OPT_SUCCESS ) {
761                                 send_ldap_result( op, rs );
762                                 lc->lc_bound = 0;
763                                 goto done;
764                         }
765                 }
766 #endif
767
768                 defaults = lutil_sasl_defaults( lc->lc_ld,
769                                 li->idassert_sasl_mech.bv_val,
770                                 li->idassert_sasl_realm.bv_val,
771                                 li->idassert_authcID.bv_val,
772                                 li->idassert_passwd.bv_val,
773                                 authzID.bv_val );
774
775                 rs->sr_err = ldap_sasl_interactive_bind_s( lc->lc_ld, binddn.bv_val,
776                                 li->idassert_sasl_mech.bv_val, NULL, NULL,
777                                 li->idassert_sasl_flags, lutil_sasl_interact,
778                                 defaults );
779
780                 lutil_sasl_freedefs( defaults );
781                 if ( freeauthz ) {
782                         slap_sl_free( authzID.bv_val, op->o_tmpmemctx );
783                 }
784
785                 rs->sr_err = slap_map_api2result( rs );
786                 if ( rs->sr_err != LDAP_SUCCESS ) {
787                         lc->lc_bound = 0;
788                         send_ldap_result( op, rs );
789
790                 } else {
791                         lc->lc_bound = 1;
792                 }
793                 goto done;
794 #endif /* HAVE_CYRUS_SASL */
795         }
796
797         switch ( li->idassert_authmethod ) {
798         case LDAP_AUTH_SIMPLE:
799                 rs->sr_err = ldap_sasl_bind( lc->lc_ld,
800                                 binddn.bv_val, LDAP_SASL_SIMPLE,
801                                 &bindcred, NULL, NULL, &msgid );
802                 break;
803
804         case LDAP_AUTH_NONE:
805                 lc->lc_bound = 1;
806                 goto done;
807
808         default:
809                 /* unsupported! */
810                 lc->lc_bound = 0;
811                 rs->sr_err = LDAP_AUTH_METHOD_NOT_SUPPORTED;
812                 send_ldap_result( op, rs );
813                 goto done;
814         }
815
816         rc = ldap_back_op_result( lc, op, rs, msgid, LDAP_BACK_SENDERR );
817         if ( rc == LDAP_SUCCESS ) {
818                 lc->lc_bound = 1;
819         }
820 done:;
821         return lc->lc_bound;
822 }
823
824 /*
825  * ldap_back_proxy_authz_ctrl() prepends a proxyAuthz control
826  * to existing server-side controls if required; if not,
827  * the existing server-side controls are placed in *pctrls.
828  * The caller, after using the controls in client API 
829  * operations, if ( *pctrls != op->o_ctrls ), should
830  * free( (*pctrls)[ 0 ] ) and free( *pctrls ).
831  * The function returns success if the control could
832  * be added if required, or if it did nothing; in the future,
833  * it might return some error if it failed.
834  * 
835  * if no bind took place yet, but the connection is bound
836  * and the "proxyauthzdn" is set, then bind as "proxyauthzdn" 
837  * and explicitly add proxyAuthz the control to every operation
838  * with the dn bound to the connection as control value.
839  *
840  * If no server-side controls are defined for the operation,
841  * simply add the proxyAuthz control; otherwise, if the
842  * proxyAuthz control is not already set, add it as
843  * the first one (FIXME: is controls order significant
844  * for security?).
845  */
846 int
847 ldap_back_proxy_authz_ctrl(
848                 struct ldapconn *lc,
849                 Operation       *op,
850                 SlapReply       *rs,
851                 LDAPControl     ***pctrls )
852 {
853         struct ldapinfo *li = (struct ldapinfo *) op->o_bd->be_private;
854         LDAPControl     **ctrls = NULL;
855         int             i = 0,
856                         mode;
857         struct berval   assertedID;
858
859         *pctrls = NULL;
860
861         rs->sr_err = LDAP_SUCCESS;
862
863         if ( ( BER_BVISNULL( &li->idassert_authcID ) || BER_BVISEMPTY( &li->idassert_authcID ) )
864                         && ( BER_BVISNULL( &li->idassert_authcDN ) || BER_BVISEMPTY( &li->idassert_authcDN ) ) ) {
865                 goto done;
866         }
867
868         if ( !op->o_conn ) {
869                 goto done;
870         }
871
872         if ( li->idassert_mode == LDAP_BACK_IDASSERT_LEGACY ) {
873                 if ( op->o_proxy_authz ) {
874                         /*
875                          * FIXME: we do not want to perform proxyAuthz
876                          * on behalf of the client, because this would
877                          * be performed with "proxyauthzdn" privileges.
878                          *
879                          * This might actually be too strict, since
880                          * the "proxyauthzdn" authzTo, and each entry's
881                          * authzFrom attributes may be crafted
882                          * to avoid unwanted proxyAuthz to take place.
883                          */
884 #if 0
885                         rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
886                         rs->sr_text = "proxyAuthz not allowed within namingContext";
887 #endif
888                         goto done;
889                 }
890
891                 if ( !BER_BVISNULL( &lc->lc_bound_ndn ) ) {
892                         goto done;
893                 }
894
895                 if ( BER_BVISNULL( &op->o_conn->c_ndn ) ) {
896                         goto done;
897                 }
898
899                 if ( BER_BVISNULL( &li->idassert_authcDN ) ) {
900                         goto done;
901                 }
902
903         } else if ( li->idassert_authmethod == LDAP_AUTH_SASL ) {
904                 if ( ( li->idassert_flags & LDAP_BACK_AUTH_NATIVE_AUTHZ )
905                                 /* && ( !BER_BVISNULL( &op->o_conn->c_ndn ) || lc->lc_bound ) */ )
906                 {
907                         /* already asserted in SASL via native authz */
908                         /* NOTE: the test on lc->lc_bound is used to trap
909                          * native authorization of anonymous users,
910                          * since in that case op->o_conn->c_ndn is NULL */
911                         goto done;
912                 }
913
914         } else if ( li->idassert_authz ) {
915                 int             rc;
916                 struct berval authcDN;
917
918                 if ( BER_BVISNULL( &op->o_conn->c_ndn ) ) {
919                         authcDN = slap_empty_bv;
920                 } else {
921                         authcDN = op->o_conn->c_ndn;
922                 }
923                 rc = slap_sasl_matches( op, li->idassert_authz,
924                                 &authcDN, & authcDN );
925                 if ( rc != LDAP_SUCCESS ) {
926                         /* op->o_conn->c_ndn is not authorized
927                          * to use idassert */
928                         return rc;
929                 }
930         }
931
932         if ( op->o_proxy_authz ) {
933                 /*
934                  * FIXME: we can:
935                  * 1) ignore the already set proxyAuthz control
936                  * 2) leave it in place, and don't set ours
937                  * 3) add both
938                  * 4) reject the operation
939                  *
940                  * option (4) is very drastic
941                  * option (3) will make the remote server reject
942                  * the operation, thus being equivalent to (4)
943                  * option (2) will likely break the idassert
944                  * assumptions, so we cannot accept it;
945                  * option (1) means that we are contradicting
946                  * the client's reques.
947                  *
948                  * I think (4) is the only correct choice.
949                  */
950                 rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
951                 rs->sr_text = "proxyAuthz not allowed within namingContext";
952         }
953
954         if ( op->o_do_not_cache && op->o_is_auth_check ) {
955                 mode = LDAP_BACK_IDASSERT_NOASSERT;
956
957         } else {
958                 mode = li->idassert_mode;
959         }
960
961         switch ( mode ) {
962         case LDAP_BACK_IDASSERT_LEGACY:
963         case LDAP_BACK_IDASSERT_SELF:
964                 /* original behavior:
965                  * assert the client's identity */
966                 if ( BER_BVISNULL( &op->o_conn->c_ndn ) ) {
967                         assertedID = slap_empty_bv;
968                 } else {
969                         assertedID = op->o_conn->c_ndn;
970                 }
971                 break;
972
973         case LDAP_BACK_IDASSERT_ANONYMOUS:
974                 /* assert "anonymous" */
975                 assertedID = slap_empty_bv;
976                 break;
977
978         case LDAP_BACK_IDASSERT_NOASSERT:
979                 /* don't assert; bind as proxyauthzdn */
980                 goto done;
981
982         case LDAP_BACK_IDASSERT_OTHERID:
983         case LDAP_BACK_IDASSERT_OTHERDN:
984                 /* assert idassert DN */
985                 assertedID = li->idassert_authzID;
986                 break;
987
988         default:
989                 assert( 0 );
990         }
991
992         if ( BER_BVISNULL( &assertedID ) ) {
993                 assertedID = slap_empty_bv;
994         }
995
996         if ( op->o_ctrls ) {
997                 for ( i = 0; op->o_ctrls[ i ]; i++ )
998                         /* just count ctrls */ ;
999         }
1000
1001         ctrls = ch_malloc( sizeof( LDAPControl * ) * (i + 2) );
1002         ctrls[ 0 ] = ch_malloc( sizeof( LDAPControl ) );
1003         
1004         ctrls[ 0 ]->ldctl_oid = LDAP_CONTROL_PROXY_AUTHZ;
1005         ctrls[ 0 ]->ldctl_iscritical = 1;
1006
1007         switch ( li->idassert_mode ) {
1008         /* already in u:ID or dn:DN form */
1009         case LDAP_BACK_IDASSERT_OTHERID:
1010         case LDAP_BACK_IDASSERT_OTHERDN:
1011                 ber_dupbv( &ctrls[ 0 ]->ldctl_value, &assertedID );
1012                 break;
1013
1014         /* needs the dn: prefix */
1015         default:
1016                 ctrls[ 0 ]->ldctl_value.bv_len = assertedID.bv_len + STRLENOF( "dn:" );
1017                 ctrls[ 0 ]->ldctl_value.bv_val = ch_malloc( ctrls[ 0 ]->ldctl_value.bv_len + 1 );
1018                 AC_MEMCPY( ctrls[ 0 ]->ldctl_value.bv_val, "dn:", STRLENOF( "dn:" ) );
1019                 AC_MEMCPY( ctrls[ 0 ]->ldctl_value.bv_val + STRLENOF( "dn:" ),
1020                                 assertedID.bv_val, assertedID.bv_len + 1 );
1021                 break;
1022         }
1023
1024         if ( op->o_ctrls ) {
1025                 for ( i = 0; op->o_ctrls[ i ]; i++ ) {
1026                         ctrls[ i + 1 ] = op->o_ctrls[ i ];
1027                 }
1028         }
1029         ctrls[ i + 1 ] = NULL;
1030
1031 done:;
1032         if ( ctrls == NULL ) {
1033                 ctrls = op->o_ctrls;
1034         }
1035
1036         *pctrls = ctrls;
1037         
1038         return rs->sr_err;
1039 }
1040
1041 int
1042 ldap_back_proxy_authz_ctrl_free( Operation *op, LDAPControl ***pctrls )
1043 {
1044         LDAPControl     **ctrls = *pctrls;
1045
1046         /* we assume that the first control is the proxyAuthz
1047          * added by back-ldap, so it's the only one we explicitly 
1048          * free */
1049         if ( ctrls && ctrls != op->o_ctrls ) {
1050                 assert( ctrls[ 0 ] );
1051
1052                 if ( !BER_BVISNULL( &ctrls[ 0 ]->ldctl_value ) ) {
1053                         free( ctrls[ 0 ]->ldctl_value.bv_val );
1054                 }
1055
1056                 free( ctrls[ 0 ] );
1057                 free( ctrls );
1058         } 
1059
1060         *pctrls = NULL;
1061
1062         return 0;
1063 }