Hello.
This is a kind of follow-up on my previous message "Trouble with
slapd-ldap in various scenarios (LdarErr: DSID-0C090627)" sent to
OpenLDAP-Technical. I did more research on topic including a code
debugging. I finally found the reason why it doesn't work and fixed the
code.
I have to use slapd-ldap in "strong bind" mode which means that user
binds using its own credentials and no identity assertion is performed.
If slapd-ldap is the only module that is processing request then
everything works fine. If slapd-ldap is processing an request forwarded
from slapd-relay (with slapo-rwm) or request to database with
slapo-translucent then the authentication problem occurs.
Let's suppose that we have remote LDAP server with disabled anonymous
binding. It could be Active Directory or OpenLDAP with following
configuration:
# --- config for remote server (remote.host.net) ---
# [cut-off] command schema, slapd files & logs definitions
# Load of modules:
modulepath /usr/lib/ldap
moduleload back_hdb.la
# VERY IMPORTANT: Disable anonymous binding
disallow bind_anon
# Configuration Database
database config
rootdn "cn=admin,cn=config"
rootpw secret
# realone.net - local Repository
database hdb
suffix "dc=realone,dc=net"
rootdn "cn=admin,dc=realone,dc=net"
rootpw secret
directory /var/lib/slapd/realone.net
index objectClass eq
index cn eq
index uid eq
Let's say there is a common content in database:
# --- Directory content (just an overview) ---
dn: dc=realone,dc=net # top, domain
dn: ou=people,dc=realone,dc=net # organizationalUnit
dn: ou=groups,dc=realone,dc=net # organizationalUnit
dn: cn=idassert,dc=realone,dc=net # simpleSecurityObject,
organizationalRole
dn: cn=admin,dc=realone,dc=net # same as above
dn: uid=super.account,ou=people,dc=realone,dc=net # inetOrgPerson,
posixAccount, shadowAccount
dn: uid=power.account,ou=people,dc=realone,dc=net # same as above
dn: uid=basic.account,ou=people,dc=realone,dc=net # same as above
dn: cn=admins,ou=groups,dc=realone,dc=net # posixGroup
dn: cn=users,ou=groups,dc=realone,dc=net # same as above
I want to use another OpenLDAP server that is configured with
slapo-translucent:
# --- config for local server (trans.local.net) ---
# [cut-off] command schema, slapd files & logs definitions
# Configuration Database
database config
rootdn "cn=admin,cn=config"
rootpw secret
# realone.net - local database extending the remote one
database bdb
suffix "dc=realone,dc=net"
directory ./database/realone.net
index objectClass eq
index cn eq
index uid eq
overlay translucent
uri ldap://remote.host.net:389/
chase-referrals true
rebind-as-user true
... or with slapd-relay (this is probably very very very rare use case
but I used it for debugging):
# --- config for local server (relay.local.net) ---
# [cut-off] command
schema, slapd files & logs definitions
# Configuration Database
database config
rootdn "cn=admin,cn=config"
rootpw secret
# domain (forwarded to remote repository)
database ldap
suffix "dc=realone,dc=net"
uri ldap://remote.host.net
chase-referrals yes
# domain alias
database relay
suffix "dc=virtual,dc=net"
relay "dc=realone,dc=net"
overlay rwm
rwm-suffixmassage "dc=virtual,dc=net" "dc=realone,dc=net"
Now, we can do usual search:
ldapsearch -x -w secret -h <host> -D <bindDN> -b <baseDN> -s sub
"(objectClass=inetOrgPerson)" dn displayName mail
(in my environment I should receive 3 records below
ou=people,dc=realone,dc=net).
But the story is getting interesting here. The command works only in
cases that slapd-ldap is used dirrectly:
===================================================================================================
# | Host | BindDN | BaseDN | Result
--+-----------------+----------------------------+-------------------+-----------------------------
1 | remote.host.net | cn=admin,dc=realone,dc=net | dc=realone,dc=net | Success
(3 records)
--+-----------------+----------------------------+-------------------+-----------------------------
2 | trans.local.net | cn=admin,dc=realone,dc=net | dc=realone,dc=net | Failure:
Anon.bind.n.allwd.
--+-----------------+----------------------------+-------------------+-----------------------------
3 | relay.local.net | cn=admin,dc=realone,dc=net | dc=realone,dc=net | Success
(3 records)
--+-----------------+----------------------------+-------------------+-----------------------------
4 | relay.local.net | cn=admin,dc=realone,dc=net | dc=virtual,dc=net | Success
(3 records) !!!!
--+-----------------+----------------------------+-------------------+-----------------------------
5 | relay.local.net | cn=admin,dc=virtual,dc=net | dc=virtual,dc=net | Failure:
Anon.bind.n.allwd.
===================================================================================================
The failures of case #2 and #5 are not expected because the connection
to remote server is handled by slapd-ldap configured the same way like
in case 3. The success in case #3 is correct (although I didn't expected
it) and it was guide to solution. I noticed that each time the request
fail the LDAP connection is correctly opened and bind request is
successful. But the search request was processed using a new connection
that was created for it and anonymous bind executed with failure.
While debugging, I found that ldap_back_getconn() is called by
ldap_back_bind() as well as ldap_back_search() request handlers. For
operations other then op_bind it tests whether connection associated
with Operation (op->o_hdr->oh_conn) was used for authentication in
previous step. If slapd-ldap is NOT using identity assertion then it
checks Operation using SLAP_IS_AUTHZ_BACKEND macro whether
op->o_conn->c_authz_backend is not NULL and is equal to current backend.
This test works fine is the slapd-ldap backend is the only which
processes the LDAP request because op->o_conn->c_authz_backend is set in
the binding handler of the frontend module - fe_op_bind(). In this, case
the test succeeds and connection created during the binding is used also
for searching.
If the slapd-ldap module is used as nested module in slapo-translucent
(case #2), then the fe_op_bind() sets variable
op->o_conn->c_authz_backend to the backend extended by
slapo-translucent. Macro SLAP_IS_AUTHZ_BACKEND returns false and a new
LDAP connection is allocated causing failure due anonymouse binding.
If the slapd-ldap module is target for slapd-relay then it behaves
similar way. In case #5 the bind request is forwarded to slapd-ldap but
slapd-relay is associated with connection as the backend who did the
bind (op->o_conn->c_authz_backend) and subsequent search request fails
the same way like above.
The success in case #4 is a kind of singularity ;-) - bind request is
forwarded directly from frontend to slapd-ldap and therefore
op->o_conn->c_authz_backend is set correctly, the subsequent search
request goes first to slapd-relay which forwards it to slapd-ldap but
SLAP_IS_AUTHZ_BACKEND test succeeds.
I attached unified diffs of changes I made to OpenLDAP (2.4.18) source
code to make it working (fix for back-relay/op.c was much more easier in
2.4.15).
It looks like everyone uses the identity assertion mechanism because it
make the above mentioned use cases working in certain configurations.
Unfortunately, it's not a solution for me to use identity assertion.
Could someone confirm my findings?
Martin Rubas
--- openldap/servers/slapd/overlays/translucent.2_4_15.c Wed Sep 16
16:27:33 2009
+++ openldap/servers/slapd/overlays/translucent.custom.c Wed Sep 16
16:27:34 2009
@@ -1199,6 +1199,10 @@
op->o_bd = &ov->db;
ov->db.be_acl = op->o_bd->be_acl;
rc = ov->db.bd_info->bi_op_bind(op, rs);
+ if (0 == rc && 0 == rs->sr_err
+ && op->o_hdr->oh_conn && !op->o_hdr->oh_conn->c_authz_backend) {
+ op->o_hdr->oh_conn->c_authz_backend = op->o_bd;
+ }
op->o_bd = db;
if (ov->bind_local) {
--- openldap/servers/slapd/back-relay/op.2_4_15.c Wed Sep 16 16:55:08 2009
+++ openldap/servers/slapd/back-relay/op.custom.c Wed Sep 16 16:55:09 2009
@@ -170,6 +170,7 @@
relay_back_op_bind( Operation *op, SlapReply *rs )
{
BackendDB *bd;
+ int rc;
/* allow rootdn as a means to auth without the need to actually
* contact the proxied DSA */
@@ -187,8 +188,14 @@
return rs->sr_err;
}
- return relay_back_op( op, rs, bd, bd->be_bind,
+ rc = relay_back_op( op, rs, bd, bd->be_bind,
( LDAP_INVALID_CREDENTIALS | RB_ERR_SEND ) );
+
+ if (0 == rc && 0 == rs->sr_err
+ && op->o_hdr->oh_conn && !op->o_hdr->oh_conn->c_authz_backend) {
+ op->o_hdr->oh_conn->c_authz_backend = bd;
+ }
+ return rc;
}
int
--- openldap/servers/slapd/back-relay/op.2_4_18.c Wed Sep 16 17:34:54 2009
+++ openldap/servers/slapd/back-relay/op.custom.c Wed Sep 09 14:36:14 2009
@@ -208,11 +208,6 @@
if ( op->o_callback == (slap_callback *) &rcb ) {
op->o_callback = op->o_callback->sc_next;
}
- if (op_bind == which && 0 == rc && 0 == rs->sr_err
- && op->o_hdr->oh_conn &&
!op->o_hdr->oh_conn->c_authz_backend) {
- op->o_hdr->oh_conn->c_authz_backend = bd;
- }
-
} else if ( fail_mode & RB_OPERR ) {
rs->sr_err = rc;