This change was a quick experiment that I'd wanted to try that works quite
authorGeoff Thorpe <>
Mon, 12 Feb 2001 02:28:29 +0000 (02:28 +0000)
committerGeoff Thorpe <>
Mon, 12 Feb 2001 02:28:29 +0000 (02:28 +0000)
well (and is a good demonstration of how encapsulating the SSL in a
memory-based state machine can make it easier to apply to different

The change implements a new command-line switch "-flipped <0|1>" which, if
set to 1, reverses the usual interpretation of a client and server for SSL
tunneling. Normally, an ssl client (ie. "-server 0") accepts "cleartext"
connections and conducts SSL/TLS over a proxied connection acting as an SSL
client. Likewise, an ssl server (ie. "-server 1") accepts connections and
conducts SSL/TLS (as an SSL server) over them and passes "cleartext" over
the proxied connection. With "-flipped 1", an SSL client (specified with
"-server 0") in fact accepts SSL connections and proxies clear, whereas an
SSL server ("-server 1") accepts clear and proxies SSL. NB: most of this
diff is command-line handling, the actual meat of the change is simply the
line or two that plugs "clean" and "dirty" file descriptors into the item
that holds the state-machine - reverse them and you get the desired

This allows a network server to be an SSL client, and a network client to
be an SSL server. Apart from curiosity value, there's a couple of possibly
interesting applications - SSL/TLS is inherently vulnerable to trivial DoS
attacks, because the SSL server usually has to perform a private key
operation first, even if the client is authenticated. With this scenario,
the network client is the SSL server and performs the first private key
operation, whereas the network server serves as the SSL client. Another
possible application is when client-only authentication is required (ie.
the underlying protocol handles (or doesn't care about) authenticating the
server). Eg. an SSL/TLS version of 'ssh' could be concocted where the
client's signed certificate is used to validate login to a server system -
whether or not the client needs to validate who the server is can be
configured at the client end rather than at the server end (ie. a complete
inversion of what happens in normal SSL/TLS).

NB: This is just an experiment/play-thing, using "-flipped 1" probably
creates something that is interoperable with exactly nothing. :-)


index 9c3ff5978b79b0db1b8a033547f24927dcc0c5ee..dbf155c67a04d822b6b53520c485ae0d38f07a07 100644 (file)
@@ -80,7 +80,7 @@ static int selector_select(tunala_selector_t *selector);
  * 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 unsigned 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);
@@ -98,6 +98,7 @@ static const char *def_dcert = NULL;
 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;
@@ -121,6 +122,7 @@ static const char *helpstring =
 " -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"
@@ -143,7 +145,24 @@ static const char *helpstring =
 "    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. */
@@ -293,6 +312,7 @@ int main(int argc, char *argv[])
        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;
@@ -384,6 +404,13 @@ 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);
@@ -512,8 +539,8 @@ main_loop:
                                        &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");
                        fprintf(stderr, "Info, new tunnel opened, now up to "
@@ -908,7 +935,7 @@ static int tunala_world_make_room(tunala_world_t *world)
 static int tunala_world_new_item(tunala_world_t *world, int fd,
-               const unsigned char *ip, unsigned short port)
+               const unsigned char *ip, unsigned short port, int flipped)
        tunala_item_t *item;
        int newfd;
@@ -929,8 +956,10 @@ static int tunala_world_new_item(tunala_world_t *world, int fd,
                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 {