Skip to content

Commit fa29bd8

Browse files
committed
At least, insofar as the new tests pass...!
1 parent aefd28a commit fa29bd8

File tree

2 files changed

+44
-2
lines changed

2 files changed

+44
-2
lines changed

Diff for: paramiko/common.py

+1
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
MSG_USERAUTH_GSSAPI_RESPONSE, MSG_USERAUTH_GSSAPI_TOKEN = range(60, 62)
3333
MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE, MSG_USERAUTH_GSSAPI_ERROR,\
3434
MSG_USERAUTH_GSSAPI_ERRTOK, MSG_USERAUTH_GSSAPI_MIC = range(63, 67)
35+
HIGHEST_USERAUTH_MESSAGE_ID = 79
3536
MSG_GLOBAL_REQUEST, MSG_REQUEST_SUCCESS, MSG_REQUEST_FAILURE = range(80, 83)
3637
MSG_CHANNEL_OPEN, MSG_CHANNEL_OPEN_SUCCESS, MSG_CHANNEL_OPEN_FAILURE, \
3738
MSG_CHANNEL_WINDOW_ADJUST, MSG_CHANNEL_DATA, MSG_CHANNEL_EXTENDED_DATA, \

Diff for: paramiko/transport.py

+43-2
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@
4949
MSG_CHANNEL_FAILURE, MSG_CHANNEL_DATA, MSG_CHANNEL_EXTENDED_DATA,
5050
MSG_CHANNEL_WINDOW_ADJUST, MSG_CHANNEL_REQUEST, MSG_CHANNEL_EOF,
5151
MSG_CHANNEL_CLOSE, MIN_WINDOW_SIZE, MIN_PACKET_SIZE, MAX_WINDOW_SIZE,
52-
DEFAULT_WINDOW_SIZE, DEFAULT_MAX_PACKET_SIZE,
52+
DEFAULT_WINDOW_SIZE, DEFAULT_MAX_PACKET_SIZE, HIGHEST_USERAUTH_MESSAGE_ID,
5353
)
5454
from paramiko.compress import ZlibCompressor, ZlibDecompressor
5555
from paramiko.dsskey import DSSKey
@@ -1760,6 +1760,43 @@ def _sanitize_packet_size(self, max_packet_size):
17601760
max_packet_size = self.default_max_packet_size
17611761
return clamp_value(MIN_PACKET_SIZE, max_packet_size, MAX_WINDOW_SIZE)
17621762

1763+
def _ensure_authed(self, ptype, message):
1764+
"""
1765+
Checks message type against current auth state.
1766+
1767+
If server mode, and auth has not succeeded, and the message is of a
1768+
post-auth type (channel open or global request) an appropriate error
1769+
response Message is crafted and returned to caller for sending.
1770+
1771+
Otherwise (client mode, authed, or pre-auth message) returns None.
1772+
"""
1773+
if (
1774+
not self.server_mode
1775+
or ptype <= HIGHEST_USERAUTH_MESSAGE_ID
1776+
or self.is_authenticated()
1777+
):
1778+
return None
1779+
# WELP. We must be dealing with someone trying to do non-auth things
1780+
# without being authed. Tell them off, based on message class.
1781+
reply = Message()
1782+
# Global requests have no details, just failure.
1783+
if ptype == MSG_GLOBAL_REQUEST:
1784+
reply.add_byte(cMSG_REQUEST_FAILURE)
1785+
# Channel opens let us reject w/ a specific type + message.
1786+
elif ptype == MSG_CHANNEL_OPEN:
1787+
kind = message.get_text()
1788+
chanid = message.get_int()
1789+
reply.add_byte(cMSG_CHANNEL_OPEN_FAILURE)
1790+
reply.add_int(chanid)
1791+
reply.add_int(OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED)
1792+
reply.add_string('')
1793+
reply.add_string('en')
1794+
# NOTE: Post-open channel messages do not need checking; the above will
1795+
# reject attemps to open channels, meaning that even if a malicious
1796+
# user tries to send a MSG_CHANNEL_REQUEST, it will simply fall under
1797+
# the logic that handles unknown channel IDs (as the channel list will
1798+
# be empty.)
1799+
return reply
17631800

17641801
def run(self):
17651802
# (use the exposed "run" method, because if we specify a thread target
@@ -1820,7 +1857,11 @@ def run(self):
18201857
continue
18211858

18221859
if ptype in self._handler_table:
1823-
self._handler_table[ptype](self, m)
1860+
error_msg = self._ensure_authed(ptype, m)
1861+
if error_msg:
1862+
self._send_message(error_msg)
1863+
else:
1864+
self._handler_table[ptype](self, m)
18241865
elif ptype in self._channel_handler_table:
18251866
chanid = m.get_int()
18261867
chan = self._channels.get(chanid)

0 commit comments

Comments
 (0)