You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

575 lines
20 KiB

7 years ago
  1. # Copyright (C) 2003-2007 Robey Pointer <robeypointer@gmail.com>
  2. #
  3. # This file is part of paramiko.
  4. #
  5. # Paramiko is free software; you can redistribute it and/or modify it under the
  6. # terms of the GNU Lesser General Public License as published by the Free
  7. # Software Foundation; either version 2.1 of the License, or (at your option)
  8. # any later version.
  9. #
  10. # Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY
  11. # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  12. # A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
  13. # details.
  14. #
  15. # You should have received a copy of the GNU Lesser General Public License
  16. # along with Paramiko; if not, write to the Free Software Foundation, Inc.,
  17. # 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  18. """
  19. Packet handling
  20. """
  21. import errno
  22. import os
  23. import socket
  24. import struct
  25. import threading
  26. import time
  27. from hmac import HMAC
  28. from paramiko import util
  29. from paramiko.common import (
  30. linefeed_byte, cr_byte_value, asbytes, MSG_NAMES, DEBUG, xffffffff,
  31. zero_byte,
  32. )
  33. from paramiko.py3compat import u, byte_ord
  34. from paramiko.ssh_exception import SSHException, ProxyCommandFailure
  35. from paramiko.message import Message
  36. def compute_hmac(key, message, digest_class):
  37. return HMAC(key, message, digest_class).digest()
  38. class NeedRekeyException (Exception):
  39. """
  40. Exception indicating a rekey is needed.
  41. """
  42. pass
  43. def first_arg(e):
  44. arg = None
  45. if type(e.args) is tuple and len(e.args) > 0:
  46. arg = e.args[0]
  47. return arg
  48. class Packetizer (object):
  49. """
  50. Implementation of the base SSH packet protocol.
  51. """
  52. # READ the secsh RFC's before raising these values. if anything,
  53. # they should probably be lower.
  54. REKEY_PACKETS = pow(2, 29)
  55. REKEY_BYTES = pow(2, 29)
  56. # Allow receiving this many packets after a re-key request before
  57. # terminating
  58. REKEY_PACKETS_OVERFLOW_MAX = pow(2, 29)
  59. # Allow receiving this many bytes after a re-key request before terminating
  60. REKEY_BYTES_OVERFLOW_MAX = pow(2, 29)
  61. def __init__(self, socket):
  62. self.__socket = socket
  63. self.__logger = None
  64. self.__closed = False
  65. self.__dump_packets = False
  66. self.__need_rekey = False
  67. self.__init_count = 0
  68. self.__remainder = bytes()
  69. # used for noticing when to re-key:
  70. self.__sent_bytes = 0
  71. self.__sent_packets = 0
  72. self.__received_bytes = 0
  73. self.__received_packets = 0
  74. self.__received_bytes_overflow = 0
  75. self.__received_packets_overflow = 0
  76. # current inbound/outbound ciphering:
  77. self.__block_size_out = 8
  78. self.__block_size_in = 8
  79. self.__mac_size_out = 0
  80. self.__mac_size_in = 0
  81. self.__block_engine_out = None
  82. self.__block_engine_in = None
  83. self.__sdctr_out = False
  84. self.__mac_engine_out = None
  85. self.__mac_engine_in = None
  86. self.__mac_key_out = bytes()
  87. self.__mac_key_in = bytes()
  88. self.__compress_engine_out = None
  89. self.__compress_engine_in = None
  90. self.__sequence_number_out = 0
  91. self.__sequence_number_in = 0
  92. # lock around outbound writes (packet computation)
  93. self.__write_lock = threading.RLock()
  94. # keepalives:
  95. self.__keepalive_interval = 0
  96. self.__keepalive_last = time.time()
  97. self.__keepalive_callback = None
  98. self.__timer = None
  99. self.__handshake_complete = False
  100. self.__timer_expired = False
  101. @property
  102. def closed(self):
  103. return self.__closed
  104. def set_log(self, log):
  105. """
  106. Set the Python log object to use for logging.
  107. """
  108. self.__logger = log
  109. def set_outbound_cipher(self, block_engine, block_size, mac_engine,
  110. mac_size, mac_key, sdctr=False):
  111. """
  112. Switch outbound data cipher.
  113. """
  114. self.__block_engine_out = block_engine
  115. self.__sdctr_out = sdctr
  116. self.__block_size_out = block_size
  117. self.__mac_engine_out = mac_engine
  118. self.__mac_size_out = mac_size
  119. self.__mac_key_out = mac_key
  120. self.__sent_bytes = 0
  121. self.__sent_packets = 0
  122. # wait until the reset happens in both directions before clearing
  123. # rekey flag
  124. self.__init_count |= 1
  125. if self.__init_count == 3:
  126. self.__init_count = 0
  127. self.__need_rekey = False
  128. def set_inbound_cipher(
  129. self, block_engine, block_size, mac_engine, mac_size, mac_key):
  130. """
  131. Switch inbound data cipher.
  132. """
  133. self.__block_engine_in = block_engine
  134. self.__block_size_in = block_size
  135. self.__mac_engine_in = mac_engine
  136. self.__mac_size_in = mac_size
  137. self.__mac_key_in = mac_key
  138. self.__received_bytes = 0
  139. self.__received_packets = 0
  140. self.__received_bytes_overflow = 0
  141. self.__received_packets_overflow = 0
  142. # wait until the reset happens in both directions before clearing
  143. # rekey flag
  144. self.__init_count |= 2
  145. if self.__init_count == 3:
  146. self.__init_count = 0
  147. self.__need_rekey = False
  148. def set_outbound_compressor(self, compressor):
  149. self.__compress_engine_out = compressor
  150. def set_inbound_compressor(self, compressor):
  151. self.__compress_engine_in = compressor
  152. def close(self):
  153. self.__closed = True
  154. self.__socket.close()
  155. def set_hexdump(self, hexdump):
  156. self.__dump_packets = hexdump
  157. def get_hexdump(self):
  158. return self.__dump_packets
  159. def get_mac_size_in(self):
  160. return self.__mac_size_in
  161. def get_mac_size_out(self):
  162. return self.__mac_size_out
  163. def need_rekey(self):
  164. """
  165. Returns ``True`` if a new set of keys needs to be negotiated. This
  166. will be triggered during a packet read or write, so it should be
  167. checked after every read or write, or at least after every few.
  168. """
  169. return self.__need_rekey
  170. def set_keepalive(self, interval, callback):
  171. """
  172. Turn on/off the callback keepalive. If ``interval`` seconds pass with
  173. no data read from or written to the socket, the callback will be
  174. executed and the timer will be reset.
  175. """
  176. self.__keepalive_interval = interval
  177. self.__keepalive_callback = callback
  178. self.__keepalive_last = time.time()
  179. def read_timer(self):
  180. self.__timer_expired = True
  181. def start_handshake(self, timeout):
  182. """
  183. Tells `Packetizer` that the handshake process started.
  184. Starts a book keeping timer that can signal a timeout in the
  185. handshake process.
  186. :param float timeout: amount of seconds to wait before timing out
  187. """
  188. if not self.__timer:
  189. self.__timer = threading.Timer(float(timeout), self.read_timer)
  190. self.__timer.start()
  191. def handshake_timed_out(self):
  192. """
  193. Checks if the handshake has timed out.
  194. If `start_handshake` wasn't called before the call to this function,
  195. the return value will always be `False`. If the handshake completed
  196. before a timeout was reached, the return value will be `False`
  197. :return: handshake time out status, as a `bool`
  198. """
  199. if not self.__timer:
  200. return False
  201. if self.__handshake_complete:
  202. return False
  203. return self.__timer_expired
  204. def complete_handshake(self):
  205. """
  206. Tells `Packetizer` that the handshake has completed.
  207. """
  208. if self.__timer:
  209. self.__timer.cancel()
  210. self.__timer_expired = False
  211. self.__handshake_complete = True
  212. def read_all(self, n, check_rekey=False):
  213. """
  214. Read as close to N bytes as possible, blocking as long as necessary.
  215. :param int n: number of bytes to read
  216. :return: the data read, as a `str`
  217. :raises:
  218. ``EOFError`` -- if the socket was closed before all the bytes could
  219. be read
  220. """
  221. out = bytes()
  222. # handle over-reading from reading the banner line
  223. if len(self.__remainder) > 0:
  224. out = self.__remainder[:n]
  225. self.__remainder = self.__remainder[n:]
  226. n -= len(out)
  227. while n > 0:
  228. got_timeout = False
  229. if self.handshake_timed_out():
  230. raise EOFError()
  231. try:
  232. x = self.__socket.recv(n)
  233. if len(x) == 0:
  234. raise EOFError()
  235. out += x
  236. n -= len(x)
  237. except socket.timeout:
  238. got_timeout = True
  239. except socket.error as e:
  240. # on Linux, sometimes instead of socket.timeout, we get
  241. # EAGAIN. this is a bug in recent (> 2.6.9) kernels but
  242. # we need to work around it.
  243. arg = first_arg(e)
  244. if arg == errno.EAGAIN:
  245. got_timeout = True
  246. elif arg == errno.EINTR:
  247. # syscall interrupted; try again
  248. pass
  249. elif self.__closed:
  250. raise EOFError()
  251. else:
  252. raise
  253. if got_timeout:
  254. if self.__closed:
  255. raise EOFError()
  256. if check_rekey and (len(out) == 0) and self.__need_rekey:
  257. raise NeedRekeyException()
  258. self._check_keepalive()
  259. return out
  260. def write_all(self, out):
  261. self.__keepalive_last = time.time()
  262. iteration_with_zero_as_return_value = 0
  263. while len(out) > 0:
  264. retry_write = False
  265. try:
  266. n = self.__socket.send(out)
  267. except socket.timeout:
  268. retry_write = True
  269. except socket.error as e:
  270. arg = first_arg(e)
  271. if arg == errno.EAGAIN:
  272. retry_write = True
  273. elif arg == errno.EINTR:
  274. # syscall interrupted; try again
  275. retry_write = True
  276. else:
  277. n = -1
  278. except ProxyCommandFailure:
  279. raise # so it doesn't get swallowed by the below catchall
  280. except Exception:
  281. # could be: (32, 'Broken pipe')
  282. n = -1
  283. if retry_write:
  284. n = 0
  285. if self.__closed:
  286. n = -1
  287. else:
  288. if n == 0 and iteration_with_zero_as_return_value > 10:
  289. # We shouldn't retry the write, but we didn't
  290. # manage to send anything over the socket. This might be an
  291. # indication that we have lost contact with the remote
  292. # side, but are yet to receive an EOFError or other socket
  293. # errors. Let's give it some iteration to try and catch up.
  294. n = -1
  295. iteration_with_zero_as_return_value += 1
  296. if n < 0:
  297. raise EOFError()
  298. if n == len(out):
  299. break
  300. out = out[n:]
  301. return
  302. def readline(self, timeout):
  303. """
  304. Read a line from the socket. We assume no data is pending after the
  305. line, so it's okay to attempt large reads.
  306. """
  307. buf = self.__remainder
  308. while linefeed_byte not in buf:
  309. buf += self._read_timeout(timeout)
  310. n = buf.index(linefeed_byte)
  311. self.__remainder = buf[n + 1:]
  312. buf = buf[:n]
  313. if (len(buf) > 0) and (buf[-1] == cr_byte_value):
  314. buf = buf[:-1]
  315. return u(buf)
  316. def send_message(self, data):
  317. """
  318. Write a block of data using the current cipher, as an SSH block.
  319. """
  320. # encrypt this sucka
  321. data = asbytes(data)
  322. cmd = byte_ord(data[0])
  323. if cmd in MSG_NAMES:
  324. cmd_name = MSG_NAMES[cmd]
  325. else:
  326. cmd_name = '${:x}'.format(cmd)
  327. orig_len = len(data)
  328. self.__write_lock.acquire()
  329. try:
  330. if self.__compress_engine_out is not None:
  331. data = self.__compress_engine_out(data)
  332. packet = self._build_packet(data)
  333. if self.__dump_packets:
  334. self._log(
  335. DEBUG,
  336. 'Write packet <{}>, length {}'.format(cmd_name, orig_len)
  337. )
  338. self._log(DEBUG, util.format_binary(packet, 'OUT: '))
  339. if self.__block_engine_out is not None:
  340. out = self.__block_engine_out.update(packet)
  341. else:
  342. out = packet
  343. # + mac
  344. if self.__block_engine_out is not None:
  345. payload = struct.pack(
  346. '>I', self.__sequence_number_out) + packet
  347. out += compute_hmac(
  348. self.__mac_key_out,
  349. payload,
  350. self.__mac_engine_out)[:self.__mac_size_out]
  351. self.__sequence_number_out = \
  352. (self.__sequence_number_out + 1) & xffffffff
  353. self.write_all(out)
  354. self.__sent_bytes += len(out)
  355. self.__sent_packets += 1
  356. sent_too_much = (
  357. self.__sent_packets >= self.REKEY_PACKETS or
  358. self.__sent_bytes >= self.REKEY_BYTES
  359. )
  360. if sent_too_much and not self.__need_rekey:
  361. # only ask once for rekeying
  362. msg = "Rekeying (hit {} packets, {} bytes sent)"
  363. self._log(DEBUG, msg.format(
  364. self.__sent_packets, self.__sent_bytes,
  365. ))
  366. self.__received_bytes_overflow = 0
  367. self.__received_packets_overflow = 0
  368. self._trigger_rekey()
  369. finally:
  370. self.__write_lock.release()
  371. def read_message(self):
  372. """
  373. Only one thread should ever be in this function (no other locking is
  374. done).
  375. :raises: `.SSHException` -- if the packet is mangled
  376. :raises: `.NeedRekeyException` -- if the transport should rekey
  377. """
  378. header = self.read_all(self.__block_size_in, check_rekey=True)
  379. if self.__block_engine_in is not None:
  380. header = self.__block_engine_in.update(header)
  381. if self.__dump_packets:
  382. self._log(DEBUG, util.format_binary(header, 'IN: '))
  383. packet_size = struct.unpack('>I', header[:4])[0]
  384. # leftover contains decrypted bytes from the first block (after the
  385. # length field)
  386. leftover = header[4:]
  387. if (packet_size - len(leftover)) % self.__block_size_in != 0:
  388. raise SSHException('Invalid packet blocking')
  389. buf = self.read_all(packet_size + self.__mac_size_in - len(leftover))
  390. packet = buf[:packet_size - len(leftover)]
  391. post_packet = buf[packet_size - len(leftover):]
  392. if self.__block_engine_in is not None:
  393. packet = self.__block_engine_in.update(packet)
  394. if self.__dump_packets:
  395. self._log(DEBUG, util.format_binary(packet, 'IN: '))
  396. packet = leftover + packet
  397. if self.__mac_size_in > 0:
  398. mac = post_packet[:self.__mac_size_in]
  399. mac_payload = struct.pack(
  400. '>II', self.__sequence_number_in, packet_size) + packet
  401. my_mac = compute_hmac(
  402. self.__mac_key_in,
  403. mac_payload,
  404. self.__mac_engine_in)[:self.__mac_size_in]
  405. if not util.constant_time_bytes_eq(my_mac, mac):
  406. raise SSHException('Mismatched MAC')
  407. padding = byte_ord(packet[0])
  408. payload = packet[1:packet_size - padding]
  409. if self.__dump_packets:
  410. self._log(
  411. DEBUG,
  412. 'Got payload ({} bytes, {} padding)'.format(
  413. packet_size, padding
  414. )
  415. )
  416. if self.__compress_engine_in is not None:
  417. payload = self.__compress_engine_in(payload)
  418. msg = Message(payload[1:])
  419. msg.seqno = self.__sequence_number_in
  420. self.__sequence_number_in = (self.__sequence_number_in + 1) & xffffffff
  421. # check for rekey
  422. raw_packet_size = packet_size + self.__mac_size_in + 4
  423. self.__received_bytes += raw_packet_size
  424. self.__received_packets += 1
  425. if self.__need_rekey:
  426. # we've asked to rekey -- give them some packets to comply before
  427. # dropping the connection
  428. self.__received_bytes_overflow += raw_packet_size
  429. self.__received_packets_overflow += 1
  430. if (self.__received_packets_overflow >=
  431. self.REKEY_PACKETS_OVERFLOW_MAX) or \
  432. (self.__received_bytes_overflow >=
  433. self.REKEY_BYTES_OVERFLOW_MAX):
  434. raise SSHException(
  435. 'Remote transport is ignoring rekey requests')
  436. elif (self.__received_packets >= self.REKEY_PACKETS) or \
  437. (self.__received_bytes >= self.REKEY_BYTES):
  438. # only ask once for rekeying
  439. err = "Rekeying (hit {} packets, {} bytes received)"
  440. self._log(DEBUG, err.format(
  441. self.__received_packets, self.__received_bytes,
  442. ))
  443. self.__received_bytes_overflow = 0
  444. self.__received_packets_overflow = 0
  445. self._trigger_rekey()
  446. cmd = byte_ord(payload[0])
  447. if cmd in MSG_NAMES:
  448. cmd_name = MSG_NAMES[cmd]
  449. else:
  450. cmd_name = '${:x}'.format(cmd)
  451. if self.__dump_packets:
  452. self._log(
  453. DEBUG,
  454. 'Read packet <{}>, length {}'.format(cmd_name, len(payload))
  455. )
  456. return cmd, msg
  457. # ...protected...
  458. def _log(self, level, msg):
  459. if self.__logger is None:
  460. return
  461. if issubclass(type(msg), list):
  462. for m in msg:
  463. self.__logger.log(level, m)
  464. else:
  465. self.__logger.log(level, msg)
  466. def _check_keepalive(self):
  467. if (
  468. not self.__keepalive_interval or
  469. not self.__block_engine_out or
  470. self.__need_rekey
  471. ):
  472. # wait till we're encrypting, and not in the middle of rekeying
  473. return
  474. now = time.time()
  475. if now > self.__keepalive_last + self.__keepalive_interval:
  476. self.__keepalive_callback()
  477. self.__keepalive_last = now
  478. def _read_timeout(self, timeout):
  479. start = time.time()
  480. while True:
  481. try:
  482. x = self.__socket.recv(128)
  483. if len(x) == 0:
  484. raise EOFError()
  485. break
  486. except socket.timeout:
  487. pass
  488. except EnvironmentError as e:
  489. if first_arg(e) == errno.EINTR:
  490. pass
  491. else:
  492. raise
  493. if self.__closed:
  494. raise EOFError()
  495. now = time.time()
  496. if now - start >= timeout:
  497. raise socket.timeout()
  498. return x
  499. def _build_packet(self, payload):
  500. # pad up at least 4 bytes, to nearest block-size (usually 8)
  501. bsize = self.__block_size_out
  502. padding = 3 + bsize - ((len(payload) + 8) % bsize)
  503. packet = struct.pack('>IB', len(payload) + padding + 1, padding)
  504. packet += payload
  505. if self.__sdctr_out or self.__block_engine_out is None:
  506. # cute trick i caught openssh doing: if we're not encrypting or
  507. # SDCTR mode (RFC4344),
  508. # don't waste random bytes for the padding
  509. packet += (zero_byte * padding)
  510. else:
  511. packet += os.urandom(padding)
  512. return packet
  513. def _trigger_rekey(self):
  514. # outside code should check for this flag
  515. self.__need_rekey = True

Powered by TurnKey Linux.