Search code examples
pythonsshopensslpyopenssl

wrapping ssh tunnel in ssl with openssl (python) almost finished


Some of you might remember a question very similar to this, as I seeked your help writin the original util in C (using libssh2 and openssl). I'm now trying to port it to python and got stuck at an unexpected place. Ported about 80% of the core and functionality in 30 minutes, and then spend 10hours+ and still haven't finished that ONE function, so I'm here again to ask for you help one more time :)

The whole source (~130 lines, should be easily readable, not complex) is available here: http://pastebin.com/Udm6Ehu3

The connecting, switching on SSL, handshaking, authentication and even sending (encrypted) commands works fine (I can see from my routers log that I log in with proper user and password).

The problem is with ftp_read in the tunnel scenario (else from self.proxy is None). One attempt was this:

def ftp_read(self, trim=False):
  if self.proxy is None:
    temp = self.s.read(READBUFF)
  else:
    while True:
      try:
        temp = self.sock.bio_read(READBUFF)
      except Exception, e:
        print type(e)
        if type(e) == SSL.WantReadError:
          try:
            self.chan.send(self.sock.bio_read(10240))
          except Exception, e:
            print type(e)
          self.chan.send(self.sock.bio_read(10240))
        elif type(e) == SSL.WantWriteError:
          self.chan.send(self.sock.bio_read(10240))

But I end up stuck at either having a blocked waiting for bio read (or channel read in the ftp_write function), or exception OpenSSL.SSL.WantReadError which, ironicly, is what I'm trying to handle.

If I comment out the ftp_read calls, the proxy scenario works fine (logging in, sending commands no problem), as mentioned. So out of read/write unencrypted, read/write encrypted I'm just missing the read tunnel encrypted.

I've spend 12hours+ now, and feel like I'm getting nowhere, so any thoughts are highly appreciated.

EDIT: I'm not asking someone to write the function for me, so if you know a thing or two about SSL (especially BIOs), and you can see an obvious flaw in my interaction between tunnel and BIO, that'll suffice as a answer :) Like: maybe the ftp_write returns more data than those 10240 bytes requested (or just sends two texts ("blabla\n", "command done.\n")) so it isn't properly flushed. Which might be true, but apparently I can't rely on .want_write()/.want_read() from pyOpenSSL to report anything but 0 bytes available.


Solution

  • Okay, so I think I manged to sort it out.

    sarnold, you'll like this updated version:

      def ftp_read(self, trim=False):
        if self.proxy is None:
          temp = self.s.read(READBUFF)
        else:
          temp = ""
          while True:
            try:
              temp += self.sock.recv(READBUFF)
              break
            except Exception, e:
              if type(e) == SSL.WantReadError:
                self.ssl_wants_read()
              elif type(e) == SSL.WantWriteError:
                self.ssl_wants_write()
    

    where ssl_wants_* is:

      def ssl_wants_read(self):
        try:
          self.chan.send(self.sock.bio_read(10240))
        except Exception, e:
          chan_output = None
        chan_output = self.chan.recv(10240)
        self.sock.bio_write(chan_output)
    
      def ssl_wants_write(self):
        self.chan.send(self.sock.bio_read(10240))
    

    Thanks for the input, sarnold. It made things a bit clearer and easier to work with. However, my issue seemed to be one missed error handling (broke out of SSL.WantReadError exception too soon).