1 /* bind.c - ldap backend bind function */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
5 * Copyright 1999-2005 The OpenLDAP Foundation.
6 * Portions Copyright 2000-2003 Pierangelo Masarati.
7 * Portions Copyright 1999-2003 Howard Chu.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted only as authorized by the OpenLDAP
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>.
19 * This work was initially developed by Howard Chu for inclusion
20 * in OpenLDAP Software and subsequently enhanced by Pierangelo
29 #include <ac/socket.h>
30 #include <ac/string.h>
34 #include "back-ldap.h"
36 #include <lutil_ldap.h>
38 #define PRINT_CONNTREE 0
41 * FIXME: temporarily disable pooled connections, as there seem to be
42 * some concurrency problem
44 /* #undef LDAP_BACK_POOLED_CONNS */
46 static LDAP_REBIND_PROC ldap_back_rebind;
49 ldap_back_proxy_authz_bind( struct ldapconn *lc, Operation *op, SlapReply *rs );
52 ldap_back_prepare_conn( struct ldapconn **lcp, Operation *op, SlapReply *rs, ldap_back_send_t sendok );
55 ldap_back_bind( Operation *op, SlapReply *rs )
57 struct ldapinfo *li = (struct ldapinfo *) op->o_bd->be_private;
63 lc = ldap_back_getconn( op, rs, LDAP_BACK_SENDERR );
68 if ( !BER_BVISNULL( &lc->lc_bound_ndn ) ) {
69 ch_free( lc->lc_bound_ndn.bv_val );
70 BER_BVZERO( &lc->lc_bound_ndn );
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,
77 &op->orb_cred, op->o_ctrls, NULL, &msgid );
78 rc = ldap_back_op_result( lc, op, rs, msgid, LDAP_BACK_SENDERR );
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->idassert_flags & LDAP_BACK_AUTH_OVERRIDE ) {
87 ldap_back_proxy_authz_bind( lc, op, rs );
88 if ( lc->lc_bound == 0 ) {
95 ber_dupbv( &lc->lc_bound_ndn, &op->o_req_ndn );
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 );
102 ber_bvreplace( &lc->lc_cred, &op->orb_cred );
103 ldap_set_rebind_proc( lc->lc_ld, ldap_back_rebind, lc );
108 /* must re-insert if local DN changed as result of bind */
109 if ( lc->lc_bound && !dn_match( &op->o_req_ndn, &lc->lc_local_ndn ) ) {
112 /* wait for all other ops to release the connection */
114 switch ( ldap_pvt_thread_mutex_trylock( &li->conn_mutex ) ) {
115 case LDAP_PVT_THREAD_EBUSY:
117 ldap_pvt_thread_yield();
121 if ( lc->lc_refcnt > 1 ) {
122 ldap_pvt_thread_mutex_unlock( &li->conn_mutex );
123 ldap_pvt_thread_yield();
129 assert( lc->lc_refcnt == 1 );
130 lc = avl_delete( &li->conntree, (caddr_t)lc,
131 ldap_back_conn_cmp );
132 assert( lc != NULL );
134 ber_bvreplace( &lc->lc_local_ndn, &op->o_req_ndn );
135 lerr = avl_insert( &li->conntree, (caddr_t)lc,
136 ldap_back_conn_cmp, ldap_back_conn_dup );
137 ldap_pvt_thread_mutex_unlock( &li->conn_mutex );
139 /* we can do this because lc_refcnt == 1 */
140 ldap_back_conn_free( lc );
146 ldap_back_release_conn( op, rs, lc );
155 * compares two struct ldapconn based on the value of the conn pointer;
159 ldap_back_conn_cmp( const void *c1, const void *c2 )
161 const struct ldapconn *lc1 = (const struct ldapconn *)c1;
162 const struct ldapconn *lc2 = (const struct ldapconn *)c2;
165 /* If local DNs don't match, it is definitely not a match */
166 rc = ber_bvcmp( &lc1->lc_local_ndn, &lc2->lc_local_ndn );
171 /* For shared sessions, conn is NULL. Only explicitly
172 * bound sessions will have non-NULL conn.
174 return SLAP_PTRCMP( lc1->lc_conn, lc2->lc_conn );
180 * returns -1 in case a duplicate struct ldapconn has been inserted;
184 ldap_back_conn_dup( void *c1, void *c2 )
186 struct ldapconn *lc1 = (struct ldapconn *)c1;
187 struct ldapconn *lc2 = (struct ldapconn *)c2;
189 /* Cannot have more than one shared session with same DN */
190 if ( dn_match( &lc1->lc_local_ndn, &lc2->lc_local_ndn ) &&
191 lc1->lc_conn == lc2->lc_conn )
199 #if PRINT_CONNTREE > 0
201 ravl_print( Avlnode *root, int depth )
210 ravl_print( root->avl_right, depth+1 );
212 for ( i = 0; i < depth; i++ ) {
217 printf( "lc(%lx) local(%s) conn(%lx) %d\n",
218 lc, lc->lc_local_ndn.bv_val, lc->lc_conn, root->avl_bf );
220 ravl_print( root->avl_left, depth+1 );
224 myprint( Avlnode *root )
226 printf( "********\n" );
229 printf( "\tNULL\n" );
232 ravl_print( root, 0 );
235 printf( "********\n" );
237 #endif /* PRINT_CONNTREE */
240 ldap_back_freeconn( Operation *op, struct ldapconn *lc )
242 struct ldapinfo *li = (struct ldapinfo *) op->o_bd->be_private;
245 switch ( ldap_pvt_thread_mutex_trylock( &li->conn_mutex ) ) {
246 case LDAP_PVT_THREAD_EBUSY:
248 ldap_pvt_thread_yield();
255 switch ( ldap_pvt_thread_mutex_trylock( &lc->lc_mutex ) ) {
256 case LDAP_PVT_THREAD_EBUSY:
258 ldap_pvt_thread_mutex_unlock( &li->conn_mutex );
259 ldap_pvt_thread_yield();
266 assert( lc->lc_refcnt > 0 );
267 if ( --lc->lc_refcnt == 0 ) {
268 lc = avl_delete( &li->conntree, (caddr_t)lc,
269 ldap_back_conn_cmp );
270 assert( lc != NULL );
272 ldap_pvt_thread_mutex_unlock( &lc->lc_mutex );
274 ldap_back_conn_free( (void *)lc );
277 ldap_pvt_thread_mutex_unlock( &lc->lc_mutex );
280 ldap_pvt_thread_mutex_unlock( &li->conn_mutex );
286 ldap_back_prepare_conn( struct ldapconn **lcp, Operation *op, SlapReply *rs, ldap_back_send_t sendok )
288 struct ldapinfo *li = (struct ldapinfo *)op->o_bd->be_private;
289 int vers = op->o_protocol;
292 assert( lcp != NULL );
294 rs->sr_err = ldap_initialize( &ld, li->url );
295 if ( rs->sr_err != LDAP_SUCCESS ) {
299 /* Set LDAP version. This will always succeed: If the client
300 * bound with a particular version, then so can we.
302 ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION, (const void *)&vers );
304 /* automatically chase referrals ("[dont-]chase-referrals" statement) */
305 if ( LDAP_BACK_CHASE_REFERRALS( li ) ) {
306 ldap_set_option( ld, LDAP_OPT_REFERRALS, LDAP_OPT_ON );
310 /* start TLS ("tls-[try-]{start,propagate}" statements) */
311 if ( ( LDAP_BACK_USE_TLS( li ) || ( op->o_conn->c_is_tls && LDAP_BACK_PROPAGATE_TLS( li ) ) )
312 && !ldap_is_ldaps_url( li->url ) )
314 #ifdef SLAP_STARTTLS_ASYNCHRONOUS
316 * use asynchronous StartTLS
317 * in case, chase referral (not implemented yet)
321 rs->sr_err = ldap_start_tls( ld, NULL, NULL, &msgid );
322 if ( rs->sr_err == LDAP_SUCCESS ) {
323 LDAPMessage *res = NULL;
325 struct timeval tv = { 0, 0 };
328 rc = ldap_result( ld, msgid, LDAP_MSG_ALL, &tv, &res );
330 rs->sr_err = LDAP_OTHER;
332 } else if ( rc == 0 ) {
339 rs->sr_err = LDAP_OTHER;
341 } else if ( rc == LDAP_RES_EXTENDED ) {
342 struct berval *data = NULL;
344 rs->sr_err = ldap_parse_extended_result( ld, res,
346 if ( rs->sr_err == LDAP_SUCCESS ) {
347 rs->sr_err = ldap_result2error( ld, res, 1 );
350 /* FIXME: in case a referral
351 * is returned, should we try
352 * using it instead of the
354 if ( rs->sr_err == LDAP_SUCCESS ) {
355 ldap_install_tls( ld );
357 } else if ( rs->sr_err == LDAP_REFERRAL ) {
358 rs->sr_err = LDAP_OTHER;
359 rs->sr_text = "unwilling to chase referral returned by Start TLS exop";
363 if ( data->bv_val ) {
364 ber_memfree( data->bv_val );
371 rs->sr_err = LDAP_OTHER;
378 #else /* ! SLAP_STARTTLS_ASYNCHRONOUS */
380 * use synchronous StartTLS
382 rs->sr_err = ldap_start_tls_s( ld, NULL, NULL );
383 #endif /* ! SLAP_STARTTLS_ASYNCHRONOUS */
385 /* if StartTLS is requested, only attempt it if the URL
386 * is not "ldaps://"; this may occur not only in case
387 * of misconfiguration, but also when used in the chain
388 * overlay, where the "uri" can be parsed out of a referral */
389 if ( rs->sr_err == LDAP_SERVER_DOWN
390 || ( rs->sr_err != LDAP_SUCCESS && LDAP_BACK_TLS_CRITICAL( li ) ) )
392 ldap_unbind_ext_s( ld, NULL, NULL );
396 /* in case Start TLS is not critical */
397 rs->sr_err = LDAP_SUCCESS;
399 #endif /* HAVE_TLS */
401 if ( *lcp == NULL ) {
402 *lcp = (struct ldapconn *)ch_calloc( 1, sizeof( struct ldapconn ) );
405 (*lcp)->lc_refcnt = 1;
408 if ( rs->sr_err != LDAP_SUCCESS ) {
409 rs->sr_err = slap_map_api2result( rs );
410 if ( sendok & LDAP_BACK_SENDERR ) {
411 if ( rs->sr_text == NULL ) {
412 rs->sr_text = "ldap_initialize() failed";
414 send_ldap_result( op, rs );
423 ldap_back_getconn( Operation *op, SlapReply *rs, ldap_back_send_t sendok )
425 struct ldapinfo *li = (struct ldapinfo *)op->o_bd->be_private;
426 struct ldapconn *lc, lc_curr = { 0 };
428 /* Searches for a ldapconn in the avl tree */
430 /* Explicit binds must not be shared */
431 if ( op->o_tag == LDAP_REQ_BIND
433 && op->o_conn->c_authz_backend
434 && op->o_bd->be_private == op->o_conn->c_authz_backend->be_private ) )
436 lc_curr.lc_conn = op->o_conn;
439 lc_curr.lc_conn = NULL;
442 /* Internal searches are privileged and shared. So is root. */
443 #ifdef LDAP_BACK_POOLED_CONNS
444 /* FIXME: there seem to be concurrency issues */
445 if ( op->o_do_not_cache || be_isroot( op ) ) {
446 lc_curr.lc_local_ndn = op->o_bd->be_rootndn;
447 lc_curr.lc_conn = NULL;
448 lc_curr.lc_ispriv = 1;
451 #endif /* LDAP_BACK_POOLED_CONNS */
453 lc_curr.lc_local_ndn = op->o_ndn;
457 switch ( ldap_pvt_thread_mutex_trylock( &li->conn_mutex ) ) {
458 case LDAP_PVT_THREAD_EBUSY:
460 ldap_pvt_thread_yield();
467 lc = (struct ldapconn *)avl_find( li->conntree,
468 (caddr_t)&lc_curr, ldap_back_conn_cmp );
472 ldap_pvt_thread_mutex_unlock( &li->conn_mutex );
474 /* Looks like we didn't get a bind. Open a new session... */
476 if ( ldap_back_prepare_conn( &lc, op, rs, sendok ) != LDAP_SUCCESS ) {
480 lc->lc_conn = lc_curr.lc_conn;
481 ber_dupbv( &lc->lc_local_ndn, &lc_curr.lc_local_ndn );
483 ldap_pvt_thread_mutex_init( &lc->lc_mutex );
485 if ( lc_curr.lc_ispriv ) {
486 ber_dupbv( &lc->lc_cred, &li->acl_passwd );
487 ber_dupbv( &lc->lc_bound_ndn, &li->acl_authcDN );
488 lc->lc_ispriv = lc_curr.lc_ispriv;
491 BER_BVZERO( &lc->lc_cred );
492 BER_BVZERO( &lc->lc_bound_ndn );
493 if ( op->o_conn && !BER_BVISEMPTY( &op->o_ndn )
494 && op->o_bd->be_private == op->o_conn->c_authz_backend->be_private )
496 ber_dupbv( &lc->lc_bound_ndn, &op->o_ndn );
502 /* Inserts the newly created ldapconn in the avl tree */
504 switch ( ldap_pvt_thread_mutex_trylock( &li->conn_mutex ) ) {
505 case LDAP_PVT_THREAD_EBUSY:
507 ldap_pvt_thread_yield();
514 assert( lc->lc_refcnt == 1 );
515 rs->sr_err = avl_insert( &li->conntree, (caddr_t)lc,
516 ldap_back_conn_cmp, ldap_back_conn_dup );
518 #if PRINT_CONNTREE > 0
519 myprint( li->conntree );
520 #endif /* PRINT_CONNTREE */
522 ldap_pvt_thread_mutex_unlock( &li->conn_mutex );
524 Debug( LDAP_DEBUG_TRACE,
525 "=>ldap_back_getconn: conn %p inserted (refcnt=%u)\n",
526 (void *)lc, lc->lc_refcnt, 0 );
528 /* Err could be -1 in case a duplicate ldapconn is inserted */
529 if ( rs->sr_err != 0 ) {
530 ldap_back_conn_free( lc );
531 rs->sr_err = LDAP_OTHER;
532 if ( op->o_conn && ( sendok & LDAP_BACK_SENDERR ) ) {
533 send_ldap_error( op, rs, LDAP_OTHER,
534 "internal server error" );
540 Debug( LDAP_DEBUG_TRACE,
541 "=>ldap_back_getconn: conn %p fetched (refcnt=%u)\n",
542 (void *)lc, lc->lc_refcnt, 0 );
549 ldap_back_release_conn(
552 struct ldapconn *lc )
554 struct ldapinfo *li = (struct ldapinfo *)op->o_bd->be_private;
557 switch ( ldap_pvt_thread_mutex_trylock( &li->conn_mutex ) ) {
558 case LDAP_PVT_THREAD_EBUSY:
560 ldap_pvt_thread_yield();
567 switch ( ldap_pvt_thread_mutex_trylock( &lc->lc_mutex ) ) {
568 case LDAP_PVT_THREAD_EBUSY:
570 ldap_pvt_thread_mutex_unlock( &li->conn_mutex );
571 ldap_pvt_thread_yield();
578 assert( lc->lc_refcnt > 0 );
580 ldap_pvt_thread_mutex_unlock( &lc->lc_mutex );
581 ldap_pvt_thread_mutex_unlock( &li->conn_mutex );
587 * Note: as the check for the value of lc->lc_bound was already here, I removed
588 * it from all the callers, and I made the function return the flag, so
589 * it can be used to simplify the check.
591 * Note: lc_mutex is locked; dolock indicates whether li->conn_mutex
592 * must be locked or not
595 ldap_back_dobind_int(
599 ldap_back_send_t sendok,
606 assert( retries >= 0 );
608 if ( !lc->lc_bound ) {
609 struct ldapinfo *li = (struct ldapinfo *)op->o_bd->be_private;
612 * FIXME: we need to let clients use proxyAuthz
613 * otherwise we cannot do symmetric pools of servers;
614 * we have to live with the fact that a user can
615 * authorize itself as any ID that is allowed
616 * by the authzTo directive of the "proxyauthzdn".
619 * NOTE: current Proxy Authorization specification
620 * and implementation do not allow proxy authorization
621 * control to be provided with Bind requests
624 * if no bind took place yet, but the connection is bound
625 * and the "idassert-authcDN" (or other ID) is set,
626 * then bind as the asserting identity and explicitly
627 * add the proxyAuthz control to every operation with the
628 * dn bound to the connection as control value.
629 * This is done also if this is the authrizing backend,
630 * but the "override" flag is given to idassert.
631 * It allows to use SASL bind and yet proxyAuthz users
633 if ( op->o_conn != NULL &&
634 !op->o_do_not_cache &&
635 ( BER_BVISNULL( &lc->lc_bound_ndn ) ||
636 ( li->idassert_flags & LDAP_BACK_AUTH_OVERRIDE ) ) )
638 (void)ldap_back_proxy_authz_bind( lc, op, rs );
642 #ifdef HAVE_CYRUS_SASL
643 if ( lc->lc_ispriv && li->acl_authmethod == LDAP_AUTH_SASL ) {
644 void *defaults = NULL;
646 #if 1 /* will deal with this later... */
647 if ( li->acl_secprops != NULL ) {
648 rc = ldap_set_option( lc->lc_ld,
649 LDAP_OPT_X_SASL_SECPROPS, li->acl_secprops);
651 if( rc != LDAP_OPT_SUCCESS ) {
652 Debug( LDAP_DEBUG_ANY, "Error: ldap_set_option "
653 "(%s,SECPROPS,\"%s\") failed!\n",
654 li->url, li->acl_secprops, 0 );
660 defaults = lutil_sasl_defaults( lc->lc_ld,
661 li->acl_sasl_mech.bv_val,
662 li->acl_sasl_realm.bv_val,
663 li->acl_authcID.bv_val,
664 li->acl_passwd.bv_val,
667 rs->sr_err = ldap_sasl_interactive_bind_s( lc->lc_ld,
668 li->acl_authcDN.bv_val,
669 li->acl_sasl_mech.bv_val, NULL, NULL,
670 LDAP_SASL_QUIET, lutil_sasl_interact,
673 lutil_sasl_freedefs( defaults );
675 rs->sr_err = slap_map_api2result( rs );
676 if ( rs->sr_err != LDAP_SUCCESS ) {
678 send_ldap_result( op, rs );
685 #endif /* HAVE_CYRUS_SASL */
688 rs->sr_err = ldap_sasl_bind( lc->lc_ld,
689 lc->lc_bound_ndn.bv_val,
690 LDAP_SASL_SIMPLE, &lc->lc_cred,
691 NULL, NULL, &msgid );
693 if ( rs->sr_err == LDAP_SERVER_DOWN ) {
697 switch ( ldap_pvt_thread_mutex_trylock( &li->conn_mutex ) ) {
698 case LDAP_PVT_THREAD_EBUSY:
700 ldap_pvt_thread_yield();
708 assert( lc->lc_refcnt > 0 );
709 if ( lc->lc_refcnt == 1 ) {
710 ldap_unbind_ext_s( lc->lc_ld, NULL, NULL );
713 /* lc here must be the regular lc, reset and ready for init */
714 rs->sr_err = ldap_back_prepare_conn( &lc, op, rs, sendok );
717 ldap_pvt_thread_mutex_unlock( &li->conn_mutex );
719 if ( rs->sr_err == LDAP_SUCCESS ) {
725 ldap_back_freeconn( op, lc );
726 rs->sr_err = slap_map_api2result( rs );
731 rc = ldap_back_op_result( lc, op, rs, msgid, sendok );
732 if ( rc == LDAP_SUCCESS ) {
743 ldap_back_dobind( struct ldapconn *lc, Operation *op, SlapReply *rs, ldap_back_send_t sendok )
747 ldap_pvt_thread_mutex_lock( &lc->lc_mutex );
748 rc = ldap_back_dobind_int( lc, op, rs, sendok, 1, 1 );
749 ldap_pvt_thread_mutex_unlock( &lc->lc_mutex );
757 * This is a callback used for chasing referrals using the same
758 * credentials as the original user on this session.
761 ldap_back_rebind( LDAP *ld, LDAP_CONST char *url, ber_tag_t request,
762 ber_int_t msgid, void *params )
764 struct ldapconn *lc = (struct ldapconn *)params;
766 /* FIXME: add checks on the URL/identity? */
768 return ldap_sasl_bind_s( ld, lc->lc_bound_ndn.bv_val,
769 LDAP_SASL_SIMPLE, &lc->lc_cred, NULL, NULL, NULL );
778 ldap_back_send_t sendok )
781 LDAPMessage *res = NULL;
784 #define ERR_OK(err) ((err) == LDAP_SUCCESS || (err) == LDAP_COMPARE_FALSE || (err) == LDAP_COMPARE_TRUE)
787 rs->sr_matched = NULL;
789 /* if the error recorded in the reply corresponds
790 * to a successful state, get the error from the
791 * remote server response */
792 if ( ERR_OK( rs->sr_err ) ) {
794 struct timeval tv = { 0, 0 };
797 /* if result parsing fails, note the failure reason */
798 switch ( ldap_result( lc->lc_ld, msgid, 1, &tv, &res ) ) {
801 tv.tv_usec = 100000; /* 0.1 s */
802 ldap_pvt_thread_yield();
806 ldap_get_option( lc->lc_ld, LDAP_OPT_ERROR_NUMBER,
811 /* otherwise get the result; if it is not
812 * LDAP_SUCCESS, record it in the reply
813 * structure (this includes
814 * LDAP_COMPARE_{TRUE|FALSE}) */
816 rc = ldap_parse_result( lc->lc_ld, res, &rs->sr_err,
817 &match, &text, NULL, NULL, 1 );
819 if ( rc != LDAP_SUCCESS ) {
825 /* if the error in the reply structure is not
826 * LDAP_SUCCESS, try to map it from client
828 if ( !ERR_OK( rs->sr_err ) ) {
829 rs->sr_err = slap_map_api2result( rs );
831 /* internal ops ( op->o_conn == NULL )
832 * must not reply to client */
833 if ( op->o_conn && !op->o_do_not_cache && match ) {
835 /* record the (massaged) matched
836 * DN into the reply structure */
837 rs->sr_matched = match;
841 ( ( sendok & LDAP_BACK_SENDOK )
842 || ( ( sendok & LDAP_BACK_SENDERR ) && rs->sr_err != LDAP_SUCCESS ) ) )
844 send_ldap_result( op, rs );
847 if ( rs->sr_matched != match ) {
848 free( (char *)rs->sr_matched );
850 rs->sr_matched = NULL;
851 ldap_memfree( match );
854 ldap_memfree( text );
857 return( ERR_OK( rs->sr_err ) ? 0 : -1 );
860 /* return true if bound, false if failed */
862 ldap_back_retry( struct ldapconn *lc, Operation *op, SlapReply *rs, ldap_back_send_t sendok )
865 struct ldapinfo *li = (struct ldapinfo *)op->o_bd->be_private;
868 switch ( ldap_pvt_thread_mutex_trylock( &li->conn_mutex ) ) {
869 case LDAP_PVT_THREAD_EBUSY:
871 ldap_pvt_thread_yield();
878 switch ( ldap_pvt_thread_mutex_trylock( &lc->lc_mutex ) ) {
879 case LDAP_PVT_THREAD_EBUSY:
881 ldap_pvt_thread_mutex_unlock( &li->conn_mutex );
882 ldap_pvt_thread_yield();
889 if ( lc->lc_refcnt == 1 ) {
890 ldap_unbind_ext_s( lc->lc_ld, NULL, NULL );
894 /* lc here must be the regular lc, reset and ready for init */
895 rc = ldap_back_prepare_conn( &lc, op, rs, sendok );
896 if ( rc == LDAP_SUCCESS ) {
897 rc = ldap_back_dobind_int( lc, op, rs, sendok, 0, 0 );
901 ldap_pvt_thread_mutex_unlock( &lc->lc_mutex );
902 ldap_pvt_thread_mutex_unlock( &li->conn_mutex );
908 ldap_back_proxy_authz_bind( struct ldapconn *lc, Operation *op, SlapReply *rs )
910 struct ldapinfo *li = (struct ldapinfo *)op->o_bd->be_private;
911 struct berval binddn = slap_empty_bv;
912 struct berval bindcred = slap_empty_bv;
918 * FIXME: we need to let clients use proxyAuthz
919 * otherwise we cannot do symmetric pools of servers;
920 * we have to live with the fact that a user can
921 * authorize itself as any ID that is allowed
922 * by the authzTo directive of the "proxyauthzdn".
925 * NOTE: current Proxy Authorization specification
926 * and implementation do not allow proxy authorization
927 * control to be provided with Bind requests
930 * if no bind took place yet, but the connection is bound
931 * and the "proxyauthzdn" is set, then bind as
932 * "proxyauthzdn" and explicitly add the proxyAuthz
933 * control to every operation with the dn bound
934 * to the connection as control value.
937 /* bind as proxyauthzdn only if no idassert mode
938 * is requested, or if the client's identity
940 switch ( li->idassert_mode ) {
941 case LDAP_BACK_IDASSERT_LEGACY:
942 if ( !BER_BVISNULL( &op->o_conn->c_ndn ) && !BER_BVISEMPTY( &op->o_conn->c_ndn ) ) {
943 if ( !BER_BVISNULL( &li->idassert_authcDN ) && !BER_BVISEMPTY( &li->idassert_authcDN ) )
945 binddn = li->idassert_authcDN;
946 bindcred = li->idassert_passwd;
953 /* NOTE: rootdn can always idassert */
954 if ( li->idassert_authz && !be_isroot( op ) ) {
955 struct berval authcDN;
957 if ( BER_BVISNULL( &op->o_conn->c_ndn ) ) {
958 authcDN = slap_empty_bv;
961 authcDN = op->o_conn->c_ndn;
963 rs->sr_err = slap_sasl_matches( op, li->idassert_authz,
964 &authcDN, &authcDN );
965 if ( rs->sr_err != LDAP_SUCCESS ) {
966 if ( li->idassert_flags & LDAP_BACK_AUTH_PRESCRIPTIVE ) {
967 send_ldap_result( op, rs );
971 rs->sr_err = LDAP_SUCCESS;
972 binddn = slap_empty_bv;
973 bindcred = slap_empty_bv;
981 binddn = li->idassert_authcDN;
982 bindcred = li->idassert_passwd;
987 if ( dobind && li->idassert_authmethod == LDAP_AUTH_SASL ) {
988 #ifdef HAVE_CYRUS_SASL
989 void *defaults = NULL;
990 struct berval authzID = BER_BVNULL;
993 /* if SASL supports native authz, prepare for it */
994 if ( ( !op->o_do_not_cache || !op->o_is_auth_check ) &&
995 ( li->idassert_flags & LDAP_BACK_AUTH_NATIVE_AUTHZ ) )
997 switch ( li->idassert_mode ) {
998 case LDAP_BACK_IDASSERT_OTHERID:
999 case LDAP_BACK_IDASSERT_OTHERDN:
1000 authzID = li->idassert_authzID;
1003 case LDAP_BACK_IDASSERT_ANONYMOUS:
1004 BER_BVSTR( &authzID, "dn:" );
1007 case LDAP_BACK_IDASSERT_SELF:
1008 if ( BER_BVISNULL( &op->o_conn->c_ndn ) ) {
1009 /* connection is not authc'd, so don't idassert */
1010 BER_BVSTR( &authzID, "dn:" );
1013 authzID.bv_len = STRLENOF( "dn:" ) + op->o_conn->c_ndn.bv_len;
1014 authzID.bv_val = slap_sl_malloc( authzID.bv_len + 1, op->o_tmpmemctx );
1015 AC_MEMCPY( authzID.bv_val, "dn:", STRLENOF( "dn:" ) );
1016 AC_MEMCPY( authzID.bv_val + STRLENOF( "dn:" ),
1017 op->o_conn->c_ndn.bv_val, op->o_conn->c_ndn.bv_len + 1 );
1026 #if 0 /* will deal with this later... */
1027 if ( sasl_secprops != NULL ) {
1028 rs->sr_err = ldap_set_option( lc->lc_ld, LDAP_OPT_X_SASL_SECPROPS,
1029 (void *) sasl_secprops );
1031 if ( rs->sr_err != LDAP_OPT_SUCCESS ) {
1032 send_ldap_result( op, rs );
1039 defaults = lutil_sasl_defaults( lc->lc_ld,
1040 li->idassert_sasl_mech.bv_val,
1041 li->idassert_sasl_realm.bv_val,
1042 li->idassert_authcID.bv_val,
1043 li->idassert_passwd.bv_val,
1046 rs->sr_err = ldap_sasl_interactive_bind_s( lc->lc_ld, binddn.bv_val,
1047 li->idassert_sasl_mech.bv_val, NULL, NULL,
1048 LDAP_SASL_QUIET, lutil_sasl_interact,
1051 lutil_sasl_freedefs( defaults );
1053 slap_sl_free( authzID.bv_val, op->o_tmpmemctx );
1056 rs->sr_err = slap_map_api2result( rs );
1057 if ( rs->sr_err != LDAP_SUCCESS ) {
1059 send_ldap_result( op, rs );
1065 #endif /* HAVE_CYRUS_SASL */
1068 switch ( li->idassert_authmethod ) {
1069 case LDAP_AUTH_SIMPLE:
1070 rs->sr_err = ldap_sasl_bind( lc->lc_ld,
1071 binddn.bv_val, LDAP_SASL_SIMPLE,
1072 &bindcred, NULL, NULL, &msgid );
1075 case LDAP_AUTH_NONE:
1082 rs->sr_err = LDAP_AUTH_METHOD_NOT_SUPPORTED;
1083 send_ldap_result( op, rs );
1087 rc = ldap_back_op_result( lc, op, rs, msgid, LDAP_BACK_SENDERR );
1088 if ( rc == LDAP_SUCCESS ) {
1092 return lc->lc_bound;
1096 * ldap_back_proxy_authz_ctrl() prepends a proxyAuthz control
1097 * to existing server-side controls if required; if not,
1098 * the existing server-side controls are placed in *pctrls.
1099 * The caller, after using the controls in client API
1100 * operations, if ( *pctrls != op->o_ctrls ), should
1101 * free( (*pctrls)[ 0 ] ) and free( *pctrls ).
1102 * The function returns success if the control could
1103 * be added if required, or if it did nothing; in the future,
1104 * it might return some error if it failed.
1106 * if no bind took place yet, but the connection is bound
1107 * and the "proxyauthzdn" is set, then bind as "proxyauthzdn"
1108 * and explicitly add proxyAuthz the control to every operation
1109 * with the dn bound to the connection as control value.
1111 * If no server-side controls are defined for the operation,
1112 * simply add the proxyAuthz control; otherwise, if the
1113 * proxyAuthz control is not already set, add it as
1116 * FIXME: is controls order significant for security?
1117 * ANSWER: controls ordering and interoperability
1118 * must be indicated by the specs of each control; if none
1119 * is specified, the order is irrelevant.
1122 ldap_back_proxy_authz_ctrl(
1123 struct ldapconn *lc,
1126 LDAPControl ***pctrls )
1128 struct ldapinfo *li = (struct ldapinfo *) op->o_bd->be_private;
1129 LDAPControl **ctrls = NULL;
1132 struct berval assertedID;
1136 rs->sr_err = LDAP_SUCCESS;
1138 /* FIXME: SASL/EXTERNAL over ldapi:// doesn't honor the authcID,
1139 * but if it is not set this test fails. We need a different
1140 * means to detect if idassert is enabled */
1141 if ( ( BER_BVISNULL( &li->idassert_authcID ) || BER_BVISEMPTY( &li->idassert_authcID ) )
1142 && ( BER_BVISNULL( &li->idassert_authcDN ) || BER_BVISEMPTY( &li->idassert_authcDN ) ) )
1147 if ( !op->o_conn || op->o_do_not_cache || be_isroot( op ) ) {
1151 if ( li->idassert_mode == LDAP_BACK_IDASSERT_LEGACY ) {
1152 if ( op->o_proxy_authz ) {
1154 * FIXME: we do not want to perform proxyAuthz
1155 * on behalf of the client, because this would
1156 * be performed with "proxyauthzdn" privileges.
1158 * This might actually be too strict, since
1159 * the "proxyauthzdn" authzTo, and each entry's
1160 * authzFrom attributes may be crafted
1161 * to avoid unwanted proxyAuthz to take place.
1164 rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
1165 rs->sr_text = "proxyAuthz not allowed within namingContext";
1170 if ( !BER_BVISNULL( &lc->lc_bound_ndn ) ) {
1174 if ( BER_BVISNULL( &op->o_conn->c_ndn ) ) {
1178 if ( BER_BVISNULL( &li->idassert_authcDN ) ) {
1182 } else if ( li->idassert_authmethod == LDAP_AUTH_SASL ) {
1183 if ( ( li->idassert_flags & LDAP_BACK_AUTH_NATIVE_AUTHZ )
1184 /* && ( !BER_BVISNULL( &op->o_conn->c_ndn ) || lc->lc_bound ) */ )
1186 /* already asserted in SASL via native authz */
1187 /* NOTE: the test on lc->lc_bound is used to trap
1188 * native authorization of anonymous users,
1189 * since in that case op->o_conn->c_ndn is NULL */
1193 } else if ( li->idassert_authz && !be_isroot( op ) ) {
1195 struct berval authcDN;
1197 if ( BER_BVISNULL( &op->o_conn->c_ndn ) ) {
1198 authcDN = slap_empty_bv;
1200 authcDN = op->o_conn->c_ndn;
1202 rc = slap_sasl_matches( op, li->idassert_authz,
1203 &authcDN, & authcDN );
1204 if ( rc != LDAP_SUCCESS ) {
1205 if ( li->idassert_flags & LDAP_BACK_AUTH_PRESCRIPTIVE )
1207 /* op->o_conn->c_ndn is not authorized
1208 * to use idassert */
1215 if ( op->o_proxy_authz ) {
1218 * 1) ignore the already set proxyAuthz control
1219 * 2) leave it in place, and don't set ours
1221 * 4) reject the operation
1223 * option (4) is very drastic
1224 * option (3) will make the remote server reject
1225 * the operation, thus being equivalent to (4)
1226 * option (2) will likely break the idassert
1227 * assumptions, so we cannot accept it;
1228 * option (1) means that we are contradicting
1229 * the client's reques.
1231 * I think (4) is the only correct choice.
1233 rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
1234 rs->sr_text = "proxyAuthz not allowed within namingContext";
1237 if ( op->o_do_not_cache && op->o_is_auth_check ) {
1238 mode = LDAP_BACK_IDASSERT_NOASSERT;
1241 mode = li->idassert_mode;
1245 case LDAP_BACK_IDASSERT_LEGACY:
1246 case LDAP_BACK_IDASSERT_SELF:
1247 /* original behavior:
1248 * assert the client's identity */
1249 if ( BER_BVISNULL( &op->o_conn->c_ndn ) ) {
1250 assertedID = slap_empty_bv;
1252 assertedID = op->o_conn->c_ndn;
1256 case LDAP_BACK_IDASSERT_ANONYMOUS:
1257 /* assert "anonymous" */
1258 assertedID = slap_empty_bv;
1261 case LDAP_BACK_IDASSERT_NOASSERT:
1262 /* don't assert; bind as proxyauthzdn */
1265 case LDAP_BACK_IDASSERT_OTHERID:
1266 case LDAP_BACK_IDASSERT_OTHERDN:
1267 /* assert idassert DN */
1268 assertedID = li->idassert_authzID;
1275 if ( BER_BVISNULL( &assertedID ) ) {
1276 assertedID = slap_empty_bv;
1279 if ( op->o_ctrls ) {
1280 for ( i = 0; op->o_ctrls[ i ]; i++ )
1281 /* just count ctrls */ ;
1284 ctrls = ch_malloc( sizeof( LDAPControl * ) * (i + 2) );
1285 ctrls[ 0 ] = ch_malloc( sizeof( LDAPControl ) );
1287 ctrls[ 0 ]->ldctl_oid = LDAP_CONTROL_PROXY_AUTHZ;
1288 ctrls[ 0 ]->ldctl_iscritical = 1;
1290 switch ( li->idassert_mode ) {
1291 /* already in u:ID or dn:DN form */
1292 case LDAP_BACK_IDASSERT_OTHERID:
1293 case LDAP_BACK_IDASSERT_OTHERDN:
1294 ber_dupbv( &ctrls[ 0 ]->ldctl_value, &assertedID );
1297 /* needs the dn: prefix */
1299 ctrls[ 0 ]->ldctl_value.bv_len = assertedID.bv_len + STRLENOF( "dn:" );
1300 ctrls[ 0 ]->ldctl_value.bv_val = ch_malloc( ctrls[ 0 ]->ldctl_value.bv_len + 1 );
1301 AC_MEMCPY( ctrls[ 0 ]->ldctl_value.bv_val, "dn:", STRLENOF( "dn:" ) );
1302 AC_MEMCPY( &ctrls[ 0 ]->ldctl_value.bv_val[ STRLENOF( "dn:" ) ],
1303 assertedID.bv_val, assertedID.bv_len + 1 );
1307 if ( op->o_ctrls ) {
1308 for ( i = 0; op->o_ctrls[ i ]; i++ ) {
1309 ctrls[ i + 1 ] = op->o_ctrls[ i ];
1312 ctrls[ i + 1 ] = NULL;
1315 if ( ctrls == NULL ) {
1316 ctrls = op->o_ctrls;
1325 ldap_back_proxy_authz_ctrl_free( Operation *op, LDAPControl ***pctrls )
1327 LDAPControl **ctrls = *pctrls;
1329 /* we assume that the first control is the proxyAuthz
1330 * added by back-ldap, so it's the only one we explicitly
1332 if ( ctrls && ctrls != op->o_ctrls ) {
1333 assert( ctrls[ 0 ] != NULL );
1335 if ( !BER_BVISNULL( &ctrls[ 0 ]->ldctl_value ) ) {
1336 free( ctrls[ 0 ]->ldctl_value.bv_val );