static SSL_CTX *initialise_ssl_ctx(int server_mode, const char *engine_id,
const char *CAfile, const char *cert, const char *key,
const char *dcert, const char *dkey, const char *cipher_list,
- const char *dh_file, const char *dh_special, int out_state,
- int out_verify, int verify_mode, unsigned int verify_depth);
+ const char *dh_file, const char *dh_special, int ctx_options,
+ int out_state, int out_verify, int verify_mode,
+ unsigned int verify_depth);
static void selector_init(tunala_selector_t *selector);
static void selector_add_listener(tunala_selector_t *selector, int fd);
static void selector_add_tunala(tunala_selector_t *selector, tunala_item_t *t);
* which case *newfd is populated. */
static int selector_get_listener(tunala_selector_t *selector, int fd, int *newfd);
static int tunala_world_new_item(tunala_world_t *world, int fd,
- const unsigned char *ip, unsigned short port);
+ const char *ip, unsigned short port, int flipped);
static void tunala_world_del_item(tunala_world_t *world, unsigned int idx);
static int tunala_item_io(tunala_selector_t *selector, tunala_item_t *item);
static const char *def_dkey = NULL;
static const char *def_engine_id = NULL;
static int def_server_mode = 0;
+static int def_flipped = 0;
static const char *def_cipher_list = NULL;
static const char *def_dh_file = NULL;
static const char *def_dh_special = NULL;
+static int def_ctx_options = 0;
+static int def_verify_mode = 0;
+static unsigned int def_verify_depth = 10;
static int def_out_state = 0;
static unsigned int def_out_verify = 0;
static int def_out_totals = 0;
-static int def_verify_mode = 0;
-static unsigned int def_verify_depth = 10;
static const char *helpstring =
"\n'Tunala' (A tunneler with a New Zealand accent)\n"
" -dkey <path|NULL> (usually for DSA, default = whatever '-dcert' is)\n"
" -engine <id|NULL> (default = NULL)\n"
" -server <0|1> (default = 0, ie. an SSL client)\n"
+" -flipped <0|1> (makes SSL servers be network clients, and vice versa)\n"
" -cipher <list> (specifies cipher list to use)\n"
" -dh_file <path> (a PEM file containing DH parameters to use)\n"
" -dh_special <NULL|generate|standard> (see below: def=NULL)\n"
-" -out_state (prints SSL handshake states)\n"
-" -out_verify <0|1|2|3> (prints certificate verification states: def=1)\n"
-" -out_totals (prints out byte-totals when a tunnel closes)\n"
+" -no_ssl2 (disable SSLv2)\n"
+" -no_ssl3 (disable SSLv3)\n"
+" -no_tls1 (disable TLSv1)\n"
" -v_peer (verify the peer certificate)\n"
" -v_strict (do not continue if peer doesn't authenticate)\n"
" -v_once (no verification in renegotiates)\n"
" -v_depth <num> (limit certificate chain depth, default = 10)\n"
+" -out_state (prints SSL handshake states)\n"
+" -out_verify <0|1|2|3> (prints certificate verification states: def=1)\n"
+" -out_totals (prints out byte-totals when a tunnel closes)\n"
" -<h|help|?> (displays this help screen)\n"
"Notes:\n"
"(1) It is recommended to specify a cert+key when operating as an SSL server.\n"
" will be obtained from (or '-dh_special NULL' for the default choice) but\n"
" you cannot specify both. For dh_special, 'generate' will create new DH\n"
" parameters on startup, and 'standard' will use embedded parameters\n"
-" instead.\n";
+" instead.\n"
+"(3) Normally an ssl client connects to an ssl server - so that an 'ssl client\n"
+" tunala' listens for 'clean' client connections and proxies ssl, and an\n"
+" 'ssl server tunala' listens for ssl connections and proxies 'clean'. With\n"
+" '-flipped 1', this behaviour is reversed so that an 'ssl server tunala'\n"
+" listens for clean client connections and proxies ssl (but participating\n"
+" as an ssl *server* in the SSL/TLS protocol), and an 'ssl client tunala'\n"
+" listens for ssl connections (participating as an ssl *client* in the\n"
+" SSL/TLS protocol) and proxies 'clean' to the end destination. This can\n"
+" be useful for allowing network access to 'servers' where only the server\n"
+" needs to authenticate the client (ie. the other way is not required).\n"
+" Even with client and server authentication, this 'technique' mitigates\n"
+" some DoS (denial-of-service) potential as it will be the network client\n"
+" having to perform the first private key operation rather than the other\n"
+" way round.\n"
+"(4) The 'technique' used by setting '-flipped 1' is probably compatible with\n"
+" absolutely nothing except another complimentary instance of 'tunala'\n"
+" running with '-flipped 1'. :-)\n";
/* Default DH parameters for use with "-dh_special standard" ... stolen striaght
* from s_server. */
static int err_str0(const char *str0)
{
- fprintf(stderr, str0);
- fprintf(stderr, "\n");
+ fprintf(stderr, "%s\n", str0);
return 1;
}
-static int err_str1(const char *str0, const char *str1)
+static int err_str1(const char *fmt, const char *str1)
{
- fprintf(stderr, str0, str1);
+ fprintf(stderr, fmt, str1);
fprintf(stderr, "\n");
return 1;
}
static int parse_max_tunnels(const char *s, unsigned int *maxtunnels)
{
unsigned long l;
- char *temp;
- l = strtoul(s, &temp, 10);
- if((temp == s) || (*temp != '\0') || (l < 1) || (l > 1024)) {
+ if(!int_strtoul(s, &l) || (l < 1) || (l > 1024)) {
fprintf(stderr, "Error, '%s' is an invalid value for "
"maxtunnels\n", s);
return 0;
static int parse_server_mode(const char *s, int *servermode)
{
unsigned long l;
- char *temp;
- l = strtoul(s, &temp, 10);
- if((temp == s) || (*temp != '\0') || (l > 1)) {
+ if(!int_strtoul(s, &l) || (l > 1)) {
fprintf(stderr, "Error, '%s' is an invalid value for the "
"server mode\n", s);
return 0;
static int parse_verify_level(const char *s, unsigned int *verify_level)
{
unsigned long l;
- char *temp;
- l = strtoul(s, &temp, 10);
- if((temp == s) || (*temp != '\0') || (l > 3)) {
+ if(!int_strtoul(s, &l) || (l > 3)) {
fprintf(stderr, "Error, '%s' is an invalid value for "
"out_verify\n", s);
return 0;
static int parse_verify_depth(const char *s, unsigned int *verify_depth)
{
unsigned long l;
- char *temp;
- l = strtoul(s, &temp, 10);
- if((temp == s) || (*temp != '\0') || (l < 1) || (l > 50)) {
+ if(!int_strtoul(s, &l) || (l < 1) || (l > 50)) {
fprintf(stderr, "Error, '%s' is an invalid value for "
"verify_depth\n", s);
return 0;
int newfd;
tunala_world_t world;
tunala_item_t *t_item;
- unsigned char *proxy_ip;
+ const char *proxy_ip;
unsigned short proxy_port;
/* Overridables */
const char *proxyhost = def_proxyhost;
const char *dkey = def_dkey;
const char *engine_id = def_engine_id;
int server_mode = def_server_mode;
+ int flipped = def_flipped;
const char *cipher_list = def_cipher_list;
const char *dh_file = def_dh_file;
const char *dh_special = def_dh_special;
+ int ctx_options = def_ctx_options;
+ int verify_mode = def_verify_mode;
+ unsigned int verify_depth = def_verify_depth;
int out_state = def_out_state;
unsigned int out_verify = def_out_verify;
int out_totals = def_out_totals;
- int verify_mode = def_verify_mode;
- unsigned int verify_depth = def_verify_depth;
/* Parse command-line arguments */
next_arg:
if(!parse_server_mode(*argv, &server_mode))
return 1;
goto next_arg;
+ } else if(strcmp(*argv, "-flipped") == 0) {
+ if(argc < 2)
+ return usage("-flipped requires an argument", 0);
+ argc--; argv++;
+ if(!parse_server_mode(*argv, &flipped))
+ return 1;
+ goto next_arg;
} else if(strcmp(*argv, "-cipher") == 0) {
if(argc < 2)
return usage("-cipher requires an argument", 0);
if(!parse_dh_special(*argv, &dh_special))
return 1;
goto next_arg;
- } else if(strcmp(*argv, "-out_state") == 0) {
- out_state = 1;
+ } else if(strcmp(*argv, "-no_ssl2") == 0) {
+ ctx_options |= SSL_OP_NO_SSLv2;
goto next_arg;
- } else if(strcmp(*argv, "-out_verify") == 0) {
- if(argc < 2)
- return usage("-out_verify requires an argument", 0);
- argc--; argv++;
- if(!parse_verify_level(*argv, &out_verify))
- return 1;
+ } else if(strcmp(*argv, "-no_ssl3") == 0) {
+ ctx_options |= SSL_OP_NO_SSLv3;
goto next_arg;
- } else if(strcmp(*argv, "-out_totals") == 0) {
- out_totals = 1;
+ } else if(strcmp(*argv, "-no_tls1") == 0) {
+ ctx_options |= SSL_OP_NO_TLSv1;
goto next_arg;
} else if(strcmp(*argv, "-v_peer") == 0) {
verify_mode |= SSL_VERIFY_PEER;
if(!parse_verify_depth(*argv, &verify_depth))
return 1;
goto next_arg;
+ } else if(strcmp(*argv, "-out_state") == 0) {
+ out_state = 1;
+ goto next_arg;
+ } else if(strcmp(*argv, "-out_verify") == 0) {
+ if(argc < 2)
+ return usage("-out_verify requires an argument", 0);
+ argc--; argv++;
+ if(!parse_verify_level(*argv, &out_verify))
+ return 1;
+ goto next_arg;
+ } else if(strcmp(*argv, "-out_totals") == 0) {
+ out_totals = 1;
+ goto next_arg;
} else if((strcmp(*argv, "-h") == 0) ||
(strcmp(*argv, "-help") == 0) ||
(strcmp(*argv, "-?") == 0)) {
/* Create the SSL_CTX */
if((world.ssl_ctx = initialise_ssl_ctx(server_mode, engine_id,
cacert, cert, key, dcert, dkey, cipher_list, dh_file,
- dh_special, out_state, out_verify, verify_mode,
- verify_depth)) == NULL)
+ dh_special, ctx_options, out_state, out_verify,
+ verify_mode, verify_depth)) == NULL)
return err_str1("initialise_ssl_ctx(engine_id=%s) failed",
(engine_id == NULL) ? "NULL" : engine_id);
err_str1("initialise_ssl_ctx(engine_id=%s) succeeded",
switch(selector_select(&world.selector)) {
case -1:
fprintf(stderr, "selector_select returned a badness error.\n");
- abort();
+ goto shouldnt_happen;
case 0:
- fprintf(stderr, "Warn, selector_select returned 0 - signal??\n");
+ fprintf(stderr, "Warn, selector_select returned 0 - signal?""?\n");
goto main_loop;
default:
break;
&world.selector, world.listen_fd,
&newfd) == 1)) {
/* We have a new connection */
- if(!tunala_world_new_item(&world, newfd,
- proxy_ip, proxy_port))
+ if(!tunala_world_new_item(&world, newfd, proxy_ip,
+ proxy_port, flipped))
fprintf(stderr, "tunala_world_new_item failed\n");
else
fprintf(stderr, "Info, new tunnel opened, now up to "
}
goto main_loop;
/* Should never get here */
+shouldnt_happen:
abort();
return 1;
}
static SSL_CTX *initialise_ssl_ctx(int server_mode, const char *engine_id,
const char *CAfile, const char *cert, const char *key,
const char *dcert, const char *dkey, const char *cipher_list,
- const char *dh_file, const char *dh_special, int out_state,
- int out_verify, int verify_mode, unsigned int verify_depth)
+ const char *dh_file, const char *dh_special, int ctx_options,
+ int out_state, int out_verify, int verify_mode,
+ unsigned int verify_depth)
{
SSL_CTX *ctx, *ret = NULL;
SSL_METHOD *meth;
if(meth == NULL)
goto err;
if(engine_id) {
+ ENGINE_load_builtin_engines();
if((e = ENGINE_by_id(engine_id)) == NULL) {
fprintf(stderr, "Error obtaining '%s' engine, openssl "
"errors follow\n", engine_id);
if((dh_file || dh_special) && !ctx_set_dh(ctx, dh_file, dh_special))
goto err;
+ /* ctx_options */
+ SSL_CTX_set_options(ctx, ctx_options);
+
/* out_state (output of SSL handshake states to screen). */
if(out_state)
cb_ssl_info_set_output(stderr);
}
static int tunala_world_new_item(tunala_world_t *world, int fd,
- const unsigned char *ip, unsigned short port)
+ const char *ip, unsigned short port, int flipped)
{
tunala_item_t *item;
int newfd;
goto err;
/* Which way round? If we're a server, "fd" is the dirty side and the
* connection we open is the clean one. For a client, it's the other way
- * around. */
- if(world->server_mode) {
+ * around. Unless, of course, we're "flipped" in which case everything
+ * gets reversed. :-) */
+ if((world->server_mode && !flipped) ||
+ (!world->server_mode && flipped)) {
item->dirty_read = item->dirty_send = fd;
item->clean_read = item->clean_send = newfd;
} else {