~bzr-pqm/bzr/bzr.dev

2070.5.1 by Andrew Bennetts
Add ChrootTransportDecorator.
1
# Copyright (C) 2006 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
"""Implementation of Transport that prevents access to locations above a set
18
root.
19
"""
2379.2.1 by Robert Collins
Rewritten chroot transport that prevents accidental chroot escapes when
20
from urlparse import urlparse
2070.5.1 by Andrew Bennetts
Add ChrootTransportDecorator.
21
22
from bzrlib import errors, urlutils
2379.2.1 by Robert Collins
Rewritten chroot transport that prevents accidental chroot escapes when
23
from bzrlib.transport import (
24
    get_transport,
25
    register_transport,
26
    Server,
27
    Transport,
28
    unregister_transport,
29
    )
2070.5.1 by Andrew Bennetts
Add ChrootTransportDecorator.
30
from bzrlib.transport.decorator import TransportDecorator, DecoratorServer
2379.2.1 by Robert Collins
Rewritten chroot transport that prevents accidental chroot escapes when
31
from bzrlib.transport.memory import MemoryTransport
32
33
34
class ChrootServer(Server):
2379.2.3 by Robert Collins
Review feedback.
35
    """User space 'chroot' facility.
36
    
37
    The server's get_url returns the url for a chroot transport mapped to the
38
    backing transport. The url is of the form chroot-xxx:/// so parent
39
    directories of the backing transport are not visible. The chroot url will
40
    not allow '..' sequences to result in requests to the chroot affecting
41
    directories outside the backing transport.
42
    """
2379.2.1 by Robert Collins
Rewritten chroot transport that prevents accidental chroot escapes when
43
44
    def __init__(self, backing_transport):
45
        self.backing_transport = backing_transport
46
47
    def _factory(self, url):
48
        assert url.startswith(self.scheme)
49
        return ChrootTransport(self, url)
50
51
    def get_url(self):
52
        return self.scheme
53
54
    def setUp(self):
55
        self.scheme = 'chroot-%d:///' % id(self)
56
        register_transport(self.scheme, self._factory)
57
58
    def tearDown(self):
59
        unregister_transport(self.scheme, self._factory)
60
61
62
class ChrootTransport(Transport):
2379.2.3 by Robert Collins
Review feedback.
63
    """A ChrootTransport.
64
65
    Please see ChrootServer for details.
66
    """
2379.2.1 by Robert Collins
Rewritten chroot transport that prevents accidental chroot escapes when
67
68
    def __init__(self, server, base):
69
        self.server = server
70
        if not base.endswith('/'):
71
            base += '/'
72
        Transport.__init__(self, base)
73
        self.base_path = self.base[len(self.server.scheme)-1:]
74
        self.scheme = self.server.scheme
75
76
    def _call(self, methodname, relpath, *args):
77
        method = getattr(self.server.backing_transport, methodname)
78
        return method(self._safe_relpath(relpath), *args)
79
80
    def _safe_relpath(self, relpath):
81
        safe_relpath = self._combine_paths(self.base_path, relpath)
82
        assert safe_relpath.startswith('/')
83
        return safe_relpath[1:]
84
85
    # Transport methods
86
    def abspath(self, relpath):
87
        return self.scheme + self._safe_relpath(relpath)
88
2070.5.1 by Andrew Bennetts
Add ChrootTransportDecorator.
89
    def append_file(self, relpath, f, mode=None):
2379.2.1 by Robert Collins
Rewritten chroot transport that prevents accidental chroot escapes when
90
        return self._call('append_file', relpath, f, mode)
91
92
    def clone(self, relpath):
93
        return ChrootTransport(self.server, self.abspath(relpath))
2070.5.1 by Andrew Bennetts
Add ChrootTransportDecorator.
94
95
    def delete(self, relpath):
2379.2.1 by Robert Collins
Rewritten chroot transport that prevents accidental chroot escapes when
96
        return self._call('delete', relpath)
2070.5.1 by Andrew Bennetts
Add ChrootTransportDecorator.
97
98
    def delete_tree(self, relpath):
2379.2.1 by Robert Collins
Rewritten chroot transport that prevents accidental chroot escapes when
99
        return self._call('delete_tree', relpath)
2070.5.1 by Andrew Bennetts
Add ChrootTransportDecorator.
100
2164.2.15 by Vincent Ladeuil
Http redirections are not followed by default. Do not use hints
101
    def get(self, relpath):
2379.2.1 by Robert Collins
Rewritten chroot transport that prevents accidental chroot escapes when
102
        return self._call('get', relpath)
2070.5.1 by Andrew Bennetts
Add ChrootTransportDecorator.
103
104
    def has(self, relpath):
2379.2.1 by Robert Collins
Rewritten chroot transport that prevents accidental chroot escapes when
105
        return self._call('has', relpath)
106
107
    def iter_files_recursive(self):
108
        backing_transport = self.server.backing_transport.clone(
109
            self._safe_relpath('.'))
110
        return backing_transport.iter_files_recursive()
111
112
    def listable(self):
113
        return self.server.backing_transport.listable()
2070.5.1 by Andrew Bennetts
Add ChrootTransportDecorator.
114
115
    def list_dir(self, relpath):
2379.2.1 by Robert Collins
Rewritten chroot transport that prevents accidental chroot escapes when
116
        return self._call('list_dir', relpath)
2070.5.1 by Andrew Bennetts
Add ChrootTransportDecorator.
117
118
    def lock_read(self, relpath):
2379.2.1 by Robert Collins
Rewritten chroot transport that prevents accidental chroot escapes when
119
        return self._call('lock_read', relpath)
2070.5.1 by Andrew Bennetts
Add ChrootTransportDecorator.
120
121
    def lock_write(self, relpath):
2379.2.1 by Robert Collins
Rewritten chroot transport that prevents accidental chroot escapes when
122
        return self._call('lock_write', relpath)
2070.5.1 by Andrew Bennetts
Add ChrootTransportDecorator.
123
124
    def mkdir(self, relpath, mode=None):
2379.2.1 by Robert Collins
Rewritten chroot transport that prevents accidental chroot escapes when
125
        return self._call('mkdir', relpath, mode)
2070.5.1 by Andrew Bennetts
Add ChrootTransportDecorator.
126
127
    def put_file(self, relpath, f, mode=None):
2379.2.1 by Robert Collins
Rewritten chroot transport that prevents accidental chroot escapes when
128
        return self._call('put_file', relpath, f, mode)
2070.5.1 by Andrew Bennetts
Add ChrootTransportDecorator.
129
130
    def rename(self, rel_from, rel_to):
2379.2.1 by Robert Collins
Rewritten chroot transport that prevents accidental chroot escapes when
131
        return self._call('rename', rel_from, self._safe_relpath(rel_to))
2070.5.1 by Andrew Bennetts
Add ChrootTransportDecorator.
132
133
    def rmdir(self, relpath):
2379.2.1 by Robert Collins
Rewritten chroot transport that prevents accidental chroot escapes when
134
        return self._call('rmdir', relpath)
2070.5.1 by Andrew Bennetts
Add ChrootTransportDecorator.
135
136
    def stat(self, relpath):
2379.2.1 by Robert Collins
Rewritten chroot transport that prevents accidental chroot escapes when
137
        return self._call('stat', relpath)
138
139
140
class TestingChrootServer(ChrootServer):
141
142
    def __init__(self):
2379.2.2 by Robert Collins
Further test-usability for chroots.
143
        """TestingChrootServer is not usable until setUp is called."""
144
145
    def setUp(self, backing_server=None):
146
        """Setup the Chroot on backing_server."""
147
        if backing_server is not None:
148
            self.backing_transport = get_transport(backing_server.get_url())
149
        else:
150
            self.backing_transport = get_transport('.')
151
        ChrootServer.setUp(self)
2070.5.1 by Andrew Bennetts
Add ChrootTransportDecorator.
152
153
154
def get_test_permutations():
155
    """Return the permutations to be used in testing."""
2379.2.1 by Robert Collins
Rewritten chroot transport that prevents accidental chroot escapes when
156
    return [(ChrootTransport, TestingChrootServer),
2070.5.1 by Andrew Bennetts
Add ChrootTransportDecorator.
157
            ]