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