[prev in list] [next in list] [prev in thread] [next in thread] 

List:       getmail
Subject:    Re: [PATCH] IMAP / Kerberos authentication
From:       "Charles Cazabon" <search-web-for-address () pyropus ! ca>
Date:       2009-03-31 17:43:12
Message-ID: 20090331174312.GA2282 () pyropus ! ca
[Download RAW message or body]

Charles Cazabon <search-web-for-address@pyropus.ca> wrote:
> Uli M <a.sporto+bee@gmail.com> wrote:
> > 
> > The attached patch adds kerberos authentication to getmail 4.8.3. I took it
> > from here[1], it's originally for offlineimap.
> 
> Thanks for the patch; this looks useful and relatively clean.  I'll likely
> incorporate it into getmail with a few tweaks.

> The attached patch adds kerberos authentication to getmail 4.8.3.

I got approval from the author of the original patch to include this in
getmail, and have made a few cleanups to it -- but I have no way to test
kerberos authentication.  Would you mind trying my attached version and
confirming it still works for you?  If so, I'll document it and include it in
the next version.

Charles

-- 
-----------------------------------------------------------------------
Charles Cazabon
GPL'ed software available at:               http://pyropus.ca/software/
-----------------------------------------------------------------------
["getmail-kerberos.patch" (text/plain)]

Index: getmailcore/retrievers.py
===================================================================
--- getmailcore/retrievers.py	(revision 265)
+++ getmailcore/retrievers.py	(working copy)
@@ -361,6 +361,7 @@
         # imaplib.IMAP4.login_cram_md5() requires the (unimplemented)
         # .authenticate(), so we can't do this yet (?).
         ConfBool(name='use_cram_md5', required=False, default=False),
+        ConfBool(name='use_kerberos', required=False, default=False),
     )
     received_from = None
     received_with = 'IMAP4'
@@ -401,6 +402,7 @@
         # imaplib.IMAP4.login_cram_md5() requires the (unimplemented)
         # .authenticate(), so we can't do this yet (?).
         ConfBool(name='use_cram_md5', required=False, default=False),
+        ConfBool(name='use_kerberos', required=False, default=False),
     )
     received_from = None
     received_with = 'IMAP4-SSL'
@@ -438,6 +440,7 @@
         # imaplib.IMAP4.login_cram_md5() requires the (unimplemented)
         # .authenticate(), so we can't do this yet (?).
         ConfBool(name='use_cram_md5', required=False, default=False),
+        ConfBool(name='use_kerberos', required=False, default=False),
         ConfString(name='envelope_recipient'),
     )
     received_from = None
@@ -479,6 +482,7 @@
         # imaplib.IMAP4.login_cram_md5() requires the (unimplemented)
         # .authenticate(), so we can't do this yet (?).
         ConfBool(name='use_cram_md5', required=False, default=False),
+        ConfBool(name='use_kerberos', required=False, default=False),
         ConfString(name='envelope_recipient'),
     )
     received_from = None
Index: getmailcore/_retrieverbases.py
===================================================================
--- getmailcore/_retrieverbases.py	(revision 265)
+++ getmailcore/_retrieverbases.py	(working copy)
@@ -44,6 +44,15 @@
 import imaplib
 import sets
 
+try:
+    # do we have a recent pykerberos?
+    HAVE_KERBEROS_GSS = False
+    import kerberos
+    if 'authGSSClientWrap' in dir(kerberos):
+        HAVE_KERBEROS_GSS = True
+except ImportError:
+    pass
+
 from getmailcore.exceptions import *
 from getmailcore.constants import *
 from getmailcore.message import *
@@ -68,6 +77,9 @@
 # 30 days.
 VANISHED_AGE = (60 * 60 * 24 * 30)
 
+# Kerberos authentication state constants
+(GSS_STATE_STEP, GSS_STATE_WRAP) = (0, 1)
+
 #
 # Mix-in classes
 #
@@ -644,7 +656,42 @@
         self.log.trace()
         self.mailbox = None
         self.uidvalidity = None
+        self.gss_step = 0
+        self.gss_vc = None
+        self.gssapi = False
 
+    def checkconf(self):
+        RetrieverSkeleton.checkconf(self)
+        if self.conf['use_kerberos'] and not HAVE_KERBEROS_GSS:
+            self.log.warning('%s: cannot use kerberos authentication; Python '
+                             'kerberos support not installed or does not '
+                             'support GSS'
+                             % self)
+
+    def gssauth(self, response):
+        if not HAVE_KERBEROS_GSS:
+            # shouldn't get here
+            raise ValueError('kerberos GSS support not available')
+        data = str(response).encode('base64')
+        if self.gss_step == GSS_STATE_STEP:
+            if not self.gss_vc:
+                (rc, self.gss_vc) = kerberos.authGSSClientInit(
+                    'imap@%s' % self.conf['server']
+                )
+                response = kerberos.authGSSClientResponse(self.gss_vc)
+            rc = kerberos.authGSSClientStep(self.gss_vc, data)
+            if rc != kerberos.AUTH_GSS_CONTINUE:
+               self.gss_step = GSS_STATE_WRAP
+        elif self.gss_step == GSS_STATE_WRAP:
+            rc = kerberos.authGSSClientUnwrap(self.gss_vc, data)
+            response = kerberos.authGSSClientResponse(self.gss_vc)
+            rc = kerberos.authGSSClientWrap(self.gss_vc, response,
+                                            self.conf['username'])
+        response = kerberos.authGSSClientResponse(self.gss_vc)
+        if not response:
+            response = ''
+        return response.decode('base64')
+
     def __del__(self):
         RetrieverSkeleton.__del__(self)
 
@@ -829,15 +876,19 @@
     def initialize(self):
         self.log.trace()
         # Handle password
-        if self.conf.get('password', None) is None:
-            self.conf['password'] = getpass.getpass('Enter password for %s:  '
-                                                    % self)
+        if (self.conf.get('password', None) is None
+                and not (HAVE_KERBEROS_GSS and self.conf['use_kerberos'])):
+            self.conf['password'] = getpass.getpass(
+                'Enter password for %s:  ' % self
+            )
         RetrieverSkeleton.initialize(self)
         try:
             self.log.trace('trying self._connect()' + os.linesep)
             self._connect()
             self.log.trace('logging in' + os.linesep)
-            if self.conf['use_cram_md5']:
+            if self.conf['use_kerberos'] and HAVE_KERBEROS_GSS:
+                self.conn.authenticate('GSSAPI', self.gssauth)
+            elif self.conf['use_cram_md5']:
                 self._parse_imapcmdresponse(
                     'login_cram_md5', self.conf['username'],
                     self.conf['password']
@@ -850,7 +901,7 @@
             self.log.debug('msgids: %s'
                            % sorted(self.msgnum_by_msgid.keys()) + os.linesep)
             self.log.debug('msgsizes: %s' % self.msgsizes + os.linesep)
-            # Remove messages from state file that are no longer in mailbox, 
+            # Remove messages from state file that are no longer in mailbox,
             # but only if the timestamp for them are old (30 days for now).
             # This is because IMAP users can have one state file but multiple
             # IMAP folders in different configuration rc files.



---------------------------------------------------------------------
To unsubscribe, e-mail: getmail-unsubscribe@lists.pyropus.ca
For additional commands, e-mail: getmail-help@lists.pyropus.ca

[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic