[MERGE] GSSAPI authentication support for FTP

John Arbash Meinel john at arbash-meinel.com
Thu Jul 17 23:20:38 BST 2008


-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

It seems LarstiQ was at lunch for a *long* time, so here goes:

Jelmer Vernooij wrote:
| Am Mittwoch, den 02.07.2008, 01:26 -0400 schrieb Martin Pool:
|> Martin Pool has voted resubmit.
|> Status is now: Resubmit
|
|> +
|> +    def gssapi_login(self, user):
|> +        # Try GSSAPI login first
|> +        resp = self.sendcmd('AUTH GSSAPI')
|> +        if resp[:3] == '334':
|> +            rc, self.vc = kerberos.authGSSClientInit("ftp@%s" %
|> self.host)
|> +            if kerberos.authGSSClientStep(self.vc, "") != 1:
|> +                while resp[:3] in ('334', '335'):
|> +                    authdata = kerberos.authGSSClientResponse(self.vc)
|> +                    resp = self.sendcmd('ADAT ' + authdata)
|> +                    if resp[:9] in ('235 ADAT=', '335 ADAT='):
|> +                        rc = kerberos.authGSSClientStep(self.vc,
|> resp[9:])
|> +                        if not ((resp[:3] == '235' and rc == 1) or
|> +                                (resp[:3] == '335' and rc == 0)):
|> +                            raise AssertionError
|> +            info("Authenticated as %s" %
|> kerberos.authGSSClientUserName(
|> +                    self.vc))
|> +
|> +            # Monkey patch ftplib
|> +            self.putcmd = self.mic_putcmd
|> +            self.getline = self.mic_getline
|> +            self.sendcmd('USER ' + user)
|> +            return resp
|>
|> When you get something unexpected back it would be nice to raise an
|> error
|> that contains e.g. resp so that if a user hits it we have some data to
|> go
|> on - and anyhow the message might help them understand what's going
|> wrong.
|> Perhaps a generic TransportError would be better than AssertionError.
|>
|> What happens if you get a response other than 334?  I would guess
|> there's
|> one that means 'not supported' which you should ignore but maybe the
|> others should at least give a warning?
| I'd rather not warn since I suspect different servers to react quite
| differently to this despite the standard. I've added a mutter for now,
| since I suspect a warning may be annoying to the majority of users of
| the ftp protcol. If it turns out GSSAPI doesn't work unexpectedly for
| people, we can always convert it into a warning.

The one thing I would request is a way to *not* use ftp+gssapi. Right
now you are registering it as a handler for plain "ftp://" whenever the
'kerberos' library is available.

For http, you can use http+urllib / http+pycurl. I guess the best thing
here would be to add ftp+ftplib ?


I might also extend this error:
+if getattr(kerberos, "authGSSClientWrap", None) is None:
+    raise errors.DependencyNotPresent('kerberos',
+                                      "missing encryption functions")

"missing encryption functions authGSSClientWrap".

+        # Try GSSAPI login first
+        resp = self.sendcmd('AUTH GSSAPI')
+        if resp[:3] == '334':
+            rc, self.vc = kerberos.authGSSClientInit("ftp@%s" % self.host)

^- I would really like to see those have better documentation about what
"334" means.
Also, if the response was "3344" this would hit. Is it better to do:
resp[:4] == '334 '

Regardless, the better thing to do (python wise) is:

if resp.startswith('334'):

I think you should generally switch things to:

if resp.startswith('SOME STRING'), you have a lot of places that are
hard to maintain if a string changes size slightly. And they all should
have at least a little bit of documentation as to what is going on. Like
# 235 server wants more FOO


+                        if not ((resp[:3] == '235' and rc == 1) or
+                                (resp[:3] == '335' and rc == 0)):
+                            raise AssertionError

^- This sounds like a real interaction issue. And not just a plain
"Assertion". At the minimum, you should have:

raise AssertionError('server gave an invalid response: %s' % resp).

But honestly, it seems like you need a real exception here, not just
AssertionError.


Also, is 'ftp at host' always the correct client string? It at least looks
a lot like a user at host sort of key. But I don't know much about kerberos
& ftp.


+class GSSAPIFtpTransport(FtpTransport):
+    def _create_connection(self, credentials=None):

^- You should really have a docstring, and at a minimum, you need a
blank space here.

+        auth = config.AuthenticationConfig()
+        if user is None:
+            user = auth.get_user('ftp', self._host, port=self._port)
+            if user is None:
+                # Default to local user
+                user = getpass.getuser()

^- Should we default to local user, or should we default to 'anonymous'?



It would also be nice to have at least 1 direct test that the
GSSAPIFtpTransport tries to gssapi_login when it goes to _create_connection.

You could make GSSAPIFtp a helper class on GSSAPIFtpTransport and
override it with a logging class in the test suite.

BB:resubmit

John
=:->

|
| [...]
|
|> Other than that it looks pretty good to me.
|
| Thanks. I've attached an updated patch that fixes the issues you
| mentioned.
|
| Cheers,
|
| Jelmer
|

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.9 (Cygwin)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org

iEYEARECAAYFAkh/xbYACgkQJdeBCYSNAAPLzwCbBQqW/hYMYxUH9XNopke4xTRy
QaQAn2FW2QwzNA7WukqBx4j3DRlVpbJ7
=zCH/
-----END PGP SIGNATURE-----



More information about the bazaar mailing list