123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233 |
- This is intended to be an example of a state-machine driven SSL application. It
- acts as an SSL tunneler (functioning as either the server or client half,
- depending on command-line arguments). *PLEASE* read the comments in tunala.h
- before you treat this stuff as anything more than a curiosity - YOU HAVE BEEN
- WARNED!! There, that's the draconian bit out of the way ...
- Why "tunala"??
- --------------
- I thought I asked you to read tunala.h?? :-)
- Show me
- -------
- If you want to simply see it running, skip to the end and see some example
- command-line arguments to demonstrate with.
- Where to look and what to do?
- -----------------------------
- The code is split up roughly coinciding with the detaching of an "abstract" SSL
- state machine (which is the purpose of all this) and its surrounding application
- specifics. This is primarily to make it possible for me to know when I could cut
- corners and when I needed to be rigorous (or at least maintain the pretense as
- such :-).
- Network stuff:
- Basically, the network part of all this is what is supposed to be abstracted out
- of the way. The intention is to illustrate one way to stick OpenSSL's mechanisms
- inside a little memory-driven sandbox and operate it like a pure state-machine.
- So, the network code is inside both ip.c (general utility functions and gory
- IPv4 details) and tunala.c itself, which takes care of application specifics
- like the main select() loop. The connectivity between the specifics of this
- application (TCP/IP tunneling and the associated network code) and the
- underlying abstract SSL state machine stuff is through the use of the "buffer_t"
- type, declared in tunala.h and implemented in buffer.c.
- State machine:
- Which leaves us, generally speaking, with the abstract "state machine" code left
- over and this is sitting inside sm.c, with declarations inside tunala.h. As can
- be seen by the definition of the state_machine_t structure and the associated
- functions to manipulate it, there are the 3 OpenSSL "handles" plus 4 buffer_t
- structures dealing with IO on both the encrypted and unencrypted sides ("dirty"
- and "clean" respectively). The "SSL" handle is what facilitates the reading and
- writing of the unencrypted (tunneled) data. The two "BIO" handles act as the
- read and write channels for encrypted tunnel traffic - in other applications
- these are often socket BIOs so that the OpenSSL framework operates with the
- network layer directly. In this example, those two BIOs are memory BIOs
- (BIO_s_mem()) so that the sending and receiving of the tunnel traffic stays
- within the state-machine, and we can handle where this gets send to (or read
- from) ourselves.
- Why?
- ----
- If you take a look at the "state_machine_t" section of tunala.h and the code in
- sm.c, you will notice that nothing related to the concept of 'transport' is
- involved. The binding to TCP/IP networking occurs in tunala.c, specifically
- within the "tunala_item_t" structure that associates a state_machine_t object
- with 4 file-descriptors. The way to best see where the bridge between the
- outside world (TCP/IP reads, writes, select()s, file-descriptors, etc) and the
- state machine is, is to examine the "tunala_item_io()" function in tunala.c.
- This is currently around lines 641-732 but of course could be subject to change.
- And...?
- -------
- Well, although that function is around 90 lines of code, it could easily have
- been a lot less only I was trying to address an easily missed "gotcha" (item (2)
- below). The main() code that drives the select/accept/IO loop initialises new
- tunala_item_t structures when connections arrive, and works out which
- file-descriptors go where depending on whether we're an SSL client or server
- (client --> accepted connection is clean and proxied is dirty, server -->
- accepted connection is dirty and proxied is clean). What that tunala_item_io()
- function is attempting to do is 2 things;
- (1) Perform all reads and writes on the network directly into the
- state_machine_t's buffers (based on a previous select() result), and only
- then allow the abstact state_machine_t to "churn()" using those buffers.
- This will cause the SSL machine to consume as much input data from the two
- "IN" buffers as possible, and generate as much output data into the two
- "OUT" buffers as possible. Back up in the main() function, the next main
- loop loop will examine these output buffers and select() for writability
- on the corresponding sockets if the buffers are non-empty.
- (2) Handle the complicated tunneling-specific issue of cascading "close"s.
- This is the reason for most of the complexity in the logic - if one side
- of the tunnel is closed, you can't simply close the other side and throw
- away the whole thing - (a) there may still be outgoing data on the other
- side of the tunnel that hasn't been sent yet, (b) the close (or things
- happening during the close) may cause more data to be generated that needs
- sending on the other side. Of course, this logic is complicated yet futher
- by the fact that it's different depending on which side closes first :-)
- state_machine_close_clean() will indicate to the state machine that the
- unencrypted side of the tunnel has closed, so any existing outgoing data
- needs to be flushed, and the SSL stream needs to be closed down using the
- appropriate shutdown sequence. state_machine_close_dirty() is simpler
- because it indicates that the SSL stream has been disconnected, so all
- that remains before closing the other side is to flush out anything that
- remains and wait for it to all be sent.
- Anyway, with those things in mind, the code should be a little easier to follow
- in terms of "what is *this* bit supposed to achieve??!!".
- How might this help?
- --------------------
- Well, the reason I wrote this is that there seemed to be rather a flood of
- questions of late on the openssl-dev and openssl-users lists about getting this
- whole IO logic thing sorted out, particularly by those who were trying to either
- use non-blocking IO, or wanted SSL in an environment where "something else" was
- handling the network already and they needed to operate in memory only. This
- code is loosely based on some other stuff I've been working on, although that
- stuff is far more complete, far more dependant on a whole slew of other
- network/framework code I don't want to incorporate here, and far harder to look
- at for 5 minutes and follow where everything is going. I will be trying over
- time to suck in a few things from that into this demo in the hopes it might be
- more useful, and maybe to even make this demo usable as a utility of its own.
- Possible things include:
- * controlling multiple processes/threads - this can be used to combat
- latencies and get passed file-descriptor limits on some systems, and it uses
- a "controller" process/thread that maintains IPC links with the
- processes/threads doing the real work.
- * cert verification rules - having some say over which certs get in or out :-)
- * control over SSL protocols and cipher suites
- * A few other things you can already do in s_client and s_server :-)
- * Support (and control over) session resuming, particularly when functioning
- as an SSL client.
- If you have a particular environment where this model might work to let you "do
- SSL" without having OpenSSL be aware of the transport, then you should find you
- could use the state_machine_t structure (or your own variant thereof) and hook
- it up to your transport stuff in much the way tunala.c matches it up with those
- 4 file-descriptors. The state_machine_churn(), state_machine_close_clean(), and
- state_machine_close_dirty() functions are the main things to understand - after
- that's done, you just have to ensure you're feeding and bleeding the 4
- state_machine buffers in a logical fashion. This state_machine loop handles not
- only handshakes and normal streaming, but also renegotiates - there's no special
- handling required beyond keeping an eye on those 4 buffers and keeping them in
- sync with your outer "loop" logic. Ie. if one of the OUT buffers is not empty,
- you need to find an opportunity to try and forward its data on. If one of the IN
- buffers is not full, you should keep an eye out for data arriving that should be
- placed there.
- This approach could hopefully also allow you to run the SSL protocol in very
- different environments. As an example, you could support encrypted event-driven
- IPC where threads/processes pass messages to each other inside an SSL layer;
- each IPC-message's payload would be in fact the "dirty" content, and the "clean"
- payload coming out of the tunnel at each end would be the real intended message.
- Likewise, this could *easily* be made to work across unix domain sockets, or
- even entirely different network/comms protocols.
- This is also a quick and easy way to do VPN if you (and the remote network's
- gateway) support virtual network devices that are encapsulted in a single
- network connection, perhaps PPP going through an SSL tunnel?
- Suggestions
- -----------
- Please let me know if you find this useful, or if there's anything wrong or
- simply too confusing about it. Patches are also welcome, but please attach a
- description of what it changes and why, and "diff -urN" format is preferred.
- Mail to geoff@openssl.org should do the trick.
- Example
- -------
- Here is an example of how to use "tunala" ...
- First, it's assumed that OpenSSL has already built, and that you are building
- inside the ./demos/tunala/ directory. If not - please correct the paths and
- flags inside the Makefile. Likewise, if you want to tweak the building, it's
- best to try and do so in the makefile (eg. removing the debug flags and adding
- optimisation flags).
- Secondly, this code has mostly only been tested on Linux. However, some
- autoconf/etc support has been added and the code has been compiled on openbsd
- and solaris using that.
- Thirdly, if you are Win32, you probably need to do some *major* rewriting of
- ip.c to stand a hope in hell. Good luck, and please mail me the diff if you do
- this, otherwise I will take a look at another time. It can certainly be done,
- but it's very non-POSIXy.
- See the INSTALL document for details on building.
- Now, if you don't have an executable "tunala" compiled, go back to "First,...".
- Rinse and repeat.
- Inside one console, try typing;
- (i) ./tunala -listen localhost:8080 -proxy localhost:8081 -cacert CA.pem \
- -cert A-client.pem -out_totals -v_peer -v_strict
- In another console, type;
- (ii) ./tunala -listen localhost:8081 -proxy localhost:23 -cacert CA.pem \
- -cert A-server.pem -server 1 -out_totals -v_peer -v_strict
- Now if you open another console and "telnet localhost 8080", you should be
- tunneled through to the telnet service on your local machine (if it's running -
- you could change it to port "22" and tunnel ssh instead if you so desired). When
- you logout of the telnet session, the tunnel should cleanly shutdown and show
- you some traffic stats in both consoles. Feel free to experiment. :-)
- Notes:
- - the format for the "-listen" argument can skip the host part (eg. "-listen
- 8080" is fine). If you do, the listening socket will listen on all interfaces
- so you can connect from other machines for example. Using the "localhost"
- form listens only on 127.0.0.1 so you can only connect locally (unless, of
- course, you've set up weird stuff with your networking in which case probably
- none of the above applies).
- - ./tunala -? gives you a list of other command-line options, but tunala.c is
- also a good place to look :-)
|