Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
At least, insofar as the new tests pass...!
  • Loading branch information
bitprophet committed Mar 13, 2018
1 parent aefd28a commit fa29bd8
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 2 deletions.
1 change: 1 addition & 0 deletions paramiko/common.py
Expand Up @@ -32,6 +32,7 @@
MSG_USERAUTH_GSSAPI_RESPONSE, MSG_USERAUTH_GSSAPI_TOKEN = range(60, 62)
MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE, MSG_USERAUTH_GSSAPI_ERROR,\
MSG_USERAUTH_GSSAPI_ERRTOK, MSG_USERAUTH_GSSAPI_MIC = range(63, 67)
HIGHEST_USERAUTH_MESSAGE_ID = 79
MSG_GLOBAL_REQUEST, MSG_REQUEST_SUCCESS, MSG_REQUEST_FAILURE = range(80, 83)
MSG_CHANNEL_OPEN, MSG_CHANNEL_OPEN_SUCCESS, MSG_CHANNEL_OPEN_FAILURE, \
MSG_CHANNEL_WINDOW_ADJUST, MSG_CHANNEL_DATA, MSG_CHANNEL_EXTENDED_DATA, \
Expand Down
45 changes: 43 additions & 2 deletions paramiko/transport.py
Expand Up @@ -49,7 +49,7 @@
MSG_CHANNEL_FAILURE, MSG_CHANNEL_DATA, MSG_CHANNEL_EXTENDED_DATA,
MSG_CHANNEL_WINDOW_ADJUST, MSG_CHANNEL_REQUEST, MSG_CHANNEL_EOF,
MSG_CHANNEL_CLOSE, MIN_WINDOW_SIZE, MIN_PACKET_SIZE, MAX_WINDOW_SIZE,
DEFAULT_WINDOW_SIZE, DEFAULT_MAX_PACKET_SIZE,
DEFAULT_WINDOW_SIZE, DEFAULT_MAX_PACKET_SIZE, HIGHEST_USERAUTH_MESSAGE_ID,
)
from paramiko.compress import ZlibCompressor, ZlibDecompressor
from paramiko.dsskey import DSSKey
Expand Down Expand Up @@ -1760,6 +1760,43 @@ def _sanitize_packet_size(self, max_packet_size):
max_packet_size = self.default_max_packet_size
return clamp_value(MIN_PACKET_SIZE, max_packet_size, MAX_WINDOW_SIZE)

def _ensure_authed(self, ptype, message):
"""
Checks message type against current auth state.
If server mode, and auth has not succeeded, and the message is of a
post-auth type (channel open or global request) an appropriate error
response Message is crafted and returned to caller for sending.
Otherwise (client mode, authed, or pre-auth message) returns None.
"""
if (
not self.server_mode
or ptype <= HIGHEST_USERAUTH_MESSAGE_ID
or self.is_authenticated()
):
return None
# WELP. We must be dealing with someone trying to do non-auth things
# without being authed. Tell them off, based on message class.
reply = Message()
# Global requests have no details, just failure.
if ptype == MSG_GLOBAL_REQUEST:
reply.add_byte(cMSG_REQUEST_FAILURE)
# Channel opens let us reject w/ a specific type + message.
elif ptype == MSG_CHANNEL_OPEN:
kind = message.get_text()
chanid = message.get_int()
reply.add_byte(cMSG_CHANNEL_OPEN_FAILURE)
reply.add_int(chanid)
reply.add_int(OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED)
reply.add_string('')
reply.add_string('en')
# NOTE: Post-open channel messages do not need checking; the above will
# reject attemps to open channels, meaning that even if a malicious
# user tries to send a MSG_CHANNEL_REQUEST, it will simply fall under
# the logic that handles unknown channel IDs (as the channel list will
# be empty.)
return reply

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

if ptype in self._handler_table:
self._handler_table[ptype](self, m)
error_msg = self._ensure_authed(ptype, m)
if error_msg:
self._send_message(error_msg)
else:
self._handler_table[ptype](self, m)
elif ptype in self._channel_handler_table:
chanid = m.get_int()
chan = self._channels.get(chanid)
Expand Down

0 comments on commit fa29bd8

Please sign in to comment.