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*"
69 static 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 tmp_rsa,
73 int ctx_options,
int out_state,
int out_verify,
int verify_mode,
81 static int selector_get_listener(
tunala_selector_t *selector,
int fd,
int *newfd);
83 const char *ip,
unsigned short port,
int flipped);
84 static void tunala_world_del_item(
tunala_world_t *world,
unsigned int idx);
91 static const char *def_proxyhost =
"127.0.0.1:443";
92 static const char *def_listenhost =
"127.0.0.1:8080";
93 static int def_max_tunnels = 50;
94 static const char *def_cacert = NULL;
95 static const char *def_cert = NULL;
96 static const char *def_key = NULL;
97 static const char *def_dcert = NULL;
98 static const char *def_dkey = NULL;
99 static const char *def_engine_id = NULL;
100 static int def_server_mode = 0;
101 static int def_flipped = 0;
102 static const char *def_cipher_list = NULL;
103 static const char *def_dh_file = NULL;
104 static const char *def_dh_special = NULL;
105 static int def_tmp_rsa = 1;
106 static int def_ctx_options = 0;
107 static int def_verify_mode = 0;
108 static unsigned int def_verify_depth = 10;
109 static int def_out_state = 0;
110 static unsigned int def_out_verify = 0;
111 static int def_out_totals = 0;
112 static int def_out_conns = 0;
114 static const char *helpstring =
115 "\n'Tunala' (A tunneler with a New Zealand accent)\n"
116 "Usage: tunala [options], where options are from;\n"
117 " -listen [host:]<port> (default = 127.0.0.1:8080)\n"
118 " -proxy <host>:<port> (default = 127.0.0.1:443)\n"
119 " -maxtunnels <num> (default = 50)\n"
120 " -cacert <path|NULL> (default = NULL)\n"
121 " -cert <path|NULL> (default = NULL)\n"
122 " -key <path|NULL> (default = whatever '-cert' is)\n"
123 " -dcert <path|NULL> (usually for DSA, default = NULL)\n"
124 " -dkey <path|NULL> (usually for DSA, default = whatever '-dcert' is)\n"
125 " -engine <id|NULL> (default = NULL)\n"
126 " -server <0|1> (default = 0, ie. an SSL client)\n"
127 " -flipped <0|1> (makes SSL servers be network clients, and vice versa)\n"
128 " -cipher <list> (specifies cipher list to use)\n"
129 " -dh_file <path> (a PEM file containing DH parameters to use)\n"
130 " -dh_special <NULL|generate|standard> (see below: def=NULL)\n"
131 " -no_tmp_rsa (don't generate temporary RSA keys)\n"
132 " -no_ssl2 (disable SSLv2)\n"
133 " -no_ssl3 (disable SSLv3)\n"
134 " -no_tls1 (disable TLSv1)\n"
135 " -v_peer (verify the peer certificate)\n"
136 " -v_strict (do not continue if peer doesn't authenticate)\n"
137 " -v_once (no verification in renegotiates)\n"
138 " -v_depth <num> (limit certificate chain depth, default = 10)\n"
139 " -out_conns (prints client connections and disconnections)\n"
140 " -out_state (prints SSL handshake states)\n"
141 " -out_verify <0|1|2|3> (prints certificate verification states: def=1)\n"
142 " -out_totals (prints out byte-totals when a tunnel closes)\n"
143 " -<h|help|?> (displays this help screen)\n"
145 "(1) It is recommended to specify a cert+key when operating as an SSL server.\n"
146 " If you only specify '-cert', the same file must contain a matching\n"
148 "(2) Either dh_file or dh_special can be used to specify where DH parameters\n"
149 " will be obtained from (or '-dh_special NULL' for the default choice) but\n"
150 " you cannot specify both. For dh_special, 'generate' will create new DH\n"
151 " parameters on startup, and 'standard' will use embedded parameters\n"
153 "(3) Normally an ssl client connects to an ssl server - so that an 'ssl client\n"
154 " tunala' listens for 'clean' client connections and proxies ssl, and an\n"
155 " 'ssl server tunala' listens for ssl connections and proxies 'clean'. With\n"
156 " '-flipped 1', this behaviour is reversed so that an 'ssl server tunala'\n"
157 " listens for clean client connections and proxies ssl (but participating\n"
158 " as an ssl *server* in the SSL/TLS protocol), and an 'ssl client tunala'\n"
159 " listens for ssl connections (participating as an ssl *client* in the\n"
160 " SSL/TLS protocol) and proxies 'clean' to the end destination. This can\n"
161 " be useful for allowing network access to 'servers' where only the server\n"
162 " needs to authenticate the client (ie. the other way is not required).\n"
163 " Even with client and server authentication, this 'technique' mitigates\n"
164 " some DoS (denial-of-service) potential as it will be the network client\n"
165 " having to perform the first private key operation rather than the other\n"
167 "(4) The 'technique' used by setting '-flipped 1' is probably compatible with\n"
168 " absolutely nothing except another complimentary instance of 'tunala'\n"
169 " running with '-flipped 1'. :-)\n";
173 static unsigned char dh512_p[]={
174 0xDA,0x58,0x3C,0x16,0xD9,0x85,0x22,0x89,0xD0,0xE4,0xAF,0x75,
175 0x6F,0x4C,0xCA,0x92,0xDD,0x4B,0xE5,0x33,0xB8,0x04,0xFB,0x0F,
176 0xED,0x94,0xEF,0x9C,0x8A,0x44,0x03,0xED,0x57,0x46,0x50,0xD3,
177 0x69,0x99,0xDB,0x29,0xD7,0x76,0x27,0x6B,0xA2,0xD3,0xD4,0x12,
178 0xE2,0x18,0xF4,0xDD,0x1E,0x08,0x4C,0xF6,0xD8,0x00,0x3E,0x7C,
181 static unsigned char dh512_g[]={
187 static DH *get_dh512(
void)
191 if ((dh=
DH_new()) == NULL)
return(NULL);
192 dh->
p=
BN_bin2bn(dh512_p,
sizeof(dh512_p),NULL);
193 dh->
g=
BN_bin2bn(dh512_g,
sizeof(dh512_g),NULL);
194 if ((dh->
p == NULL) || (dh->
g == NULL))
200 static int usage(
const char *errstr,
int isunknownarg)
203 fprintf(stderr,
"Error: unknown argument '%s'\n", errstr);
205 fprintf(stderr,
"Error: %s\n", errstr);
206 fprintf(stderr,
"%s\n", helpstring);
210 static int err_str0(
const char *str0)
212 fprintf(stderr,
"%s\n", str0);
216 static int err_str1(
const char *fmt,
const char *str1)
218 fprintf(stderr, fmt, str1);
219 fprintf(stderr,
"\n");
223 static int parse_max_tunnels(
const char *s,
unsigned int *maxtunnels)
227 fprintf(stderr,
"Error, '%s' is an invalid value for "
231 *maxtunnels = (
unsigned int)l;
235 static int parse_server_mode(
const char *s,
int *servermode)
239 fprintf(stderr,
"Error, '%s' is an invalid value for the "
243 *servermode = (int)l;
247 static int parse_dh_special(
const char *s,
const char **dh_special)
249 if((strcmp(s,
"NULL") == 0) || (strcmp(s,
"generate") == 0) ||
250 (strcmp(s,
"standard") == 0)) {
254 fprintf(stderr,
"Error, '%s' is an invalid value for 'dh_special'\n", s);
258 static int parse_verify_level(
const char *s,
unsigned int *verify_level)
262 fprintf(stderr,
"Error, '%s' is an invalid value for "
266 *verify_level = (
unsigned int)l;
270 static int parse_verify_depth(
const char *s,
unsigned int *
verify_depth)
274 fprintf(stderr,
"Error, '%s' is an invalid value for "
275 "verify_depth\n", s);
278 *verify_depth = (
unsigned int)l;
283 static const char *io_stats_dirty =
284 " SSL traffic; %8lu bytes in, %8lu bytes out\n";
285 static const char *io_stats_clean =
286 " clear traffic; %8lu bytes in, %8lu bytes out\n";
288 int main(
int argc,
char *argv[])
294 const char *proxy_ip;
295 unsigned short proxy_port;
297 const char *proxyhost = def_proxyhost;
298 const char *listenhost = def_listenhost;
299 unsigned int max_tunnels = def_max_tunnels;
300 const char *cacert = def_cacert;
301 const char *cert = def_cert;
302 const char *
key = def_key;
303 const char *dcert = def_dcert;
304 const char *dkey = def_dkey;
305 const char *engine_id = def_engine_id;
306 int server_mode = def_server_mode;
307 int flipped = def_flipped;
308 const char *cipher_list = def_cipher_list;
309 const char *dh_file = def_dh_file;
310 const char *dh_special = def_dh_special;
311 int tmp_rsa = def_tmp_rsa;
312 int ctx_options = def_ctx_options;
313 int verify_mode = def_verify_mode;
314 unsigned int verify_depth = def_verify_depth;
315 int out_state = def_out_state;
316 unsigned int out_verify = def_out_verify;
317 int out_totals = def_out_totals;
318 int out_conns = def_out_conns;
324 if(strcmp(*argv,
"-listen") == 0) {
326 return usage(
"-listen requires an argument", 0);
330 }
else if(strcmp(*argv,
"-proxy") == 0) {
332 return usage(
"-proxy requires an argument", 0);
336 }
else if(strcmp(*argv,
"-maxtunnels") == 0) {
338 return usage(
"-maxtunnels requires an argument", 0);
340 if(!parse_max_tunnels(*argv, &max_tunnels))
343 }
else if(strcmp(*argv,
"-cacert") == 0) {
345 return usage(
"-cacert requires an argument", 0);
347 if(strcmp(*argv,
"NULL") == 0)
352 }
else if(strcmp(*argv,
"-cert") == 0) {
354 return usage(
"-cert requires an argument", 0);
356 if(strcmp(*argv,
"NULL") == 0)
361 }
else if(strcmp(*argv,
"-key") == 0) {
363 return usage(
"-key requires an argument", 0);
365 if(strcmp(*argv,
"NULL") == 0)
370 }
else if(strcmp(*argv,
"-dcert") == 0) {
372 return usage(
"-dcert requires an argument", 0);
374 if(strcmp(*argv,
"NULL") == 0)
379 }
else if(strcmp(*argv,
"-dkey") == 0) {
381 return usage(
"-dkey requires an argument", 0);
383 if(strcmp(*argv,
"NULL") == 0)
388 }
else if(strcmp(*argv,
"-engine") == 0) {
390 return usage(
"-engine requires an argument", 0);
394 }
else if(strcmp(*argv,
"-server") == 0) {
396 return usage(
"-server requires an argument", 0);
398 if(!parse_server_mode(*argv, &server_mode))
401 }
else if(strcmp(*argv,
"-flipped") == 0) {
403 return usage(
"-flipped requires an argument", 0);
405 if(!parse_server_mode(*argv, &flipped))
408 }
else if(strcmp(*argv,
"-cipher") == 0) {
410 return usage(
"-cipher requires an argument", 0);
414 }
else if(strcmp(*argv,
"-dh_file") == 0) {
416 return usage(
"-dh_file requires an argument", 0);
418 return usage(
"cannot mix -dh_file with "
423 }
else if(strcmp(*argv,
"-dh_special") == 0) {
425 return usage(
"-dh_special requires an argument", 0);
427 return usage(
"cannot mix -dh_file with "
430 if(!parse_dh_special(*argv, &dh_special))
433 }
else if(strcmp(*argv,
"-no_tmp_rsa") == 0) {
436 }
else if(strcmp(*argv,
"-no_ssl2") == 0) {
439 }
else if(strcmp(*argv,
"-no_ssl3") == 0) {
442 }
else if(strcmp(*argv,
"-no_tls1") == 0) {
445 }
else if(strcmp(*argv,
"-v_peer") == 0) {
448 }
else if(strcmp(*argv,
"-v_strict") == 0) {
451 }
else if(strcmp(*argv,
"-v_once") == 0) {
454 }
else if(strcmp(*argv,
"-v_depth") == 0) {
456 return usage(
"-v_depth requires an argument", 0);
458 if(!parse_verify_depth(*argv, &verify_depth))
461 }
else if(strcmp(*argv,
"-out_state") == 0) {
464 }
else if(strcmp(*argv,
"-out_verify") == 0) {
466 return usage(
"-out_verify requires an argument", 0);
468 if(!parse_verify_level(*argv, &out_verify))
471 }
else if(strcmp(*argv,
"-out_totals") == 0) {
474 }
else if(strcmp(*argv,
"-out_conns") == 0) {
477 }
else if((strcmp(*argv,
"-h") == 0) ||
478 (strcmp(*argv,
"-help") == 0) ||
479 (strcmp(*argv,
"-?") == 0)) {
480 fprintf(stderr,
"%s\n", helpstring);
483 return usage(*argv, 1);
486 if(!cert && !dcert && server_mode)
487 fprintf(stderr,
"WARNING: you are running an SSL server without "
488 "a certificate - this may not work!\n");
492 return err_str0(
"ip_initialise failed");
494 if((world.
ssl_ctx = initialise_ssl_ctx(server_mode, engine_id,
495 cacert, cert, key, dcert, dkey, cipher_list, dh_file,
496 dh_special, tmp_rsa, ctx_options, out_state, out_verify,
497 verify_mode, verify_depth)) == NULL)
498 return err_str1(
"initialise_ssl_ctx(engine_id=%s) failed",
499 (engine_id == NULL) ?
"NULL" : engine_id);
501 fprintf(stderr,
"Info, engine '%s' initialised\n", engine_id);
504 return err_str1(
"ip_create_listener(%s) failed", listenhost);
505 fprintf(stderr,
"Info, listening on '%s'\n", listenhost);
507 return err_str1(
"ip_parse_address(%s) failed", proxyhost);
508 fprintf(stderr,
"Info, proxying to '%s' (%d.%d.%d.%d:%d)\n", proxyhost,
509 (
int)proxy_ip[0], (
int)proxy_ip[1],
510 (
int)proxy_ip[2], (
int)proxy_ip[3], (
int)proxy_port);
511 fprintf(stderr,
"Info, set maxtunnels to %d\n", (
int)max_tunnels);
512 fprintf(stderr,
"Info, set to operate as an SSL %s\n",
513 (server_mode ?
"server" :
"client"));
529 switch(selector_select(&world.
selector)) {
532 fprintf(stderr,
"selector_select returned a "
534 goto shouldnt_happen;
536 fprintf(stderr,
"Warn, selector interrupted by a signal\n");
539 fprintf(stderr,
"Warn, selector_select returned 0 - signal?""?\n");
545 if((world.
tunnels_used < max_tunnels) && (selector_get_listener(
549 if(!tunala_world_new_item(&world, newfd, proxy_ip,
550 proxy_port, flipped))
551 fprintf(stderr,
"tunala_world_new_item failed\n");
553 fprintf(stderr,
"Info, new tunnel opened, now up to "
562 if(!tunala_item_io(&world.
selector, t_item)) {
568 fprintf(stderr,
"Tunnel closing, traffic stats follow\n");
570 fprintf(stderr, io_stats_dirty,
579 fprintf(stderr, io_stats_clean,
585 tunala_world_del_item(&world, loop);
587 fprintf(stderr,
"Info, tunnel closed, down to %d\n",
607 static int ctx_set_cert(
SSL_CTX *ctx,
const char *cert,
const char *
key)
616 if((fp = fopen(cert,
"r")) == NULL) {
617 fprintf(stderr,
"Error opening cert file '%s'\n", cert);
620 if(!PEM_read_X509(fp, &x509, NULL, NULL)) {
621 fprintf(stderr,
"Error reading PEM cert from '%s'\n",
626 fprintf(stderr,
"Error, cert in '%s' can not be used\n",
633 fprintf(stderr,
"Info, operating with cert in '%s'\n", cert);
640 fprintf(stderr,
"Error, can't specify a key without a "
641 "corresponding certificate\n");
643 fprintf(stderr,
"Error, ctx_set_cert called with "
649 if((fp = fopen(key,
"r")) == NULL) {
650 fprintf(stderr,
"Error opening key file '%s'\n", key);
654 fprintf(stderr,
"Error reading PEM key from '%s'\n",
659 fprintf(stderr,
"Error, key in '%s' can not be used\n",
663 fprintf(stderr,
"Info, operating with key in '%s'\n", key);
665 fprintf(stderr,
"Info, operating without a cert or key\n");
677 static int ctx_set_dh(
SSL_CTX *ctx,
const char *dh_file,
const char *dh_special)
683 if(strcmp(dh_special,
"NULL") == 0)
685 if(strcmp(dh_special,
"standard") == 0) {
686 if((dh = get_dh512()) == NULL) {
687 fprintf(stderr,
"Error, can't parse 'standard'"
691 fprintf(stderr,
"Info, using 'standard' DH parameters\n");
694 if(strcmp(dh_special,
"generate") != 0)
698 fprintf(stderr,
"Info, generating DH parameters ... ");
702 fprintf(stderr,
"error!\n");
707 fprintf(stderr,
"complete\n");
711 if((fp = fopen(dh_file,
"r")) == NULL) {
712 fprintf(stderr,
"Error, couldn't open '%s' for DH parameters\n",
716 dh = PEM_read_DHparams(fp, NULL, NULL, NULL);
719 fprintf(stderr,
"Error, could not parse DH parameters from '%s'\n",
723 fprintf(stderr,
"Info, using DH parameters from file '%s'\n", dh_file);
730 static SSL_CTX *initialise_ssl_ctx(
int server_mode,
const char *engine_id,
731 const char *CAfile,
const char *cert,
const char *key,
732 const char *dcert,
const char *dkey,
const char *cipher_list,
733 const char *dh_file,
const char *dh_special,
int tmp_rsa,
734 int ctx_options,
int out_state,
int out_verify,
int verify_mode,
735 unsigned int verify_depth)
737 SSL_CTX *ctx = NULL, *ret = NULL;
750 fprintf(stderr,
"Error obtaining '%s' engine, openssl "
751 "errors follow\n", engine_id);
755 fprintf(stderr,
"Error assigning '%s' engine, openssl "
756 "errors follow\n", engine_id);
767 fprintf(stderr,
"Error loading CA cert(s) in '%s'\n",
771 fprintf(stderr,
"Info, operating with CA cert(s) in '%s'\n",
774 fprintf(stderr,
"Info, operating without a CA cert(-list)\n");
776 fprintf(stderr,
"Error setting default verify paths\n");
781 if((cert || key) && !ctx_set_cert(ctx, cert, key))
784 if((dcert || dkey) && !ctx_set_cert(ctx, dcert, dkey))
793 fprintf(stderr,
"Error setting cipher list '%s'\n",
797 fprintf(stderr,
"Info, set cipher list '%s'\n", cipher_list);
799 fprintf(stderr,
"Info, operating with default cipher list\n");
802 if((dh_file || dh_special) && !ctx_set_dh(ctx, dh_file, dh_special))
851 #define SEL_EXCEPTS 0x00
852 #define SEL_READS 0x01
853 #define SEL_SENDS 0x02
868 selector_add_raw_fd(selector, fd,
SEL_READS);
912 static int selector_get_listener(
tunala_selector_t *selector,
int fd,
int *newfd)
929 unsigned int newsize;
936 if((newarray = malloc(newsize *
sizeof(
tunala_item_t))) == NULL)
940 memcpy(newarray, world->
tunnels,
951 const char *ip,
unsigned short port,
int flipped)
957 if(!tunala_world_make_room(world))
960 fprintf(stderr,
"Error creating new SSL\n");
993 static void tunala_world_del_item(
tunala_world_t *world,
unsigned int idx)
1019 int c_r, c_s, d_r, d_s;
1035 c_r = c_s = d_r = d_s = 0;
1045 if(!c_r && !c_s && !d_r && !d_s)