Re: [Mailzu-users] LDAP Authentication mod
Brought to you by:
trilexcom
|
From: Ron G. <rg...@sh...> - 2005-10-25 20:40:12
|
Sam Tran wrote: >On 10/24/05, Ron Grant <rg...@sh...> wrote: > > >>Therefore, neither of the lookup types (statically composed DN, or >>directory-wide search using a single attribute) would work. I also >>wanted the Quarantine to allow only "Enabled'" users (or rather, >>specifically deny "Disabled" users). >> >> >> > >I understand that this additional LDAP filter can restrict login. But >I am not sure why >neither of the lookup types (statically composed DN, or directory-wide >search using a single attribute) would work. Could you give me an >example please? > > All of our Mail Accounts are listed within the Domain objects they belong in - so "fr...@ex..." has a DN of "mai...@be...,dc=bedrock.com,o=Bedrock Mining Co." So there is no one "container" where all the mail objects are - there are hundreds, and could be thousands. Also, for each of these domains, there can be a separate Mail Manager object (variation of inetOrgPerson) who is authorized to add or edit Mail Accounts for that domain (or for his entire company). This object (for simple customer database reasons) may also have a "mail" attribute. For instance, the user at "cn=Barney,dc=bedrock.com,o=Bedrock Mining Co." could be the Manager, and his "mail" attribute may be "ba...@be...". His Mail Account is a separate object (to avoid his having to use his cleartext POP password to logon to the highly secured Management Interface). In this case, doing a search for "mai...@be..." will yield TWO objects, with two separate passwords, only one of which is the correct object. > > >>It appeared that MailZu was using the filter "mailAttr=%m", but there is >>only one place where this filter is passed on to the LDAP module itself, >>so with the addition of an extra (and optional) text string in the >>config file, ooh, let's call it "ldap_objectType", you can compose a >>complex filter to narrow down the type of object that would yield a >>successful search and subsequent bind. >> >> >> > >I have actually two problems with this method: >1. I still cannot restrict DN that was statically composed. >2. I cannot restrict login based on group membership. > >Therefore I think it may be better to do a user login restriction >check after we get the user DN in the Auth.class.php. What do you >think? > >Sam > > Hmmm!! Good points. I combine Authentication and Authorization in one filter, but really they're separate purposes. Uniquely identifying a login based on what they enter into a password field is one thing, but refusing a valid login because of accounting issues is another. I had modified LDAPEngine.class.php to only use a filter if "search for DN first" method was used, though there's no reason why someone might not want to narrow the search to only Mail Accounts, but still compose the DN statically. Perhaps they have a "ou=Employees,o=Bedrock Mining Co" tree where all Contacts are kept with a DN of type "cn=Full Name,ou=Employees,o=Bedrock Mining Co", but only some of the inetOrgPerson objects have Auxiliary objectclass courierMailAccount. In this case they'd want to find a specific DN, but maybe ONLY if it was a courierMailAccount (or an amavisAccount, to restrict MailZu to users who are actually getting quarantined in the first place). So perhaps instead of using an "objectType" we could just use a "filter" like Amavisd does (and postfix and courier-imap ...) and substitute the "%m" for the submitted mail address instead of ANDing a filter. This way both methods could use the filter - in the case of the "search for DN first" method, the default filter would be "login=%m", while in the "compose the DN" method, the default would be NOT to use a filter, just to accept the DN as given. I've included a diff at the end of this message, let me know if you'd prefer not to get these in the mailing list. (I know nothing of Active Directory, only LDAP, so I don't know if what I'm adding is breaking any AD stuff. (endusers who read this - b'ware the patch, me boy!):-) As for restrictions, I agree that having a separate Authorization check makes sense: The advantage of separating the two would be that the Authorization check could easily be made generic (moved to Auth.class, perhaps in the isAllowedToLogin function), to apply to SQL authentication methods as well. You would also be able to spit out a custom error message if a field failed an Authorization check - so that trying to login with "status=Deadbeat" would produce an error message saying "Pay your bill" rather than just repeated "Incorrect User or Password" attempts, or a generic "Not authorized here". So the filter I'm using "(&(mail=%m)(objectclass=balServiceMailAccount)(!(serviceStatus=Disabled)))" would be better accomplished by using "(&(mail=%m)(objectclass=balServiceMailAccount))" for unique Authentication, and then checking the serviceStatus field for Authorization later. As for Group membership, well it's kind of an LDAP specific thing, but it seems to me that if you added group checking, it'd be nice to add a Mail Admin group, too. I stopped using Group membership in favour of ACLs some time ago - I'd rather have a flag in the user's Attributes that says he or she can or can't do stuff - that way if I delete a user I don't have to remember to remove an entry from a group, as well. But if you're returning Attributes from LDAPEngine for Authorization purposes, it seems to me that it'd be best to modify the loadUserData function to do a search for "(&(objectclass=groupOfUniqueNames)(uniqueMember=$dn))" and add the DNs or gidNumbers or whatever to the attributes of the User, and compare those in the isAllowedToLogin function. I wouldn't mind submitting that routine myself, but I also see a mod I'd suggest for loadUserData that would ensure a unique return from the search, and I have a few days of work backed up already......perhaps once my beta testers are hammering away on what I have now..... P.S. here's the diff diff -C1 -r ../MailZu_0.6RC3/config/config.php.sample ./config/config.php.sample *** ../MailZu_0.6RC3/config/config.php.sample Tue Aug 30 14:03:36 2005 --- ./config/config.php.sample Tue Oct 25 13:32:05 2005 *************** *** 146,147 **** --- 146,153 ---- + // Optional LDAP filter to use when searching for the User DN + // or verifying the composed DN + // Defaults to "$conf['auth']['ldap_login'] = %m" if not set + // Set to '' to disable filtering for user_identifier and user_container. + $conf['auth']['ldap_filter'] = ''; + diff -C1 -r ../MailZu_0.6RC3/lib/LDAPEngine.class.php ./lib/LDAPEngine.class.php *** ../MailZu_0.6RC3/lib/LDAPEngine.class.php Tue Aug 30 14:03:36 2005 --- ./lib/LDAPEngine.class.php Tue Oct 25 12:21:33 2005 *************** *** 110,111 **** --- 110,112 ---- $this->mailAttr = $conf['auth']['ldap_mailAttr']; + $this->filter = $conf['auth']['ldap_filter']; $this->searchUser = $conf['auth']['ldap_searchUser']; *************** *** 224,228 **** $dn = "$this->userIdentifier=$userlogin," . "$this->userContainer," . $this->basedn; } else { // Search for user dn ! $searchFilter = $this->login . "=" . $userlogin; $dn = $this->searchUserDN($searchFilter); --- 225,234 ---- $dn = "$this->userIdentifier=$userlogin," . "$this->userContainer," . $this->basedn; + + // If a filter was supplied, verify the DN by doing a base scope search + if ( ! empty($this->filter) ) { + $dn = $this->searchUserDN(str_replace("%m",$userlogin,$this->filter),$dn); + } } else { // Search for user dn ! $searchFilter = ( empty($this->filter) ) ? "$this->login=$userlogin" : str_replace("%m",$userlogin,$this->filter) ; $dn = $this->searchUserDN($searchFilter); *************** *** 272,274 **** */ ! function searchUserDN($searchFilter) { --- 278,280 ---- */ ! function searchUserDN($searchFilter,$readDN = '') { *************** *** 292,294 **** ! $sr = ldap_search( $this->connection, $this->getSearchBase(), $searchFilter, array('dn')); $entries = ldap_get_entries( $this->connection, $sr); --- 298,305 ---- ! // if readDN supplied, base scope search instead of subtree ! if ( empty($readDN) ) { ! $sr = ldap_search( $this->connection, $this->getSearchBase(), $searchFilter, array('dn')); ! } else { ! $sr = ldap_read( $this->connection, $dn, $searchFilter, array('dn')); ! } $entries = ldap_get_entries( $this->connection, $sr); |