Skip to content

Commit

Permalink
Port to python-gssapi from pykerberos
Browse files Browse the repository at this point in the history
python-gssapi has a visible, active upstream and a more pleasant
interface.  python-gssapi is present in most distributions, while
pykerberos is slated for removal from Fedora/RHEL/CentOS.

Tested-by: Robbie Harwood <rharwood@redhat.com>
Signed-off-by: Robbie Harwood <rharwood@redhat.com>
  • Loading branch information
frozencemetery committed Mar 1, 2018
1 parent 67d35db commit c381d3a
Showing 1 changed file with 30 additions and 39 deletions.
69 changes: 30 additions & 39 deletions offlineimap/imapserver.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@

import hmac
import socket
import base64
import json
import urllib
import time
Expand All @@ -36,13 +35,10 @@


try:
# do we have a recent pykerberos?
have_gss = False
import kerberos
if 'authGSSClientWrap' in dir(kerberos):
have_gss = True
import gssapi
have_gss = True
except ImportError:
pass
have_gss = False


class IMAPServer(object):
Expand All @@ -55,9 +51,6 @@ class IMAPServer(object):
delim The server's folder delimiter. Only valid after acquireconnection()
"""

GSS_STATE_STEP = 0
GSS_STATE_WRAP = 1

def __init__(self, repos):
""":repos: a IMAPRepository instance."""

Expand Down Expand Up @@ -127,7 +120,6 @@ def __init__(self, repos):
self.connectionlock = Lock()
self.reference = repos.getreference()
self.idlefolders = repos.getidlefolders()
self.gss_step = self.GSS_STATE_STEP
self.gss_vc = None
self.gssapi = False

Expand Down Expand Up @@ -267,33 +259,35 @@ def __xoauth2handler(self, response):
self.ui.debug('imap', 'xoauth2handler: returning "%s"'% auth_string)
return auth_string

def __gssauth(self, response):
data = base64.b64encode(response)
# Perform the next step handling a GSSAPI connection.
# Client sends first, so token will be ignored if there is no context.
def __gsshandler(self, token):
if token == "":
token = None
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 as err:
# Kerberos errored out on us, respond with None to cancel the
if not self.gss_vc:
name = gssapi.Name('imap@' + self.hostname,
gssapi.NameType.hostbased_service)
self.gss_vc = gssapi.SecurityContext(usage="initiate",
name=name)

if not self.gss_vc.complete:
response = self.gss_vc.step(token)
return response if response else ""

# Don't bother checking qop because we're over a TLS channel
# already. But hey, if some server started encrypting tomorrow,
# we'd be ready since krb5 always requests integrity and
# confidentiality support.
response = self.gss_vc.unwrap(token)
response = self.gss_vc.wrap(response.message, response.encrypted)
return response.message if response.message else ""
except gssapi.exceptions.GSSError as err:
# GSSAPI errored out on us; respond with None to cancel the
# authentication
self.ui.debug('imap', '%s: %s'% (err[0][0], err[1][0]))
self.ui.debug('imap', err.gen_message())
return None

if not response:
response = ''
return base64.b64decode(response)

def __start_tls(self, imapobj):
if 'STARTTLS' in imapobj.capabilities and not self.usessl:
self.ui.debug('imap', 'Using STARTTLS connection')
Expand Down Expand Up @@ -327,16 +321,14 @@ def __authn_gssapi(self, imapobj):

self.connectionlock.acquire()
try:
imapobj.authenticate('GSSAPI', self.__gssauth)
imapobj.authenticate('GSSAPI', self.__gsshandler)
return True
except imapobj.error as e:
self.gssapi = False
raise
else:
self.gssapi = True
kerberos.authGSSClientClean(self.gss_vc)
self.gss_vc = None
self.gss_step = self.GSS_STATE_STEP
finally:
self.connectionlock.release()

Expand Down Expand Up @@ -680,8 +672,7 @@ def close(self):
self.assignedconnections = []
self.availableconnections = []
self.lastowner = {}
# reset kerberos state
self.gss_step = self.GSS_STATE_STEP
# reset GSSAPI state
self.gss_vc = None
self.gssapi = False

Expand Down

0 comments on commit c381d3a

Please sign in to comment.