|
49 | 49 | MSG_CHANNEL_FAILURE, MSG_CHANNEL_DATA, MSG_CHANNEL_EXTENDED_DATA, |
50 | 50 | MSG_CHANNEL_WINDOW_ADJUST, MSG_CHANNEL_REQUEST, MSG_CHANNEL_EOF, |
51 | 51 | 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, |
53 | 53 | ) |
54 | 54 | from paramiko.compress import ZlibCompressor, ZlibDecompressor |
55 | 55 | from paramiko.dsskey import DSSKey |
@@ -1760,6 +1760,43 @@ def _sanitize_packet_size(self, max_packet_size): |
1760 | 1760 | max_packet_size = self.default_max_packet_size |
1761 | 1761 | return clamp_value(MIN_PACKET_SIZE, max_packet_size, MAX_WINDOW_SIZE) |
1762 | 1762 |
|
| 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 |
1763 | 1800 |
|
1764 | 1801 | def run(self): |
1765 | 1802 | # (use the exposed "run" method, because if we specify a thread target |
@@ -1820,7 +1857,11 @@ def run(self): |
1820 | 1857 | continue |
1821 | 1858 |
|
1822 | 1859 | 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) |
1824 | 1865 | elif ptype in self._channel_handler_table: |
1825 | 1866 | chanid = m.get_int() |
1826 | 1867 | chan = self._channels.get(chanid) |
|
0 commit comments