~bzr-pqm/bzr/bzr.dev

3331.2.4 by Jelmer Vernooij
Move GSSAPI support to a separate file.
1
# Copyright (C) 2005, 2006, 2007, 2008 Canonical Ltd
2
#
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
7
#
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
# GNU General Public License for more details.
12
#
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
17
"""Support for secure authentication using GSSAPI over FTP.
18
19
See RFC2228 for details.
20
"""
21
3331.2.11 by Jelmer Vernooij
Review comments from Martin.
22
import base64, ftplib, getpass, socket
23
3331.2.6 by Jelmer Vernooij
Fix formatting, check for completeness of kerberos functions.
24
from bzrlib import (
25
    config, 
26
    errors,
27
    )
3331.2.4 by Jelmer Vernooij
Move GSSAPI support to a separate file.
28
from bzrlib.trace import info, mutter
29
from bzrlib.transport.ftp import FtpTransport
30
from bzrlib.transport import register_transport_proto, register_transport
31
32
try:
33
    import kerberos
34
except ImportError, e:
35
    mutter('failed to import kerberos lib: %s', e)
36
    raise errors.DependencyNotPresent('kerberos', e)
37
3331.2.6 by Jelmer Vernooij
Fix formatting, check for completeness of kerberos functions.
38
if getattr(kerberos, "authGSSClientWrap", None) is None:
39
    raise errors.DependencyNotPresent('kerberos', 
3649.3.3 by Jelmer Vernooij
Review feedback from John.
40
        "missing encryption function authGSSClientWrap")
3331.2.6 by Jelmer Vernooij
Fix formatting, check for completeness of kerberos functions.
41
3331.2.11 by Jelmer Vernooij
Review comments from Martin.
42
43
class GSSAPIFtp(ftplib.FTP):
3331.2.4 by Jelmer Vernooij
Move GSSAPI support to a separate file.
44
    """Extended version of ftplib.FTP that can authenticate using GSSAPI."""
3331.2.11 by Jelmer Vernooij
Review comments from Martin.
45
3331.2.4 by Jelmer Vernooij
Move GSSAPI support to a separate file.
46
    def mic_putcmd(self, line):
3331.2.7 by Jelmer Vernooij
Fix handling of multilines.
47
        rc = kerberos.authGSSClientWrap(self.vc, 
3331.2.10 by Jelmer Vernooij
Review feedback from Vincent.
48
            base64.b64encode(line), kerberos.authGSSClientUserName(self.vc))
3331.2.4 by Jelmer Vernooij
Move GSSAPI support to a separate file.
49
        wrapped = kerberos.authGSSClientResponse(self.vc)
50
        ftplib.FTP.putcmd(self, "MIC " + wrapped)
51
3331.2.7 by Jelmer Vernooij
Fix handling of multilines.
52
    def mic_getline(self):
53
        resp = ftplib.FTP.getline(self)
3331.2.10 by Jelmer Vernooij
Review feedback from Vincent.
54
        if resp[:4] != '631 ':
55
            raise AssertionError
3331.2.7 by Jelmer Vernooij
Fix handling of multilines.
56
        rc = kerberos.authGSSClientUnwrap(self.vc, resp[4:].strip("\r\n"))
3331.2.6 by Jelmer Vernooij
Fix formatting, check for completeness of kerberos functions.
57
        response = base64.b64decode(kerberos.authGSSClientResponse(self.vc))
58
        return response
3331.2.4 by Jelmer Vernooij
Move GSSAPI support to a separate file.
59
3331.2.7 by Jelmer Vernooij
Fix handling of multilines.
60
    def gssapi_login(self, user):
3331.2.4 by Jelmer Vernooij
Move GSSAPI support to a separate file.
61
        # Try GSSAPI login first
3649.3.3 by Jelmer Vernooij
Review feedback from John.
62
63
        # Used FTP response codes:
64
        # 235 [ADAT=base64data] - indicates that the security data exchange 
65
        #     completed successfully.
66
        # 334 [ADAT=base64data] - indicates that the requested security 
67
        #     mechanism is ok, and includes security data to be used by the 
68
        #     client to construct the next command.
69
        # 335 [ADAT=base64data] - indicates that the security data is
70
        #     acceptable, and more is required to complete the security 
71
        #     data exchange.
72
3331.2.4 by Jelmer Vernooij
Move GSSAPI support to a separate file.
73
        resp = self.sendcmd('AUTH GSSAPI')
3649.3.5 by Jelmer Vernooij
use startswith.
74
        if resp.startswith('334 '):
3331.2.4 by Jelmer Vernooij
Move GSSAPI support to a separate file.
75
            rc, self.vc = kerberos.authGSSClientInit("ftp@%s" % self.host)
3331.2.7 by Jelmer Vernooij
Fix handling of multilines.
76
            if kerberos.authGSSClientStep(self.vc, "") != 1:
3649.3.5 by Jelmer Vernooij
use startswith.
77
                while resp[:4] in ('334 ', '335 '):
3331.2.7 by Jelmer Vernooij
Fix handling of multilines.
78
                    authdata = kerberos.authGSSClientResponse(self.vc)
79
                    resp = self.sendcmd('ADAT ' + authdata)
80
                    if resp[:9] in ('235 ADAT=', '335 ADAT='):
81
                        rc = kerberos.authGSSClientStep(self.vc, resp[9:])
3649.3.5 by Jelmer Vernooij
use startswith.
82
                        if not ((resp.startswith('235 ') and rc == 1) or 
83
                                (resp.startswith('335 ') and rc == 0)):
3649.3.3 by Jelmer Vernooij
Review feedback from John.
84
                            raise ftplib.error_reply, resp
3331.2.4 by Jelmer Vernooij
Move GSSAPI support to a separate file.
85
            info("Authenticated as %s" % kerberos.authGSSClientUserName(
86
                    self.vc))
3331.2.7 by Jelmer Vernooij
Fix handling of multilines.
87
3331.2.4 by Jelmer Vernooij
Move GSSAPI support to a separate file.
88
            # Monkey patch ftplib
89
            self.putcmd = self.mic_putcmd
3331.2.7 by Jelmer Vernooij
Fix handling of multilines.
90
            self.getline = self.mic_getline
91
            self.sendcmd('USER ' + user)
92
            return resp
3331.2.11 by Jelmer Vernooij
Review comments from Martin.
93
        mutter("Unable to use GSSAPI authentication: %s", resp)
94
95
96
class GSSAPIFtpTransport(FtpTransport):
3649.3.3 by Jelmer Vernooij
Review feedback from John.
97
    """FTP transport implementation that will try to use GSSAPI authentication.
98
99
    """
100
3331.2.4 by Jelmer Vernooij
Move GSSAPI support to a separate file.
101
    def _create_connection(self, credentials=None):
102
        """Create a new connection with the provided credentials.
103
104
        :param credentials: The credentials needed to establish the connection.
105
106
        :return: The created connection and its associated credentials.
107
3331.2.11 by Jelmer Vernooij
Review comments from Martin.
108
        The credentials are a tuple with the username and password. The 
109
        password is used if GSSAPI Authentication is not available.
110
111
        The username and password can both be None, in which case the 
112
        credentials specified in the URL or provided by the 
113
        AuthenticationConfig() are used.
3331.2.4 by Jelmer Vernooij
Move GSSAPI support to a separate file.
114
        """
115
        if credentials is None:
116
            user, password = self._user, self._password
117
        else:
118
            user, password = credentials
119
120
        auth = config.AuthenticationConfig()
121
        if user is None:
122
            user = auth.get_user('ftp', self._host, port=self._port)
123
            if user is None:
124
                # Default to local user
125
                user = getpass.getuser()
126
127
        mutter("Constructing FTP instance against %r" %
128
               ((self._host, self._port, user, '********',
129
                self.is_active),))
130
        try:
3331.2.11 by Jelmer Vernooij
Review comments from Martin.
131
            connection = GSSAPIFtp()
3331.2.4 by Jelmer Vernooij
Move GSSAPI support to a separate file.
132
            connection.connect(host=self._host, port=self._port)
133
            try:
3331.2.7 by Jelmer Vernooij
Fix handling of multilines.
134
                connection.gssapi_login(user=user)
3331.2.4 by Jelmer Vernooij
Move GSSAPI support to a separate file.
135
            except ftplib.error_perm, e:
136
                if user and user != 'anonymous' and \
137
                        password is None: # '' is a valid password
138
                    password = auth.get_password('ftp', self._host, user,
139
                                                 port=self._port)
140
                connection.login(user=user, passwd=password)
141
            connection.set_pasv(not self.is_active)
142
        except socket.error, e:
143
            raise errors.SocketConnectionError(self._host, self._port,
144
                                               msg='Unable to connect to',
145
                                               orig_error= e)
146
        except ftplib.error_perm, e:
147
            raise errors.TransportError(msg="Error setting up connection:"
148
                                        " %s" % str(e), orig_error=e)
149
        return connection, (user, password)
150
151
152
def get_test_permutations():
153
    """Return the permutations to be used in testing."""
3331.2.8 by Jelmer Vernooij
Run regular FTP tests against SecureFTP transport.
154
    from bzrlib import tests
155
    if tests.FTPServerFeature.available():
156
        from bzrlib.tests import ftp_server
3331.2.11 by Jelmer Vernooij
Review comments from Martin.
157
        return [(GSSAPIFtpTransport, ftp_server.FTPServer)]
3331.2.8 by Jelmer Vernooij
Run regular FTP tests against SecureFTP transport.
158
    else:
3331.2.10 by Jelmer Vernooij
Review feedback from Vincent.
159
        return []