Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Port to python-gssapi from pykerberos #529

Closed
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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