Bug 48780 - Enable mod_authnz_ldap to accept valid client certificates as sufficient authentication
Summary: Enable mod_authnz_ldap to accept valid client certificates as sufficient auth...
Status: RESOLVED LATER
Alias: None
Product: Apache httpd-2
Classification: Unclassified
Component: mod_authn_ldap (show other bugs)
Version: 2.2.14
Hardware: All All
: P2 enhancement with 6 votes (vote)
Target Milestone: ---
Assignee: Apache HTTPD Bugs Mailing List
URL:
Keywords: MassUpdate
Depends on:
Blocks:
 
Reported: 2010-02-19 22:25 UTC by Peter Thomas
Modified: 2018-11-07 21:08 UTC (History)
2 users (show)



Attachments
Proposed patch (2.72 KB, patch)
2010-02-19 22:26 UTC, Peter Thomas
Details | Diff
Updated patch that implements AuthType Certificate (12.81 KB, patch)
2010-03-08 23:52 UTC, Peter Thomas
Details | Diff
Improved Certificate provider for 2.2.x (17.19 KB, patch)
2010-04-06 23:28 UTC, Peter Thomas
Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Peter Thomas 2010-02-19 22:25:15 UTC
Some directory implementations do not require users to bind to the LDAP server.  Client authentication is accepted upon presentation of a valid SSL certificate.

The proposed enhancement adds a configuration directive to mod_authnz_ldap to  perform the LDAP query for the user's DN without first binding as the user.
Comment 1 Peter Thomas 2010-02-19 22:26:40 UTC
Created attachment 25026 [details]
Proposed patch
Comment 2 Eric Covener 2010-02-21 17:02:28 UTC
IIUC current patch needs to deal with the case where a cert wasn't provided (SSLVerifyClient optional) or where r->user didn't come from the cert.

IMO cert-based authn or authz belongs outside of LDAP.  LDAP could have a second provider that just checks to make sure current r->user can be found in LDAP w/o looking at password (which is the additional check we're getting with this patch over just clientcert == authenticated)
Comment 3 Peter Thomas 2010-02-22 14:59:03 UTC
(In reply to comment #2)
> IIUC current patch needs to deal with the case where a cert wasn't provided
> (SSLVerifyClient optional) or where r->user didn't come from the cert.

If SSLVerifyClient is optional or none we could have a case where the user did not provide a certificate, but instead entered a DN via a regular basic authentication dialog or other basic authn provider.  This might be useful for testing or debugging, but should raise a warning at a minimum.  I've create a similar patch to Joomla in PHP, but am new to the Apache API--I'm certainly open to augmented patches that add such checks and warnings.

We also should have a warning if SSLVerifyClient is generous and the client cert provided was not signed by a common root CA.

Perhaps we should also have a warning if this directive is specified but mod_ssl is not even loaded, or if it is loaded without the +FakeBasicAuth option.

> IMO cert-based authn or authz belongs outside of LDAP.  LDAP could have a
> second provider that just checks to make sure current r->user can be found in
> LDAP w/o looking at password (which is the additional check we're getting with
> this patch over just clientcert == authenticated)

The goal here is to leverage the great work in the authz side of mod_auth_ldap for cases where the business rule is that users may authenticate with their client certificate, without an LDAP password.  As the module stands, one cannot pull back the group and attribute information to match the LDAP Require* directives without passing a user password.  Since mod_ldap's API already supports a "user bind-free" compare, branching on an optional--non-default--LDAPAuth directive seemed like the simplest approach to augment mod_auth_ldap to use that feature.  I'd like to see this implemented--perhaps after the additional "sanity checks" are added, or implement it with subsequent enhancement requests pending in BZ to add the checks as time permits.
Comment 4 Peter Thomas 2010-02-25 01:10:56 UTC
(In reply to comment #2)
> IMO cert-based authn or authz belongs outside of LDAP.  LDAP could have a

I think you're right -- c.f. discussion in Bug #31418 -- we can probably fix the behavior higher up in the stack by making the behavior of +FakeBasicAuth more flexible.

I actually got everything "behaving" by using mod_auth_anon, but the one-line version of the subject DN does not match the LDAP entryDN, so authorization by mod_authnz_ldap still failed--but at least I got that far!
Comment 5 Peter Thomas 2010-03-08 23:52:58 UTC
Created attachment 25105 [details]
Updated patch that implements AuthType Certificate

Based on feedback, created a new AuthType attempting to non-intrusively leverage the existing code.  Please provide feedback.  I can only verify that it builds right now.  I'm having some issues creating a test environment, but I hope to be able to do some integration testing tomorrow.
Comment 6 Peter Thomas 2010-03-08 23:53:28 UTC
I'm attaching a first draft of an implementation of AuthType Certificate that doesn't conflict with AuthType Basic--and in fact can be stacked with Basic providers, if desired.
Comment 7 Peter Thomas 2010-03-24 20:55:07 UTC
The following is an e-mail sent to modules-dev on the subject.

All:

I've been working on integrating mod_ssl and mod_authnz_ldap for non-password-based environments.  I contemplate "AuthType Certificate" in https://issues.apache.org/bugzilla/show_bug.cgi?id=48780 .  This enhancement is targeted for environments where the user is authenticated if they:

1) present a valid SSL client certificate, and
2) a single object corresponding[*1] to that user's certificate exists at the targeted LDAP server.

To take advantage of the flexibility and utility of the existing module, I'm extending mod_authnz_ldap instead of writing a separate handler. For example, once authenticated one can then leverage the "Require ldap-*" directives in mod_authnz_ldap.  mod_authnz_ldap also populates the environment with all requested LDAP attributes in AUTHENTICATE_* environment variables. These can be used in subsequent request processing [such as fine-grained access control or other logic within request handlers].

To implement the initial "DN matching" approach, I had to make a change to mod_ssl.c to pull out an RFC2253 compliant representation of the subject DN.  My debugging so far suggests this may be causing me problems--I've included the patch diff at the end of this e-mail for review and suggestions.

I hope to have a comprehensive prototype patch available shortly for others that want to test this out.  A summary of the changes made to date follows:

-----
Added:

modules/aaa/mod_auth_cert.c
* provider module defining AuthType Certificate based
* registers check user hook "authenticate_certificate_user"
* TODO:  (from [*1], above) matching certificate subject DN to LDAP object DN is overly restrictive; someday implement a more general approach which might be based on creating a filter expression to match DN components, certificate attributes, &c.
-----
Modified:

Modouls/aaa/mod_auth.h:
* appended check_certificate member to authn_provider struct

modules/aaa/config.m4:
* add "APACHE_MODULE(auth_cert, X.509 certificate authentication, , , most)"

modules/aaa/mod_authnz_ldap.c:
* added authn_ldap_check_certificate, a wrapper for authn_ldap_check_password after testing for certificate auth pre-conditions
* changed authn_ldap_check_password to use util_ldap_cache_getuserdn instead of ..._checkuserid if AuthType is Certificate
* registered authn_ldap_check_certificate as the check_certificate function for 

Modules/ssl/mod_ssl.h:
* TODO:  Make the following item configurable, defaulting to original behavior [ I need RFC2253 format because that is how DNs are stored in our LDAP server ]
* changed ssl_var_lookup(…, "SSL_CLIENT_S_DN") to return RFC2253-compliant DN instead of using deprecated X509_NAME_oneline 

Issues/other TODO items:

* TODO: enhance APR-Util & mod_ldap to support two-way SSL and ldap_sasl_bind_s for environments that support SASL EXTERNAL authentication based on the LDAP client's certificate; right now mod_ldap only supports simple binding--anonymous, or with a binddn and password.
* ssl_var_lookup(…, "SSL_CLIENT_S_DN") bails out unexpectedly when called from mod_auth_cert.c:authenticate_certificate_user
  [I know it works elsewhere, because I can get the user name logged in access_log by using SSLUserName SSL_CLIENT_S_DN]

Here's the diff fragment if anyone wants to take a stab at helping me puzzle out what I've done wrong:

--- http-2.2.15-baseline/modules/ssl//ssl_engine_vars.c Sat Feb 27 16:00:58 2010
--- http-2.2.15/modules/ssl//ssl_engine_vars.c  Tue Mar 23 14:22:53 2010
@@ -367,10 +367,20 @@
     }
     else if (strcEQ(var, "S_DN")) {
         xsname = X509_get_subject_name(xs);
-        cp = X509_NAME_oneline(xsname, NULL, 0);
-        result = apr_pstrdup(p, cp);
-        modssl_free(cp);
-        resdup = FALSO;
+        BIO *bio;
+        int n;
+        
+        if ((bio = BIO_new(BIO_s_mem())) == NULL) {
+            result = NULL;
+        } else {
+            X509_NAME_print_ex(bio, xsname, 0, XN_FLAG_RFC2253);
+            n = BIO_pending(bio);
+            result = apr_pcalloc(p, n+1);
+            n = BIO_read(bio, result, n);
+            result[n] = NUL;
+            BIO_free(bio);
+            resdup = FALSE;
+        }
     }
     else if (strlen(var) > 5 && strcEQn(var, "S_DN_", 5)) {
         xsname = X509_get_subject_name(xs)
Comment 8 Peter Thomas 2010-04-06 23:28:09 UTC
Created attachment 25237 [details]
Improved Certificate provider for 2.2.x

Addresses the previous limitations. Tested in integration environment against all Require ldap-* directives.
Comment 9 Martin 2011-08-10 09:59:53 UTC
(In reply to comment #8)
> Created attachment 25237 [details]
> Improved Certificate provider for 2.2.x
> 
> Addresses the previous limitations. Tested in integration environment against
> all Require ldap-* directives.

Hi Peter.

I'm testing your module, but I have any idea how to setup the  http.conf for do the right validation.
Can you give me a example please?

Kind regards.

Martin
Comment 10 Peter Thomas 2012-05-10 21:25:15 UTC
(In reply to comment #9)
> (In reply to comment #8)
> I'm testing your module, but I have any idea how to setup the  http.conf for
> do the right validation.
> Can you give me a example please?

Martin, I apologize for the ridiculously long latency; I changed employers, and thus didn't see this question.

I left my example configurations behind, too, but keys are setting:

AuthLDAPURL ...
Require ssl [or appropriate variation]
SSLUsername HTTPS_CLIENT_S_DN [assumbing that your certificate subject DN matches the LDAP user object DN]

Let me know where you are running into problems, and I may be able to give you more specific guidance.
Comment 11 Esmond Pitt 2012-08-13 04:23:20 UTC
It should be noted that much of this can be implemented with the existing implementation. See the config extract below. Provided the LDAP user object concerned (a) has a password of 'password' as per +FakeBasicAuth and (b) has an attribute which matches the SubjectDN of the client certificate, the client will be authenticated correctly.

However the user's SubjectDN-valued attribute needs to be in the strange format returned by OpenLDAP, so it will match, so it actually can't be a proper LDAP DN-valued attribute (such as 'seeAlso'). And it also can't in general be the DN of the object itself, as the SubjectDN of the certificate is beyond the server's control and is most unlikely to agree with the LDAP tree's root.

So to weigh in on the above discussion:

1. A way is needed to avoid the bind step for directories that support that. I personally don't have this requirement and I'm not aware what it actually amounts to: I'm perfectly happy with a password of 'password', and I can configure the user in LDAP via the password policy from being able to break it.

2. A way is needed to acquire the SubjectDN of the certificate in proper X.500 DN format, and a configuration directive to enable it, or perhaps another env variable.

3. A way is needed to search the user subtree on an arbitrary attribute using that DN as the target value. We already have this part, in AuthLDAPURL, we just don't have a way of getting (2) or feeding the result into it.

4. The authenticated user is then subject to whatever require valid-user/ldap-user/ldap-group constraints may be defined, as per my example below.

	<Location /cRegister/WestNet >
		Order Deny,Allow
		Deny from none
		Allow from all
		Satisfy All
		# Fake LDAP Basic authentication,
		# after http://httpd.apache.org/docs/2.2/ssl/ssl_howto.html#certauthenticate
		SSLOptions +OptRenegotiate +StdEnvVars +StrictRequire +FakeBasicAuth 
		SSLVerifyClient require
		AuthBasicProvider	ldap
		AuthLDAPBindDN "uid=apache-httpd,ou=Applications,ou=System,dc=example,dc=com"
		AuthLDAPBindPassword "xyz"
		# Lookup the SubbjectDN of the certificate in OpenSSL format in the 'o' attribute.
		# The OpenSSL format for this is e.g. /C=US/ST=CA/L=SF/O=WestNet Inc./OU=Unknown/CN=WestNet Inc.,
		# which sadly isn't a valid LDAP DN, even though they are both ultimately X.509 DNs.
		# So we have to match on a non-DN-valued attribute, in this case 'o'.
		# So a target entry that satisfies all this must:
		# 1. Be an inetOrgPerson
		# 2. Be under ou=Users,dc=example,dc=com.
		# 3. Have a password of 'password', see the documentation for FakeBasicAuth.
		# 4. Have an 'o' attribute that matches the SubjectDN of the certificate in OpenSSL format as above.
		# 5. That subject must be a member of the Westnet role.
		AuthLDAPURL ldap://localhost/ou=Users,dc=verismartlabs,dc=com?o?sub?(objectClass=inetOrgPerson)
		# Require that such a user exists
		Require valid-user
		# Require that such a user is a member of the Westnet role.
		AuthLDAPGroupAttribute	roleOccupant
		Require ldap-group cn=WestNet,cn=Roles,dc=verismartlabs,dc=com
	</Location>
Comment 12 William A. Rowe Jr. 2018-11-07 21:08:41 UTC
Please help us to refine our list of open and current defects; this is a mass update of old and inactive Bugzilla reports which reflect user error, already resolved defects, and still-existing defects in httpd.

As repeatedly announced, the Apache HTTP Server Project has discontinued all development and patch review of the 2.2.x series of releases. The final release 2.2.34 was published in July 2017, and no further evaluation of bug reports or security risks will be considered or published for 2.2.x releases. All reports older than 2.4.x have been updated to status RESOLVED/LATER; no further action is expected unless the report still applies to a current version of httpd.

If your report represented a question or confusion about how to use an httpd feature, an unexpected server behavior, problems building or installing httpd, or working with an external component (a third party module, browser etc.) we ask you to start by bringing your question to the User Support and Discussion mailing list, see [https://httpd.apache.org/lists.html#http-users] for details. Include a link to this Bugzilla report for completeness with your question.

If your report was clearly a defect in httpd or a feature request, we ask that you retest using a modern httpd release (2.4.33 or later) released in the past year. If it can be reproduced, please reopen this bug and change the Version field above to the httpd version you have reconfirmed with.

Your help in identifying defects or enhancements still applicable to the current httpd server software release is greatly appreciated.