diff --git a/conf/kolab.conf b/conf/kolab.conf --- a/conf/kolab.conf +++ b/conf/kolab.conf @@ -340,6 +340,32 @@ ; }, ; ] +; SingleSignOn against external ldap directory (e.g. ActiveDirectory) +; Parameters: +; sso_uri: URI of external LDAP server +; sso_bind_dn: DN of an user in external LDAP server who has the right to search +; sso_bind_pw: password of sso_bind_dn. +; sso_base_dn: Base DN that ist searched on external ldap server +; sso_kolab_uid_attr: atrribute from kolab ldap database to search on externel ldap server. +; sso_ext_uid_attr: attribute on external ldap to search. +; sso_sync_password: if set to True (case sensitive) the password a user supplies (e.g. roundcube,kolab-webadmin or cyrus-imapd login). +' will be synced to local kolab database after successfull authenticcation. Without been +; synced, the user will be reauthenticated each time, kolab-saslauthd ist asked (which may be very oftern). +; Note: If you change your password on the external LDAP server, your local password will still be valid +; until you use your new password against kolab/roundcube/cyrus-imapd (having sso_sync_password set to True) +; Note2: You need to have a ldap_uri (see above in this file) that connect over ssl (e.g. ldaps:/lcoalhost). +; Password sync to local LDAP server will not work otherwise. +; Note3: If you dont use sso_sync_password some parts of kolab that dont use authentication over sasl +; my not work correctly. E.g. kolab-webadmin directly authenticates againt local LDAP directory. +; uncomment next line to enable authentiction against external ldap directory +;sso_uri = ldap://ad.external.org:389 +sso_bind_dn = CN=Kolab Service,CN=Users,DC=ad,DC=external,DC=org +sso_bind_pw = Welcome +sso_base_dn = DC=ad,DC=external,DC=org +sso_kolab_uid_attr = uid +sso_ext_uid_attr = sAMAccountName +sso_sync_password = True + [kolab_smtp_access_policy] cache_uri = mysql://user:pass@localhost/database cache_retention = 86400 diff --git a/pykolab/auth/ldap/__init__.py b/pykolab/auth/ldap/__init__.py --- a/pykolab/auth/ldap/__init__.py +++ b/pykolab/auth/ldap/__init__.py @@ -1505,6 +1505,9 @@ return False except ldap.INVALID_CREDENTIALS: + if self._bind_sso(bind_dn, bind_pw): + return True + log.error( _l("Invalid DN, username and/or password for '%s'.") % ( bind_dn @@ -1518,6 +1521,118 @@ return True + def _bind_sso(self, bind_dn=None, bind_pw=None): + sso_uri = self.config_get('ldap','sso_uri') + + if sso_uri is None: + return False + + sso_bind_dn = self.config_get('ldap','sso_bind_dn') + sso_bind_pw = self.config_get('ldap','sso_bind_pw') + sso_base_dn = self.config_get('ldap','sso_base_dn') + sso_kolab_uid_attr = self.config_get('ldap','sso_kolab_uid_attr') + sso_ext_uid_attr = self.config_get('ldap','sso_ext_uid_attr') + sso_sync_password = self.config_get('ldap','sso_sync_password') + + if sso_bind_dn is None: + log.error("sso_bind_dn in kolab.conf is missing") + + return False + + if sso_bind_pw is None: + log.error("sso_bind_pw in kolab.conf is missing") + + return False + + if sso_base_dn is None: + log.error("sso_base_dn in kolab.conf is missing") + + return False + + if sso_kolab_uid_attr is None: + log.error("sso_kolab_uid_attr in kolab.conf is missing") + + return False + + if sso_ext_uid_attr is None: + log.error("sso_ext_uid_attr in kolab.conf is missing") + + return False + + if sso_sync_password is None: + log.error("sso_sync_password in kolab.conf is missing") + + return False + + timeout = float(self.config_get('ldap', 'timeout', default=10)) + base_dn = auth_cache.get_entry(self.domain) + + self._bind() + + try: + # find kolab uid attr of current user on local ldap server + + _search = self.ldap.search_st( + bind_dn, + ldap.SCOPE_SUBTREE, + filterstr=None, + attrlist=[sso_kolab_uid_attr], + timeout=timeout + ) + except Exception, errmsg: + log.error(errmsg) + + uid = _search[0][1][sso_kolab_uid_attr][0] + + log.warning("Cannot authenticate against local password. Using SSO LDAP Server: %r" % (sso_uri)) + + try: + sso_conn = ldap.ldapobject.ReconnectLDAPObject( sso_uri ) + sso_conn.simple_bind_s( sso_bind_dn, sso_bind_pw ) + + except Exception, errmsg: + log.error("Could not bind SSO LDAP Server %r: %r" % (sso_uri,errmsg)) + return False + + try: + # find dn of corresponding user on external ldap server + + _search = sso_conn.search_st( + sso_base_dn, + ldap.SCOPE_SUBTREE, + sso_ext_uid_attr+"="+uid, + ['dn'], + attrsonly=True, + timeout=timeout + ) + + sso_ldap_user_dn = _search[0][0] + + log.info("External DN: %r" % (sso_ldap_user_dn)) + + sso_conn.simple_bind_s( sso_ldap_user_dn, bind_pw ) + + retval = True + + if sso_sync_password == 'True': + try: + log.info("Writing password to local LDAP entry: %r" % (bind_dn)) + + self._bind_priv() + self.ldap_priv.passwd_s(bind_dn, '', bind_pw) + except Exception, errmsg: + log.error(("Password coult not be written: %r") % (errmsg)) + pass + else: + log.warning("Password will not be synced to local LDAP entry: %r" % (bind_dn)) + + except Exception, errmsg: + log.error(("External authentication failed: %r") % (errmsg)) + + return False + + return retval + def _bind_priv(self): if self.ldap_priv is None: self.connect(True)