I needed to authenticate a website for a school against an Active Directory server today. I found the job surprisingly easy.
My first attempt was using a pam smb module, and an apache pam module. This worked well, but had a couple of flaws:
Oh well, scrapped that idea.
I looked at the smb modules for apache. This was a port of the pam_smb module to the apache api, didn't really gain me much, except it removed the limitation on one /etc/pam.d/ file for apache. Not that this really was much of a problem if you didn't have group support.
my final approach to the problem was an ldap authentication module for apache. This hit the nail on the head.
The major stumbling block I had was trying to find out the BaseDN. If you bind anonymously you can't search or anything useful. To bind as someone useful, you have to know their dn, including the BaseDN. Turns out the BaseDN was the name of the 'domain' with dc1?'s inserted. so if your domain is 'example.com', your baseDN is dc=example,dc=com. I'm not sure if this can be configured to be something else.
<Directory /var/www/staff>
AuthLDAPURL ldap://ads.example.com:389/OU=Users,OU=Teachers,DC=example,DC=com?sAMAccountName?sub?(objectClass=user) AuthLDAPBindDN cn=user,cn=Users,dc=example,dc=com AuthLDAPBindPassword password-here
AuthType? Basic
AuthName? "Mumble School Intranet"
require valid-user
</Directory>
/var/www/staff should be the path that you want to secure.
ads.example.com should be the hostname of your ads server, I suspect you can use something like _ldap._tcp.example.com here, but I didn't experiment, comments anyone?
user should be some user which has read privilege to the directory (which is likely to be a full name with a space, so you may need to put double quotes around the entire string following AuthLDAPBindDN)
password-here should be users password
and voila! It worked.
where:
the sAMAccountName is the ldap attribute ActiveDirectory uses for storing the username.
Note that Windows doesn't provide any really useful ways of seeing the LDAP structure, and thus knowing how to construct your LDAP queries can be tricky. There is a very helpful tool called "Ldp.exe" in the Windows Support Tools package (see http://support.microsoft.com/default.aspx?scid=kb;en-us;301423 - and note that there is an article for win2k3 also). Using this tool can be fairly intimidating. See: http://support.microsoft.com/default.aspx?scid=kb;EN-US;224543 for an overview.
1?: Domain Component
This is caused by mod_ldap trying to use LDAP v2. This appears to cause some (unspecified) problems with Active Directory. So I found a patch by Jeff Costlow (j.costlow at f5.com) (may whatever deity he worships provide him with many years of good health and fortune) which allows you to force LDAP version 3.
This prevents the error above from occuring, and now only authorised people can login.
There is a nasty security flaw in mod_ldap that while that error is appearing in your logs you can login as any user (even if that user doesn't exist). This is because when that error occurs mod_ldap fails and returns 0. However 0 turns out to be "success" instead of "failure". Ooops!
The following was posted to the NT BugTraq list (don't ask), and I thought it may be appropriate.
Here is the code I use to change a password in Active Directory. It uses the perl-ldap module from http://perl-ldap.sourceforge.net/
Ther perl-ldap FAQ provides examples of Active Directory password resets, and normal LDAP password resets: http://search.cpan.org/~gbarr/perl-ldap/lib/Net/LDAP/FAQ.pod (or perldoc Net::LDAP::FAQ )
It is part of a bigger program that we use to synchronize our LDAP to Active Directory. (one way synchronization from LDAP to AD, except for passwords which are two-way) I have reduced it down to a simple command line program that reads name & password from stdin. It does a very simplistic unicode conversion that will fail for non-ascii characters. Feel free to use it for whatever you like.
use strict; use Net::LDAPS;
my($Ad, $mesg, $uid, $pass, $npass, $dn, $rtn);
($uid, $pass) = split(" ",<STDIN>);
if (($uid eq "") or ($pass eq "")) {
print "Uid and/or password missing in input\n"; exit 1;
}
print "Trying to set $uid to password $pass\n";
$Ad = Net::LDAPS->new("dc.test.uva.nl", version =3)
or print "Unable to connect to AD server\n", exit 2;
$Ad->bind(dn ="CN=ad,OU=Admin,DC=test,DC=uva,DC=nl", password => "gandalf")
or print "Unable to bind to AD server\n", exit 2;
$mesg = $Ad->search(base "DC=s-res,DC=uva,DC=nl", filter ="cn=$uid"); if($mesg->count ! 1) {
print "AD lookup failed for user $uid\n"; exit 3;
}
map { $npass .= "$_\000" } split(//, "\"$pass\"");
$dn = $mesg->entry(0)->dn;
$rtn = $Ad->modify($dn, replace { "unicodePwd" =$npass }); if($rtn->{'resultCode'} ! 0) {
print "User $uid, setting password failed\n"; exit 2;
}
print "Password for $uid changed in AD\n"; exit 0;
Another good method for authentication with apache is to use one of the webISO's (web initial sign on) see http://middleware.internet2.edu/webiso/ . In particular the webISO provided by http://www.pubcookie.org is flexible and can be used with IIS aswell. It can either authenticate against LDAP, kerberos, unix password file (/etc/shadow), or pam modules. It give single sign on to web apps.
One page links to ActiveDirectoryAuthenticationNotes:
lib/plugin/WlugLicense.php:99: Warning: Invalid argument supplied for foreach()
lib/plugin/WlugLicense.php:111: Notice: Undefined variable: ignore_authors
lib/plugin/WlugLicense.php:111: Notice: Undefined variable: ignore_authors