1   
  2   
  3   
  4   
  5   
  6   
  7   
  8   
  9   
 10   
 11   
 12   
 13   
 14   
 15   
 16   
 17   
 18   
 19  """ 
 20  Packetizer. 
 21  """ 
 22   
 23  import errno 
 24  import select 
 25  import socket 
 26  import struct 
 27  import threading 
 28  import time 
 29   
 30  from paramiko.common import * 
 31  from paramiko import util 
 32  from paramiko.ssh_exception import SSHException 
 33  from paramiko.message import Message 
 34   
 35   
 36  got_r_hmac = False 
 37  try: 
 38      import r_hmac 
 39      got_r_hmac = True 
 40  except ImportError: 
 41      pass 
 43      if got_r_hmac: 
 44          return r_hmac.HMAC(key, message, digest_class).digest() 
 45      from Crypto.Hash import HMAC 
 46      return HMAC.HMAC(key, message, digest_class).digest() 
  47   
 48   
 51   
 52   
 54      """ 
 55      Implementation of the base SSH packet protocol. 
 56      """ 
 57   
 58       
 59       
 60      REKEY_PACKETS = pow(2, 30) 
 61      REKEY_BYTES = pow(2, 30) 
 62       
 64          self.__socket = socket 
 65          self.__logger = None 
 66          self.__closed = False 
 67          self.__dump_packets = False 
 68          self.__need_rekey = False 
 69          self.__init_count = 0 
 70          self.__remainder = '' 
 71           
 72           
 73          self.__sent_bytes = 0 
 74          self.__sent_packets = 0 
 75          self.__received_bytes = 0 
 76          self.__received_packets = 0 
 77          self.__received_packets_overflow = 0 
 78           
 79           
 80          self.__block_size_out = 8 
 81          self.__block_size_in = 8 
 82          self.__mac_size_out = 0 
 83          self.__mac_size_in = 0 
 84          self.__block_engine_out = None 
 85          self.__block_engine_in = None 
 86          self.__mac_engine_out = None 
 87          self.__mac_engine_in = None 
 88          self.__mac_key_out = '' 
 89          self.__mac_key_in = '' 
 90          self.__compress_engine_out = None 
 91          self.__compress_engine_in = None 
 92          self.__sequence_number_out = 0L 
 93          self.__sequence_number_in = 0L 
 94   
 95           
 96          self.__write_lock = threading.RLock() 
 97   
 98           
 99          self.__keepalive_interval = 0 
100          self.__keepalive_last = time.time() 
101          self.__keepalive_callback = None 
 102           
104          """ 
105          Set the python log object to use for logging. 
106          """ 
107          self.__logger = log 
 108       
110          """ 
111          Switch outbound data cipher. 
112          """ 
113          self.__block_engine_out = block_engine 
114          self.__block_size_out = block_size 
115          self.__mac_engine_out = mac_engine 
116          self.__mac_size_out = mac_size 
117          self.__mac_key_out = mac_key 
118          self.__sent_bytes = 0 
119          self.__sent_packets = 0 
120           
121          self.__init_count |= 1 
122          if self.__init_count == 3: 
123              self.__init_count = 0 
124              self.__need_rekey = False 
 125       
127          """ 
128          Switch inbound data cipher. 
129          """ 
130          self.__block_engine_in = block_engine 
131          self.__block_size_in = block_size 
132          self.__mac_engine_in = mac_engine 
133          self.__mac_size_in = mac_size 
134          self.__mac_key_in = mac_key 
135          self.__received_bytes = 0 
136          self.__received_packets = 0 
137          self.__received_packets_overflow = 0 
138           
139          self.__init_count |= 2 
140          if self.__init_count == 3: 
141              self.__init_count = 0 
142              self.__need_rekey = False 
 143       
145          self.__compress_engine_out = compressor 
 146       
148          self.__compress_engine_in = compressor 
 149           
151          self.__closed = True 
152          self.__socket.close() 
 153   
155          self.__dump_packets = hexdump 
 156           
158          return self.__dump_packets 
 159       
161          return self.__mac_size_in 
 162       
164          return self.__mac_size_out 
 165   
167          """ 
168          Returns C{True} if a new set of keys needs to be negotiated.  This 
169          will be triggered during a packet read or write, so it should be 
170          checked after every read or write, or at least after every few. 
171           
172          @return: C{True} if a new set of keys needs to be negotiated 
173          """ 
174          return self.__need_rekey 
 175       
177          """ 
178          Turn on/off the callback keepalive.  If C{interval} seconds pass with 
179          no data read from or written to the socket, the callback will be 
180          executed and the timer will be reset. 
181          """ 
182          self.__keepalive_interval = interval 
183          self.__keepalive_callback = callback 
184          self.__keepalive_last = time.time() 
 185       
186 -    def read_all(self, n, check_rekey=False): 
 187          """ 
188          Read as close to N bytes as possible, blocking as long as necessary. 
189           
190          @param n: number of bytes to read 
191          @type n: int 
192          @return: the data read 
193          @rtype: str 
194          @raise EOFError: if the socket was closed before all the bytes could 
195              be read 
196          """ 
197          out = '' 
198           
199          if len(self.__remainder) > 0: 
200              out = self.__remainder[:n] 
201              self.__remainder = self.__remainder[n:] 
202              n -= len(out) 
203          if PY22: 
204              return self._py22_read_all(n, out) 
205          while n > 0: 
206              got_timeout = False 
207              try: 
208                  x = self.__socket.recv(n) 
209                  if len(x) == 0: 
210                      raise EOFError() 
211                  out += x 
212                  n -= len(x) 
213              except socket.timeout: 
214                  got_timeout = True 
215              except socket.error, e: 
216                   
217                   
218                   
219                  if (type(e.args) is tuple) and (len(e.args) > 0) and (e.args[0] == errno.EAGAIN): 
220                      got_timeout = True 
221                  elif (type(e.args) is tuple) and (len(e.args) > 0) and (e.args[0] == errno.EINTR): 
222                       
223                      pass 
224                  elif self.__closed: 
225                      raise EOFError() 
226                  else: 
227                      raise 
228              if got_timeout: 
229                  if self.__closed: 
230                      raise EOFError() 
231                  if check_rekey and (len(out) == 0) and self.__need_rekey: 
232                      raise NeedRekeyException() 
233                  self._check_keepalive() 
234          return out 
 235   
237          self.__keepalive_last = time.time() 
238          while len(out) > 0: 
239              got_timeout = False 
240              try: 
241                  n = self.__socket.send(out) 
242              except socket.timeout: 
243                  got_timeout = True 
244              except socket.error, e: 
245                  if (type(e.args) is tuple) and (len(e.args) > 0) and (e.args[0] == errno.EAGAIN): 
246                      got_timeout = True 
247                  elif (type(e.args) is tuple) and (len(e.args) > 0) and (e.args[0] == errno.EINTR): 
248                       
249                      pass 
250                  else: 
251                      n = -1 
252              except Exception: 
253                   
254                  n = -1 
255              if got_timeout: 
256                  n = 0 
257                  if self.__closed: 
258                      n = -1 
259              if n < 0: 
260                  raise EOFError() 
261              if n == len(out): 
262                  break 
263              out = out[n:] 
264          return 
 265           
267          """ 
268          Read a line from the socket.  We assume no data is pending after the 
269          line, so it's okay to attempt large reads. 
270          """ 
271          buf = self.__remainder 
272          while not '\n' in buf: 
273              buf += self._read_timeout(timeout) 
274          n = buf.index('\n') 
275          self.__remainder = buf[n+1:] 
276          buf = buf[:n] 
277          if (len(buf) > 0) and (buf[-1] == '\r'): 
278              buf = buf[:-1] 
279          return buf 
 280           
282          """ 
283          Write a block of data using the current cipher, as an SSH block. 
284          """ 
285           
286          data = str(data) 
287          cmd = ord(data[0]) 
288          if cmd in MSG_NAMES: 
289              cmd_name = MSG_NAMES[cmd] 
290          else: 
291              cmd_name = '$%x' % cmd 
292          orig_len = len(data) 
293          self.__write_lock.acquire() 
294          try: 
295              if self.__compress_engine_out is not None: 
296                  data = self.__compress_engine_out(data) 
297              packet = self._build_packet(data) 
298              if self.__dump_packets: 
299                  self._log(DEBUG, 'Write packet <%s>, length %d' % (cmd_name, orig_len)) 
300                  self._log(DEBUG, util.format_binary(packet, 'OUT: ')) 
301              if self.__block_engine_out != None: 
302                  out = self.__block_engine_out.encrypt(packet) 
303              else: 
304                  out = packet 
305               
306              if self.__block_engine_out != None: 
307                  payload = struct.pack('>I', self.__sequence_number_out) + packet 
308                  out += compute_hmac(self.__mac_key_out, payload, self.__mac_engine_out)[:self.__mac_size_out] 
309              self.__sequence_number_out = (self.__sequence_number_out + 1) & 0xffffffffL 
310              self.write_all(out) 
311   
312              self.__sent_bytes += len(out) 
313              self.__sent_packets += 1 
314              if (self.__sent_packets % 100) == 0: 
315                   
316                  randpool.stir() 
317              if ((self.__sent_packets >= self.REKEY_PACKETS) or (self.__sent_bytes >= self.REKEY_BYTES)) \ 
318                     and not self.__need_rekey: 
319                   
320                  self._log(DEBUG, 'Rekeying (hit %d packets, %d bytes sent)' % 
321                            (self.__sent_packets, self.__sent_bytes)) 
322                  self.__received_packets_overflow = 0 
323                  self._trigger_rekey() 
324          finally: 
325              self.__write_lock.release() 
 326   
328          """ 
329          Only one thread should ever be in this function (no other locking is 
330          done). 
331           
332          @raise SSHException: if the packet is mangled 
333          @raise NeedRekeyException: if the transport should rekey 
334          """ 
335          header = self.read_all(self.__block_size_in, check_rekey=True) 
336          if self.__block_engine_in != None: 
337              header = self.__block_engine_in.decrypt(header) 
338          if self.__dump_packets: 
339              self._log(DEBUG, util.format_binary(header, 'IN: ')); 
340          packet_size = struct.unpack('>I', header[:4])[0] 
341           
342          leftover = header[4:] 
343          if (packet_size - len(leftover)) % self.__block_size_in != 0: 
344              raise SSHException('Invalid packet blocking') 
345          buf = self.read_all(packet_size + self.__mac_size_in - len(leftover)) 
346          packet = buf[:packet_size - len(leftover)] 
347          post_packet = buf[packet_size - len(leftover):] 
348          if self.__block_engine_in != None: 
349              packet = self.__block_engine_in.decrypt(packet) 
350          if self.__dump_packets: 
351              self._log(DEBUG, util.format_binary(packet, 'IN: ')); 
352          packet = leftover + packet 
353   
354          if self.__mac_size_in > 0: 
355              mac = post_packet[:self.__mac_size_in] 
356              mac_payload = struct.pack('>II', self.__sequence_number_in, packet_size) + packet 
357              my_mac = compute_hmac(self.__mac_key_in, mac_payload, self.__mac_engine_in)[:self.__mac_size_in] 
358              if my_mac != mac: 
359                  raise SSHException('Mismatched MAC') 
360          padding = ord(packet[0]) 
361          payload = packet[1:packet_size - padding] 
362          randpool.add_event() 
363          if self.__dump_packets: 
364              self._log(DEBUG, 'Got payload (%d bytes, %d padding)' % (packet_size, padding)) 
365   
366          if self.__compress_engine_in is not None: 
367              payload = self.__compress_engine_in(payload) 
368   
369          msg = Message(payload[1:]) 
370          msg.seqno = self.__sequence_number_in 
371          self.__sequence_number_in = (self.__sequence_number_in + 1) & 0xffffffffL 
372           
373           
374          self.__received_bytes += packet_size + self.__mac_size_in + 4 
375          self.__received_packets += 1 
376          if self.__need_rekey: 
377               
378               
379              self.__received_packets_overflow += 1 
380              if self.__received_packets_overflow >= 20: 
381                  raise SSHException('Remote transport is ignoring rekey requests') 
382          elif (self.__received_packets >= self.REKEY_PACKETS) or \ 
383               (self.__received_bytes >= self.REKEY_BYTES): 
384               
385              self._log(DEBUG, 'Rekeying (hit %d packets, %d bytes received)' % 
386                        (self.__received_packets, self.__received_bytes)) 
387              self.__received_packets_overflow = 0 
388              self._trigger_rekey() 
389   
390          cmd = ord(payload[0]) 
391          if cmd in MSG_NAMES: 
392              cmd_name = MSG_NAMES[cmd] 
393          else: 
394              cmd_name = '$%x' % cmd 
395          if self.__dump_packets: 
396              self._log(DEBUG, 'Read packet <%s>, length %d' % (cmd_name, len(payload))) 
397          return cmd, msg 
 398   
399   
400       
401       
402       
403 -    def _log(self, level, msg): 
 404          if self.__logger is None: 
405              return 
406          if issubclass(type(msg), list): 
407              for m in msg: 
408                  self.__logger.log(level, m) 
409          else: 
410              self.__logger.log(level, msg) 
 411   
413          if (not self.__keepalive_interval) or (not self.__block_engine_out) or \ 
414              self.__need_rekey: 
415               
416              return 
417          now = time.time() 
418          if now > self.__keepalive_last + self.__keepalive_interval: 
419              self.__keepalive_callback() 
420              self.__keepalive_last = now 
 421       
423          while n > 0: 
424              r, w, e = select.select([self.__socket], [], [], 0.1) 
425              if self.__socket not in r: 
426                  if self.__closed: 
427                      raise EOFError() 
428                  self._check_keepalive() 
429              else: 
430                  x = self.__socket.recv(n) 
431                  if len(x) == 0: 
432                      raise EOFError() 
433                  out += x 
434                  n -= len(x) 
435          return out 
 436   
438          start = time.time() 
439          while True: 
440              r, w, e = select.select([self.__socket], [], [], 0.1) 
441              if self.__socket in r: 
442                  x = self.__socket.recv(1) 
443                  if len(x) == 0: 
444                      raise EOFError() 
445                  break 
446              if self.__closed: 
447                  raise EOFError() 
448              now = time.time() 
449              if now - start >= timeout: 
450                  raise socket.timeout() 
451          return x 
 452   
454          if PY22: 
455              return self._py22_read_timeout(timeout) 
456          start = time.time() 
457          while True: 
458              try: 
459                  x = self.__socket.recv(128) 
460                  if len(x) == 0: 
461                      raise EOFError() 
462                  break 
463              except socket.timeout: 
464                  pass 
465              if self.__closed: 
466                  raise EOFError() 
467              now = time.time() 
468              if now - start >= timeout: 
469                  raise socket.timeout() 
470          return x 
 471   
473           
474          bsize = self.__block_size_out 
475          padding = 3 + bsize - ((len(payload) + 8) % bsize) 
476          packet = struct.pack('>IB', len(payload) + padding + 1, padding) 
477          packet += payload 
478          if self.__block_engine_out is not None: 
479              packet += randpool.get_bytes(padding) 
480          else: 
481               
482               
483              packet += (chr(0) * padding) 
484          return packet 
 485   
487           
488          self.__need_rekey = True 
  489