
Go directly to: update (2006-02-22) | patch
auth_ldap is a module for Apache 1.3 by Dave Carrigan.
On Jul 4, 2001, auth_ldap 1.6.0 was released.
On Jan 9, 2006, auth_ldap 1.6.1 was released. This release fixes a security issue, and incorporates other contributed patches into auth_ldap.
The following info deals with auth_ldap 1.6.0. For an update, scroll to the bottom of this page.
The module (auth_ldap 1.6.0) seems to work fine, except under certain circumstances.
Brent Putnam wrote a patch in December 2001, but unfortunately that patch never got into the release.
Here's the reason why he wrote the patch:
Hello everyone,
There have been reports previously on the list that auth_ldap was
inconsistently binding as the AuthLDAPBindDN in order to search for
non-publicly visible user accounts, making authentication of these
accounts unreliable. We have to restrict public visibility of some
users in our environment, so we rely on this feature heavily. I have
debugged this problem and written a patch that I believe addresses it.
The patch is attached.
The problem was happening apparently because in
ldap_authenticate_basic_user(), when the module binds as the actual user trying to authenticate, the boundas flag of the LDAPconnection struct was being set to bind_user. However, the bounddn member was not being changed - so it was still set to the AuthLDAPBindDN value from the first, initial search bind.
Then on the next authentication by that httpd child process, in auth_ldap_find_connection() bounddn is compared to the configuration sec->binddn value (AuthLDAPBindDN) and since they
match, boundas is set to bind_system.
Then finally in auth_ldap_connect_to_server() since boundas is now set to bind_system, it returns immediately without rebinding - however the LDAP server connection at this point is still bound as the last successfully authenticated user, and it is as that DN that the directory search is being done for the new user. If that DN doesn't have permissions to see the new non-public user that is trying to authenticate, the search fails and the new user can't authenticate.
The non-public user would be successful if he/she were the first user to authenticate via that httpd child process, because for the very first
search, it _would_ bind as AuthLDAPBindDN. What was interesting was that if the last user to successfully authenticate on that particular
httpd child process was the same non-public user currently trying to
authenticate, the authentication would succeed - because a DN typically has permissions to search for and find itself!
The patch basically just ensures that when boundas is set to bind_user, bounddn is set to the user's DN, and when boundas is set to bind_none, bounddn is set to NULL. I also added the PID to the debug error output for the failed search message.
Comments and suggestions are welcome.
Thanks,
Brent Putman
Georgetown University
After applying the patch, I discoverd there where still problems when AuthLDAPBindDN was not set (thus binding anonymously).
Stig Venaas wrote a patch to solve both problems, in February 2004.
His explanations:
I've looked at the problem, and I think I have a solution. I haven't actually tested it though, since I don't have a test environment ready.
It seems to me that the patch is solving this the wrong way, and it
has some side effects for anonymous binds. It tries to set bounddn
to NULL to show an error, using the fact that NULL is different
from binddn, but in the anonymous case, binddn IS NULL.
The patch does this because, as it says in the patch description:
The problem was happening apparently because in ldap_authenticate_basic_user(), when the module binds as the actual user trying to authenticate, the boundas flag of the LDAPconnection struct was being set to bind_user. However, the bounddn member was not being changed - so it was still set to the AuthLDAPBindDN value from the first, initial search bind. Then on the next authentication by that...
So it tried to work around the bug that the bounddn wasn't changed.
Rather than doing what the patch does, I think one should make sure
that bounddn is changed after every successful bind.
If you look for simple_bind_s() in auth_ldap.c, you will find it in
two places. At the first place, you will a few lines below see this:
sec->ldc->bounddn = sec->binddn? strdup(sec->binddn) : NULL;
sec->ldc->boundas = bind_system;
While at the second place, it does this:
sec->ldc->boundas = bind_user;
if ((result =
ldap_simple_bind_s(sec->ldc->ldap, sec->dn, const_cast(sent_pw))) ==
LDAP_SERVER_DOWN) {
ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r,
"{%d} Server is down; reconnecting and starting over", (int)getpid());
auth_ldap_free_connection(r, 1);
goto start_over;
}
if (result != LDAP_SUCCESS) {
auth_ldap_log_reason(r, "User bind as %s failed: LDAP error: %s; URI %s",
sec->dn, ldap_err2string(result), r->uri);
ap_note_basic_auth_failure(r);
RELMUTEX(conf->mtx);
RELMUTEX(sec->ldc->mtx);
return AUTH_REQUIRED;
}
So, as you can see, it sets boundas even if bind fails, and it never
sets bounddn!
I would move boundas and set bounddn after the test for LDAP_SUCCESS.
To conclude, I think you should forget that other patch, and use the
patch below instead. At least the change seems pretty logical to me
(after spending an hour, trying to see what really happens, in
hindsight I think I should have seen it immediately:) Because I'm
paranoid, I also added a check that the the dn returned by
ldap_get_dn() isn't NULL. If something is wrong ldap_get_dn() will
return NULL, which would result in anonymous bind instead of the
user bind, so the user would be authenticated without really
checking the password.
I've not tested this, I may have missed something. Let me know how
it goes.
Unfortunately, after applying Stig's patch, there still was a problem, which I described, very fuzzy, as:
If you try to login with a non-existing username, the result is that the browser displays 'Authententication is needed' and does NOT redisplay the password prompt. You can reload untill you weigh an ounce, but it will always display that page and never give you a chance to supply username/pass again.
Opera even says ' the browser send a reply code that we don't understand' or something along those lines.
Konquerer has no problems whatsoever.
But Galeon, IE (win), Mozilla, Netscape (win) all display that page and then you have to close the browser to get to that password popup-box again!
Solved by Stig thusly:
Anyway, I think I have good news!!! It just took me a while to find it. AFAICT things are working with the patch I suggested,
except for this problem with not getting new authentication request if wrong username.
After a lot of checking I was 100% it sent AUTH_REQUIRED in both cases. Finally my brain connected and I realised that then something else must be different. And I found it (:
When it works, it does "ap_note_basic_auth_failure(r);" before it
returns AUTH_REQUIRED. In the case where it doesn't find exactly
1 match, this is not done.
So, if you look at the code at about line 635, you see:
ldap_msgfree(res);
RELMUTEX(conf->mtx);
RELMUTEX(sec->ldc->mtx);
return sec->auth_authoritative? AUTH_REQUIRED: DECLINED;
}
What you need to do, is add ap_note_basic_auth_failure(r);
before the RELMUTEX stuff I think. So, change to this:
ldap_msgfree(res);
ap_note_basic_auth_failure(r);
RELMUTEX(conf->mtx);
RELMUTEX(sec->ldc->mtx);
return sec->auth_authoritative? AUTH_REQUIRED: DECLINED;
}
I tried to check whether the bind logic is ok, and it looks to me
like the search is always done as anonymous with the patch I
suggested earlier.
Update Feb 22, 2006
After I downloaded, compiled and installed auth_ldap 1.6.1, which incorporates the patches that Stig Venaas made, I noticed several problems. First of all, I couldn't log in anymore. The error in the apache error log was:
Could not connect to LDAP server: No such file or directory
It turns out that this can be resolved by commenting a couple of lines in auth_ldap.c:
//#if defined(WITH_OPENLDAP) && LDAP_VENDOR_VERSION >= 20000
//if ((ldap_initialize(&(sec->ldc->ldap), (sec->url))) != LDAP_SUCCESS) {
//#else
if ((sec->ldc->ldap = ldap_init(sec->host, sec->port)) == NULL) {
//#endif
For a discussion about this, and a proper patch, look here.
After this problem was solved, there where intermittent login problems, much like the ones that we experienced before Stig Venaas patched auth_ldap 1.6.0.
To resolve this issue, Dave Carrigan (the author of auth_ldap) has suggested that I add the security fix (which is only one line of code) to the patch for 1.6.0.
Message-ID: <43FB85A1.8010202@rudedog.org>
Date: Tue, 21 Feb 2006 13:26:57 -0800
From: Dave Carrigan
To: ace@xxxxxx.an
Ace Suares wrote:
> My entire environment is broken now, and I need to
> restart the webserver often to allow people to login.
> And I can't go back to 1.6.0.
If all you want from 1.6.1 is the security fix, then you should go back to your copy of 1.6.0 and make the following change:
Around line 90, change the line that reads
ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, r, buf);
To
ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, r, "%s", buf);
Dave Carrigan
Patch
You can find the gzipped patch here.
This is a patch for auth_ldap 1.6.0!
How to apply the patch: