Bug#359213: Kerberos

* John Goerzen (jgoerzen@complete.org) wrote:
> tages 359213 help
> thanks
>
> Hi folks,
>
> Unfortunately, I lack both the expertise to add Kerberos
> authentication and a way to test this.  A patch would be very welcome
> :-)

Well it was a bit unclear but there's a patch for offlineimap attached
to Guido's message:

http://bugs.debian.org/cgi-bin/bugreport.cgi?msg=10;filename=support-GSSAPI-via-pykerberos.diff;att=1;bug=359213

I've tried it out and it works great! Except that if the Kerberos
layer throws an exception it wouldn't fail over to plain
authentication properly. Attached is a slightly reworked patch that I
tested it in the following situations:

1. python-kerberos uninstalled, with kerberos credentials.
   Prompted for password authentication as expected.

2. python-kerberos installed, with kerberos credentials.
   Keberos authentication was successful and my mail was fetched
   without password prompting. Yeehaw!

3. python-kerberos installed, with no kerberos credentials.
   Prompted for    password authentication     as expected.

I think those are all the interesting situations. So I think this is
ready to go in, with a Suggest on python-kerberos (>=
1.0+mk080218-1).

--
Eric Dorland <eric@kuroneko.ca>
ICQ: #61138586, Jabber: hooty@jabber.com
This commit is contained in:
Eric Dorland 2008-03-09 00:46:51 -05:00 committed by John Goerzen
parent cf1a1b31e2
commit f7b5aa4ba3

View File

@ -21,7 +21,16 @@ from offlineimap import imaplibutil, imaputil, threadutil
from offlineimap.ui import UIBase from offlineimap.ui import UIBase
from threading import * from threading import *
import thread, hmac, os import thread, hmac, os
import base64
try:
# do we have a recent pykerberos?
have_gss = False
import kerberos
if 'authGSSClientWrap' in dir(kerberos):
have_gss = True
except ImportError:
pass
class UsefulIMAPMixIn: class UsefulIMAPMixIn:
def getstate(self): def getstate(self):
@ -58,6 +67,8 @@ class UsefulIMAP4_SSL(UsefulIMAPMixIn, imaplibutil.WrappedIMAP4_SSL):
class UsefulIMAP4_Tunnel(UsefulIMAPMixIn, imaplibutil.IMAP4_Tunnel): pass class UsefulIMAP4_Tunnel(UsefulIMAPMixIn, imaplibutil.IMAP4_Tunnel): pass
class IMAPServer: class IMAPServer:
GSS_STATE_STEP = 0
GSS_STATE_WRAP = 1
def __init__(self, config, reposname, def __init__(self, config, reposname,
username = None, password = None, hostname = None, username = None, password = None, hostname = None,
port = None, ssl = 1, maxconnections = 1, tunnel = None, port = None, ssl = 1, maxconnections = 1, tunnel = None,
@ -86,6 +97,9 @@ class IMAPServer:
self.semaphore = BoundedSemaphore(self.maxconnections) self.semaphore = BoundedSemaphore(self.maxconnections)
self.connectionlock = Lock() self.connectionlock = Lock()
self.reference = reference self.reference = reference
self.gss_step = self.GSS_STATE_STEP
self.gss_vc = None
self.gssapi = False
def getpassword(self): def getpassword(self):
if self.goodpassword != None: if self.goodpassword != None:
@ -135,6 +149,33 @@ class IMAPServer:
'Attempting plain authentication') 'Attempting plain authentication')
imapobj.login(self.username, self.getpassword()) imapobj.login(self.username, self.getpassword())
def gssauth(self, response):
data = base64.b64encode(response)
try:
if self.gss_step == self.GSS_STATE_STEP:
if not self.gss_vc:
rc, self.gss_vc = kerberos.authGSSClientInit('imap@' +
self.hostname)
response = kerberos.authGSSClientResponse(self.gss_vc)
rc = kerberos.authGSSClientStep(self.gss_vc, data)
if rc != kerberos.AUTH_GSS_CONTINUE:
self.gss_step = self.GSS_STATE_WRAP
elif self.gss_step == self.GSS_STATE_WRAP:
rc = kerberos.authGSSClientUnwrap(self.gss_vc, data)
response = kerberos.authGSSClientResponse(self.gss_vc)
rc = kerberos.authGSSClientWrap(self.gss_vc, response,
self.username)
response = kerberos.authGSSClientResponse(self.gss_vc)
except kerberos.GSSError, err:
# Kerberos errored out on us, respond with None to cancel the
# authentication
UIBase.getglobalui().debug('imap',
'%s: %s' % (err[0][0], err[1][0]))
return None
if not response:
response = ''
return base64.b64decode(response)
def acquireconnection(self): def acquireconnection(self):
"""Fetches a connection from the pool, making sure to create a new one """Fetches a connection from the pool, making sure to create a new one
@ -186,6 +227,20 @@ class IMAPServer:
if not self.tunnel: if not self.tunnel:
try: try:
# Try GSSAPI and continue if it fails
if 'AUTH=GSSAPI' in imapobj.capabilities and have_gss:
UIBase.getglobalui().debug('imap',
'Attempting GSSAPI authentication')
try:
imapobj.authenticate('GSSAPI', self.gssauth)
except imapobj.error, val:
UIBase.getglobalui().debug('imap',
'GSSAPI Authentication failed')
else:
self.gssapi = True
self.password = None
if not self.gssapi:
if 'AUTH=CRAM-MD5' in imapobj.capabilities: if 'AUTH=CRAM-MD5' in imapobj.capabilities:
UIBase.getglobalui().debug('imap', UIBase.getglobalui().debug('imap',
'Attempting CRAM-MD5 authentication') 'Attempting CRAM-MD5 authentication')