1
votes

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.

1
Wrapping SSH in SSL? Besides the obvious criticism, remember that tunneling TCP over TCP is a dark road that leads to madness. sites.inka.de/bigred/devel/tcp-tcp.html - tylerl
I'm not surprised you've gone mad :) I suggest writing some wrappers that will read like more_to_server() to hide all those self.chan.send(self.sock.bio_read()) and reverse operations. Or split the proxy code from the non-proxy code completely. Anything to reduce the pile of i vs s vs sock vs chan :) - sarnold
Valid points, both of you, hehe. Thanks. I planned to write them up as ssl_wants_read() and ssl_wants_write() as I've done in the C code, but for now I just tried to get something working :) - Josh
Tyler: duely noted, however I wish to avoid setting up a proxy on the machine I have ssh access to, so right now, this seems to me to be the only option. - Josh

1 Answers

0
votes

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).