Line 0
Link Here
|
|
|
1 |
Source: https://launchpadlibrarian.net/370632961/24_ssh_hostnames-lp1710979 |
2 |
Description: Refuse to connect to ssh hostnames starting with a dash. Fixes LP:1710979 |
3 |
Author: Jelmer Vernooij <jelmer@jelmer.uk> |
4 |
Origin: commit, Revision ID: jelmer@jelmer.uk-20170819145828-qk2p7qlg5j2fbsiz |
5 |
|
6 |
* Security fix: hostnames starting with a dash in bzr+ssh URLs |
7 |
are now filtered out when using a subprocess SSH client. |
8 |
. |
9 |
Thanks to Augie Fackler for reporting. |
10 |
(Jelmer Vernooij, #1710979) |
11 |
|
12 |
|
13 |
=== modified file 'bzrlib/tests/test_ssh_transport.py' |
14 |
--- |
15 |
bzrlib/tests/test_ssh_transport.py | 38 ++++++++++++++++++++++++++++++++++++- |
16 |
bzrlib/transport/ssh.py | 16 +++++++++++++-- |
17 |
2 files changed, 51 insertions(+), 3 deletions(-) |
18 |
|
19 |
Index: bzrlib/tests/test_ssh_transport.py |
20 |
=================================================================== |
21 |
--- bzrlib/tests/test_ssh_transport.py |
22 |
+++ bzrlib/tests/test_ssh_transport.py |
23 |
@@ -22,6 +22,7 @@ from bzrlib.transport.ssh import ( |
24 |
SSHCorpSubprocessVendor, |
25 |
LSHSubprocessVendor, |
26 |
SSHVendorManager, |
27 |
+ StrangeHostname, |
28 |
) |
29 |
|
30 |
|
31 |
@@ -161,6 +162,19 @@ class SSHVendorManagerTests(TestCase): |
32 |
|
33 |
class SubprocessVendorsTests(TestCase): |
34 |
|
35 |
+ def test_openssh_command_tricked(self): |
36 |
+ vendor = OpenSSHSubprocessVendor() |
37 |
+ self.assertEqual( |
38 |
+ vendor._get_vendor_specific_argv( |
39 |
+ "user", "-oProxyCommand=blah", 100, command=["bzr"]), |
40 |
+ ["ssh", "-oForwardX11=no", "-oForwardAgent=no", |
41 |
+ "-oClearAllForwardings=yes", |
42 |
+ "-oNoHostAuthenticationForLocalhost=yes", |
43 |
+ "-p", "100", |
44 |
+ "-l", "user", |
45 |
+ "--", |
46 |
+ "-oProxyCommand=blah", "bzr"]) |
47 |
+ |
48 |
def test_openssh_command_arguments(self): |
49 |
vendor = OpenSSHSubprocessVendor() |
50 |
self.assertEqual( |
51 |
@@ -171,6 +185,7 @@ class SubprocessVendorsTests(TestCase): |
52 |
"-oNoHostAuthenticationForLocalhost=yes", |
53 |
"-p", "100", |
54 |
"-l", "user", |
55 |
+ "--", |
56 |
"host", "bzr"] |
57 |
) |
58 |
|
59 |
@@ -184,9 +199,16 @@ class SubprocessVendorsTests(TestCase): |
60 |
"-oNoHostAuthenticationForLocalhost=yes", |
61 |
"-p", "100", |
62 |
"-l", "user", |
63 |
- "-s", "host", "sftp"] |
64 |
+ "-s", "--", "host", "sftp"] |
65 |
) |
66 |
|
67 |
+ def test_openssh_command_tricked(self): |
68 |
+ vendor = SSHCorpSubprocessVendor() |
69 |
+ self.assertRaises( |
70 |
+ StrangeHostname, |
71 |
+ vendor._get_vendor_specific_argv, |
72 |
+ "user", "-oProxyCommand=host", 100, command=["bzr"]) |
73 |
+ |
74 |
def test_sshcorp_command_arguments(self): |
75 |
vendor = SSHCorpSubprocessVendor() |
76 |
self.assertEqual( |
77 |
@@ -209,6 +231,13 @@ class SubprocessVendorsTests(TestCase): |
78 |
"-s", "sftp", "host"] |
79 |
) |
80 |
|
81 |
+ def test_lsh_command_tricked(self): |
82 |
+ vendor = LSHSubprocessVendor() |
83 |
+ self.assertRaises( |
84 |
+ StrangeHostname, |
85 |
+ vendor._get_vendor_specific_argv, |
86 |
+ "user", "-oProxyCommand=host", 100, command=["bzr"]) |
87 |
+ |
88 |
def test_lsh_command_arguments(self): |
89 |
vendor = LSHSubprocessVendor() |
90 |
self.assertEqual( |
91 |
@@ -231,6 +260,13 @@ class SubprocessVendorsTests(TestCase): |
92 |
"--subsystem", "sftp", "host"] |
93 |
) |
94 |
|
95 |
+ def test_plink_command_tricked(self): |
96 |
+ vendor = PLinkSubprocessVendor() |
97 |
+ self.assertRaises( |
98 |
+ StrangeHostname, |
99 |
+ vendor._get_vendor_specific_argv, |
100 |
+ "user", "-oProxyCommand=host", 100, command=["bzr"]) |
101 |
+ |
102 |
def test_plink_command_arguments(self): |
103 |
vendor = PLinkSubprocessVendor() |
104 |
self.assertEqual( |
105 |
Index: bzrlib/transport/ssh.py |
106 |
=================================================================== |
107 |
--- bzrlib/transport/ssh.py |
108 |
+++ bzrlib/transport/ssh.py |
109 |
@@ -46,6 +46,10 @@ else: |
110 |
from paramiko.sftp_client import SFTPClient |
111 |
|
112 |
|
113 |
+class StrangeHostname(errors.BzrError): |
114 |
+ _fmt = "Refusing to connect to strange SSH hostname %(hostname)s" |
115 |
+ |
116 |
+ |
117 |
SYSTEM_HOSTKEYS = {} |
118 |
BZR_HOSTKEYS = {} |
119 |
|
120 |
@@ -360,6 +364,11 @@ class SubprocessVendor(SSHVendor): |
121 |
# tests, but beware of using PIPE which may hang due to not being read. |
122 |
_stderr_target = None |
123 |
|
124 |
+ @staticmethod |
125 |
+ def _check_hostname(arg): |
126 |
+ if arg.startswith('-'): |
127 |
+ raise StrangeHostname(hostname=arg) |
128 |
+ |
129 |
def _connect(self, argv): |
130 |
# Attempt to make a socketpair to use as stdin/stdout for the SSH |
131 |
# subprocess. We prefer sockets to pipes because they support |
132 |
@@ -424,9 +433,9 @@ class OpenSSHSubprocessVendor(Subprocess |
133 |
if username is not None: |
134 |
args.extend(['-l', username]) |
135 |
if subsystem is not None: |
136 |
- args.extend(['-s', host, subsystem]) |
137 |
+ args.extend(['-s', '--', host, subsystem]) |
138 |
else: |
139 |
- args.extend([host] + command) |
140 |
+ args.extend(['--', host] + command) |
141 |
return args |
142 |
|
143 |
register_ssh_vendor('openssh', OpenSSHSubprocessVendor()) |
144 |
@@ -439,6 +448,7 @@ class SSHCorpSubprocessVendor(Subprocess |
145 |
|
146 |
def _get_vendor_specific_argv(self, username, host, port, subsystem=None, |
147 |
command=None): |
148 |
+ self._check_hostname(host) |
149 |
args = [self.executable_path, '-x'] |
150 |
if port is not None: |
151 |
args.extend(['-p', str(port)]) |
152 |
@@ -460,6 +470,7 @@ class LSHSubprocessVendor(SubprocessVend |
153 |
|
154 |
def _get_vendor_specific_argv(self, username, host, port, subsystem=None, |
155 |
command=None): |
156 |
+ self._check_hostname(host) |
157 |
args = [self.executable_path] |
158 |
if port is not None: |
159 |
args.extend(['-p', str(port)]) |
160 |
@@ -481,6 +492,7 @@ class PLinkSubprocessVendor(SubprocessVe |
161 |
|
162 |
def _get_vendor_specific_argv(self, username, host, port, subsystem=None, |
163 |
command=None): |
164 |
+ self._check_hostname(host) |
165 |
args = [self.executable_path, '-x', '-a', '-ssh', '-2', '-batch'] |
166 |
if port is not None: |
167 |
args.extend(['-P', str(port)]) |