Merge lp:~alecu/ubuntuone-client/proxy-tunnel-cookies into lp:ubuntuone-client

Proposed by Alejandro J. Cura
Status: Merged
Approved by: Alejandro J. Cura
Approved revision: 1240
Merged at revision: 1212
Proposed branch: lp:~alecu/ubuntuone-client/proxy-tunnel-cookies
Merge into: lp:ubuntuone-client
Prerequisite: lp:~alecu/ubuntuone-client/proxy-tunnel-auth
Diff against target: 480 lines (+121/-36)
7 files modified
tests/proxy/__init__.py (+2/-0)
tests/proxy/test_tunnel_client.py (+22/-8)
tests/proxy/test_tunnel_server.py (+39/-9)
tests/syncdaemon/test_tunnel_runner.py (+6/-2)
ubuntuone/proxy/common.py (+2/-0)
ubuntuone/proxy/tunnel_client.py (+26/-11)
ubuntuone/proxy/tunnel_server.py (+24/-6)
To merge this branch: bzr merge lp:~alecu/ubuntuone-client/proxy-tunnel-cookies
Reviewer Review Type Date Requested Status
Manuel de la Peña (community) Approve
Diego Sarmentero (community) Approve
Review via email: mp+97791@code.launchpad.net

Commit message

- Only allow connections that provide the right cookie thru the tunnel (LP: #929207).

Description of the change

- Only allow connections that provide the right cookie thru the tunnel (LP: #929207).

To post a comment you must log in.
1240. By Alejandro J. Cura

Merged proxy-tunnel-auth into proxy-tunnel-cookies.

Revision history for this message
Diego Sarmentero (diegosarmentero) wrote :

+1

review: Approve
Revision history for this message
Manuel de la Peña (mandel) wrote :

Looks good from spain.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'tests/proxy/__init__.py'
--- tests/proxy/__init__.py 2012-02-24 02:30:57 +0000
+++ tests/proxy/__init__.py 2012-03-16 13:44:20 +0000
@@ -24,6 +24,7 @@
24SIMPLERESOURCE = "simpleresource"24SIMPLERESOURCE = "simpleresource"
25DUMMY_KEY_FILENAME = "dummy.key"25DUMMY_KEY_FILENAME = "dummy.key"
26DUMMY_CERT_FILENAME = "dummy.cert"26DUMMY_CERT_FILENAME = "dummy.cert"
27FAKE_COOKIE = "fa:ke:co:ok:ie"
2728
2829
29class SaveHTTPChannel(http.HTTPChannel):30class SaveHTTPChannel(http.HTTPChannel):
@@ -137,6 +138,7 @@
137138
138 connected = True139 connected = True
139 disconnecting = False140 disconnecting = False
141 cookie = None
140142
141 def loseConnection(self):143 def loseConnection(self):
142 """Mark the connection as lost."""144 """Mark the connection as lost."""
143145
=== modified file 'tests/proxy/test_tunnel_client.py'
--- tests/proxy/test_tunnel_client.py 2012-03-14 17:38:31 +0000
+++ tests/proxy/test_tunnel_client.py 2012-03-16 13:44:20 +0000
@@ -23,6 +23,7 @@
2323
24from tests.proxy import (24from tests.proxy import (
25 FakeTransport,25 FakeTransport,
26 FAKE_COOKIE,
26 MockWebServer,27 MockWebServer,
27 SAMPLE_CONTENT,28 SAMPLE_CONTENT,
28 SIMPLERESOURCE,29 SIMPLERESOURCE,
@@ -72,11 +73,12 @@
72 yield super(TunnelClientProtocolTestCase, self).setUp()73 yield super(TunnelClientProtocolTestCase, self).setUp()
73 self.host, self.port = "9.9.9.9", 876574 self.host, self.port = "9.9.9.9", 8765
74 fake_addr = object()75 fake_addr = object()
76 self.cookie = FAKE_COOKIE
75 self.other_proto = SavingProtocol()77 self.other_proto = SavingProtocol()
76 other_factory = protocol.ClientFactory()78 other_factory = protocol.ClientFactory()
77 other_factory.buildProtocol = lambda _addr: self.other_proto79 other_factory.buildProtocol = lambda _addr: self.other_proto
78 tunnel_client_factory = tunnel_client.TunnelClientFactory(self.host,80 tunnel_client_factory = tunnel_client.TunnelClientFactory(self.host,
79 self.port, other_factory)81 self.port, other_factory, self.cookie)
80 tunnel_client_proto = tunnel_client_factory.buildProtocol(fake_addr)82 tunnel_client_proto = tunnel_client_factory.buildProtocol(fake_addr)
81 tunnel_client_proto.transport = FakeTransport()83 tunnel_client_proto.transport = FakeTransport()
82 tunnel_client_proto.connectionMade()84 tunnel_client_proto.connectionMade()
@@ -91,6 +93,13 @@
91 self.assertTrue(written.endswith(CRLF * 2),93 self.assertTrue(written.endswith(CRLF * 2),
92 "Ends with a double CRLF")94 "Ends with a double CRLF")
9395
96 def test_sends_cookie_header(self):
97 """Sends the expected cookie header."""
98 expected = "%s: %s" % (tunnel_client.TUNNEL_COOKIE_HEADER, self.cookie)
99 written = self.tunnel_client_proto.transport.getvalue()
100 headers = written.split(CRLF)[1:]
101 self.assertIn(expected, headers)
102
94 def test_handles_successful_connection(self):103 def test_handles_successful_connection(self):
95 """A successful connection is handled."""104 """A successful connection is handled."""
96 self.tunnel_client_proto.dataReceived(FAKE_HEADER)105 self.tunnel_client_proto.dataReceived(FAKE_HEADER)
@@ -132,7 +141,8 @@
132 def test_forwards_started(self):141 def test_forwards_started(self):
133 """The factory forwards the startedConnecting call."""142 """The factory forwards the startedConnecting call."""
134 fake_other_factory = FakeOtherFactory()143 fake_other_factory = FakeOtherFactory()
135 tcf = tunnel_client.TunnelClientFactory(None, None, fake_other_factory)144 tcf = tunnel_client.TunnelClientFactory(None, None, fake_other_factory,
145 FAKE_COOKIE)
136 fake_connector = object()146 fake_connector = object()
137 tcf.startedConnecting(fake_connector)147 tcf.startedConnecting(fake_connector)
138 self.assertEqual(fake_other_factory.started_called, (fake_connector,))148 self.assertEqual(fake_other_factory.started_called, (fake_connector,))
@@ -141,7 +151,8 @@
141 """The factory forwards the clientConnectionFailed call."""151 """The factory forwards the clientConnectionFailed call."""
142 fake_reason = object()152 fake_reason = object()
143 fake_other_factory = FakeOtherFactory()153 fake_other_factory = FakeOtherFactory()
144 tcf = tunnel_client.TunnelClientFactory(None, None, fake_other_factory)154 tcf = tunnel_client.TunnelClientFactory(None, None, fake_other_factory,
155 FAKE_COOKIE)
145 fake_connector = object()156 fake_connector = object()
146 tcf.clientConnectionFailed(fake_connector, fake_reason)157 tcf.clientConnectionFailed(fake_connector, fake_reason)
147 self.assertEqual(fake_other_factory.failed_called,158 self.assertEqual(fake_other_factory.failed_called,
@@ -151,7 +162,8 @@
151 """The factory forwards the clientConnectionLost call."""162 """The factory forwards the clientConnectionLost call."""
152 fake_reason = object()163 fake_reason = object()
153 fake_other_factory = FakeOtherFactory()164 fake_other_factory = FakeOtherFactory()
154 tcf = tunnel_client.TunnelClientFactory(None, None, fake_other_factory)165 tcf = tunnel_client.TunnelClientFactory(None, None, fake_other_factory,
166 FAKE_COOKIE)
155 fake_connector = object()167 fake_connector = object()
156 tcf.clientConnectionLost(fake_connector, fake_reason)168 tcf.clientConnectionLost(fake_connector, fake_reason)
157 self.assertEqual(fake_other_factory.lost_called,169 self.assertEqual(fake_other_factory.lost_called,
@@ -172,13 +184,15 @@
172 self.dest_url = self.ws.get_iri().encode("utf-8") + SIMPLERESOURCE184 self.dest_url = self.ws.get_iri().encode("utf-8") + SIMPLERESOURCE
173 self.dest_ssl_url = (self.ws.get_ssl_iri().encode("utf-8") +185 self.dest_ssl_url = (self.ws.get_ssl_iri().encode("utf-8") +
174 SIMPLERESOURCE)186 SIMPLERESOURCE)
175 self.tunnel_server = TunnelServer()187 self.cookie = FAKE_COOKIE
188 self.tunnel_server = TunnelServer(self.cookie)
176 self.addCleanup(self.tunnel_server.shutdown)189 self.addCleanup(self.tunnel_server.shutdown)
177190
178 @defer.inlineCallbacks191 @defer.inlineCallbacks
179 def test_connects_right(self):192 def test_connects_right(self):
180 """Uses the CONNECT method on the tunnel."""193 """Uses the CONNECT method on the tunnel."""
181 tunnel_client = TunnelClient("0.0.0.0", self.tunnel_server.port)194 tunnel_client = TunnelClient("0.0.0.0", self.tunnel_server.port,
195 self.cookie)
182 factory = client.HTTPClientFactory(self.dest_url)196 factory = client.HTTPClientFactory(self.dest_url)
183 scheme, host, port, path = client._parse(self.dest_url)197 scheme, host, port, path = client._parse(self.dest_url)
184 tunnel_client.connectTCP(host, port, factory)198 tunnel_client.connectTCP(host, port, factory)
@@ -188,8 +202,8 @@
188 @defer.inlineCallbacks202 @defer.inlineCallbacks
189 def test_starts_tls_connection(self):203 def test_starts_tls_connection(self):
190 """TLS is started after connecting; control passed to the client."""204 """TLS is started after connecting; control passed to the client."""
191 tunnel_client = TunnelClient("0.0.0.0",205 tunnel_client = TunnelClient("0.0.0.0", self.tunnel_server.port,
192 self.tunnel_server.port)206 self.cookie)
193 factory = client.HTTPClientFactory(self.dest_ssl_url)207 factory = client.HTTPClientFactory(self.dest_ssl_url)
194 scheme, host, port, path = client._parse(self.dest_ssl_url)208 scheme, host, port, path = client._parse(self.dest_ssl_url)
195 context_factory = ssl.ClientContextFactory()209 context_factory = ssl.ClientContextFactory()
196210
=== modified file 'tests/proxy/test_tunnel_server.py'
--- tests/proxy/test_tunnel_server.py 2012-03-16 13:44:20 +0000
+++ tests/proxy/test_tunnel_server.py 2012-03-16 13:44:20 +0000
@@ -26,6 +26,7 @@
2626
27from tests.proxy import (27from tests.proxy import (
28 FakeTransport,28 FakeTransport,
29 FAKE_COOKIE,
29 MockWebServer,30 MockWebServer,
30 SAMPLE_CONTENT,31 SAMPLE_CONTENT,
31 SIMPLERESOURCE,32 SIMPLERESOURCE,
@@ -38,6 +39,7 @@
38 "CONNECT %s HTTP/1.0" + CRLF +39 "CONNECT %s HTTP/1.0" + CRLF +
39 "Header1: value1" + CRLF +40 "Header1: value1" + CRLF +
40 "Header2: value2" + CRLF +41 "Header2: value2" + CRLF +
42 tunnel_server.TUNNEL_COOKIE_HEADER + ": %s" + CRLF +
41 CRLF +43 CRLF +
42 "GET %s HTTP/1.0" + CRLF + CRLF44 "GET %s HTTP/1.0" + CRLF + CRLF
43)45)
@@ -130,7 +132,8 @@
130 self.ws = MockWebServer()132 self.ws = MockWebServer()
131 self.addCleanup(self.ws.stop)133 self.addCleanup(self.ws.stop)
132 self.dest_url = self.ws.get_iri().encode("utf-8") + SIMPLERESOURCE134 self.dest_url = self.ws.get_iri().encode("utf-8") + SIMPLERESOURCE
133 self.tunnel_server = tunnel_server.TunnelServer()135 self.cookie = FAKE_COOKIE
136 self.tunnel_server = tunnel_server.TunnelServer(self.cookie)
134 self.addCleanup(self.tunnel_server.shutdown)137 self.addCleanup(self.tunnel_server.shutdown)
135138
136 def test_init(self):139 def test_init(self):
@@ -148,7 +151,8 @@
148 def test_complete_connection(self):151 def test_complete_connection(self):
149 """Test from the tunnel server down."""152 """Test from the tunnel server down."""
150 url = urlparse(self.dest_url)153 url = urlparse(self.dest_url)
151 fake_session = FAKE_SESSION_TEMPLATE % (url.netloc, url.path)154 fake_session = FAKE_SESSION_TEMPLATE % (
155 url.netloc, self.cookie, url.path)
152 client = FakeClientFactory(fake_session)156 client = FakeClientFactory(fake_session)
153 reactor.connectTCP("0.0.0.0", self.tunnel_server.port, client)157 reactor.connectTCP("0.0.0.0", self.tunnel_server.port, client)
154 response = yield client.response158 response = yield client.response
@@ -196,11 +200,14 @@
196 self.addCleanup(self.ws.stop)200 self.addCleanup(self.ws.stop)
197 self.dest_url = self.ws.get_iri().encode("utf-8") + SIMPLERESOURCE201 self.dest_url = self.ws.get_iri().encode("utf-8") + SIMPLERESOURCE
198 self.transport = FakeTransport()202 self.transport = FakeTransport()
203 self.transport.cookie = FAKE_COOKIE
199 self.fake_client = FakeClient()204 self.fake_client = FakeClient()
200 self.proto = tunnel_server.ServerTunnelProtocol(205 self.proto = tunnel_server.ServerTunnelProtocol(
201 lambda _: self.fake_client)206 lambda _: self.fake_client)
202 self.fake_client.protocol = self.proto207 self.fake_client.protocol = self.proto
203 self.proto.transport = self.transport208 self.proto.transport = self.transport
209 self.cookie_line = "%s: %s" % (tunnel_server.TUNNEL_COOKIE_HEADER,
210 FAKE_COOKIE)
204211
205 def test_broken_request(self):212 def test_broken_request(self):
206 """Broken request."""213 """Broken request."""
@@ -223,7 +230,8 @@
223 def test_connection_is_established(self):230 def test_connection_is_established(self):
224 """The response code is sent."""231 """The response code is sent."""
225 expected = "HTTP/1.0 200 Proxy connection established" + CRLF232 expected = "HTTP/1.0 200 Proxy connection established" + CRLF
226 self.proto.dataReceived("CONNECT 127.0.0.1:9999 HTTP/1.0" + CRLF * 2)233 self.proto.dataReceived("CONNECT 127.0.0.1:9999 HTTP/1.0" + CRLF +
234 self.cookie_line + CRLF * 2)
227 self.assertTrue(self.transport.getvalue().startswith(expected),235 self.assertTrue(self.transport.getvalue().startswith(expected),
228 "First line must be the response status")236 "First line must be the response status")
229237
@@ -232,7 +240,8 @@
232 error = tunnel_server.ConnectionError()240 error = tunnel_server.ConnectionError()
233 self.patch(self.fake_client, "connection_result", defer.fail(error))241 self.patch(self.fake_client, "connection_result", defer.fail(error))
234 expected = "HTTP/1.0 500 Connection error" + CRLF242 expected = "HTTP/1.0 500 Connection error" + CRLF
235 self.proto.dataReceived("CONNECT 127.0.0.1:9999 HTTP/1.0" + CRLF * 2)243 self.proto.dataReceived("CONNECT 127.0.0.1:9999 HTTP/1.0" + CRLF +
244 self.cookie_line + CRLF * 2)
236 self.assertTrue(self.transport.getvalue().startswith(expected),245 self.assertTrue(self.transport.getvalue().startswith(expected),
237 "The connection should fail at this point.")246 "The connection should fail at this point.")
238247
@@ -247,10 +256,25 @@
247 "Header2: value2" + CRLF + CRLF)256 "Header2: value2" + CRLF + CRLF)
248 self.assertEqual(self.proto.received_headers, expected)257 self.assertEqual(self.proto.received_headers, expected)
249258
259 def test_cookie_header_present(self):
260 """The cookie header must be present."""
261 self.proto.received_headers = [
262 (tunnel_server.TUNNEL_COOKIE_HEADER, FAKE_COOKIE),
263 ]
264 self.proto.verify_cookie()
265
266 def test_cookie_header_absent(self):
267 """The tunnel should refuse connections without the cookie."""
268 self.proto.received_headers = []
269 exception = self.assertRaises(tunnel_server.ConnectionError,
270 self.proto.verify_cookie)
271 self.assertEqual(exception.code, 418)
272
250 def test_successful_connect(self):273 def test_successful_connect(self):
251 """A successful connect thru the tunnel."""274 """A successful connect thru the tunnel."""
252 url = urlparse(self.dest_url)275 url = urlparse(self.dest_url)
253 data = FAKE_SESSION_TEMPLATE % (url.netloc, url.path)276 data = FAKE_SESSION_TEMPLATE % (url.netloc, self.transport.cookie,
277 url.path)
254 self.proto.dataReceived(data)278 self.proto.dataReceived(data)
255 lines = self.transport.getvalue().split(CRLF)279 lines = self.transport.getvalue().split(CRLF)
256 self.assertEqual(lines[-1], SAMPLE_CONTENT)280 self.assertEqual(lines[-1], SAMPLE_CONTENT)
@@ -264,6 +288,7 @@
264 def test_keyring_credentials_are_retried(self):288 def test_keyring_credentials_are_retried(self):
265 """Wrong credentials are retried with values from keyring."""289 """Wrong credentials are retried with values from keyring."""
266 self.fake_client.check_credentials = True290 self.fake_client.check_credentials = True
291 self.patch(self.proto, "verify_cookie", lambda: None)
267 self.patch(self.proto, "error_response",292 self.patch(self.proto, "error_response",
268 lambda code, desc: self.fail(desc))293 lambda code, desc: self.fail(desc))
269 self.proto.proxy_domain = "xxx"294 self.proto.proxy_domain = "xxx"
@@ -610,12 +635,17 @@
610 tunnel_server.main([])635 tunnel_server.main([])
611 self.assertEqual(len(self.called), 1)636 self.assertEqual(len(self.called), 1)
612637
613 def test_on_proxies_enabled_prints_port(self):638 def test_on_proxies_enabled_prints_port_and_cookie(self):
614 """With proxies enabled print port to stdout and start the mainloop."""639 """With proxies enabled print port to stdout and start the mainloop."""
640 self.patch(tunnel_server.uuid, "uuid4", lambda: FAKE_COOKIE)
615 self.proxies_enabled = True641 self.proxies_enabled = True
616 tunnel_server.main(["example.com", "443"])642 port = 443
617 self.assertIn(tunnel_server.TUNNEL_PORT_LABEL + ":",643 tunnel_server.main(["example.com", str(port)])
618 self.fake_stdout.getvalue())644 stdout = self.fake_stdout.getvalue()
645
646 self.assertIn(tunnel_server.TUNNEL_PORT_LABEL + ": ", stdout)
647 cookie_line = tunnel_server.TUNNEL_COOKIE_LABEL + ": " + FAKE_COOKIE
648 self.assertIn(cookie_line, stdout)
619649
620 def test_on_proxies_disabled_exit(self):650 def test_on_proxies_disabled_exit(self):
621 """With proxies disabled, print a message and exit gracefully."""651 """With proxies disabled, print a message and exit gracefully."""
622652
=== modified file 'tests/syncdaemon/test_tunnel_runner.py'
--- tests/syncdaemon/test_tunnel_runner.py 2012-03-12 17:42:57 +0000
+++ tests/syncdaemon/test_tunnel_runner.py 2012-03-16 13:44:20 +0000
@@ -18,6 +18,7 @@
18from twisted.internet import defer, error, reactor, task18from twisted.internet import defer, error, reactor, task
19from twisted.trial.unittest import TestCase19from twisted.trial.unittest import TestCase
2020
21from tests.proxy import FAKE_COOKIE
21from ubuntuone.proxy import tunnel_client22from ubuntuone.proxy import tunnel_client
22from ubuntuone.syncdaemon import tunnel_runner23from ubuntuone.syncdaemon import tunnel_runner
2324
@@ -106,9 +107,12 @@
106 self.assertEqual(client, clock)107 self.assertEqual(client, clock)
107108
108 @defer.inlineCallbacks109 @defer.inlineCallbacks
109 def test_tunnel_process_prints_port_number(self):110 def test_tunnel_process_prints_port_number_and_cookie(self):
110 """The tunnel process prints the port number."""111 """The tunnel process prints the port number."""
111 received = "%s: %d\n" % (tunnel_client.TUNNEL_PORT_LABEL, FAKE_PORT)112 received = "%s: %d\n%s: %s\n" % (
113 tunnel_client.TUNNEL_PORT_LABEL, FAKE_PORT,
114 tunnel_client.TUNNEL_COOKIE_LABEL, FAKE_COOKIE)
112 self.process_protocol.outReceived(received)115 self.process_protocol.outReceived(received)
113 client = yield self.tr.get_client()116 client = yield self.tr.get_client()
114 self.assertEqual(client.tunnel_port, FAKE_PORT)117 self.assertEqual(client.tunnel_port, FAKE_PORT)
118 self.assertEqual(client.cookie, FAKE_COOKIE)
115119
=== modified file 'ubuntuone/proxy/common.py'
--- ubuntuone/proxy/common.py 2012-03-16 13:44:20 +0000
+++ ubuntuone/proxy/common.py 2012-03-16 13:44:20 +0000
@@ -19,6 +19,8 @@
1919
20CRLF = "\r\n"20CRLF = "\r\n"
21TUNNEL_PORT_LABEL = "Tunnel port"21TUNNEL_PORT_LABEL = "Tunnel port"
22TUNNEL_COOKIE_LABEL = "Tunnel cookie"
23TUNNEL_COOKIE_HEADER = "Proxy-Tunnel-Cookie"
2224
2325
24class BaseTunnelProtocol(basic.LineReceiver):26class BaseTunnelProtocol(basic.LineReceiver):
2527
=== modified file 'ubuntuone/proxy/tunnel_client.py'
--- ubuntuone/proxy/tunnel_client.py 2012-03-14 17:38:31 +0000
+++ ubuntuone/proxy/tunnel_client.py 2012-03-16 13:44:20 +0000
@@ -19,7 +19,13 @@
1919
20from twisted.internet import protocol, reactor20from twisted.internet import protocol, reactor
2121
22from ubuntuone.proxy.common import BaseTunnelProtocol, CRLF, TUNNEL_PORT_LABEL22from ubuntuone.proxy.common import (
23 BaseTunnelProtocol,
24 CRLF,
25 TUNNEL_COOKIE_LABEL,
26 TUNNEL_COOKIE_HEADER,
27 TUNNEL_PORT_LABEL,
28)
2329
24METHOD_LINE = "CONNECT %s:%d HTTP/1.0" + CRLF30METHOD_LINE = "CONNECT %s:%d HTTP/1.0" + CRLF
25LOCALHOST = "127.0.0.1"31LOCALHOST = "127.0.0.1"
@@ -34,7 +40,8 @@
34 method_line = METHOD_LINE % (self.factory.tunnel_host,40 method_line = METHOD_LINE % (self.factory.tunnel_host,
35 self.factory.tunnel_port)41 self.factory.tunnel_port)
36 headers = {42 headers = {
37 "User-Agent": "Ubuntu One tunnel client"43 "User-Agent": "Ubuntu One tunnel client",
44 TUNNEL_COOKIE_HEADER: self.factory.cookie,
38 }45 }
39 self.transport.write(method_line +46 self.transport.write(method_line +
40 self.format_headers(headers) +47 self.format_headers(headers) +
@@ -70,13 +77,14 @@
7077
71 protocol = TunnelClientProtocol78 protocol = TunnelClientProtocol
7279
73 def __init__(self, tunnel_host, tunnel_port, other_factory,80 def __init__(self, tunnel_host, tunnel_port, other_factory, cookie,
74 context_factory=None):81 context_factory=None):
75 """Initialize this factory."""82 """Initialize this factory."""
76 self.tunnel_host = tunnel_host83 self.tunnel_host = tunnel_host
77 self.tunnel_port = tunnel_port84 self.tunnel_port = tunnel_port
78 self.other_factory = other_factory85 self.other_factory = other_factory
79 self.context_factory = context_factory86 self.context_factory = context_factory
87 self.cookie = cookie
8088
81 def startedConnecting(self, connector):89 def startedConnecting(self, connector):
82 """Forward this call to the other factory."""90 """Forward this call to the other factory."""
@@ -94,16 +102,17 @@
94class TunnelClient(object):102class TunnelClient(object):
95 """A client for the proxy tunnel."""103 """A client for the proxy tunnel."""
96104
97 def __init__(self, tunnel_host, tunnel_port):105 def __init__(self, tunnel_host, tunnel_port, cookie):
98 """Initialize this client."""106 """Initialize this client."""
99 self.tunnel_host = tunnel_host107 self.tunnel_host = tunnel_host
100 self.tunnel_port = tunnel_port108 self.tunnel_port = tunnel_port
109 self.cookie = cookie
101110
102 def connectTCP(self, host, port, factory, *args, **kwargs):111 def connectTCP(self, host, port, factory, *args, **kwargs):
103 """A connectTCP going thru the tunnel."""112 """A connectTCP going thru the tunnel."""
104 logger.info("Connecting (TCP) to %r:%r via tunnel at %r:%r",113 logger.info("Connecting (TCP) to %r:%r via tunnel at %r:%r",
105 host, port, self.tunnel_host, self.tunnel_port)114 host, port, self.tunnel_host, self.tunnel_port)
106 tunnel_factory = TunnelClientFactory(host, port, factory)115 tunnel_factory = TunnelClientFactory(host, port, factory, self.cookie)
107 return reactor.connectTCP(self.tunnel_host, self.tunnel_port,116 return reactor.connectTCP(self.tunnel_host, self.tunnel_port,
108 tunnel_factory, *args, **kwargs)117 tunnel_factory, *args, **kwargs)
109118
@@ -112,7 +121,7 @@
112 """A connectSSL going thru the tunnel."""121 """A connectSSL going thru the tunnel."""
113 logger.info("Connecting (SSL) to %r:%r via tunnel at %r:%r",122 logger.info("Connecting (SSL) to %r:%r via tunnel at %r:%r",
114 host, port, self.tunnel_host, self.tunnel_port)123 host, port, self.tunnel_host, self.tunnel_port)
115 tunnel_factory = TunnelClientFactory(host, port, factory,124 tunnel_factory = TunnelClientFactory(host, port, factory, self.cookie,
116 contextFactory)125 contextFactory)
117 return reactor.connectTCP(self.tunnel_host, self.tunnel_port,126 return reactor.connectTCP(self.tunnel_host, self.tunnel_port,
118 tunnel_factory, *args, **kwargs)127 tunnel_factory, *args, **kwargs)
@@ -127,6 +136,8 @@
127 """Initialize this protocol."""136 """Initialize this protocol."""
128 self.client_d = client_d137 self.client_d = client_d
129 self.timer = None138 self.timer = None
139 self.port = None
140 self.cookie = None
130141
131 def connectionMade(self):142 def connectionMade(self):
132 """The process has started, start a timer."""143 """The process has started, start a timer."""
@@ -159,8 +170,12 @@
159 for line in data.split("\n"):170 for line in data.split("\n"):
160 if line.startswith(TUNNEL_PORT_LABEL):171 if line.startswith(TUNNEL_PORT_LABEL):
161 _header, port = line.split(":", 1)172 _header, port = line.split(":", 1)
162 port = int(port.strip())173 self.port = int(port.strip())
163 logger.info("Tunnel process listening on port %r.", port)174 if line.startswith(TUNNEL_COOKIE_LABEL):
164 client = TunnelClient(LOCALHOST, port)175 _header, cookie = line.split(":", 1)
165 self.client_d.callback(client)176 self.cookie = cookie.strip()
166 break177
178 if self.port and self.cookie:
179 logger.info("Tunnel process listening on port %r.", self.port)
180 client = TunnelClient(LOCALHOST, self.port, self.cookie)
181 self.client_d.callback(client)
167182
=== modified file 'ubuntuone/proxy/tunnel_server.py'
--- ubuntuone/proxy/tunnel_server.py 2012-03-16 13:44:20 +0000
+++ ubuntuone/proxy/tunnel_server.py 2012-03-16 13:44:20 +0000
@@ -30,6 +30,7 @@
30"""30"""
3131
32import sys32import sys
33import uuid
3334
34from PyQt4.QtCore import QCoreApplication, QTimer35from PyQt4.QtCore import QCoreApplication, QTimer
35from PyQt4.QtNetwork import (36from PyQt4.QtNetwork import (
@@ -46,7 +47,13 @@
4647
47from ubuntu_sso.keyring import Keyring48from ubuntu_sso.keyring import Keyring
48from ubuntu_sso.utils.webclient import gsettings49from ubuntu_sso.utils.webclient import gsettings
49from ubuntuone.proxy.common import BaseTunnelProtocol, CRLF, TUNNEL_PORT_LABEL50from ubuntuone.proxy.common import (
51 BaseTunnelProtocol,
52 CRLF,
53 TUNNEL_COOKIE_HEADER,
54 TUNNEL_COOKIE_LABEL,
55 TUNNEL_PORT_LABEL,
56)
50from ubuntuone.proxy.logger import logger57from ubuntuone.proxy.logger import logger
5158
52DEFAULT_CODE = 50059DEFAULT_CODE = 500
@@ -219,10 +226,17 @@
219 except ValueError:226 except ValueError:
220 self.error_response(400, "Bad request")227 self.error_response(400, "Bad request")
221228
229 def verify_cookie(self):
230 """Fail if the cookie is wrong or missing."""
231 cookie_received = dict(self.received_headers).get(TUNNEL_COOKIE_HEADER)
232 if cookie_received != self.transport.cookie:
233 raise ConnectionError(418, "Please see RFC 2324")
234
222 @defer.inlineCallbacks235 @defer.inlineCallbacks
223 def headers_done(self):236 def headers_done(self):
224 """An empty line was received, start connecting and switch mode."""237 """An empty line was received, start connecting and switch mode."""
225 try:238 try:
239 self.verify_cookie()
226 try:240 try:
227 logger.info("Connecting once")241 logger.info("Connecting once")
228 self.client = self.client_class(self)242 self.client = self.client_class(self)
@@ -267,8 +281,9 @@
267281
268 implements(interfaces.ITransport)282 implements(interfaces.ITransport)
269283
270 def __init__(self, local_socket):284 def __init__(self, local_socket, cookie):
271 """Initialize this Tunnel instance."""285 """Initialize this Tunnel instance."""
286 self.cookie = cookie
272 self.disconnecting = False287 self.disconnecting = False
273 self.local_socket = local_socket288 self.local_socket = local_socket
274 self.protocol = ServerTunnelProtocol(RemoteSocket)289 self.protocol = ServerTunnelProtocol(RemoteSocket)
@@ -300,9 +315,10 @@
300class TunnelServer(object):315class TunnelServer(object):
301 """A server for tunnel instances."""316 """A server for tunnel instances."""
302317
303 def __init__(self):318 def __init__(self, cookie):
304 """Initialize this tunnel instance."""319 """Initialize this tunnel instance."""
305 self.tunnels = []320 self.tunnels = []
321 self.cookie = cookie
306 self.server = QTcpServer(QCoreApplication.instance())322 self.server = QTcpServer(QCoreApplication.instance())
307 self.server.newConnection.connect(self.new_connection)323 self.server.newConnection.connect(self.new_connection)
308 self.server.listen(QHostAddress.LocalHost, 0)324 self.server.listen(QHostAddress.LocalHost, 0)
@@ -312,7 +328,7 @@
312 """On a new connection create a new tunnel instance."""328 """On a new connection create a new tunnel instance."""
313 logger.info("New connection made")329 logger.info("New connection made")
314 local_socket = self.server.nextPendingConnection()330 local_socket = self.server.nextPendingConnection()
315 tunnel = Tunnel(local_socket)331 tunnel = Tunnel(local_socket, self.cookie)
316 self.tunnels.append(tunnel)332 self.tunnels.append(tunnel)
317333
318 def shutdown(self):334 def shutdown(self):
@@ -352,7 +368,9 @@
352 from dbus.mainloop.qt import DBusQtMainLoop368 from dbus.mainloop.qt import DBusQtMainLoop
353 DBusQtMainLoop(set_as_default=True)369 DBusQtMainLoop(set_as_default=True)
354 app = QCoreApplication(argv)370 app = QCoreApplication(argv)
355 tunnel_server = TunnelServer()371 cookie = str(uuid.uuid4())
356 sys.stdout.write("%s: %d\n" % (TUNNEL_PORT_LABEL, tunnel_server.port))372 tunnel_server = TunnelServer(cookie)
373 sys.stdout.write("%s: %d\n" % (TUNNEL_PORT_LABEL, tunnel_server.port) +
374 "%s: %s\n" % (TUNNEL_COOKIE_LABEL, cookie))
357 sys.stdout.flush()375 sys.stdout.flush()
358 app.exec_()376 app.exec_()

Subscribers

People subscribed via source and target branches