summaryrefslogtreecommitdiff
path: root/src/lib/libssl/src/demos/tunala/tunala.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/libssl/src/demos/tunala/tunala.c')
-rw-r--r--src/lib/libssl/src/demos/tunala/tunala.c1093
1 files changed, 1093 insertions, 0 deletions
diff --git a/src/lib/libssl/src/demos/tunala/tunala.c b/src/lib/libssl/src/demos/tunala/tunala.c
new file mode 100644
index 0000000000..e802a6209f
--- /dev/null
+++ b/src/lib/libssl/src/demos/tunala/tunala.c
@@ -0,0 +1,1093 @@
1#if defined(NO_BUFFER) || defined(NO_IP) || defined(NO_OPENSSL)
2#error "Badness, NO_BUFFER, NO_IP or NO_OPENSSL is defined, turn them *off*"
3#endif
4
5/* Include our bits'n'pieces */
6#include "tunala.h"
7
8
9/********************************************/
10/* Our local types that specify our "world" */
11/********************************************/
12
13/* These represent running "tunnels". Eg. if you wanted to do SSL in a
14 * "message-passing" scanario, the "int" file-descriptors might be replaced by
15 * thread or process IDs, and the "select" code might be replaced by message
16 * handling code. Whatever. */
17typedef struct _tunala_item_t {
18 /* The underlying SSL state machine. This is a data-only processing unit
19 * and we communicate with it by talking to its four "buffers". */
20 state_machine_t sm;
21 /* The file-descriptors for the "dirty" (encrypted) side of the SSL
22 * setup. In actuality, this is typically a socket and both values are
23 * identical. */
24 int dirty_read, dirty_send;
25 /* The file-descriptors for the "clean" (unencrypted) side of the SSL
26 * setup. These could be stdin/stdout, a socket (both values the same),
27 * or whatever you like. */
28 int clean_read, clean_send;
29} tunala_item_t;
30
31/* This structure is used as the data for running the main loop. Namely, in a
32 * network format such as this, it is stuff for select() - but as pointed out,
33 * when moving the real-world to somewhere else, this might be replaced by
34 * something entirely different. It's basically the stuff that controls when
35 * it's time to do some "work". */
36typedef struct _select_sets_t {
37 int max; /* As required as the first argument to select() */
38 fd_set reads, sends, excepts; /* As passed to select() */
39} select_sets_t;
40typedef struct _tunala_selector_t {
41 select_sets_t last_selected; /* Results of the last select() */
42 select_sets_t next_select; /* What we'll next select on */
43} tunala_selector_t;
44
45/* This structure is *everything*. We do it to avoid the use of globals so that,
46 * for example, it would be easier to shift things around between async-IO,
47 * thread-based, or multi-fork()ed (or combinations thereof). */
48typedef struct _tunala_world_t {
49 /* The file-descriptor we "listen" on for new connections */
50 int listen_fd;
51 /* The array of tunnels */
52 tunala_item_t *tunnels;
53 /* the number of tunnels in use and allocated, respectively */
54 unsigned int tunnels_used, tunnels_size;
55 /* Our outside "loop" context stuff */
56 tunala_selector_t selector;
57 /* Our SSL_CTX, which is configured as the SSL client or server and has
58 * the various cert-settings and callbacks configured. */
59 SSL_CTX *ssl_ctx;
60 /* Simple flag with complex logic :-) Indicates whether we're an SSL
61 * server or an SSL client. */
62 int server_mode;
63} tunala_world_t;
64
65/*****************************/
66/* Internal static functions */
67/*****************************/
68
69static SSL_CTX *initialise_ssl_ctx(int server_mode, const char *engine_id,
70 const char *CAfile, const char *cert, const char *key,
71 const char *dcert, const char *dkey, const char *cipher_list,
72 const char *dh_file, const char *dh_special, int ctx_options,
73 int out_state, int out_verify, int verify_mode,
74 unsigned int verify_depth);
75static void selector_init(tunala_selector_t *selector);
76static void selector_add_listener(tunala_selector_t *selector, int fd);
77static void selector_add_tunala(tunala_selector_t *selector, tunala_item_t *t);
78static int selector_select(tunala_selector_t *selector);
79/* This returns -1 for error, 0 for no new connections, or 1 for success, in
80 * which case *newfd is populated. */
81static int selector_get_listener(tunala_selector_t *selector, int fd, int *newfd);
82static int tunala_world_new_item(tunala_world_t *world, int fd,
83 const char *ip, unsigned short port, int flipped);
84static void tunala_world_del_item(tunala_world_t *world, unsigned int idx);
85static int tunala_item_io(tunala_selector_t *selector, tunala_item_t *item);
86
87/*********************************************/
88/* MAIN FUNCTION (and its utility functions) */
89/*********************************************/
90
91static const char *def_proxyhost = "127.0.0.1:443";
92static const char *def_listenhost = "127.0.0.1:8080";
93static int def_max_tunnels = 50;
94static const char *def_cacert = NULL;
95static const char *def_cert = NULL;
96static const char *def_key = NULL;
97static const char *def_dcert = NULL;
98static const char *def_dkey = NULL;
99static const char *def_engine_id = NULL;
100static int def_server_mode = 0;
101static int def_flipped = 0;
102static const char *def_cipher_list = NULL;
103static const char *def_dh_file = NULL;
104static const char *def_dh_special = NULL;
105static int def_ctx_options = 0;
106static int def_verify_mode = 0;
107static unsigned int def_verify_depth = 10;
108static int def_out_state = 0;
109static unsigned int def_out_verify = 0;
110static int def_out_totals = 0;
111static int def_out_conns = 0;
112
113static const char *helpstring =
114"\n'Tunala' (A tunneler with a New Zealand accent)\n"
115"Usage: tunala [options], where options are from;\n"
116" -listen [host:]<port> (default = 127.0.0.1:8080)\n"
117" -proxy <host>:<port> (default = 127.0.0.1:443)\n"
118" -maxtunnels <num> (default = 50)\n"
119" -cacert <path|NULL> (default = NULL)\n"
120" -cert <path|NULL> (default = NULL)\n"
121" -key <path|NULL> (default = whatever '-cert' is)\n"
122" -dcert <path|NULL> (usually for DSA, default = NULL)\n"
123" -dkey <path|NULL> (usually for DSA, default = whatever '-dcert' is)\n"
124" -engine <id|NULL> (default = NULL)\n"
125" -server <0|1> (default = 0, ie. an SSL client)\n"
126" -flipped <0|1> (makes SSL servers be network clients, and vice versa)\n"
127" -cipher <list> (specifies cipher list to use)\n"
128" -dh_file <path> (a PEM file containing DH parameters to use)\n"
129" -dh_special <NULL|generate|standard> (see below: def=NULL)\n"
130" -no_ssl2 (disable SSLv2)\n"
131" -no_ssl3 (disable SSLv3)\n"
132" -no_tls1 (disable TLSv1)\n"
133" -v_peer (verify the peer certificate)\n"
134" -v_strict (do not continue if peer doesn't authenticate)\n"
135" -v_once (no verification in renegotiates)\n"
136" -v_depth <num> (limit certificate chain depth, default = 10)\n"
137" -out_conns (prints client connections and disconnections)\n"
138" -out_state (prints SSL handshake states)\n"
139" -out_verify <0|1|2|3> (prints certificate verification states: def=1)\n"
140" -out_totals (prints out byte-totals when a tunnel closes)\n"
141" -<h|help|?> (displays this help screen)\n"
142"Notes:\n"
143"(1) It is recommended to specify a cert+key when operating as an SSL server.\n"
144" If you only specify '-cert', the same file must contain a matching\n"
145" private key.\n"
146"(2) Either dh_file or dh_special can be used to specify where DH parameters\n"
147" will be obtained from (or '-dh_special NULL' for the default choice) but\n"
148" you cannot specify both. For dh_special, 'generate' will create new DH\n"
149" parameters on startup, and 'standard' will use embedded parameters\n"
150" instead.\n"
151"(3) Normally an ssl client connects to an ssl server - so that an 'ssl client\n"
152" tunala' listens for 'clean' client connections and proxies ssl, and an\n"
153" 'ssl server tunala' listens for ssl connections and proxies 'clean'. With\n"
154" '-flipped 1', this behaviour is reversed so that an 'ssl server tunala'\n"
155" listens for clean client connections and proxies ssl (but participating\n"
156" as an ssl *server* in the SSL/TLS protocol), and an 'ssl client tunala'\n"
157" listens for ssl connections (participating as an ssl *client* in the\n"
158" SSL/TLS protocol) and proxies 'clean' to the end destination. This can\n"
159" be useful for allowing network access to 'servers' where only the server\n"
160" needs to authenticate the client (ie. the other way is not required).\n"
161" Even with client and server authentication, this 'technique' mitigates\n"
162" some DoS (denial-of-service) potential as it will be the network client\n"
163" having to perform the first private key operation rather than the other\n"
164" way round.\n"
165"(4) The 'technique' used by setting '-flipped 1' is probably compatible with\n"
166" absolutely nothing except another complimentary instance of 'tunala'\n"
167" running with '-flipped 1'. :-)\n";
168
169/* Default DH parameters for use with "-dh_special standard" ... stolen striaght
170 * from s_server. */
171static unsigned char dh512_p[]={
172 0xDA,0x58,0x3C,0x16,0xD9,0x85,0x22,0x89,0xD0,0xE4,0xAF,0x75,
173 0x6F,0x4C,0xCA,0x92,0xDD,0x4B,0xE5,0x33,0xB8,0x04,0xFB,0x0F,
174 0xED,0x94,0xEF,0x9C,0x8A,0x44,0x03,0xED,0x57,0x46,0x50,0xD3,
175 0x69,0x99,0xDB,0x29,0xD7,0x76,0x27,0x6B,0xA2,0xD3,0xD4,0x12,
176 0xE2,0x18,0xF4,0xDD,0x1E,0x08,0x4C,0xF6,0xD8,0x00,0x3E,0x7C,
177 0x47,0x74,0xE8,0x33,
178 };
179static unsigned char dh512_g[]={
180 0x02,
181 };
182
183/* And the function that parses the above "standard" parameters, again, straight
184 * out of s_server. */
185static DH *get_dh512(void)
186 {
187 DH *dh=NULL;
188
189 if ((dh=DH_new()) == NULL) return(NULL);
190 dh->p=BN_bin2bn(dh512_p,sizeof(dh512_p),NULL);
191 dh->g=BN_bin2bn(dh512_g,sizeof(dh512_g),NULL);
192 if ((dh->p == NULL) || (dh->g == NULL))
193 return(NULL);
194 return(dh);
195 }
196
197/* Various help/error messages used by main() */
198static int usage(const char *errstr, int isunknownarg)
199{
200 if(isunknownarg)
201 fprintf(stderr, "Error: unknown argument '%s'\n", errstr);
202 else
203 fprintf(stderr, "Error: %s\n", errstr);
204 fprintf(stderr, "%s\n", helpstring);
205 return 1;
206}
207
208static int err_str0(const char *str0)
209{
210 fprintf(stderr, "%s\n", str0);
211 return 1;
212}
213
214static int err_str1(const char *fmt, const char *str1)
215{
216 fprintf(stderr, fmt, str1);
217 fprintf(stderr, "\n");
218 return 1;
219}
220
221static int parse_max_tunnels(const char *s, unsigned int *maxtunnels)
222{
223 unsigned long l;
224 if(!int_strtoul(s, &l) || (l < 1) || (l > 1024)) {
225 fprintf(stderr, "Error, '%s' is an invalid value for "
226 "maxtunnels\n", s);
227 return 0;
228 }
229 *maxtunnels = (unsigned int)l;
230 return 1;
231}
232
233static int parse_server_mode(const char *s, int *servermode)
234{
235 unsigned long l;
236 if(!int_strtoul(s, &l) || (l > 1)) {
237 fprintf(stderr, "Error, '%s' is an invalid value for the "
238 "server mode\n", s);
239 return 0;
240 }
241 *servermode = (int)l;
242 return 1;
243}
244
245static int parse_dh_special(const char *s, const char **dh_special)
246{
247 if((strcmp(s, "NULL") == 0) || (strcmp(s, "generate") == 0) ||
248 (strcmp(s, "standard") == 0)) {
249 *dh_special = s;
250 return 1;
251 }
252 fprintf(stderr, "Error, '%s' is an invalid value for 'dh_special'\n", s);
253 return 0;
254}
255
256static int parse_verify_level(const char *s, unsigned int *verify_level)
257{
258 unsigned long l;
259 if(!int_strtoul(s, &l) || (l > 3)) {
260 fprintf(stderr, "Error, '%s' is an invalid value for "
261 "out_verify\n", s);
262 return 0;
263 }
264 *verify_level = (unsigned int)l;
265 return 1;
266}
267
268static int parse_verify_depth(const char *s, unsigned int *verify_depth)
269{
270 unsigned long l;
271 if(!int_strtoul(s, &l) || (l < 1) || (l > 50)) {
272 fprintf(stderr, "Error, '%s' is an invalid value for "
273 "verify_depth\n", s);
274 return 0;
275 }
276 *verify_depth = (unsigned int)l;
277 return 1;
278}
279
280/* Some fprintf format strings used when tunnels close */
281static const char *io_stats_dirty =
282" SSL traffic; %8lu bytes in, %8lu bytes out\n";
283static const char *io_stats_clean =
284" clear traffic; %8lu bytes in, %8lu bytes out\n";
285
286int main(int argc, char *argv[])
287{
288 unsigned int loop;
289 int newfd;
290 tunala_world_t world;
291 tunala_item_t *t_item;
292 const char *proxy_ip;
293 unsigned short proxy_port;
294 /* Overridables */
295 const char *proxyhost = def_proxyhost;
296 const char *listenhost = def_listenhost;
297 unsigned int max_tunnels = def_max_tunnels;
298 const char *cacert = def_cacert;
299 const char *cert = def_cert;
300 const char *key = def_key;
301 const char *dcert = def_dcert;
302 const char *dkey = def_dkey;
303 const char *engine_id = def_engine_id;
304 int server_mode = def_server_mode;
305 int flipped = def_flipped;
306 const char *cipher_list = def_cipher_list;
307 const char *dh_file = def_dh_file;
308 const char *dh_special = def_dh_special;
309 int ctx_options = def_ctx_options;
310 int verify_mode = def_verify_mode;
311 unsigned int verify_depth = def_verify_depth;
312 int out_state = def_out_state;
313 unsigned int out_verify = def_out_verify;
314 int out_totals = def_out_totals;
315 int out_conns = def_out_conns;
316
317/* Parse command-line arguments */
318next_arg:
319 argc--; argv++;
320 if(argc > 0) {
321 if(strcmp(*argv, "-listen") == 0) {
322 if(argc < 2)
323 return usage("-listen requires an argument", 0);
324 argc--; argv++;
325 listenhost = *argv;
326 goto next_arg;
327 } else if(strcmp(*argv, "-proxy") == 0) {
328 if(argc < 2)
329 return usage("-proxy requires an argument", 0);
330 argc--; argv++;
331 proxyhost = *argv;
332 goto next_arg;
333 } else if(strcmp(*argv, "-maxtunnels") == 0) {
334 if(argc < 2)
335 return usage("-maxtunnels requires an argument", 0);
336 argc--; argv++;
337 if(!parse_max_tunnels(*argv, &max_tunnels))
338 return 1;
339 goto next_arg;
340 } else if(strcmp(*argv, "-cacert") == 0) {
341 if(argc < 2)
342 return usage("-cacert requires an argument", 0);
343 argc--; argv++;
344 if(strcmp(*argv, "NULL") == 0)
345 cacert = NULL;
346 else
347 cacert = *argv;
348 goto next_arg;
349 } else if(strcmp(*argv, "-cert") == 0) {
350 if(argc < 2)
351 return usage("-cert requires an argument", 0);
352 argc--; argv++;
353 if(strcmp(*argv, "NULL") == 0)
354 cert = NULL;
355 else
356 cert = *argv;
357 goto next_arg;
358 } else if(strcmp(*argv, "-key") == 0) {
359 if(argc < 2)
360 return usage("-key requires an argument", 0);
361 argc--; argv++;
362 if(strcmp(*argv, "NULL") == 0)
363 key = NULL;
364 else
365 key = *argv;
366 goto next_arg;
367 } else if(strcmp(*argv, "-dcert") == 0) {
368 if(argc < 2)
369 return usage("-dcert requires an argument", 0);
370 argc--; argv++;
371 if(strcmp(*argv, "NULL") == 0)
372 dcert = NULL;
373 else
374 dcert = *argv;
375 goto next_arg;
376 } else if(strcmp(*argv, "-dkey") == 0) {
377 if(argc < 2)
378 return usage("-dkey requires an argument", 0);
379 argc--; argv++;
380 if(strcmp(*argv, "NULL") == 0)
381 dkey = NULL;
382 else
383 dkey = *argv;
384 goto next_arg;
385 } else if(strcmp(*argv, "-engine") == 0) {
386 if(argc < 2)
387 return usage("-engine requires an argument", 0);
388 argc--; argv++;
389 engine_id = *argv;
390 goto next_arg;
391 } else if(strcmp(*argv, "-server") == 0) {
392 if(argc < 2)
393 return usage("-server requires an argument", 0);
394 argc--; argv++;
395 if(!parse_server_mode(*argv, &server_mode))
396 return 1;
397 goto next_arg;
398 } else if(strcmp(*argv, "-flipped") == 0) {
399 if(argc < 2)
400 return usage("-flipped requires an argument", 0);
401 argc--; argv++;
402 if(!parse_server_mode(*argv, &flipped))
403 return 1;
404 goto next_arg;
405 } else if(strcmp(*argv, "-cipher") == 0) {
406 if(argc < 2)
407 return usage("-cipher requires an argument", 0);
408 argc--; argv++;
409 cipher_list = *argv;
410 goto next_arg;
411 } else if(strcmp(*argv, "-dh_file") == 0) {
412 if(argc < 2)
413 return usage("-dh_file requires an argument", 0);
414 if(dh_special)
415 return usage("cannot mix -dh_file with "
416 "-dh_special", 0);
417 argc--; argv++;
418 dh_file = *argv;
419 goto next_arg;
420 } else if(strcmp(*argv, "-dh_special") == 0) {
421 if(argc < 2)
422 return usage("-dh_special requires an argument", 0);
423 if(dh_file)
424 return usage("cannot mix -dh_file with "
425 "-dh_special", 0);
426 argc--; argv++;
427 if(!parse_dh_special(*argv, &dh_special))
428 return 1;
429 goto next_arg;
430 } else if(strcmp(*argv, "-no_ssl2") == 0) {
431 ctx_options |= SSL_OP_NO_SSLv2;
432 goto next_arg;
433 } else if(strcmp(*argv, "-no_ssl3") == 0) {
434 ctx_options |= SSL_OP_NO_SSLv3;
435 goto next_arg;
436 } else if(strcmp(*argv, "-no_tls1") == 0) {
437 ctx_options |= SSL_OP_NO_TLSv1;
438 goto next_arg;
439 } else if(strcmp(*argv, "-v_peer") == 0) {
440 verify_mode |= SSL_VERIFY_PEER;
441 goto next_arg;
442 } else if(strcmp(*argv, "-v_strict") == 0) {
443 verify_mode |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
444 goto next_arg;
445 } else if(strcmp(*argv, "-v_once") == 0) {
446 verify_mode |= SSL_VERIFY_CLIENT_ONCE;
447 goto next_arg;
448 } else if(strcmp(*argv, "-v_depth") == 0) {
449 if(argc < 2)
450 return usage("-v_depth requires an argument", 0);
451 argc--; argv++;
452 if(!parse_verify_depth(*argv, &verify_depth))
453 return 1;
454 goto next_arg;
455 } else if(strcmp(*argv, "-out_state") == 0) {
456 out_state = 1;
457 goto next_arg;
458 } else if(strcmp(*argv, "-out_verify") == 0) {
459 if(argc < 2)
460 return usage("-out_verify requires an argument", 0);
461 argc--; argv++;
462 if(!parse_verify_level(*argv, &out_verify))
463 return 1;
464 goto next_arg;
465 } else if(strcmp(*argv, "-out_totals") == 0) {
466 out_totals = 1;
467 goto next_arg;
468 } else if(strcmp(*argv, "-out_conns") == 0) {
469 out_conns = 1;
470 goto next_arg;
471 } else if((strcmp(*argv, "-h") == 0) ||
472 (strcmp(*argv, "-help") == 0) ||
473 (strcmp(*argv, "-?") == 0)) {
474 fprintf(stderr, "%s\n", helpstring);
475 return 0;
476 } else
477 return usage(*argv, 1);
478 }
479 /* Run any sanity checks we want here */
480 if(!cert && !dcert && server_mode)
481 fprintf(stderr, "WARNING: you are running an SSL server without "
482 "a certificate - this may not work!\n");
483
484 /* Initialise network stuff */
485 if(!ip_initialise())
486 return err_str0("ip_initialise failed");
487 /* Create the SSL_CTX */
488 if((world.ssl_ctx = initialise_ssl_ctx(server_mode, engine_id,
489 cacert, cert, key, dcert, dkey, cipher_list, dh_file,
490 dh_special, ctx_options, out_state, out_verify,
491 verify_mode, verify_depth)) == NULL)
492 return err_str1("initialise_ssl_ctx(engine_id=%s) failed",
493 (engine_id == NULL) ? "NULL" : engine_id);
494 if(engine_id)
495 fprintf(stderr, "Info, engine '%s' initialised\n", engine_id);
496 /* Create the listener */
497 if((world.listen_fd = ip_create_listener(listenhost)) == -1)
498 return err_str1("ip_create_listener(%s) failed", listenhost);
499 fprintf(stderr, "Info, listening on '%s'\n", listenhost);
500 if(!ip_parse_address(proxyhost, &proxy_ip, &proxy_port, 0))
501 return err_str1("ip_parse_address(%s) failed", proxyhost);
502 fprintf(stderr, "Info, proxying to '%s' (%d.%d.%d.%d:%d)\n", proxyhost,
503 (int)proxy_ip[0], (int)proxy_ip[1],
504 (int)proxy_ip[2], (int)proxy_ip[3], (int)proxy_port);
505 fprintf(stderr, "Info, set maxtunnels to %d\n", (int)max_tunnels);
506 fprintf(stderr, "Info, set to operate as an SSL %s\n",
507 (server_mode ? "server" : "client"));
508 /* Initialise the rest of the stuff */
509 world.tunnels_used = world.tunnels_size = 0;
510 world.tunnels = NULL;
511 world.server_mode = server_mode;
512 selector_init(&world.selector);
513
514/* We're ready to loop */
515main_loop:
516 /* Should we listen for *new* tunnels? */
517 if(world.tunnels_used < max_tunnels)
518 selector_add_listener(&world.selector, world.listen_fd);
519 /* We should add in our existing tunnels */
520 for(loop = 0; loop < world.tunnels_used; loop++)
521 selector_add_tunala(&world.selector, world.tunnels + loop);
522 /* Now do the select */
523 switch(selector_select(&world.selector)) {
524 case -1:
525 fprintf(stderr, "selector_select returned a badness error.\n");
526 goto shouldnt_happen;
527 case 0:
528 fprintf(stderr, "Warn, selector_select returned 0 - signal?""?\n");
529 goto main_loop;
530 default:
531 break;
532 }
533 /* Accept new connection if we should and can */
534 if((world.tunnels_used < max_tunnels) && (selector_get_listener(
535 &world.selector, world.listen_fd,
536 &newfd) == 1)) {
537 /* We have a new connection */
538 if(!tunala_world_new_item(&world, newfd, proxy_ip,
539 proxy_port, flipped))
540 fprintf(stderr, "tunala_world_new_item failed\n");
541 else if(out_conns)
542 fprintf(stderr, "Info, new tunnel opened, now up to "
543 "%d\n", world.tunnels_used);
544 }
545 /* Give each tunnel its moment, note the while loop is because it makes
546 * the logic easier than with "for" to deal with an array that may shift
547 * because of deletes. */
548 loop = 0;
549 t_item = world.tunnels;
550 while(loop < world.tunnels_used) {
551 if(!tunala_item_io(&world.selector, t_item)) {
552 /* We're closing whether for reasons of an error or a
553 * natural close. Don't increment loop or t_item because
554 * the next item is moving to us! */
555 if(!out_totals)
556 goto skip_totals;
557 fprintf(stderr, "Tunnel closing, traffic stats follow\n");
558 /* Display the encrypted (over the network) stats */
559 fprintf(stderr, io_stats_dirty,
560 buffer_total_in(state_machine_get_buffer(
561 &t_item->sm,SM_DIRTY_IN)),
562 buffer_total_out(state_machine_get_buffer(
563 &t_item->sm,SM_DIRTY_OUT)));
564 /* Display the local (tunnelled) stats. NB: Data we
565 * *receive* is data sent *out* of the state_machine on
566 * its 'clean' side. Hence the apparent back-to-front
567 * OUT/IN mixup here :-) */
568 fprintf(stderr, io_stats_clean,
569 buffer_total_out(state_machine_get_buffer(
570 &t_item->sm,SM_CLEAN_OUT)),
571 buffer_total_in(state_machine_get_buffer(
572 &t_item->sm,SM_CLEAN_IN)));
573skip_totals:
574 tunala_world_del_item(&world, loop);
575 if(out_conns)
576 fprintf(stderr, "Info, tunnel closed, down to %d\n",
577 world.tunnels_used);
578 }
579 else {
580 /* Move to the next item */
581 loop++;
582 t_item++;
583 }
584 }
585 goto main_loop;
586 /* Should never get here */
587shouldnt_happen:
588 abort();
589 return 1;
590}
591
592/****************/
593/* OpenSSL bits */
594/****************/
595
596static int ctx_set_cert(SSL_CTX *ctx, const char *cert, const char *key)
597{
598 FILE *fp = NULL;
599 X509 *x509 = NULL;
600 EVP_PKEY *pkey = NULL;
601 int toret = 0; /* Assume an error */
602
603 /* cert */
604 if(cert) {
605 if((fp = fopen(cert, "r")) == NULL) {
606 fprintf(stderr, "Error opening cert file '%s'\n", cert);
607 goto err;
608 }
609 if(!PEM_read_X509(fp, &x509, NULL, NULL)) {
610 fprintf(stderr, "Error reading PEM cert from '%s'\n",
611 cert);
612 goto err;
613 }
614 if(!SSL_CTX_use_certificate(ctx, x509)) {
615 fprintf(stderr, "Error, cert in '%s' can not be used\n",
616 cert);
617 goto err;
618 }
619 /* Clear the FILE* for reuse in the "key" code */
620 fclose(fp);
621 fp = NULL;
622 fprintf(stderr, "Info, operating with cert in '%s'\n", cert);
623 /* If a cert was given without matching key, we assume the same
624 * file contains the required key. */
625 if(!key)
626 key = cert;
627 } else {
628 if(key)
629 fprintf(stderr, "Error, can't specify a key without a "
630 "corresponding certificate\n");
631 else
632 fprintf(stderr, "Error, ctx_set_cert called with "
633 "NULLs!\n");
634 goto err;
635 }
636 /* key */
637 if(key) {
638 if((fp = fopen(key, "r")) == NULL) {
639 fprintf(stderr, "Error opening key file '%s'\n", key);
640 goto err;
641 }
642 if(!PEM_read_PrivateKey(fp, &pkey, NULL, NULL)) {
643 fprintf(stderr, "Error reading PEM key from '%s'\n",
644 key);
645 goto err;
646 }
647 if(!SSL_CTX_use_PrivateKey(ctx, pkey)) {
648 fprintf(stderr, "Error, key in '%s' can not be used\n",
649 key);
650 goto err;
651 }
652 fprintf(stderr, "Info, operating with key in '%s'\n", key);
653 } else
654 fprintf(stderr, "Info, operating without a cert or key\n");
655 /* Success */
656 toret = 1; err:
657 if(x509)
658 X509_free(x509);
659 if(pkey)
660 EVP_PKEY_free(pkey);
661 if(fp)
662 fclose(fp);
663 return toret;
664}
665
666static int ctx_set_dh(SSL_CTX *ctx, const char *dh_file, const char *dh_special)
667{
668 DH *dh = NULL;
669 FILE *fp = NULL;
670
671 if(dh_special) {
672 if(strcmp(dh_special, "NULL") == 0)
673 return 1;
674 if(strcmp(dh_special, "standard") == 0) {
675 if((dh = get_dh512()) == NULL) {
676 fprintf(stderr, "Error, can't parse 'standard'"
677 " DH parameters\n");
678 return 0;
679 }
680 fprintf(stderr, "Info, using 'standard' DH parameters\n");
681 goto do_it;
682 }
683 if(strcmp(dh_special, "generate") != 0)
684 /* This shouldn't happen - screening values is handled
685 * in main(). */
686 abort();
687 fprintf(stderr, "Info, generating DH parameters ... ");
688 fflush(stderr);
689 if((dh = DH_generate_parameters(512, DH_GENERATOR_5,
690 NULL, NULL)) == NULL) {
691 fprintf(stderr, "error!\n");
692 return 0;
693 }
694 fprintf(stderr, "complete\n");
695 goto do_it;
696 }
697 /* So, we're loading dh_file */
698 if((fp = fopen(dh_file, "r")) == NULL) {
699 fprintf(stderr, "Error, couldn't open '%s' for DH parameters\n",
700 dh_file);
701 return 0;
702 }
703 dh = PEM_read_DHparams(fp, NULL, NULL, NULL);
704 fclose(fp);
705 if(dh == NULL) {
706 fprintf(stderr, "Error, could not parse DH parameters from '%s'\n",
707 dh_file);
708 return 0;
709 }
710 fprintf(stderr, "Info, using DH parameters from file '%s'\n", dh_file);
711do_it:
712 SSL_CTX_set_tmp_dh(ctx, dh);
713 DH_free(dh);
714 return 1;
715}
716
717static SSL_CTX *initialise_ssl_ctx(int server_mode, const char *engine_id,
718 const char *CAfile, const char *cert, const char *key,
719 const char *dcert, const char *dkey, const char *cipher_list,
720 const char *dh_file, const char *dh_special, int ctx_options,
721 int out_state, int out_verify, int verify_mode,
722 unsigned int verify_depth)
723{
724 SSL_CTX *ctx = NULL, *ret = NULL;
725 SSL_METHOD *meth;
726 ENGINE *e = NULL;
727
728 OpenSSL_add_ssl_algorithms();
729 SSL_load_error_strings();
730
731 meth = (server_mode ? SSLv23_server_method() : SSLv23_client_method());
732 if(meth == NULL)
733 goto err;
734 if(engine_id) {
735 ENGINE_load_builtin_engines();
736 if((e = ENGINE_by_id(engine_id)) == NULL) {
737 fprintf(stderr, "Error obtaining '%s' engine, openssl "
738 "errors follow\n", engine_id);
739 goto err;
740 }
741 if(!ENGINE_set_default(e, ENGINE_METHOD_ALL)) {
742 fprintf(stderr, "Error assigning '%s' engine, openssl "
743 "errors follow\n", engine_id);
744 goto err;
745 }
746 ENGINE_free(e);
747 }
748 if((ctx = SSL_CTX_new(meth)) == NULL)
749 goto err;
750 /* cacert */
751 if(CAfile) {
752 if(!X509_STORE_load_locations(SSL_CTX_get_cert_store(ctx),
753 CAfile, NULL)) {
754 fprintf(stderr, "Error loading CA cert(s) in '%s'\n",
755 CAfile);
756 goto err;
757 }
758 fprintf(stderr, "Info, operating with CA cert(s) in '%s'\n",
759 CAfile);
760 } else
761 fprintf(stderr, "Info, operating without a CA cert(-list)\n");
762 if(!SSL_CTX_set_default_verify_paths(ctx)) {
763 fprintf(stderr, "Error setting default verify paths\n");
764 goto err;
765 }
766
767 /* cert and key */
768 if((cert || key) && !ctx_set_cert(ctx, cert, key))
769 goto err;
770 /* dcert and dkey */
771 if((dcert || dkey) && !ctx_set_cert(ctx, dcert, dkey))
772 goto err;
773
774 /* cipher_list */
775 if(cipher_list) {
776 if(!SSL_CTX_set_cipher_list(ctx, cipher_list)) {
777 fprintf(stderr, "Error setting cipher list '%s'\n",
778 cipher_list);
779 goto err;
780 }
781 fprintf(stderr, "Info, set cipher list '%s'\n", cipher_list);
782 } else
783 fprintf(stderr, "Info, operating with default cipher list\n");
784
785 /* dh_file & dh_special */
786 if((dh_file || dh_special) && !ctx_set_dh(ctx, dh_file, dh_special))
787 goto err;
788
789 /* ctx_options */
790 SSL_CTX_set_options(ctx, ctx_options);
791
792 /* out_state (output of SSL handshake states to screen). */
793 if(out_state)
794 cb_ssl_info_set_output(stderr);
795
796 /* out_verify */
797 if(out_verify > 0) {
798 cb_ssl_verify_set_output(stderr);
799 cb_ssl_verify_set_level(out_verify);
800 }
801
802 /* verify_depth */
803 cb_ssl_verify_set_depth(verify_depth);
804
805 /* Success! (includes setting verify_mode) */
806 SSL_CTX_set_info_callback(ctx, cb_ssl_info);
807 SSL_CTX_set_verify(ctx, verify_mode, cb_ssl_verify);
808 ret = ctx;
809err:
810 if(!ret) {
811 ERR_print_errors_fp(stderr);
812 if(ctx)
813 SSL_CTX_free(ctx);
814 }
815 return ret;
816}
817
818/*****************/
819/* Selector bits */
820/*****************/
821
822static void selector_sets_init(select_sets_t *s)
823{
824 s->max = 0;
825 FD_ZERO(&s->reads);
826 FD_ZERO(&s->sends);
827 FD_ZERO(&s->excepts);
828}
829static void selector_init(tunala_selector_t *selector)
830{
831 selector_sets_init(&selector->last_selected);
832 selector_sets_init(&selector->next_select);
833}
834
835#define SEL_EXCEPTS 0x00
836#define SEL_READS 0x01
837#define SEL_SENDS 0x02
838static void selector_add_raw_fd(tunala_selector_t *s, int fd, int flags)
839{
840 FD_SET(fd, &s->next_select.excepts);
841 if(flags & SEL_READS)
842 FD_SET(fd, &s->next_select.reads);
843 if(flags & SEL_SENDS)
844 FD_SET(fd, &s->next_select.sends);
845 /* Adjust "max" */
846 if(s->next_select.max < (fd + 1))
847 s->next_select.max = fd + 1;
848}
849
850static void selector_add_listener(tunala_selector_t *selector, int fd)
851{
852 selector_add_raw_fd(selector, fd, SEL_READS);
853}
854
855static void selector_add_tunala(tunala_selector_t *s, tunala_item_t *t)
856{
857 /* Set clean read if sm.clean_in is not full */
858 if(t->clean_read != -1) {
859 selector_add_raw_fd(s, t->clean_read,
860 (buffer_full(state_machine_get_buffer(&t->sm,
861 SM_CLEAN_IN)) ? SEL_EXCEPTS : SEL_READS));
862 }
863 /* Set clean send if sm.clean_out is not empty */
864 if(t->clean_send != -1) {
865 selector_add_raw_fd(s, t->clean_send,
866 (buffer_empty(state_machine_get_buffer(&t->sm,
867 SM_CLEAN_OUT)) ? SEL_EXCEPTS : SEL_SENDS));
868 }
869 /* Set dirty read if sm.dirty_in is not full */
870 if(t->dirty_read != -1) {
871 selector_add_raw_fd(s, t->dirty_read,
872 (buffer_full(state_machine_get_buffer(&t->sm,
873 SM_DIRTY_IN)) ? SEL_EXCEPTS : SEL_READS));
874 }
875 /* Set dirty send if sm.dirty_out is not empty */
876 if(t->dirty_send != -1) {
877 selector_add_raw_fd(s, t->dirty_send,
878 (buffer_empty(state_machine_get_buffer(&t->sm,
879 SM_DIRTY_OUT)) ? SEL_EXCEPTS : SEL_SENDS));
880 }
881}
882
883static int selector_select(tunala_selector_t *selector)
884{
885 memcpy(&selector->last_selected, &selector->next_select,
886 sizeof(select_sets_t));
887 selector_sets_init(&selector->next_select);
888 return select(selector->last_selected.max,
889 &selector->last_selected.reads,
890 &selector->last_selected.sends,
891 &selector->last_selected.excepts, NULL);
892}
893
894/* This returns -1 for error, 0 for no new connections, or 1 for success, in
895 * which case *newfd is populated. */
896static int selector_get_listener(tunala_selector_t *selector, int fd, int *newfd)
897{
898 if(FD_ISSET(fd, &selector->last_selected.excepts))
899 return -1;
900 if(!FD_ISSET(fd, &selector->last_selected.reads))
901 return 0;
902 if((*newfd = ip_accept_connection(fd)) == -1)
903 return -1;
904 return 1;
905}
906
907/************************/
908/* "Tunala" world stuff */
909/************************/
910
911static int tunala_world_make_room(tunala_world_t *world)
912{
913 unsigned int newsize;
914 tunala_item_t *newarray;
915
916 if(world->tunnels_used < world->tunnels_size)
917 return 1;
918 newsize = (world->tunnels_size == 0 ? 16 :
919 ((world->tunnels_size * 3) / 2));
920 if((newarray = malloc(newsize * sizeof(tunala_item_t))) == NULL)
921 return 0;
922 memset(newarray, 0, newsize * sizeof(tunala_item_t));
923 if(world->tunnels_used > 0)
924 memcpy(newarray, world->tunnels,
925 world->tunnels_used * sizeof(tunala_item_t));
926 if(world->tunnels_size > 0)
927 free(world->tunnels);
928 /* migrate */
929 world->tunnels = newarray;
930 world->tunnels_size = newsize;
931 return 1;
932}
933
934static int tunala_world_new_item(tunala_world_t *world, int fd,
935 const char *ip, unsigned short port, int flipped)
936{
937 tunala_item_t *item;
938 int newfd;
939 SSL *new_ssl = NULL;
940
941 if(!tunala_world_make_room(world))
942 return 0;
943 if((new_ssl = SSL_new(world->ssl_ctx)) == NULL) {
944 fprintf(stderr, "Error creating new SSL\n");
945 ERR_print_errors_fp(stderr);
946 return 0;
947 }
948 item = world->tunnels + (world->tunnels_used++);
949 state_machine_init(&item->sm);
950 item->clean_read = item->clean_send =
951 item->dirty_read = item->dirty_send = -1;
952 if((newfd = ip_create_connection_split(ip, port)) == -1)
953 goto err;
954 /* Which way round? If we're a server, "fd" is the dirty side and the
955 * connection we open is the clean one. For a client, it's the other way
956 * around. Unless, of course, we're "flipped" in which case everything
957 * gets reversed. :-) */
958 if((world->server_mode && !flipped) ||
959 (!world->server_mode && flipped)) {
960 item->dirty_read = item->dirty_send = fd;
961 item->clean_read = item->clean_send = newfd;
962 } else {
963 item->clean_read = item->clean_send = fd;
964 item->dirty_read = item->dirty_send = newfd;
965 }
966 /* We use the SSL's "app_data" to indicate a call-back induced "kill" */
967 SSL_set_app_data(new_ssl, NULL);
968 if(!state_machine_set_SSL(&item->sm, new_ssl, world->server_mode))
969 goto err;
970 return 1;
971err:
972 tunala_world_del_item(world, world->tunnels_used - 1);
973 return 0;
974
975}
976
977static void tunala_world_del_item(tunala_world_t *world, unsigned int idx)
978{
979 tunala_item_t *item = world->tunnels + idx;
980 if(item->clean_read != -1)
981 close(item->clean_read);
982 if(item->clean_send != item->clean_read)
983 close(item->clean_send);
984 item->clean_read = item->clean_send = -1;
985 if(item->dirty_read != -1)
986 close(item->dirty_read);
987 if(item->dirty_send != item->dirty_read)
988 close(item->dirty_send);
989 item->dirty_read = item->dirty_send = -1;
990 state_machine_close(&item->sm);
991 /* OK, now we fix the item array */
992 if(idx + 1 < world->tunnels_used)
993 /* We need to scroll entries to the left */
994 memmove(world->tunnels + idx,
995 world->tunnels + (idx + 1),
996 (world->tunnels_used - (idx + 1)) *
997 sizeof(tunala_item_t));
998 world->tunnels_used--;
999}
1000
1001static int tunala_item_io(tunala_selector_t *selector, tunala_item_t *item)
1002{
1003 int c_r, c_s, d_r, d_s; /* Four boolean flags */
1004
1005 /* Take ourselves out of the gene-pool if there was an except */
1006 if((item->clean_read != -1) && FD_ISSET(item->clean_read,
1007 &selector->last_selected.excepts))
1008 return 0;
1009 if((item->clean_send != -1) && FD_ISSET(item->clean_send,
1010 &selector->last_selected.excepts))
1011 return 0;
1012 if((item->dirty_read != -1) && FD_ISSET(item->dirty_read,
1013 &selector->last_selected.excepts))
1014 return 0;
1015 if((item->dirty_send != -1) && FD_ISSET(item->dirty_send,
1016 &selector->last_selected.excepts))
1017 return 0;
1018 /* Grab our 4 IO flags */
1019 c_r = c_s = d_r = d_s = 0;
1020 if(item->clean_read != -1)
1021 c_r = FD_ISSET(item->clean_read, &selector->last_selected.reads);
1022 if(item->clean_send != -1)
1023 c_s = FD_ISSET(item->clean_send, &selector->last_selected.sends);
1024 if(item->dirty_read != -1)
1025 d_r = FD_ISSET(item->dirty_read, &selector->last_selected.reads);
1026 if(item->dirty_send != -1)
1027 d_s = FD_ISSET(item->dirty_send, &selector->last_selected.sends);
1028 /* If no IO has happened for us, skip needless data looping */
1029 if(!c_r && !c_s && !d_r && !d_s)
1030 return 1;
1031 if(c_r)
1032 c_r = (buffer_from_fd(state_machine_get_buffer(&item->sm,
1033 SM_CLEAN_IN), item->clean_read) <= 0);
1034 if(c_s)
1035 c_s = (buffer_to_fd(state_machine_get_buffer(&item->sm,
1036 SM_CLEAN_OUT), item->clean_send) <= 0);
1037 if(d_r)
1038 d_r = (buffer_from_fd(state_machine_get_buffer(&item->sm,
1039 SM_DIRTY_IN), item->dirty_read) <= 0);
1040 if(d_s)
1041 d_s = (buffer_to_fd(state_machine_get_buffer(&item->sm,
1042 SM_DIRTY_OUT), item->dirty_send) <= 0);
1043 /* If any of the flags is non-zero, that means they need closing */
1044 if(c_r) {
1045 close(item->clean_read);
1046 if(item->clean_send == item->clean_read)
1047 item->clean_send = -1;
1048 item->clean_read = -1;
1049 }
1050 if(c_s && (item->clean_send != -1)) {
1051 close(item->clean_send);
1052 if(item->clean_send == item->clean_read)
1053 item->clean_read = -1;
1054 item->clean_send = -1;
1055 }
1056 if(d_r) {
1057 close(item->dirty_read);
1058 if(item->dirty_send == item->dirty_read)
1059 item->dirty_send = -1;
1060 item->dirty_read = -1;
1061 }
1062 if(d_s && (item->dirty_send != -1)) {
1063 close(item->dirty_send);
1064 if(item->dirty_send == item->dirty_read)
1065 item->dirty_read = -1;
1066 item->dirty_send = -1;
1067 }
1068 /* This function name is attributed to the term donated by David
1069 * Schwartz on openssl-dev, message-ID:
1070 * <NCBBLIEPOCNJOAEKBEAKEEDGLIAA.davids@webmaster.com>. :-) */
1071 if(!state_machine_churn(&item->sm))
1072 /* If the SSL closes, it will also zero-out the _in buffers
1073 * and will in future process just outgoing data. As and
1074 * when the outgoing data has gone, it will return zero
1075 * here to tell us to bail out. */
1076 return 0;
1077 /* Otherwise, we return zero if both sides are dead. */
1078 if(((item->clean_read == -1) || (item->clean_send == -1)) &&
1079 ((item->dirty_read == -1) || (item->dirty_send == -1)))
1080 return 0;
1081 /* If only one side closed, notify the SSL of this so it can take
1082 * appropriate action. */
1083 if((item->clean_read == -1) || (item->clean_send == -1)) {
1084 if(!state_machine_close_clean(&item->sm))
1085 return 0;
1086 }
1087 if((item->dirty_read == -1) || (item->dirty_send == -1)) {
1088 if(!state_machine_close_dirty(&item->sm))
1089 return 0;
1090 }
1091 return 1;
1092}
1093