Hi,
We have some problems with certificate authentication when the master server is behind a back-ldap proxy.
We have openldap 2.4.21 on Suse Linux Enterprise Server 10 SP3 and these are the details of our scenario:
The master server: server1.example.com has the following slapd.conf file:
access to dn.base="" by * read
access to dn.base="cn=Subschema" by * read
access to attrs=userPassword,userPKCS12 by self write by dn.exact="CN=admin_w_cert,O=Internet Widgits Pty Ltd,ST=Some-State,C=AU" read by * auth
access to attrs=shadowLastChange by self write by * read
access to * by * read
# # Security SSL # TLSCipherSuite HIGH:MEDIUM:+SSLv3 TLSCertificateFile /etc/ssl/certs/server1.example.com.pem TLSCertificateKeyFile /etc/ssl/private/server1.example.com.key TLSCACertificatePath /etc/ssl/cacerts/ TLSVerifyClient demand
# #Log level # loglevel 256
# Require authentication require authc
####################################################################### # HDB database definitions #######################################################################
database hdb suffix "dc=example,dc=com" checkpoint 1024 5 cachesize 10000 rootdn "cn=Manager,dc=example,dc=com"
rootpw secret
# Indices to maintain index objectClass eq
# Overlay ppolicy overlay ppolicy
Authentication is required, and we give access to the user passwords for the dn of a certificate.
When we search for passwords using the certificate we get the following:
root# ldapsearch -LLL -b 'uid=user_w_pass,ou=people,dc=example,dc=com' -H ldaps://server1.example.com userPassword
SASL/EXTERNAL authentication started SASL username: CN=admin_w_cert,O=Internet Widgits Pty Ltd,ST=Some-State,C=AU SASL SSF: 0 dn: uid=user_w_pass,ou=people,dc=example,dc=com userPassword:: e2NyeXB0fTcyMXpQbU4waWdKaU0=
The root user (ldap client) has a ~/.ldaprc file with: TLS_CACERTDIR /etc/ssl/cacerts/ TLS_CERT /etc/ssl/certs/admin_w_cert.pem TLS_KEY /etc/ssl/private/admin_w_cert.key TLS_REQCERT demand SASL_MECH EXTERNAL
In /var/log/messages we get: ldap-master[22358]: conn=1000 fd=11 ACCEPT from IP=server1.example.com:40899 (IP=server1.example.com:636) ldap-master[22358]: conn=1000 fd=11 TLS established tls_ssf=256 ssf=256 ldap-master[22358]: conn=1000 op=0 BIND dn="" method=163 ldap-master[22358]: conn=1000 op=0 BIND authcid="cn=admin_w_cert,o=internet widgits pty ltd,st=some-state,c=au" authzid="cn=admin_w_cert,o=internet widgits pty ltd,st=some-state,c=au" ldap-master[22358]: conn=1000 op=0 BIND dn="cn=admin_w_cert,o=internet widgits pty ltd,st=some-state,c=au" mech=EXTERNAL sasl_ssf=0 ssf=256 ldap-master[22358]: conn=1000 op=0 RESULT tag=97 err=0 text= ldap-master[22358]: conn=1000 op=1 SRCH base="uid=user_w_pass,ou=people,dc=example,dc=com" scope=2 deref=0 filter="(objectClass=*)" ldap-master[22358]: conn=1000 op=1 SRCH attr=userPassword ldap-master[22358]: conn=1000 op=1 SEARCH RESULT tag=101 err=0 nentries=1 text= ldap-master[22358]: conn=1000 op=2 UNBIND ldap-master[22358]: conn=1000 fd=11 closed
This is the correct behavior for us. The problem appears when we introduce a back-ldap proxy between the client and the master. The proxy server (proxy-server1.example.com) is listening in port 1636 and its slapd.conf file is:
# # Security SSL # TLSCipherSuite HIGH:MEDIUM:+SSLv3 TLSCACertificatePath /etc/ssl/cacerts/ TLSCertificateFile /etc/ssl/certs/proxy-server1.example.com.pem TLSCertificateKeyFile /etc/ssl/private/proxy-server1.example.com.key TLSVerifyClient demand
# Log level loglevel 256
####################################################################### # Database definitions ####################################################################### database ldap
rebind-as-user true
suffix "dc=example,dc=com"
uri "ldaps://server1.example.com" tls ldaps tls_cert=/etc/ssl/certs/proxy-server1.example.com.pem tls_key=/etc/ssl/private/proxy-server1.example.com.key tls_cacertdir=/etc/ssl/cacerts/
If we search for passwords through the proxy we get: root # ldapsearch -LLL -b 'uid=user_w_pass,ou=people,dc=example,dc=com' -H ldaps://proxy-server1.example.com:1636 userPassword
SASL/EXTERNAL authentication started SASL username: CN=admin_w_cert,O=Internet Widgits Pty Ltd,ST=Some-State,C=AU SASL SSF: 0 Server is unwilling to perform (53) Additional information: authentication required
In the /var/log/messages the following messages appear:
ldap-proxy[22802]: conn=1001 fd=8 ACCEPT from IP=proxy-server1.example.com:60712 (IP=proxy-server1.example.com:1636) ldap-proxy[22802]: conn=1001 fd=8 TLS established tls_ssf=256 ssf=256 ldap-proxy[22802]: conn=1001 op=0 BIND dn="" method=163 ldap-proxy[22802]: conn=1001 op=0 BIND authcid="cn=admin_w_cert,o=internet widgits pty ltd,st=some-state,c=au" authzid="cn=admin_w_cert,o=internet widgits pty ltd,st=some-state,c=au" ldap-proxy[22802]: conn=1001 op=0 BIND dn="cn=admin_w_cert,o=internet widgits pty ltd,st=some-state,c=au" mech=EXTERNAL sasl_ssf=0 ssf=256 ldap-proxy[22802]: conn=1001 op=0 RESULT tag=97 err=0 text= ldap-proxy[22802]: conn=1001 op=1 SRCH base="uid=user_w_pass,ou=people,dc=example,dc=com" scope=2 deref=0 filter="(objectClass=*)" ldap-proxy[22802]: conn=1001 op=1 SRCH attr=userPassword
ldap-master[22358]: conn=1008 op=2 SRCH base="uid=user_w_pass,ou=people,dc=example,dc=com" scope=2 deref=0 filter="(objectClass=*)" ldap-master[22358]: conn=1008 op=2 SRCH attr=userPassword ldap-master[22358]: conn=1008 op=2 SEARCH RESULT tag=101 err=53 nentries=0 text=authentication required
ldap-proxy[22802]: conn=1001 op=1 SEARCH RESULT tag=101 err=53 nentries=0 text=authentication required ldap-proxy[22802]: conn=1001 op=2 UNBIND ldap-proxy[22802]: conn=1001 fd=8 closed
The /root/.ldaprc file is the same than the previous one.
When we increase the logging level we discover this: .... ldap-proxy[23008]: conn=1000 op=0 do_bind ldap-proxy[23008]: >>> dnPrettyNormal: <> ldap-proxy[23008]: <<< dnPrettyNormal: <>, <> ldap-proxy[23008]: conn=1000 op=0 BIND dn="" method=163 ldap-proxy[23008]: do_bind: dn () SASL mech EXTERNAL ldap-proxy[23008]: ==> sasl_bind: dn="" mech=EXTERNAL datalen=0 ldap-proxy[23008]: SASL Canonicalize [conn=1000]: authcid="cn=admin_w_cert,o=internet widgits pty ltd,st=some-state,c=au" ldap-proxy[23008]: slap_sasl_getdn: conn 1000 id=cn=admin_w_cert,o=internet widgits pty ltd,st=some-state,c=au [len=61] ldap-proxy[23008]: ==>slap_sasl2dn: converting SASL name cn=admin_w_cert,o=internet widgits pty ltd,st=some-state,c=au to a DN ldap-proxy[23008]: <==slap_sasl2dn: Converted SASL name to <nothing> ldap-proxy[23008]: SASL Canonicalize [conn=1000]: slapAuthcDN="cn=admin_w_cert,o=internet widgits pty ltd,st=some-state,c=au" ldap-proxy[23008]: SASL proxy authorize [conn=1000]: authcid="cn=admin_w_cert,o=internet widgits pty ltd,st=some-state,c=au" authzid="cn=admin_w_cert,o=internet widgits pty ltd,st=some-state,c=au" ldap-proxy[23008]: conn=1000 op=0 BIND authcid="cn=admin_w_cert,o=internet widgits pty ltd,st=some-state,c=au" authzid="cn=admin_w_cert,o=internet widgits pty ltd,st=some-state,c=au" ldap-proxy[23008]: SASL Authorize [conn=1000]: proxy authorization allowed authzDN="" ldap-proxy[23008]: send_ldap_sasl: err=0 len=-1 ldap-proxy[23008]: conn=1000 op=0 BIND dn="cn=admin_w_cert,o=internet widgits pty ltd,st=some-state,c=au" mech=EXTERNAL sasl_ssf=0 ssf=256 ldap-proxy[23008]: do_bind: SASL/EXTERNAL bind: dn="cn=admin_w_cert,o=internet widgits pty ltd,st=some-state,c=au" sasl_ssf=0 ldap-proxy[23008]: send_ldap_response: msgid=1 tag=97 err=0 ldap-proxy[23008]: conn=1000 op=0 RESULT tag=97 err=0 text= ldap-proxy[23008]: <== slap_sasl_bind: rc=0 .... ldap-proxy[23008]: conn=1000 op=1 SRCH base="uid=user_w_pass,ou=people,dc=example,dc=com" scope=2 deref=0 filter="(objectClass=*)" ldap-proxy[23008]: conn=1000 op=1 SRCH attr=userPassword ldap-proxy[23008]: ==> limits_get: conn=1000 op=1 self="cn=admin_w_cert,o=internet widgits pty ltd,st=some-state,c=au" this="uid=user_w_pass,ou=people,dc=example,dc=com" ldap-master[22983]: daemon: activity on 1 descriptor ldap-master[22983]: daemon: activity on: ldap-master[22983]: ldap-master[22983]: slap_listener_activate(7): ldap-master[22983]: daemon: epoll: listen=7 busy ldap-master[22983]: >>> slap_listener(ldaps://server1.example.com) ..... ldap-master[22983]: conn=1000 op=0 do_bind ldap-master[22983]: >>> dnPrettyNormal: <> ldap-master[22983]: <<< dnPrettyNormal: <>, <> ldap-master[22983]: conn=1000 op=0 BIND dn="" method=128 ldap-master[22983]: do_bind: version=3 dn="" method=128 ldap-master[22983]: send_ldap_result: conn=1000 op=0 p=3 ldap-master[22983]: send_ldap_result: err=0 matched="" text="" ldap-master[22983]: send_ldap_response: msgid=1 tag=97 err=0 ldap-master[22983]: conn=1000 op=0 RESULT tag=97 err=0 text= ldap-master[22983]: do_bind: v3 anonymous bind
Therefore the proxy is binding anonymously in the master, instead of using the dn of the certificate.
Is there any problem with the SASL EXTERNAL method?
If we use SIMPLE authentication through the proxy, there is no problem: root # ldapsearch -LLL -x -b 'uid=user_w_pass,ou=people,dc=example,dc=com' -H ldaps://proxy-server1.example.com:1636 -D 'uid=user_w_pass,ou=people,dc=example,dc=com' -W userPassword Enter LDAP Password:
dn: uid=user_w_pass,ou=people,dc=example,dc=com userPassword:: e2NyeXB0fTcyMXpQbU4waWdKaU0=
The problem is that you probably do not realize that the proxy cannot do a cert-based authentication on behalf of the client because it doesn't have the client's private key (which is correct). You need the proxy perform an identity assertion: bind to the remote server with its own identity, and then assert the client's identity using proxy authorization.
To do this, you need to:
a) define some means for the proxy to bind to the remote server, e.g. using cert-based SASL EXTERNAL, or simple bind under TLS, or whatever;
b) configure the remote server so that the proxy's identity defined in (a) is allowed to proxy authz as whatever client's identity you want to accept; this requires to use the directive "authz-policy"; you may need to use the "authz-regexp" if you intend to map the client's identity; and you'll need to populate the "authzTo" operational attribute of the entry corresponding to the proxy's identity.
c) add to the proxy configuration the directive
idassert-bind bindmethod=<what you chose for (a)> <bind parameters for (a)> mode=self
This way, the proxy will:
- authc the client locally
- authc as itself with respect to the remote host
- proxy operations adding the proxyAuthz control with the identity of the client
See slapd-ldap(5) for details on the syntax of the idassert-* directives.
p.