Add SASL PLAIN authentication method
- this method isn't as deprecated as IMAP LOGIN; - it allows to keep hashed passwords on the server side; - it has the ability to specify that the remote identity is different from authenticating username, so it even can be useful in some cases (e.g., migrated mailboxes); configuration variable "remote_identity" was introduced to leverage this functionality. From: Andreas Mack <andreas.mack@konsec.com> Signed-off-by: Eygene Ryabinkin <rea@codelabs.ru>
This commit is contained in:
parent
7d313f49dc
commit
acaa96291d
@ -16,6 +16,7 @@ WIP (add new stuff for the next release)
|
||||
* Honor the timezone of emails (Tobias Thierer)
|
||||
* Allow mbnames output to be sorted by a custom sort key by specifying
|
||||
a 'sort_keyfunc' function in the [mbnames] section of the config.
|
||||
* Support SASL PLAIN authentication method. (Andreas Mack)
|
||||
|
||||
OfflineIMAP v6.5.5-rc1 (2012-09-05)
|
||||
===================================
|
||||
|
@ -353,6 +353,18 @@ ssl = yes
|
||||
# Specify the remote user name.
|
||||
remoteuser = username
|
||||
|
||||
# Specify the user to be authorized as. Sometimes we want to
|
||||
# authenticate with our login/password, but tell the server that we
|
||||
# really want to be treated as some other user; perhaps server will
|
||||
# allow us to do that (or, may be, not). Some IMAP servers migrate
|
||||
# account names using this functionality: your credentials remain
|
||||
# intact, but remote identity changes.
|
||||
#
|
||||
# Currently this variable is used only for SASL PLAIN authentication
|
||||
# mechanism.
|
||||
#
|
||||
# remote_identity = authzuser
|
||||
|
||||
# There are six ways to specify the password for the IMAP server:
|
||||
#
|
||||
# 1. No password at all specified in the config file.
|
||||
|
@ -55,6 +55,7 @@ class IMAPServer:
|
||||
self.tunnel = repos.getpreauthtunnel()
|
||||
self.usessl = repos.getssl()
|
||||
self.username = None if self.tunnel else repos.getuser()
|
||||
self.user_identity = repos.get_remote_identity()
|
||||
self.password = None
|
||||
self.passworderror = None
|
||||
self.goodpassword = None
|
||||
@ -133,6 +134,24 @@ class IMAPServer:
|
||||
self.ui.debug('imap', 'Attempting IMAP LOGIN authentication')
|
||||
imapobj.login(self.username, self.getpassword())
|
||||
|
||||
|
||||
def plainhandler(self, response):
|
||||
"""
|
||||
Implements SASL PLAIN authentication, RFC 4616,
|
||||
http://tools.ietf.org/html/rfc4616
|
||||
|
||||
"""
|
||||
authc = self.username
|
||||
passwd = self.getpassword()
|
||||
authz = ''
|
||||
if self.user_identity != None:
|
||||
authz = self.user_identity
|
||||
NULL = u'\x00'
|
||||
retval = NULL.join((authz, authc, passwd)).encode('utf-8')
|
||||
self.ui.debug('imap', 'plainhandler: returning %s' % retval)
|
||||
return retval
|
||||
|
||||
|
||||
def gssauth(self, response):
|
||||
data = base64.b64encode(response)
|
||||
try:
|
||||
@ -213,6 +232,8 @@ class IMAPServer:
|
||||
"TLS connection: %s" % str(e),
|
||||
OfflineImapError.ERROR.REPO)
|
||||
|
||||
# Hashed authenticators come first: they don't reveal
|
||||
# passwords.
|
||||
if 'AUTH=CRAM-MD5' in imapobj.capabilities:
|
||||
tried_to_authn = True
|
||||
self.ui.debug('imap', 'Attempting '
|
||||
@ -224,6 +245,18 @@ class IMAPServer:
|
||||
self.ui.warn('CRAM-MD5 authentication failed: %s' % e)
|
||||
exc_stack.append(('CRAM-MD5', e))
|
||||
|
||||
# Try plaintext authenticators.
|
||||
if 'AUTH=PLAIN' in imapobj.capabilities:
|
||||
tried_to_authn = True
|
||||
self.ui.debug('imap', 'Attempting '
|
||||
'PLAIN authentication')
|
||||
try:
|
||||
imapobj.authenticate('PLAIN', self.plainhandler)
|
||||
return
|
||||
except imapobj.error as e:
|
||||
self.ui.warn('PLAIN authentication failed: %s' % e)
|
||||
exc_stack.append(('PLAIN', e))
|
||||
|
||||
# Last resort: use LOGIN command,
|
||||
# unless LOGINDISABLED is advertized (RFC 2595)
|
||||
if 'LOGINDISABLED' in imapobj.capabilities:
|
||||
|
@ -115,6 +115,18 @@ class IMAPRepository(BaseRepository):
|
||||
"'%s' specified." % self,
|
||||
OfflineImapError.ERROR.REPO)
|
||||
|
||||
|
||||
def get_remote_identity(self):
|
||||
"""
|
||||
Remote identity is used for certain SASL mechanisms
|
||||
(currently -- PLAIN) to inform server about the ID
|
||||
we want to authorize as instead of our login name.
|
||||
|
||||
"""
|
||||
|
||||
return self.getconf('remote_identity', default=None)
|
||||
|
||||
|
||||
def getuser(self):
|
||||
user = None
|
||||
localeval = self.localeval
|
||||
|
Loading…
Reference in New Issue
Block a user