1*ebfedea0SLionel SambucThis is intended to be an example of a state-machine driven SSL application. It 2*ebfedea0SLionel Sambucacts as an SSL tunneler (functioning as either the server or client half, 3*ebfedea0SLionel Sambucdepending on command-line arguments). *PLEASE* read the comments in tunala.h 4*ebfedea0SLionel Sambucbefore you treat this stuff as anything more than a curiosity - YOU HAVE BEEN 5*ebfedea0SLionel SambucWARNED!! There, that's the draconian bit out of the way ... 6*ebfedea0SLionel Sambuc 7*ebfedea0SLionel Sambuc 8*ebfedea0SLionel SambucWhy "tunala"?? 9*ebfedea0SLionel Sambuc-------------- 10*ebfedea0SLionel Sambuc 11*ebfedea0SLionel SambucI thought I asked you to read tunala.h?? :-) 12*ebfedea0SLionel Sambuc 13*ebfedea0SLionel Sambuc 14*ebfedea0SLionel SambucShow me 15*ebfedea0SLionel Sambuc------- 16*ebfedea0SLionel Sambuc 17*ebfedea0SLionel SambucIf you want to simply see it running, skip to the end and see some example 18*ebfedea0SLionel Sambuccommand-line arguments to demonstrate with. 19*ebfedea0SLionel Sambuc 20*ebfedea0SLionel Sambuc 21*ebfedea0SLionel SambucWhere to look and what to do? 22*ebfedea0SLionel Sambuc----------------------------- 23*ebfedea0SLionel Sambuc 24*ebfedea0SLionel SambucThe code is split up roughly coinciding with the detaching of an "abstract" SSL 25*ebfedea0SLionel Sambucstate machine (which is the purpose of all this) and its surrounding application 26*ebfedea0SLionel Sambucspecifics. This is primarily to make it possible for me to know when I could cut 27*ebfedea0SLionel Sambuccorners and when I needed to be rigorous (or at least maintain the pretense as 28*ebfedea0SLionel Sambucsuch :-). 29*ebfedea0SLionel Sambuc 30*ebfedea0SLionel SambucNetwork stuff: 31*ebfedea0SLionel Sambuc 32*ebfedea0SLionel SambucBasically, the network part of all this is what is supposed to be abstracted out 33*ebfedea0SLionel Sambucof the way. The intention is to illustrate one way to stick OpenSSL's mechanisms 34*ebfedea0SLionel Sambucinside a little memory-driven sandbox and operate it like a pure state-machine. 35*ebfedea0SLionel SambucSo, the network code is inside both ip.c (general utility functions and gory 36*ebfedea0SLionel SambucIPv4 details) and tunala.c itself, which takes care of application specifics 37*ebfedea0SLionel Sambuclike the main select() loop. The connectivity between the specifics of this 38*ebfedea0SLionel Sambucapplication (TCP/IP tunneling and the associated network code) and the 39*ebfedea0SLionel Sambucunderlying abstract SSL state machine stuff is through the use of the "buffer_t" 40*ebfedea0SLionel Sambuctype, declared in tunala.h and implemented in buffer.c. 41*ebfedea0SLionel Sambuc 42*ebfedea0SLionel SambucState machine: 43*ebfedea0SLionel Sambuc 44*ebfedea0SLionel SambucWhich leaves us, generally speaking, with the abstract "state machine" code left 45*ebfedea0SLionel Sambucover and this is sitting inside sm.c, with declarations inside tunala.h. As can 46*ebfedea0SLionel Sambucbe seen by the definition of the state_machine_t structure and the associated 47*ebfedea0SLionel Sambucfunctions to manipulate it, there are the 3 OpenSSL "handles" plus 4 buffer_t 48*ebfedea0SLionel Sambucstructures dealing with IO on both the encrypted and unencrypted sides ("dirty" 49*ebfedea0SLionel Sambucand "clean" respectively). The "SSL" handle is what facilitates the reading and 50*ebfedea0SLionel Sambucwriting of the unencrypted (tunneled) data. The two "BIO" handles act as the 51*ebfedea0SLionel Sambucread and write channels for encrypted tunnel traffic - in other applications 52*ebfedea0SLionel Sambucthese are often socket BIOs so that the OpenSSL framework operates with the 53*ebfedea0SLionel Sambucnetwork layer directly. In this example, those two BIOs are memory BIOs 54*ebfedea0SLionel Sambuc(BIO_s_mem()) so that the sending and receiving of the tunnel traffic stays 55*ebfedea0SLionel Sambucwithin the state-machine, and we can handle where this gets send to (or read 56*ebfedea0SLionel Sambucfrom) ourselves. 57*ebfedea0SLionel Sambuc 58*ebfedea0SLionel Sambuc 59*ebfedea0SLionel SambucWhy? 60*ebfedea0SLionel Sambuc---- 61*ebfedea0SLionel Sambuc 62*ebfedea0SLionel SambucIf you take a look at the "state_machine_t" section of tunala.h and the code in 63*ebfedea0SLionel Sambucsm.c, you will notice that nothing related to the concept of 'transport' is 64*ebfedea0SLionel Sambucinvolved. The binding to TCP/IP networking occurs in tunala.c, specifically 65*ebfedea0SLionel Sambucwithin the "tunala_item_t" structure that associates a state_machine_t object 66*ebfedea0SLionel Sambucwith 4 file-descriptors. The way to best see where the bridge between the 67*ebfedea0SLionel Sambucoutside world (TCP/IP reads, writes, select()s, file-descriptors, etc) and the 68*ebfedea0SLionel Sambucstate machine is, is to examine the "tunala_item_io()" function in tunala.c. 69*ebfedea0SLionel SambucThis is currently around lines 641-732 but of course could be subject to change. 70*ebfedea0SLionel Sambuc 71*ebfedea0SLionel Sambuc 72*ebfedea0SLionel SambucAnd...? 73*ebfedea0SLionel Sambuc------- 74*ebfedea0SLionel Sambuc 75*ebfedea0SLionel SambucWell, although that function is around 90 lines of code, it could easily have 76*ebfedea0SLionel Sambucbeen a lot less only I was trying to address an easily missed "gotcha" (item (2) 77*ebfedea0SLionel Sambucbelow). The main() code that drives the select/accept/IO loop initialises new 78*ebfedea0SLionel Sambuctunala_item_t structures when connections arrive, and works out which 79*ebfedea0SLionel Sambucfile-descriptors go where depending on whether we're an SSL client or server 80*ebfedea0SLionel Sambuc(client --> accepted connection is clean and proxied is dirty, server --> 81*ebfedea0SLionel Sambucaccepted connection is dirty and proxied is clean). What that tunala_item_io() 82*ebfedea0SLionel Sambucfunction is attempting to do is 2 things; 83*ebfedea0SLionel Sambuc 84*ebfedea0SLionel Sambuc (1) Perform all reads and writes on the network directly into the 85*ebfedea0SLionel Sambuc state_machine_t's buffers (based on a previous select() result), and only 86*ebfedea0SLionel Sambuc then allow the abstact state_machine_t to "churn()" using those buffers. 87*ebfedea0SLionel Sambuc This will cause the SSL machine to consume as much input data from the two 88*ebfedea0SLionel Sambuc "IN" buffers as possible, and generate as much output data into the two 89*ebfedea0SLionel Sambuc "OUT" buffers as possible. Back up in the main() function, the next main 90*ebfedea0SLionel Sambuc loop loop will examine these output buffers and select() for writability 91*ebfedea0SLionel Sambuc on the corresponding sockets if the buffers are non-empty. 92*ebfedea0SLionel Sambuc 93*ebfedea0SLionel Sambuc (2) Handle the complicated tunneling-specific issue of cascading "close"s. 94*ebfedea0SLionel Sambuc This is the reason for most of the complexity in the logic - if one side 95*ebfedea0SLionel Sambuc of the tunnel is closed, you can't simply close the other side and throw 96*ebfedea0SLionel Sambuc away the whole thing - (a) there may still be outgoing data on the other 97*ebfedea0SLionel Sambuc side of the tunnel that hasn't been sent yet, (b) the close (or things 98*ebfedea0SLionel Sambuc happening during the close) may cause more data to be generated that needs 99*ebfedea0SLionel Sambuc sending on the other side. Of course, this logic is complicated yet futher 100*ebfedea0SLionel Sambuc by the fact that it's different depending on which side closes first :-) 101*ebfedea0SLionel Sambuc state_machine_close_clean() will indicate to the state machine that the 102*ebfedea0SLionel Sambuc unencrypted side of the tunnel has closed, so any existing outgoing data 103*ebfedea0SLionel Sambuc needs to be flushed, and the SSL stream needs to be closed down using the 104*ebfedea0SLionel Sambuc appropriate shutdown sequence. state_machine_close_dirty() is simpler 105*ebfedea0SLionel Sambuc because it indicates that the SSL stream has been disconnected, so all 106*ebfedea0SLionel Sambuc that remains before closing the other side is to flush out anything that 107*ebfedea0SLionel Sambuc remains and wait for it to all be sent. 108*ebfedea0SLionel Sambuc 109*ebfedea0SLionel SambucAnyway, with those things in mind, the code should be a little easier to follow 110*ebfedea0SLionel Sambucin terms of "what is *this* bit supposed to achieve??!!". 111*ebfedea0SLionel Sambuc 112*ebfedea0SLionel Sambuc 113*ebfedea0SLionel SambucHow might this help? 114*ebfedea0SLionel Sambuc-------------------- 115*ebfedea0SLionel Sambuc 116*ebfedea0SLionel SambucWell, the reason I wrote this is that there seemed to be rather a flood of 117*ebfedea0SLionel Sambucquestions of late on the openssl-dev and openssl-users lists about getting this 118*ebfedea0SLionel Sambucwhole IO logic thing sorted out, particularly by those who were trying to either 119*ebfedea0SLionel Sambucuse non-blocking IO, or wanted SSL in an environment where "something else" was 120*ebfedea0SLionel Sambuchandling the network already and they needed to operate in memory only. This 121*ebfedea0SLionel Sambuccode is loosely based on some other stuff I've been working on, although that 122*ebfedea0SLionel Sambucstuff is far more complete, far more dependant on a whole slew of other 123*ebfedea0SLionel Sambucnetwork/framework code I don't want to incorporate here, and far harder to look 124*ebfedea0SLionel Sambucat for 5 minutes and follow where everything is going. I will be trying over 125*ebfedea0SLionel Sambuctime to suck in a few things from that into this demo in the hopes it might be 126*ebfedea0SLionel Sambucmore useful, and maybe to even make this demo usable as a utility of its own. 127*ebfedea0SLionel SambucPossible things include: 128*ebfedea0SLionel Sambuc 129*ebfedea0SLionel Sambuc * controlling multiple processes/threads - this can be used to combat 130*ebfedea0SLionel Sambuc latencies and get passed file-descriptor limits on some systems, and it uses 131*ebfedea0SLionel Sambuc a "controller" process/thread that maintains IPC links with the 132*ebfedea0SLionel Sambuc processes/threads doing the real work. 133*ebfedea0SLionel Sambuc 134*ebfedea0SLionel Sambuc * cert verification rules - having some say over which certs get in or out :-) 135*ebfedea0SLionel Sambuc 136*ebfedea0SLionel Sambuc * control over SSL protocols and cipher suites 137*ebfedea0SLionel Sambuc 138*ebfedea0SLionel Sambuc * A few other things you can already do in s_client and s_server :-) 139*ebfedea0SLionel Sambuc 140*ebfedea0SLionel Sambuc * Support (and control over) session resuming, particularly when functioning 141*ebfedea0SLionel Sambuc as an SSL client. 142*ebfedea0SLionel Sambuc 143*ebfedea0SLionel SambucIf you have a particular environment where this model might work to let you "do 144*ebfedea0SLionel SambucSSL" without having OpenSSL be aware of the transport, then you should find you 145*ebfedea0SLionel Sambuccould use the state_machine_t structure (or your own variant thereof) and hook 146*ebfedea0SLionel Sambucit up to your transport stuff in much the way tunala.c matches it up with those 147*ebfedea0SLionel Sambuc4 file-descriptors. The state_machine_churn(), state_machine_close_clean(), and 148*ebfedea0SLionel Sambucstate_machine_close_dirty() functions are the main things to understand - after 149*ebfedea0SLionel Sambucthat's done, you just have to ensure you're feeding and bleeding the 4 150*ebfedea0SLionel Sambucstate_machine buffers in a logical fashion. This state_machine loop handles not 151*ebfedea0SLionel Sambuconly handshakes and normal streaming, but also renegotiates - there's no special 152*ebfedea0SLionel Sambuchandling required beyond keeping an eye on those 4 buffers and keeping them in 153*ebfedea0SLionel Sambucsync with your outer "loop" logic. Ie. if one of the OUT buffers is not empty, 154*ebfedea0SLionel Sambucyou need to find an opportunity to try and forward its data on. If one of the IN 155*ebfedea0SLionel Sambucbuffers is not full, you should keep an eye out for data arriving that should be 156*ebfedea0SLionel Sambucplaced there. 157*ebfedea0SLionel Sambuc 158*ebfedea0SLionel SambucThis approach could hopefully also allow you to run the SSL protocol in very 159*ebfedea0SLionel Sambucdifferent environments. As an example, you could support encrypted event-driven 160*ebfedea0SLionel SambucIPC where threads/processes pass messages to each other inside an SSL layer; 161*ebfedea0SLionel Sambuceach IPC-message's payload would be in fact the "dirty" content, and the "clean" 162*ebfedea0SLionel Sambucpayload coming out of the tunnel at each end would be the real intended message. 163*ebfedea0SLionel SambucLikewise, this could *easily* be made to work across unix domain sockets, or 164*ebfedea0SLionel Sambuceven entirely different network/comms protocols. 165*ebfedea0SLionel Sambuc 166*ebfedea0SLionel SambucThis is also a quick and easy way to do VPN if you (and the remote network's 167*ebfedea0SLionel Sambucgateway) support virtual network devices that are encapsulted in a single 168*ebfedea0SLionel Sambucnetwork connection, perhaps PPP going through an SSL tunnel? 169*ebfedea0SLionel Sambuc 170*ebfedea0SLionel Sambuc 171*ebfedea0SLionel SambucSuggestions 172*ebfedea0SLionel Sambuc----------- 173*ebfedea0SLionel Sambuc 174*ebfedea0SLionel SambucPlease let me know if you find this useful, or if there's anything wrong or 175*ebfedea0SLionel Sambucsimply too confusing about it. Patches are also welcome, but please attach a 176*ebfedea0SLionel Sambucdescription of what it changes and why, and "diff -urN" format is preferred. 177*ebfedea0SLionel SambucMail to geoff@openssl.org should do the trick. 178*ebfedea0SLionel Sambuc 179*ebfedea0SLionel Sambuc 180*ebfedea0SLionel SambucExample 181*ebfedea0SLionel Sambuc------- 182*ebfedea0SLionel Sambuc 183*ebfedea0SLionel SambucHere is an example of how to use "tunala" ... 184*ebfedea0SLionel Sambuc 185*ebfedea0SLionel SambucFirst, it's assumed that OpenSSL has already built, and that you are building 186*ebfedea0SLionel Sambucinside the ./demos/tunala/ directory. If not - please correct the paths and 187*ebfedea0SLionel Sambucflags inside the Makefile. Likewise, if you want to tweak the building, it's 188*ebfedea0SLionel Sambucbest to try and do so in the makefile (eg. removing the debug flags and adding 189*ebfedea0SLionel Sambucoptimisation flags). 190*ebfedea0SLionel Sambuc 191*ebfedea0SLionel SambucSecondly, this code has mostly only been tested on Linux. However, some 192*ebfedea0SLionel Sambucautoconf/etc support has been added and the code has been compiled on openbsd 193*ebfedea0SLionel Sambucand solaris using that. 194*ebfedea0SLionel Sambuc 195*ebfedea0SLionel SambucThirdly, if you are Win32, you probably need to do some *major* rewriting of 196*ebfedea0SLionel Sambucip.c to stand a hope in hell. Good luck, and please mail me the diff if you do 197*ebfedea0SLionel Sambucthis, otherwise I will take a look at another time. It can certainly be done, 198*ebfedea0SLionel Sambucbut it's very non-POSIXy. 199*ebfedea0SLionel Sambuc 200*ebfedea0SLionel SambucSee the INSTALL document for details on building. 201*ebfedea0SLionel Sambuc 202*ebfedea0SLionel SambucNow, if you don't have an executable "tunala" compiled, go back to "First,...". 203*ebfedea0SLionel SambucRinse and repeat. 204*ebfedea0SLionel Sambuc 205*ebfedea0SLionel SambucInside one console, try typing; 206*ebfedea0SLionel Sambuc 207*ebfedea0SLionel Sambuc(i) ./tunala -listen localhost:8080 -proxy localhost:8081 -cacert CA.pem \ 208*ebfedea0SLionel Sambuc -cert A-client.pem -out_totals -v_peer -v_strict 209*ebfedea0SLionel Sambuc 210*ebfedea0SLionel SambucIn another console, type; 211*ebfedea0SLionel Sambuc 212*ebfedea0SLionel Sambuc(ii) ./tunala -listen localhost:8081 -proxy localhost:23 -cacert CA.pem \ 213*ebfedea0SLionel Sambuc -cert A-server.pem -server 1 -out_totals -v_peer -v_strict 214*ebfedea0SLionel Sambuc 215*ebfedea0SLionel SambucNow if you open another console and "telnet localhost 8080", you should be 216*ebfedea0SLionel Sambuctunneled through to the telnet service on your local machine (if it's running - 217*ebfedea0SLionel Sambucyou could change it to port "22" and tunnel ssh instead if you so desired). When 218*ebfedea0SLionel Sambucyou logout of the telnet session, the tunnel should cleanly shutdown and show 219*ebfedea0SLionel Sambucyou some traffic stats in both consoles. Feel free to experiment. :-) 220*ebfedea0SLionel Sambuc 221*ebfedea0SLionel SambucNotes: 222*ebfedea0SLionel Sambuc 223*ebfedea0SLionel Sambuc - the format for the "-listen" argument can skip the host part (eg. "-listen 224*ebfedea0SLionel Sambuc 8080" is fine). If you do, the listening socket will listen on all interfaces 225*ebfedea0SLionel Sambuc so you can connect from other machines for example. Using the "localhost" 226*ebfedea0SLionel Sambuc form listens only on 127.0.0.1 so you can only connect locally (unless, of 227*ebfedea0SLionel Sambuc course, you've set up weird stuff with your networking in which case probably 228*ebfedea0SLionel Sambuc none of the above applies). 229*ebfedea0SLionel Sambuc 230*ebfedea0SLionel Sambuc - ./tunala -? gives you a list of other command-line options, but tunala.c is 231*ebfedea0SLionel Sambuc also a good place to look :-) 232*ebfedea0SLionel Sambuc 233*ebfedea0SLionel Sambuc 234