I'm writing a small ftp project for my own amusement which is suppose to be able to do 4 things:
- connect directly to a ftp
- connect directly to a ftp with SSL (wrapper)
- connect through ssh tunnel to a ftp
- connect through ssh tunnel to a ftp with SSL.
I'm writing my program in plain C (unix, not that it matters in this case) using standard libraries for 1 and 2, using libssh2 for 3 and 4 in addition to OpenSSL for 2 and 4.
I'm able to get 1-3 working, but not 4. Here's where I'm at:
- done by opening a socket to host:port, connecting, writing/reading from the socket.
- done by opening a socket to host:port, connecting, writing "AUTH SSL", initiate a SSL object with BIO from previous socket -> SSL_connect(), SSL_read(), SSL_write().
- done by opening a tunnel between target and localhost (although i'm not sure what i'm using the localhost bind for in my approac:)
something like:
test_ssh_cha开发者_JS百科nnel = libssh2_channel_direct_tcpip_ex(test_ssh_session, "100.100.100.100", 21, "127.0.0.1", 21);
I then write/read to that channel (libssh2_channel_read()) - which, as I see it, gives the following flow: plain text -> send over ssh -> deliver plain text to target from ssh host. For the purpose of 3. that is fine and does the job.
Now, for 4. I'm stuck, as I (trying to keep it simple) need to somehow turn this channel into a socket. So the way I see it I have two choices:
- a. Make a pseudo socket and every time i need to read/write, I read/write from the channel, send/retrieve it into the socket, and let SSL_connect(pseudo_socket) communicate with my pseudo socket, every time taking in what the ssl_write sends, send it to the channel, and vice versa.
or
- b. set up a BIO buffer (there are many more BIO functions than I can wrap my head around; the documentation hasn't been utterly helpful) and somehow read/write to that.
Ideally I'd go for 2, for this reason: Since my project is written in C, maintaining a socket read/write while executing other code will become a bit more complicated than I'd prefer.
However, b. gives a problem: I'm worried how the handshaking will work; in particular I'm afraid that I end up handshaking with localhost instead of the remotehost so to speak.
To summarize my question: Can I, through the channel, read/write in SSL (put a wrapper around it), so that the flow for 4. becomes: plain text -> SSL(plaintext) -> through ssh -> deliver SSL(plaintext) to target host from ssh host?
It's a bit long, but I hope it was understandable. If not, please let me know, and I'll clearify. From googling/searching stackoverflow it seems it's me and a guy working with mysql who has this same problem, and limited answers.
ANY input is greatly appreciated!
- James
Yes, option 2 is the right way to go. Create a BIO pair with BIO_make_bio_pair()
, and assign them to the SSL object with SSL_set_bio()
.
You then read the encrypted-side SSL data with BIO_read()
and write it to the libssh tunnel, and read from the libssh tunnel and write it with BIO_write()
.
Addendum:
When you use this method, your SSL object doesn't have a file descriptor of its own - the BIOs replace the file descriptor / socket. Instead of reading and writing from a file descriptor, OpenSSL will read and write from the BIOs you provided.
The BIOs are simply an interface that sit between the OpenSSL library and your own code. They're a way for you to implement the actual shipping of the encrypted-side SSL data to and from the other side (instead of OpenSSL directly using a socket).
When you do a BIO_read()
on the BIO you provided as wbio
to SSL_set_bio()
, you will read the encrypted-side SSL data, and you must then send this on to the other side yourself (presumably using some libssh2
function). Similarly, when you recieve encrypted-side SSL data from the other side (again, from some libssh2
function), you pump it into SSL by using BIO_write()
on the BIO you provided as the rbio
.
Perhaps this illustration will help. When you read and write from the SSL object, OpenSSL will just read and write from the underlying BIO, leaving the data there for you to deal with later:
+------+ +-----+ +-----+
| Your | SSL_write() | SSL | BIO_read() | BIO |
| code | ------------> | | <------------ | |
| | | | | |
| | | | BIO_write() | |
| | | | ------------> | |
+------+ +-----+ +-----+
+------+ +-----+ +-----+
| Your | SSL_read() | SSL | BIO_read() | BIO |
| code | <----------- | | <------------ | |
| | | | | |
| | | | BIO_write() | |
| | | | ------------> | |
+------+ +-----+ +-----+
(Note, though, that an SSL_write()
may cause a read from the underlying BIO, and vice-versa).
When there is data in the wbio
, you must read it and ship it to the other side:
+------+ +-----+
| Your | BIO_read() | BIO |
| code | <----------- | |
| | +-----+
| | +---------+
| | libssh2_channel_write() | libssh2 |
| | ------------------------> | | -> (... to other side)
| | +---------+
+------+
Conversely, when there is data available from the other side, you should read it and pass it into the rbio
:
+------+
| Your | +---------+
| code | libssh2_channel_read() | libssh2 |
| | <----------------------- | | -> (... from other side)
| | +---------+
| | +-----+
| | BIO_write() | BIO |
| | -----------> | |
| | +-----+
+------+
精彩评论