1109998SmarkmThis is intended to be an example of a state-machine driven SSL application. It 2109998Smarkmacts as an SSL tunneler (functioning as either the server or client half, 3109998Smarkmdepending on command-line arguments). *PLEASE* read the comments in tunala.h 4109998Smarkmbefore you treat this stuff as anything more than a curiosity - YOU HAVE BEEN 5109998SmarkmWARNED!! There, that's the draconian bit out of the way ... 6109998Smarkm 7109998Smarkm 8109998SmarkmWhy "tunala"?? 9109998Smarkm-------------- 10109998Smarkm 11109998SmarkmI thought I asked you to read tunala.h?? :-) 12109998Smarkm 13109998Smarkm 14109998SmarkmShow me 15109998Smarkm------- 16109998Smarkm 17109998SmarkmIf you want to simply see it running, skip to the end and see some example 18109998Smarkmcommand-line arguments to demonstrate with. 19109998Smarkm 20109998Smarkm 21109998SmarkmWhere to look and what to do? 22109998Smarkm----------------------------- 23109998Smarkm 24109998SmarkmThe code is split up roughly coinciding with the detaching of an "abstract" SSL 25109998Smarkmstate machine (which is the purpose of all this) and its surrounding application 26109998Smarkmspecifics. This is primarily to make it possible for me to know when I could cut 27109998Smarkmcorners and when I needed to be rigorous (or at least maintain the pretense as 28109998Smarkmsuch :-). 29109998Smarkm 30109998SmarkmNetwork stuff: 31109998Smarkm 32109998SmarkmBasically, the network part of all this is what is supposed to be abstracted out 33109998Smarkmof the way. The intention is to illustrate one way to stick OpenSSL's mechanisms 34109998Smarkminside a little memory-driven sandbox and operate it like a pure state-machine. 35109998SmarkmSo, the network code is inside both ip.c (general utility functions and gory 36109998SmarkmIPv4 details) and tunala.c itself, which takes care of application specifics 37109998Smarkmlike the main select() loop. The connectivity between the specifics of this 38109998Smarkmapplication (TCP/IP tunneling and the associated network code) and the 39109998Smarkmunderlying abstract SSL state machine stuff is through the use of the "buffer_t" 40109998Smarkmtype, declared in tunala.h and implemented in buffer.c. 41109998Smarkm 42109998SmarkmState machine: 43109998Smarkm 44109998SmarkmWhich leaves us, generally speaking, with the abstract "state machine" code left 45109998Smarkmover and this is sitting inside sm.c, with declarations inside tunala.h. As can 46109998Smarkmbe seen by the definition of the state_machine_t structure and the associated 47109998Smarkmfunctions to manipulate it, there are the 3 OpenSSL "handles" plus 4 buffer_t 48109998Smarkmstructures dealing with IO on both the encrypted and unencrypted sides ("dirty" 49109998Smarkmand "clean" respectively). The "SSL" handle is what facilitates the reading and 50109998Smarkmwriting of the unencrypted (tunneled) data. The two "BIO" handles act as the 51109998Smarkmread and write channels for encrypted tunnel traffic - in other applications 52109998Smarkmthese are often socket BIOs so that the OpenSSL framework operates with the 53109998Smarkmnetwork layer directly. In this example, those two BIOs are memory BIOs 54109998Smarkm(BIO_s_mem()) so that the sending and receiving of the tunnel traffic stays 55109998Smarkmwithin the state-machine, and we can handle where this gets send to (or read 56109998Smarkmfrom) ourselves. 57109998Smarkm 58109998Smarkm 59109998SmarkmWhy? 60109998Smarkm---- 61109998Smarkm 62109998SmarkmIf you take a look at the "state_machine_t" section of tunala.h and the code in 63109998Smarkmsm.c, you will notice that nothing related to the concept of 'transport' is 64109998Smarkminvolved. The binding to TCP/IP networking occurs in tunala.c, specifically 65109998Smarkmwithin the "tunala_item_t" structure that associates a state_machine_t object 66109998Smarkmwith 4 file-descriptors. The way to best see where the bridge between the 67109998Smarkmoutside world (TCP/IP reads, writes, select()s, file-descriptors, etc) and the 68109998Smarkmstate machine is, is to examine the "tunala_item_io()" function in tunala.c. 69109998SmarkmThis is currently around lines 641-732 but of course could be subject to change. 70109998Smarkm 71109998Smarkm 72109998SmarkmAnd...? 73109998Smarkm------- 74109998Smarkm 75109998SmarkmWell, although that function is around 90 lines of code, it could easily have 76109998Smarkmbeen a lot less only I was trying to address an easily missed "gotcha" (item (2) 77109998Smarkmbelow). The main() code that drives the select/accept/IO loop initialises new 78109998Smarkmtunala_item_t structures when connections arrive, and works out which 79109998Smarkmfile-descriptors go where depending on whether we're an SSL client or server 80109998Smarkm(client --> accepted connection is clean and proxied is dirty, server --> 81109998Smarkmaccepted connection is dirty and proxied is clean). What that tunala_item_io() 82109998Smarkmfunction is attempting to do is 2 things; 83109998Smarkm 84109998Smarkm (1) Perform all reads and writes on the network directly into the 85109998Smarkm state_machine_t's buffers (based on a previous select() result), and only 86109998Smarkm then allow the abstact state_machine_t to "churn()" using those buffers. 87109998Smarkm This will cause the SSL machine to consume as much input data from the two 88109998Smarkm "IN" buffers as possible, and generate as much output data into the two 89109998Smarkm "OUT" buffers as possible. Back up in the main() function, the next main 90109998Smarkm loop loop will examine these output buffers and select() for writability 91109998Smarkm on the corresponding sockets if the buffers are non-empty. 92109998Smarkm 93109998Smarkm (2) Handle the complicated tunneling-specific issue of cascading "close"s. 94109998Smarkm This is the reason for most of the complexity in the logic - if one side 95109998Smarkm of the tunnel is closed, you can't simply close the other side and throw 96109998Smarkm away the whole thing - (a) there may still be outgoing data on the other 97109998Smarkm side of the tunnel that hasn't been sent yet, (b) the close (or things 98109998Smarkm happening during the close) may cause more data to be generated that needs 99109998Smarkm sending on the other side. Of course, this logic is complicated yet futher 100109998Smarkm by the fact that it's different depending on which side closes first :-) 101109998Smarkm state_machine_close_clean() will indicate to the state machine that the 102109998Smarkm unencrypted side of the tunnel has closed, so any existing outgoing data 103109998Smarkm needs to be flushed, and the SSL stream needs to be closed down using the 104109998Smarkm appropriate shutdown sequence. state_machine_close_dirty() is simpler 105109998Smarkm because it indicates that the SSL stream has been disconnected, so all 106109998Smarkm that remains before closing the other side is to flush out anything that 107109998Smarkm remains and wait for it to all be sent. 108109998Smarkm 109109998SmarkmAnyway, with those things in mind, the code should be a little easier to follow 110109998Smarkmin terms of "what is *this* bit supposed to achieve??!!". 111109998Smarkm 112109998Smarkm 113109998SmarkmHow might this help? 114109998Smarkm-------------------- 115109998Smarkm 116109998SmarkmWell, the reason I wrote this is that there seemed to be rather a flood of 117109998Smarkmquestions of late on the openssl-dev and openssl-users lists about getting this 118109998Smarkmwhole IO logic thing sorted out, particularly by those who were trying to either 119109998Smarkmuse non-blocking IO, or wanted SSL in an environment where "something else" was 120109998Smarkmhandling the network already and they needed to operate in memory only. This 121109998Smarkmcode is loosely based on some other stuff I've been working on, although that 122109998Smarkmstuff is far more complete, far more dependant on a whole slew of other 123109998Smarkmnetwork/framework code I don't want to incorporate here, and far harder to look 124109998Smarkmat for 5 minutes and follow where everything is going. I will be trying over 125109998Smarkmtime to suck in a few things from that into this demo in the hopes it might be 126109998Smarkmmore useful, and maybe to even make this demo usable as a utility of its own. 127109998SmarkmPossible things include: 128109998Smarkm 129109998Smarkm * controlling multiple processes/threads - this can be used to combat 130109998Smarkm latencies and get passed file-descriptor limits on some systems, and it uses 131109998Smarkm a "controller" process/thread that maintains IPC links with the 132109998Smarkm processes/threads doing the real work. 133109998Smarkm 134109998Smarkm * cert verification rules - having some say over which certs get in or out :-) 135109998Smarkm 136109998Smarkm * control over SSL protocols and cipher suites 137109998Smarkm 138109998Smarkm * A few other things you can already do in s_client and s_server :-) 139109998Smarkm 140109998Smarkm * Support (and control over) session resuming, particularly when functioning 141109998Smarkm as an SSL client. 142109998Smarkm 143109998SmarkmIf you have a particular environment where this model might work to let you "do 144109998SmarkmSSL" without having OpenSSL be aware of the transport, then you should find you 145109998Smarkmcould use the state_machine_t structure (or your own variant thereof) and hook 146109998Smarkmit up to your transport stuff in much the way tunala.c matches it up with those 147109998Smarkm4 file-descriptors. The state_machine_churn(), state_machine_close_clean(), and 148109998Smarkmstate_machine_close_dirty() functions are the main things to understand - after 149109998Smarkmthat's done, you just have to ensure you're feeding and bleeding the 4 150109998Smarkmstate_machine buffers in a logical fashion. This state_machine loop handles not 151109998Smarkmonly handshakes and normal streaming, but also renegotiates - there's no special 152109998Smarkmhandling required beyond keeping an eye on those 4 buffers and keeping them in 153109998Smarkmsync with your outer "loop" logic. Ie. if one of the OUT buffers is not empty, 154109998Smarkmyou need to find an opportunity to try and forward its data on. If one of the IN 155109998Smarkmbuffers is not full, you should keep an eye out for data arriving that should be 156109998Smarkmplaced there. 157109998Smarkm 158109998SmarkmThis approach could hopefully also allow you to run the SSL protocol in very 159109998Smarkmdifferent environments. As an example, you could support encrypted event-driven 160109998SmarkmIPC where threads/processes pass messages to each other inside an SSL layer; 161109998Smarkmeach IPC-message's payload would be in fact the "dirty" content, and the "clean" 162109998Smarkmpayload coming out of the tunnel at each end would be the real intended message. 163109998SmarkmLikewise, this could *easily* be made to work across unix domain sockets, or 164109998Smarkmeven entirely different network/comms protocols. 165109998Smarkm 166109998SmarkmThis is also a quick and easy way to do VPN if you (and the remote network's 167109998Smarkmgateway) support virtual network devices that are encapsulted in a single 168109998Smarkmnetwork connection, perhaps PPP going through an SSL tunnel? 169109998Smarkm 170109998Smarkm 171109998SmarkmSuggestions 172109998Smarkm----------- 173109998Smarkm 174109998SmarkmPlease let me know if you find this useful, or if there's anything wrong or 175109998Smarkmsimply too confusing about it. Patches are also welcome, but please attach a 176109998Smarkmdescription of what it changes and why, and "diff -urN" format is preferred. 177109998SmarkmMail to geoff@openssl.org should do the trick. 178109998Smarkm 179109998Smarkm 180109998SmarkmExample 181109998Smarkm------- 182109998Smarkm 183109998SmarkmHere is an example of how to use "tunala" ... 184109998Smarkm 185109998SmarkmFirst, it's assumed that OpenSSL has already built, and that you are building 186109998Smarkminside the ./demos/tunala/ directory. If not - please correct the paths and 187109998Smarkmflags inside the Makefile. Likewise, if you want to tweak the building, it's 188109998Smarkmbest to try and do so in the makefile (eg. removing the debug flags and adding 189109998Smarkmoptimisation flags). 190109998Smarkm 191109998SmarkmSecondly, this code has mostly only been tested on Linux. However, some 192109998Smarkmautoconf/etc support has been added and the code has been compiled on openbsd 193109998Smarkmand solaris using that. 194109998Smarkm 195109998SmarkmThirdly, if you are Win32, you probably need to do some *major* rewriting of 196109998Smarkmip.c to stand a hope in hell. Good luck, and please mail me the diff if you do 197109998Smarkmthis, otherwise I will take a look at another time. It can certainly be done, 198109998Smarkmbut it's very non-POSIXy. 199109998Smarkm 200109998SmarkmSee the INSTALL document for details on building. 201109998Smarkm 202109998SmarkmNow, if you don't have an executable "tunala" compiled, go back to "First,...". 203109998SmarkmRinse and repeat. 204109998Smarkm 205109998SmarkmInside one console, try typing; 206109998Smarkm 207109998Smarkm(i) ./tunala -listen localhost:8080 -proxy localhost:8081 -cacert CA.pem \ 208109998Smarkm -cert A-client.pem -out_totals -v_peer -v_strict 209109998Smarkm 210109998SmarkmIn another console, type; 211109998Smarkm 212109998Smarkm(ii) ./tunala -listen localhost:8081 -proxy localhost:23 -cacert CA.pem \ 213109998Smarkm -cert A-server.pem -server 1 -out_totals -v_peer -v_strict 214109998Smarkm 215109998SmarkmNow if you open another console and "telnet localhost 8080", you should be 216109998Smarkmtunneled through to the telnet service on your local machine (if it's running - 217109998Smarkmyou could change it to port "22" and tunnel ssh instead if you so desired). When 218109998Smarkmyou logout of the telnet session, the tunnel should cleanly shutdown and show 219109998Smarkmyou some traffic stats in both consoles. Feel free to experiment. :-) 220109998Smarkm 221109998SmarkmNotes: 222109998Smarkm 223109998Smarkm - the format for the "-listen" argument can skip the host part (eg. "-listen 224109998Smarkm 8080" is fine). If you do, the listening socket will listen on all interfaces 225109998Smarkm so you can connect from other machines for example. Using the "localhost" 226109998Smarkm form listens only on 127.0.0.1 so you can only connect locally (unless, of 227109998Smarkm course, you've set up weird stuff with your networking in which case probably 228109998Smarkm none of the above applies). 229109998Smarkm 230109998Smarkm - ./tunala -? gives you a list of other command-line options, but tunala.c is 231109998Smarkm also a good place to look :-) 232109998Smarkm 233109998Smarkm 234