1109998Smarkm#if defined(NO_BUFFER) || defined(NO_IP) || defined(NO_OPENSSL) 2296465Sdelphij# error "Badness, NO_BUFFER, NO_IP or NO_OPENSSL is defined, turn them *off*" 3109998Smarkm#endif 4109998Smarkm 5109998Smarkm/* Include our bits'n'pieces */ 6109998Smarkm#include "tunala.h" 7109998Smarkm 8109998Smarkm/********************************************/ 9109998Smarkm/* Our local types that specify our "world" */ 10109998Smarkm/********************************************/ 11109998Smarkm 12296465Sdelphij/* 13296465Sdelphij * These represent running "tunnels". Eg. if you wanted to do SSL in a 14296465Sdelphij * "message-passing" scanario, the "int" file-descriptors might be replaced 15296465Sdelphij * by thread or process IDs, and the "select" code might be replaced by 16296465Sdelphij * message handling code. Whatever. 17296465Sdelphij */ 18109998Smarkmtypedef struct _tunala_item_t { 19296465Sdelphij /* 20296465Sdelphij * The underlying SSL state machine. This is a data-only processing unit 21296465Sdelphij * and we communicate with it by talking to its four "buffers". 22296465Sdelphij */ 23296465Sdelphij state_machine_t sm; 24296465Sdelphij /* 25296465Sdelphij * The file-descriptors for the "dirty" (encrypted) side of the SSL 26296465Sdelphij * setup. In actuality, this is typically a socket and both values are 27296465Sdelphij * identical. 28296465Sdelphij */ 29296465Sdelphij int dirty_read, dirty_send; 30296465Sdelphij /* 31296465Sdelphij * The file-descriptors for the "clean" (unencrypted) side of the SSL 32296465Sdelphij * setup. These could be stdin/stdout, a socket (both values the same), 33296465Sdelphij * or whatever you like. 34296465Sdelphij */ 35296465Sdelphij int clean_read, clean_send; 36109998Smarkm} tunala_item_t; 37109998Smarkm 38296465Sdelphij/* 39296465Sdelphij * This structure is used as the data for running the main loop. Namely, in a 40109998Smarkm * network format such as this, it is stuff for select() - but as pointed out, 41109998Smarkm * when moving the real-world to somewhere else, this might be replaced by 42109998Smarkm * something entirely different. It's basically the stuff that controls when 43296465Sdelphij * it's time to do some "work". 44296465Sdelphij */ 45109998Smarkmtypedef struct _select_sets_t { 46296465Sdelphij int max; /* As required as the first argument to 47296465Sdelphij * select() */ 48296465Sdelphij fd_set reads, sends, excepts; /* As passed to select() */ 49109998Smarkm} select_sets_t; 50109998Smarkmtypedef struct _tunala_selector_t { 51296465Sdelphij select_sets_t last_selected; /* Results of the last select() */ 52296465Sdelphij select_sets_t next_select; /* What we'll next select on */ 53109998Smarkm} tunala_selector_t; 54109998Smarkm 55296465Sdelphij/* 56296465Sdelphij * This structure is *everything*. We do it to avoid the use of globals so 57296465Sdelphij * that, for example, it would be easier to shift things around between 58296465Sdelphij * async-IO, thread-based, or multi-fork()ed (or combinations thereof). 59296465Sdelphij */ 60109998Smarkmtypedef struct _tunala_world_t { 61296465Sdelphij /* The file-descriptor we "listen" on for new connections */ 62296465Sdelphij int listen_fd; 63296465Sdelphij /* The array of tunnels */ 64296465Sdelphij tunala_item_t *tunnels; 65296465Sdelphij /* the number of tunnels in use and allocated, respectively */ 66296465Sdelphij unsigned int tunnels_used, tunnels_size; 67296465Sdelphij /* Our outside "loop" context stuff */ 68296465Sdelphij tunala_selector_t selector; 69296465Sdelphij /* 70296465Sdelphij * Our SSL_CTX, which is configured as the SSL client or server and has 71296465Sdelphij * the various cert-settings and callbacks configured. 72296465Sdelphij */ 73296465Sdelphij SSL_CTX *ssl_ctx; 74296465Sdelphij /* 75296465Sdelphij * Simple flag with complex logic :-) Indicates whether we're an SSL 76296465Sdelphij * server or an SSL client. 77296465Sdelphij */ 78296465Sdelphij int server_mode; 79109998Smarkm} tunala_world_t; 80109998Smarkm 81109998Smarkm/*****************************/ 82109998Smarkm/* Internal static functions */ 83109998Smarkm/*****************************/ 84109998Smarkm 85109998Smarkmstatic SSL_CTX *initialise_ssl_ctx(int server_mode, const char *engine_id, 86296465Sdelphij const char *CAfile, const char *cert, 87296465Sdelphij const char *key, const char *dcert, 88296465Sdelphij const char *dkey, const char *cipher_list, 89296465Sdelphij const char *dh_file, 90296465Sdelphij const char *dh_special, int tmp_rsa, 91296465Sdelphij int ctx_options, int out_state, 92296465Sdelphij int out_verify, int verify_mode, 93296465Sdelphij unsigned int verify_depth); 94296465Sdelphijstatic void selector_init(tunala_selector_t * selector); 95296465Sdelphijstatic void selector_add_listener(tunala_selector_t * selector, int fd); 96296465Sdelphijstatic void selector_add_tunala(tunala_selector_t * selector, 97296465Sdelphij tunala_item_t * t); 98296465Sdelphijstatic int selector_select(tunala_selector_t * selector); 99296465Sdelphij/* 100296465Sdelphij * This returns -1 for error, 0 for no new connections, or 1 for success, in 101296465Sdelphij * which case *newfd is populated. 102296465Sdelphij */ 103296465Sdelphijstatic int selector_get_listener(tunala_selector_t * selector, int fd, 104296465Sdelphij int *newfd); 105296465Sdelphijstatic int tunala_world_new_item(tunala_world_t * world, int fd, 106296465Sdelphij const char *ip, unsigned short port, 107296465Sdelphij int flipped); 108296465Sdelphijstatic void tunala_world_del_item(tunala_world_t * world, unsigned int idx); 109296465Sdelphijstatic int tunala_item_io(tunala_selector_t * selector, tunala_item_t * item); 110109998Smarkm 111109998Smarkm/*********************************************/ 112109998Smarkm/* MAIN FUNCTION (and its utility functions) */ 113109998Smarkm/*********************************************/ 114109998Smarkm 115109998Smarkmstatic const char *def_proxyhost = "127.0.0.1:443"; 116109998Smarkmstatic const char *def_listenhost = "127.0.0.1:8080"; 117109998Smarkmstatic int def_max_tunnels = 50; 118109998Smarkmstatic const char *def_cacert = NULL; 119109998Smarkmstatic const char *def_cert = NULL; 120109998Smarkmstatic const char *def_key = NULL; 121109998Smarkmstatic const char *def_dcert = NULL; 122109998Smarkmstatic const char *def_dkey = NULL; 123109998Smarkmstatic const char *def_engine_id = NULL; 124109998Smarkmstatic int def_server_mode = 0; 125109998Smarkmstatic int def_flipped = 0; 126109998Smarkmstatic const char *def_cipher_list = NULL; 127109998Smarkmstatic const char *def_dh_file = NULL; 128109998Smarkmstatic const char *def_dh_special = NULL; 129160814Ssimonstatic int def_tmp_rsa = 1; 130109998Smarkmstatic int def_ctx_options = 0; 131109998Smarkmstatic int def_verify_mode = 0; 132109998Smarkmstatic unsigned int def_verify_depth = 10; 133109998Smarkmstatic int def_out_state = 0; 134109998Smarkmstatic unsigned int def_out_verify = 0; 135109998Smarkmstatic int def_out_totals = 0; 136109998Smarkmstatic int def_out_conns = 0; 137109998Smarkm 138109998Smarkmstatic const char *helpstring = 139296465Sdelphij "\n'Tunala' (A tunneler with a New Zealand accent)\n" 140296465Sdelphij "Usage: tunala [options], where options are from;\n" 141296465Sdelphij " -listen [host:]<port> (default = 127.0.0.1:8080)\n" 142296465Sdelphij " -proxy <host>:<port> (default = 127.0.0.1:443)\n" 143296465Sdelphij " -maxtunnels <num> (default = 50)\n" 144296465Sdelphij " -cacert <path|NULL> (default = NULL)\n" 145296465Sdelphij " -cert <path|NULL> (default = NULL)\n" 146296465Sdelphij " -key <path|NULL> (default = whatever '-cert' is)\n" 147296465Sdelphij " -dcert <path|NULL> (usually for DSA, default = NULL)\n" 148296465Sdelphij " -dkey <path|NULL> (usually for DSA, default = whatever '-dcert' is)\n" 149296465Sdelphij " -engine <id|NULL> (default = NULL)\n" 150296465Sdelphij " -server <0|1> (default = 0, ie. an SSL client)\n" 151296465Sdelphij " -flipped <0|1> (makes SSL servers be network clients, and vice versa)\n" 152296465Sdelphij " -cipher <list> (specifies cipher list to use)\n" 153296465Sdelphij " -dh_file <path> (a PEM file containing DH parameters to use)\n" 154296465Sdelphij " -dh_special <NULL|generate|standard> (see below: def=NULL)\n" 155296465Sdelphij " -no_tmp_rsa (don't generate temporary RSA keys)\n" 156296465Sdelphij " -no_ssl2 (disable SSLv2)\n" 157296465Sdelphij " -no_ssl3 (disable SSLv3)\n" 158296465Sdelphij " -no_tls1 (disable TLSv1)\n" 159296465Sdelphij " -v_peer (verify the peer certificate)\n" 160296465Sdelphij " -v_strict (do not continue if peer doesn't authenticate)\n" 161296465Sdelphij " -v_once (no verification in renegotiates)\n" 162296465Sdelphij " -v_depth <num> (limit certificate chain depth, default = 10)\n" 163296465Sdelphij " -out_conns (prints client connections and disconnections)\n" 164296465Sdelphij " -out_state (prints SSL handshake states)\n" 165296465Sdelphij " -out_verify <0|1|2|3> (prints certificate verification states: def=1)\n" 166296465Sdelphij " -out_totals (prints out byte-totals when a tunnel closes)\n" 167296465Sdelphij " -<h|help|?> (displays this help screen)\n" 168296465Sdelphij "Notes:\n" 169296465Sdelphij "(1) It is recommended to specify a cert+key when operating as an SSL server.\n" 170296465Sdelphij " If you only specify '-cert', the same file must contain a matching\n" 171296465Sdelphij " private key.\n" 172296465Sdelphij "(2) Either dh_file or dh_special can be used to specify where DH parameters\n" 173296465Sdelphij " will be obtained from (or '-dh_special NULL' for the default choice) but\n" 174296465Sdelphij " you cannot specify both. For dh_special, 'generate' will create new DH\n" 175296465Sdelphij " parameters on startup, and 'standard' will use embedded parameters\n" 176296465Sdelphij " instead.\n" 177296465Sdelphij "(3) Normally an ssl client connects to an ssl server - so that an 'ssl client\n" 178296465Sdelphij " tunala' listens for 'clean' client connections and proxies ssl, and an\n" 179296465Sdelphij " 'ssl server tunala' listens for ssl connections and proxies 'clean'. With\n" 180296465Sdelphij " '-flipped 1', this behaviour is reversed so that an 'ssl server tunala'\n" 181296465Sdelphij " listens for clean client connections and proxies ssl (but participating\n" 182296465Sdelphij " as an ssl *server* in the SSL/TLS protocol), and an 'ssl client tunala'\n" 183296465Sdelphij " listens for ssl connections (participating as an ssl *client* in the\n" 184296465Sdelphij " SSL/TLS protocol) and proxies 'clean' to the end destination. This can\n" 185296465Sdelphij " be useful for allowing network access to 'servers' where only the server\n" 186296465Sdelphij " needs to authenticate the client (ie. the other way is not required).\n" 187296465Sdelphij " Even with client and server authentication, this 'technique' mitigates\n" 188296465Sdelphij " some DoS (denial-of-service) potential as it will be the network client\n" 189296465Sdelphij " having to perform the first private key operation rather than the other\n" 190296465Sdelphij " way round.\n" 191296465Sdelphij "(4) The 'technique' used by setting '-flipped 1' is probably compatible with\n" 192296465Sdelphij " absolutely nothing except another complimentary instance of 'tunala'\n" 193296465Sdelphij " running with '-flipped 1'. :-)\n"; 194109998Smarkm 195296465Sdelphij/* 196296465Sdelphij * Default DH parameters for use with "-dh_special standard" ... stolen 197296465Sdelphij * striaght from s_server. 198296465Sdelphij */ 199296465Sdelphijstatic unsigned char dh512_p[] = { 200296465Sdelphij 0xDA, 0x58, 0x3C, 0x16, 0xD9, 0x85, 0x22, 0x89, 0xD0, 0xE4, 0xAF, 0x75, 201296465Sdelphij 0x6F, 0x4C, 0xCA, 0x92, 0xDD, 0x4B, 0xE5, 0x33, 0xB8, 0x04, 0xFB, 0x0F, 202296465Sdelphij 0xED, 0x94, 0xEF, 0x9C, 0x8A, 0x44, 0x03, 0xED, 0x57, 0x46, 0x50, 0xD3, 203296465Sdelphij 0x69, 0x99, 0xDB, 0x29, 0xD7, 0x76, 0x27, 0x6B, 0xA2, 0xD3, 0xD4, 0x12, 204296465Sdelphij 0xE2, 0x18, 0xF4, 0xDD, 0x1E, 0x08, 0x4C, 0xF6, 0xD8, 0x00, 0x3E, 0x7C, 205296465Sdelphij 0x47, 0x74, 0xE8, 0x33, 206296465Sdelphij}; 207109998Smarkm 208296465Sdelphijstatic unsigned char dh512_g[] = { 209296465Sdelphij 0x02, 210296465Sdelphij}; 211296465Sdelphij 212296465Sdelphij/* 213296465Sdelphij * And the function that parses the above "standard" parameters, again, 214296465Sdelphij * straight out of s_server. 215296465Sdelphij */ 216109998Smarkmstatic DH *get_dh512(void) 217296465Sdelphij{ 218296465Sdelphij DH *dh = NULL; 219109998Smarkm 220296465Sdelphij if ((dh = DH_new()) == NULL) 221296465Sdelphij return (NULL); 222296465Sdelphij dh->p = BN_bin2bn(dh512_p, sizeof(dh512_p), NULL); 223296465Sdelphij dh->g = BN_bin2bn(dh512_g, sizeof(dh512_g), NULL); 224296465Sdelphij if ((dh->p == NULL) || (dh->g == NULL)) 225296465Sdelphij return (NULL); 226296465Sdelphij return (dh); 227296465Sdelphij} 228109998Smarkm 229109998Smarkm/* Various help/error messages used by main() */ 230109998Smarkmstatic int usage(const char *errstr, int isunknownarg) 231109998Smarkm{ 232296465Sdelphij if (isunknownarg) 233296465Sdelphij fprintf(stderr, "Error: unknown argument '%s'\n", errstr); 234296465Sdelphij else 235296465Sdelphij fprintf(stderr, "Error: %s\n", errstr); 236296465Sdelphij fprintf(stderr, "%s\n", helpstring); 237296465Sdelphij return 1; 238109998Smarkm} 239109998Smarkm 240109998Smarkmstatic int err_str0(const char *str0) 241109998Smarkm{ 242296465Sdelphij fprintf(stderr, "%s\n", str0); 243296465Sdelphij return 1; 244109998Smarkm} 245109998Smarkm 246109998Smarkmstatic int err_str1(const char *fmt, const char *str1) 247109998Smarkm{ 248296465Sdelphij fprintf(stderr, fmt, str1); 249296465Sdelphij fprintf(stderr, "\n"); 250296465Sdelphij return 1; 251109998Smarkm} 252109998Smarkm 253109998Smarkmstatic int parse_max_tunnels(const char *s, unsigned int *maxtunnels) 254109998Smarkm{ 255296465Sdelphij unsigned long l; 256296465Sdelphij if (!int_strtoul(s, &l) || (l < 1) || (l > 1024)) { 257296465Sdelphij fprintf(stderr, "Error, '%s' is an invalid value for " 258296465Sdelphij "maxtunnels\n", s); 259296465Sdelphij return 0; 260296465Sdelphij } 261296465Sdelphij *maxtunnels = (unsigned int)l; 262296465Sdelphij return 1; 263109998Smarkm} 264109998Smarkm 265109998Smarkmstatic int parse_server_mode(const char *s, int *servermode) 266109998Smarkm{ 267296465Sdelphij unsigned long l; 268296465Sdelphij if (!int_strtoul(s, &l) || (l > 1)) { 269296465Sdelphij fprintf(stderr, "Error, '%s' is an invalid value for the " 270296465Sdelphij "server mode\n", s); 271296465Sdelphij return 0; 272296465Sdelphij } 273296465Sdelphij *servermode = (int)l; 274296465Sdelphij return 1; 275109998Smarkm} 276109998Smarkm 277109998Smarkmstatic int parse_dh_special(const char *s, const char **dh_special) 278109998Smarkm{ 279296465Sdelphij if ((strcmp(s, "NULL") == 0) || (strcmp(s, "generate") == 0) || 280296465Sdelphij (strcmp(s, "standard") == 0)) { 281296465Sdelphij *dh_special = s; 282296465Sdelphij return 1; 283296465Sdelphij } 284296465Sdelphij fprintf(stderr, "Error, '%s' is an invalid value for 'dh_special'\n", s); 285296465Sdelphij return 0; 286109998Smarkm} 287109998Smarkm 288109998Smarkmstatic int parse_verify_level(const char *s, unsigned int *verify_level) 289109998Smarkm{ 290296465Sdelphij unsigned long l; 291296465Sdelphij if (!int_strtoul(s, &l) || (l > 3)) { 292296465Sdelphij fprintf(stderr, "Error, '%s' is an invalid value for " 293296465Sdelphij "out_verify\n", s); 294296465Sdelphij return 0; 295296465Sdelphij } 296296465Sdelphij *verify_level = (unsigned int)l; 297296465Sdelphij return 1; 298109998Smarkm} 299109998Smarkm 300109998Smarkmstatic int parse_verify_depth(const char *s, unsigned int *verify_depth) 301109998Smarkm{ 302296465Sdelphij unsigned long l; 303296465Sdelphij if (!int_strtoul(s, &l) || (l < 1) || (l > 50)) { 304296465Sdelphij fprintf(stderr, "Error, '%s' is an invalid value for " 305296465Sdelphij "verify_depth\n", s); 306296465Sdelphij return 0; 307296465Sdelphij } 308296465Sdelphij *verify_depth = (unsigned int)l; 309296465Sdelphij return 1; 310109998Smarkm} 311109998Smarkm 312109998Smarkm/* Some fprintf format strings used when tunnels close */ 313109998Smarkmstatic const char *io_stats_dirty = 314296465Sdelphij " SSL traffic; %8lu bytes in, %8lu bytes out\n"; 315109998Smarkmstatic const char *io_stats_clean = 316296465Sdelphij " clear traffic; %8lu bytes in, %8lu bytes out\n"; 317109998Smarkm 318109998Smarkmint main(int argc, char *argv[]) 319109998Smarkm{ 320296465Sdelphij unsigned int loop; 321296465Sdelphij int newfd; 322296465Sdelphij tunala_world_t world; 323296465Sdelphij tunala_item_t *t_item; 324296465Sdelphij const char *proxy_ip; 325296465Sdelphij unsigned short proxy_port; 326296465Sdelphij /* Overridables */ 327296465Sdelphij const char *proxyhost = def_proxyhost; 328296465Sdelphij const char *listenhost = def_listenhost; 329296465Sdelphij unsigned int max_tunnels = def_max_tunnels; 330296465Sdelphij const char *cacert = def_cacert; 331296465Sdelphij const char *cert = def_cert; 332296465Sdelphij const char *key = def_key; 333296465Sdelphij const char *dcert = def_dcert; 334296465Sdelphij const char *dkey = def_dkey; 335296465Sdelphij const char *engine_id = def_engine_id; 336296465Sdelphij int server_mode = def_server_mode; 337296465Sdelphij int flipped = def_flipped; 338296465Sdelphij const char *cipher_list = def_cipher_list; 339296465Sdelphij const char *dh_file = def_dh_file; 340296465Sdelphij const char *dh_special = def_dh_special; 341296465Sdelphij int tmp_rsa = def_tmp_rsa; 342296465Sdelphij int ctx_options = def_ctx_options; 343296465Sdelphij int verify_mode = def_verify_mode; 344296465Sdelphij unsigned int verify_depth = def_verify_depth; 345296465Sdelphij int out_state = def_out_state; 346296465Sdelphij unsigned int out_verify = def_out_verify; 347296465Sdelphij int out_totals = def_out_totals; 348296465Sdelphij int out_conns = def_out_conns; 349109998Smarkm 350109998Smarkm/* Parse command-line arguments */ 351296465Sdelphij next_arg: 352296465Sdelphij argc--; 353296465Sdelphij argv++; 354296465Sdelphij if (argc > 0) { 355296465Sdelphij if (strcmp(*argv, "-listen") == 0) { 356296465Sdelphij if (argc < 2) 357296465Sdelphij return usage("-listen requires an argument", 0); 358296465Sdelphij argc--; 359296465Sdelphij argv++; 360296465Sdelphij listenhost = *argv; 361296465Sdelphij goto next_arg; 362296465Sdelphij } else if (strcmp(*argv, "-proxy") == 0) { 363296465Sdelphij if (argc < 2) 364296465Sdelphij return usage("-proxy requires an argument", 0); 365296465Sdelphij argc--; 366296465Sdelphij argv++; 367296465Sdelphij proxyhost = *argv; 368296465Sdelphij goto next_arg; 369296465Sdelphij } else if (strcmp(*argv, "-maxtunnels") == 0) { 370296465Sdelphij if (argc < 2) 371296465Sdelphij return usage("-maxtunnels requires an argument", 0); 372296465Sdelphij argc--; 373296465Sdelphij argv++; 374296465Sdelphij if (!parse_max_tunnels(*argv, &max_tunnels)) 375296465Sdelphij return 1; 376296465Sdelphij goto next_arg; 377296465Sdelphij } else if (strcmp(*argv, "-cacert") == 0) { 378296465Sdelphij if (argc < 2) 379296465Sdelphij return usage("-cacert requires an argument", 0); 380296465Sdelphij argc--; 381296465Sdelphij argv++; 382296465Sdelphij if (strcmp(*argv, "NULL") == 0) 383296465Sdelphij cacert = NULL; 384296465Sdelphij else 385296465Sdelphij cacert = *argv; 386296465Sdelphij goto next_arg; 387296465Sdelphij } else if (strcmp(*argv, "-cert") == 0) { 388296465Sdelphij if (argc < 2) 389296465Sdelphij return usage("-cert requires an argument", 0); 390296465Sdelphij argc--; 391296465Sdelphij argv++; 392296465Sdelphij if (strcmp(*argv, "NULL") == 0) 393296465Sdelphij cert = NULL; 394296465Sdelphij else 395296465Sdelphij cert = *argv; 396296465Sdelphij goto next_arg; 397296465Sdelphij } else if (strcmp(*argv, "-key") == 0) { 398296465Sdelphij if (argc < 2) 399296465Sdelphij return usage("-key requires an argument", 0); 400296465Sdelphij argc--; 401296465Sdelphij argv++; 402296465Sdelphij if (strcmp(*argv, "NULL") == 0) 403296465Sdelphij key = NULL; 404296465Sdelphij else 405296465Sdelphij key = *argv; 406296465Sdelphij goto next_arg; 407296465Sdelphij } else if (strcmp(*argv, "-dcert") == 0) { 408296465Sdelphij if (argc < 2) 409296465Sdelphij return usage("-dcert requires an argument", 0); 410296465Sdelphij argc--; 411296465Sdelphij argv++; 412296465Sdelphij if (strcmp(*argv, "NULL") == 0) 413296465Sdelphij dcert = NULL; 414296465Sdelphij else 415296465Sdelphij dcert = *argv; 416296465Sdelphij goto next_arg; 417296465Sdelphij } else if (strcmp(*argv, "-dkey") == 0) { 418296465Sdelphij if (argc < 2) 419296465Sdelphij return usage("-dkey requires an argument", 0); 420296465Sdelphij argc--; 421296465Sdelphij argv++; 422296465Sdelphij if (strcmp(*argv, "NULL") == 0) 423296465Sdelphij dkey = NULL; 424296465Sdelphij else 425296465Sdelphij dkey = *argv; 426296465Sdelphij goto next_arg; 427296465Sdelphij } else if (strcmp(*argv, "-engine") == 0) { 428296465Sdelphij if (argc < 2) 429296465Sdelphij return usage("-engine requires an argument", 0); 430296465Sdelphij argc--; 431296465Sdelphij argv++; 432296465Sdelphij engine_id = *argv; 433296465Sdelphij goto next_arg; 434296465Sdelphij } else if (strcmp(*argv, "-server") == 0) { 435296465Sdelphij if (argc < 2) 436296465Sdelphij return usage("-server requires an argument", 0); 437296465Sdelphij argc--; 438296465Sdelphij argv++; 439296465Sdelphij if (!parse_server_mode(*argv, &server_mode)) 440296465Sdelphij return 1; 441296465Sdelphij goto next_arg; 442296465Sdelphij } else if (strcmp(*argv, "-flipped") == 0) { 443296465Sdelphij if (argc < 2) 444296465Sdelphij return usage("-flipped requires an argument", 0); 445296465Sdelphij argc--; 446296465Sdelphij argv++; 447296465Sdelphij if (!parse_server_mode(*argv, &flipped)) 448296465Sdelphij return 1; 449296465Sdelphij goto next_arg; 450296465Sdelphij } else if (strcmp(*argv, "-cipher") == 0) { 451296465Sdelphij if (argc < 2) 452296465Sdelphij return usage("-cipher requires an argument", 0); 453296465Sdelphij argc--; 454296465Sdelphij argv++; 455296465Sdelphij cipher_list = *argv; 456296465Sdelphij goto next_arg; 457296465Sdelphij } else if (strcmp(*argv, "-dh_file") == 0) { 458296465Sdelphij if (argc < 2) 459296465Sdelphij return usage("-dh_file requires an argument", 0); 460296465Sdelphij if (dh_special) 461296465Sdelphij return usage("cannot mix -dh_file with " "-dh_special", 0); 462296465Sdelphij argc--; 463296465Sdelphij argv++; 464296465Sdelphij dh_file = *argv; 465296465Sdelphij goto next_arg; 466296465Sdelphij } else if (strcmp(*argv, "-dh_special") == 0) { 467296465Sdelphij if (argc < 2) 468296465Sdelphij return usage("-dh_special requires an argument", 0); 469296465Sdelphij if (dh_file) 470296465Sdelphij return usage("cannot mix -dh_file with " "-dh_special", 0); 471296465Sdelphij argc--; 472296465Sdelphij argv++; 473296465Sdelphij if (!parse_dh_special(*argv, &dh_special)) 474296465Sdelphij return 1; 475296465Sdelphij goto next_arg; 476296465Sdelphij } else if (strcmp(*argv, "-no_tmp_rsa") == 0) { 477296465Sdelphij tmp_rsa = 0; 478296465Sdelphij goto next_arg; 479296465Sdelphij } else if (strcmp(*argv, "-no_ssl2") == 0) { 480296465Sdelphij ctx_options |= SSL_OP_NO_SSLv2; 481296465Sdelphij goto next_arg; 482296465Sdelphij } else if (strcmp(*argv, "-no_ssl3") == 0) { 483296465Sdelphij ctx_options |= SSL_OP_NO_SSLv3; 484296465Sdelphij goto next_arg; 485296465Sdelphij } else if (strcmp(*argv, "-no_tls1") == 0) { 486296465Sdelphij ctx_options |= SSL_OP_NO_TLSv1; 487296465Sdelphij goto next_arg; 488296465Sdelphij } else if (strcmp(*argv, "-v_peer") == 0) { 489296465Sdelphij verify_mode |= SSL_VERIFY_PEER; 490296465Sdelphij goto next_arg; 491296465Sdelphij } else if (strcmp(*argv, "-v_strict") == 0) { 492296465Sdelphij verify_mode |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT; 493296465Sdelphij goto next_arg; 494296465Sdelphij } else if (strcmp(*argv, "-v_once") == 0) { 495296465Sdelphij verify_mode |= SSL_VERIFY_CLIENT_ONCE; 496296465Sdelphij goto next_arg; 497296465Sdelphij } else if (strcmp(*argv, "-v_depth") == 0) { 498296465Sdelphij if (argc < 2) 499296465Sdelphij return usage("-v_depth requires an argument", 0); 500296465Sdelphij argc--; 501296465Sdelphij argv++; 502296465Sdelphij if (!parse_verify_depth(*argv, &verify_depth)) 503296465Sdelphij return 1; 504296465Sdelphij goto next_arg; 505296465Sdelphij } else if (strcmp(*argv, "-out_state") == 0) { 506296465Sdelphij out_state = 1; 507296465Sdelphij goto next_arg; 508296465Sdelphij } else if (strcmp(*argv, "-out_verify") == 0) { 509296465Sdelphij if (argc < 2) 510296465Sdelphij return usage("-out_verify requires an argument", 0); 511296465Sdelphij argc--; 512296465Sdelphij argv++; 513296465Sdelphij if (!parse_verify_level(*argv, &out_verify)) 514296465Sdelphij return 1; 515296465Sdelphij goto next_arg; 516296465Sdelphij } else if (strcmp(*argv, "-out_totals") == 0) { 517296465Sdelphij out_totals = 1; 518296465Sdelphij goto next_arg; 519296465Sdelphij } else if (strcmp(*argv, "-out_conns") == 0) { 520296465Sdelphij out_conns = 1; 521296465Sdelphij goto next_arg; 522296465Sdelphij } else if ((strcmp(*argv, "-h") == 0) || 523296465Sdelphij (strcmp(*argv, "-help") == 0) || 524296465Sdelphij (strcmp(*argv, "-?") == 0)) { 525296465Sdelphij fprintf(stderr, "%s\n", helpstring); 526296465Sdelphij return 0; 527296465Sdelphij } else 528296465Sdelphij return usage(*argv, 1); 529296465Sdelphij } 530296465Sdelphij /* Run any sanity checks we want here */ 531296465Sdelphij if (!cert && !dcert && server_mode) 532296465Sdelphij fprintf(stderr, "WARNING: you are running an SSL server without " 533296465Sdelphij "a certificate - this may not work!\n"); 534109998Smarkm 535296465Sdelphij /* Initialise network stuff */ 536296465Sdelphij if (!ip_initialise()) 537296465Sdelphij return err_str0("ip_initialise failed"); 538296465Sdelphij /* Create the SSL_CTX */ 539296465Sdelphij if ((world.ssl_ctx = initialise_ssl_ctx(server_mode, engine_id, 540296465Sdelphij cacert, cert, key, dcert, dkey, 541296465Sdelphij cipher_list, dh_file, dh_special, 542296465Sdelphij tmp_rsa, ctx_options, out_state, 543296465Sdelphij out_verify, verify_mode, 544296465Sdelphij verify_depth)) == NULL) 545296465Sdelphij return err_str1("initialise_ssl_ctx(engine_id=%s) failed", 546296465Sdelphij (engine_id == NULL) ? "NULL" : engine_id); 547296465Sdelphij if (engine_id) 548296465Sdelphij fprintf(stderr, "Info, engine '%s' initialised\n", engine_id); 549296465Sdelphij /* Create the listener */ 550296465Sdelphij if ((world.listen_fd = ip_create_listener(listenhost)) == -1) 551296465Sdelphij return err_str1("ip_create_listener(%s) failed", listenhost); 552296465Sdelphij fprintf(stderr, "Info, listening on '%s'\n", listenhost); 553296465Sdelphij if (!ip_parse_address(proxyhost, &proxy_ip, &proxy_port, 0)) 554296465Sdelphij return err_str1("ip_parse_address(%s) failed", proxyhost); 555296465Sdelphij fprintf(stderr, "Info, proxying to '%s' (%d.%d.%d.%d:%d)\n", proxyhost, 556296465Sdelphij (int)proxy_ip[0], (int)proxy_ip[1], 557296465Sdelphij (int)proxy_ip[2], (int)proxy_ip[3], (int)proxy_port); 558296465Sdelphij fprintf(stderr, "Info, set maxtunnels to %d\n", (int)max_tunnels); 559296465Sdelphij fprintf(stderr, "Info, set to operate as an SSL %s\n", 560296465Sdelphij (server_mode ? "server" : "client")); 561296465Sdelphij /* Initialise the rest of the stuff */ 562296465Sdelphij world.tunnels_used = world.tunnels_size = 0; 563296465Sdelphij world.tunnels = NULL; 564296465Sdelphij world.server_mode = server_mode; 565296465Sdelphij selector_init(&world.selector); 566109998Smarkm 567109998Smarkm/* We're ready to loop */ 568296465Sdelphij main_loop: 569296465Sdelphij /* Should we listen for *new* tunnels? */ 570296465Sdelphij if (world.tunnels_used < max_tunnels) 571296465Sdelphij selector_add_listener(&world.selector, world.listen_fd); 572296465Sdelphij /* We should add in our existing tunnels */ 573296465Sdelphij for (loop = 0; loop < world.tunnels_used; loop++) 574296465Sdelphij selector_add_tunala(&world.selector, world.tunnels + loop); 575296465Sdelphij /* Now do the select */ 576296465Sdelphij switch (selector_select(&world.selector)) { 577296465Sdelphij case -1: 578296465Sdelphij if (errno != EINTR) { 579296465Sdelphij fprintf(stderr, "selector_select returned a " "badness error.\n"); 580296465Sdelphij goto shouldnt_happen; 581296465Sdelphij } 582296465Sdelphij fprintf(stderr, "Warn, selector interrupted by a signal\n"); 583296465Sdelphij goto main_loop; 584296465Sdelphij case 0: 585296465Sdelphij fprintf(stderr, "Warn, selector_select returned 0 - signal?" "?\n"); 586296465Sdelphij goto main_loop; 587296465Sdelphij default: 588296465Sdelphij break; 589296465Sdelphij } 590296465Sdelphij /* Accept new connection if we should and can */ 591296465Sdelphij if ((world.tunnels_used < max_tunnels) 592296465Sdelphij && (selector_get_listener(&world.selector, world.listen_fd, &newfd) == 593296465Sdelphij 1)) { 594296465Sdelphij /* We have a new connection */ 595296465Sdelphij if (!tunala_world_new_item(&world, newfd, proxy_ip, 596296465Sdelphij proxy_port, flipped)) 597296465Sdelphij fprintf(stderr, "tunala_world_new_item failed\n"); 598296465Sdelphij else if (out_conns) 599296465Sdelphij fprintf(stderr, "Info, new tunnel opened, now up to " 600296465Sdelphij "%d\n", world.tunnels_used); 601296465Sdelphij } 602296465Sdelphij /* 603296465Sdelphij * Give each tunnel its moment, note the while loop is because it makes 604296465Sdelphij * the logic easier than with "for" to deal with an array that may shift 605296465Sdelphij * because of deletes. 606296465Sdelphij */ 607296465Sdelphij loop = 0; 608296465Sdelphij t_item = world.tunnels; 609296465Sdelphij while (loop < world.tunnels_used) { 610296465Sdelphij if (!tunala_item_io(&world.selector, t_item)) { 611296465Sdelphij /* 612296465Sdelphij * We're closing whether for reasons of an error or a natural 613296465Sdelphij * close. Don't increment loop or t_item because the next item is 614296465Sdelphij * moving to us! 615296465Sdelphij */ 616296465Sdelphij if (!out_totals) 617296465Sdelphij goto skip_totals; 618296465Sdelphij fprintf(stderr, "Tunnel closing, traffic stats follow\n"); 619296465Sdelphij /* Display the encrypted (over the network) stats */ 620296465Sdelphij fprintf(stderr, io_stats_dirty, 621296465Sdelphij buffer_total_in(state_machine_get_buffer 622296465Sdelphij (&t_item->sm, SM_DIRTY_IN)), 623296465Sdelphij buffer_total_out(state_machine_get_buffer 624296465Sdelphij (&t_item->sm, SM_DIRTY_OUT))); 625296465Sdelphij /* 626296465Sdelphij * Display the local (tunnelled) stats. NB: Data we *receive* is 627296465Sdelphij * data sent *out* of the state_machine on its 'clean' side. 628296465Sdelphij * Hence the apparent back-to-front OUT/IN mixup here :-) 629296465Sdelphij */ 630296465Sdelphij fprintf(stderr, io_stats_clean, 631296465Sdelphij buffer_total_out(state_machine_get_buffer 632296465Sdelphij (&t_item->sm, SM_CLEAN_OUT)), 633296465Sdelphij buffer_total_in(state_machine_get_buffer 634296465Sdelphij (&t_item->sm, SM_CLEAN_IN))); 635296465Sdelphij skip_totals: 636296465Sdelphij tunala_world_del_item(&world, loop); 637296465Sdelphij if (out_conns) 638296465Sdelphij fprintf(stderr, "Info, tunnel closed, down to %d\n", 639296465Sdelphij world.tunnels_used); 640296465Sdelphij } else { 641296465Sdelphij /* Move to the next item */ 642296465Sdelphij loop++; 643296465Sdelphij t_item++; 644296465Sdelphij } 645296465Sdelphij } 646296465Sdelphij goto main_loop; 647296465Sdelphij /* Should never get here */ 648296465Sdelphij shouldnt_happen: 649296465Sdelphij abort(); 650296465Sdelphij return 1; 651109998Smarkm} 652109998Smarkm 653109998Smarkm/****************/ 654109998Smarkm/* OpenSSL bits */ 655109998Smarkm/****************/ 656109998Smarkm 657109998Smarkmstatic int ctx_set_cert(SSL_CTX *ctx, const char *cert, const char *key) 658109998Smarkm{ 659296465Sdelphij FILE *fp = NULL; 660296465Sdelphij X509 *x509 = NULL; 661296465Sdelphij EVP_PKEY *pkey = NULL; 662296465Sdelphij int toret = 0; /* Assume an error */ 663109998Smarkm 664296465Sdelphij /* cert */ 665296465Sdelphij if (cert) { 666296465Sdelphij if ((fp = fopen(cert, "r")) == NULL) { 667296465Sdelphij fprintf(stderr, "Error opening cert file '%s'\n", cert); 668296465Sdelphij goto err; 669296465Sdelphij } 670296465Sdelphij if (!PEM_read_X509(fp, &x509, NULL, NULL)) { 671296465Sdelphij fprintf(stderr, "Error reading PEM cert from '%s'\n", cert); 672296465Sdelphij goto err; 673296465Sdelphij } 674296465Sdelphij if (!SSL_CTX_use_certificate(ctx, x509)) { 675296465Sdelphij fprintf(stderr, "Error, cert in '%s' can not be used\n", cert); 676296465Sdelphij goto err; 677296465Sdelphij } 678296465Sdelphij /* Clear the FILE* for reuse in the "key" code */ 679296465Sdelphij fclose(fp); 680296465Sdelphij fp = NULL; 681296465Sdelphij fprintf(stderr, "Info, operating with cert in '%s'\n", cert); 682296465Sdelphij /* 683296465Sdelphij * If a cert was given without matching key, we assume the same file 684296465Sdelphij * contains the required key. 685296465Sdelphij */ 686296465Sdelphij if (!key) 687296465Sdelphij key = cert; 688296465Sdelphij } else { 689296465Sdelphij if (key) 690296465Sdelphij fprintf(stderr, "Error, can't specify a key without a " 691296465Sdelphij "corresponding certificate\n"); 692296465Sdelphij else 693296465Sdelphij fprintf(stderr, "Error, ctx_set_cert called with " "NULLs!\n"); 694296465Sdelphij goto err; 695296465Sdelphij } 696296465Sdelphij /* key */ 697296465Sdelphij if (key) { 698296465Sdelphij if ((fp = fopen(key, "r")) == NULL) { 699296465Sdelphij fprintf(stderr, "Error opening key file '%s'\n", key); 700296465Sdelphij goto err; 701296465Sdelphij } 702296465Sdelphij if (!PEM_read_PrivateKey(fp, &pkey, NULL, NULL)) { 703296465Sdelphij fprintf(stderr, "Error reading PEM key from '%s'\n", key); 704296465Sdelphij goto err; 705296465Sdelphij } 706296465Sdelphij if (!SSL_CTX_use_PrivateKey(ctx, pkey)) { 707296465Sdelphij fprintf(stderr, "Error, key in '%s' can not be used\n", key); 708296465Sdelphij goto err; 709296465Sdelphij } 710296465Sdelphij fprintf(stderr, "Info, operating with key in '%s'\n", key); 711296465Sdelphij } else 712296465Sdelphij fprintf(stderr, "Info, operating without a cert or key\n"); 713296465Sdelphij /* Success */ 714296465Sdelphij toret = 1; 715296465Sdelphij err: 716296465Sdelphij if (x509) 717296465Sdelphij X509_free(x509); 718296465Sdelphij if (pkey) 719296465Sdelphij EVP_PKEY_free(pkey); 720296465Sdelphij if (fp) 721296465Sdelphij fclose(fp); 722296465Sdelphij return toret; 723109998Smarkm} 724109998Smarkm 725296465Sdelphijstatic int ctx_set_dh(SSL_CTX *ctx, const char *dh_file, 726296465Sdelphij const char *dh_special) 727109998Smarkm{ 728296465Sdelphij DH *dh = NULL; 729296465Sdelphij FILE *fp = NULL; 730109998Smarkm 731296465Sdelphij if (dh_special) { 732296465Sdelphij if (strcmp(dh_special, "NULL") == 0) 733296465Sdelphij return 1; 734296465Sdelphij if (strcmp(dh_special, "standard") == 0) { 735296465Sdelphij if ((dh = get_dh512()) == NULL) { 736296465Sdelphij fprintf(stderr, "Error, can't parse 'standard'" 737296465Sdelphij " DH parameters\n"); 738296465Sdelphij return 0; 739296465Sdelphij } 740296465Sdelphij fprintf(stderr, "Info, using 'standard' DH parameters\n"); 741296465Sdelphij goto do_it; 742296465Sdelphij } 743296465Sdelphij if (strcmp(dh_special, "generate") != 0) 744296465Sdelphij /* 745296465Sdelphij * This shouldn't happen - screening values is handled in main(). 746296465Sdelphij */ 747296465Sdelphij abort(); 748296465Sdelphij fprintf(stderr, "Info, generating DH parameters ... "); 749296465Sdelphij fflush(stderr); 750296465Sdelphij if ((dh = DH_generate_parameters(512, DH_GENERATOR_5, 751296465Sdelphij NULL, NULL)) == NULL) { 752296465Sdelphij fprintf(stderr, "error!\n"); 753296465Sdelphij return 0; 754296465Sdelphij } 755296465Sdelphij fprintf(stderr, "complete\n"); 756296465Sdelphij goto do_it; 757296465Sdelphij } 758296465Sdelphij /* So, we're loading dh_file */ 759296465Sdelphij if ((fp = fopen(dh_file, "r")) == NULL) { 760296465Sdelphij fprintf(stderr, "Error, couldn't open '%s' for DH parameters\n", 761296465Sdelphij dh_file); 762296465Sdelphij return 0; 763296465Sdelphij } 764296465Sdelphij dh = PEM_read_DHparams(fp, NULL, NULL, NULL); 765296465Sdelphij fclose(fp); 766296465Sdelphij if (dh == NULL) { 767296465Sdelphij fprintf(stderr, "Error, could not parse DH parameters from '%s'\n", 768296465Sdelphij dh_file); 769296465Sdelphij return 0; 770296465Sdelphij } 771296465Sdelphij fprintf(stderr, "Info, using DH parameters from file '%s'\n", dh_file); 772296465Sdelphij do_it: 773296465Sdelphij SSL_CTX_set_tmp_dh(ctx, dh); 774296465Sdelphij DH_free(dh); 775296465Sdelphij return 1; 776109998Smarkm} 777109998Smarkm 778109998Smarkmstatic SSL_CTX *initialise_ssl_ctx(int server_mode, const char *engine_id, 779296465Sdelphij const char *CAfile, const char *cert, 780296465Sdelphij const char *key, const char *dcert, 781296465Sdelphij const char *dkey, const char *cipher_list, 782296465Sdelphij const char *dh_file, 783296465Sdelphij const char *dh_special, int tmp_rsa, 784296465Sdelphij int ctx_options, int out_state, 785296465Sdelphij int out_verify, int verify_mode, 786296465Sdelphij unsigned int verify_depth) 787109998Smarkm{ 788296465Sdelphij SSL_CTX *ctx = NULL, *ret = NULL; 789296465Sdelphij SSL_METHOD *meth; 790296465Sdelphij ENGINE *e = NULL; 791109998Smarkm 792296465Sdelphij OpenSSL_add_ssl_algorithms(); 793296465Sdelphij SSL_load_error_strings(); 794109998Smarkm 795296465Sdelphij meth = (server_mode ? SSLv23_server_method() : SSLv23_client_method()); 796296465Sdelphij if (meth == NULL) 797296465Sdelphij goto err; 798296465Sdelphij if (engine_id) { 799296465Sdelphij ENGINE_load_builtin_engines(); 800296465Sdelphij if ((e = ENGINE_by_id(engine_id)) == NULL) { 801296465Sdelphij fprintf(stderr, "Error obtaining '%s' engine, openssl " 802296465Sdelphij "errors follow\n", engine_id); 803296465Sdelphij goto err; 804296465Sdelphij } 805296465Sdelphij if (!ENGINE_set_default(e, ENGINE_METHOD_ALL)) { 806296465Sdelphij fprintf(stderr, "Error assigning '%s' engine, openssl " 807296465Sdelphij "errors follow\n", engine_id); 808296465Sdelphij goto err; 809296465Sdelphij } 810296465Sdelphij ENGINE_free(e); 811296465Sdelphij } 812296465Sdelphij if ((ctx = SSL_CTX_new(meth)) == NULL) 813296465Sdelphij goto err; 814296465Sdelphij /* cacert */ 815296465Sdelphij if (CAfile) { 816296465Sdelphij if (!X509_STORE_load_locations(SSL_CTX_get_cert_store(ctx), 817296465Sdelphij CAfile, NULL)) { 818296465Sdelphij fprintf(stderr, "Error loading CA cert(s) in '%s'\n", CAfile); 819296465Sdelphij goto err; 820296465Sdelphij } 821296465Sdelphij fprintf(stderr, "Info, operating with CA cert(s) in '%s'\n", CAfile); 822296465Sdelphij } else 823296465Sdelphij fprintf(stderr, "Info, operating without a CA cert(-list)\n"); 824296465Sdelphij if (!SSL_CTX_set_default_verify_paths(ctx)) { 825296465Sdelphij fprintf(stderr, "Error setting default verify paths\n"); 826296465Sdelphij goto err; 827296465Sdelphij } 828109998Smarkm 829296465Sdelphij /* cert and key */ 830296465Sdelphij if ((cert || key) && !ctx_set_cert(ctx, cert, key)) 831296465Sdelphij goto err; 832296465Sdelphij /* dcert and dkey */ 833296465Sdelphij if ((dcert || dkey) && !ctx_set_cert(ctx, dcert, dkey)) 834296465Sdelphij goto err; 835296465Sdelphij /* temporary RSA key generation */ 836296465Sdelphij if (tmp_rsa) 837296465Sdelphij SSL_CTX_set_tmp_rsa_callback(ctx, cb_generate_tmp_rsa); 838109998Smarkm 839296465Sdelphij /* cipher_list */ 840296465Sdelphij if (cipher_list) { 841296465Sdelphij if (!SSL_CTX_set_cipher_list(ctx, cipher_list)) { 842296465Sdelphij fprintf(stderr, "Error setting cipher list '%s'\n", cipher_list); 843296465Sdelphij goto err; 844296465Sdelphij } 845296465Sdelphij fprintf(stderr, "Info, set cipher list '%s'\n", cipher_list); 846296465Sdelphij } else 847296465Sdelphij fprintf(stderr, "Info, operating with default cipher list\n"); 848109998Smarkm 849296465Sdelphij /* dh_file & dh_special */ 850296465Sdelphij if ((dh_file || dh_special) && !ctx_set_dh(ctx, dh_file, dh_special)) 851296465Sdelphij goto err; 852109998Smarkm 853296465Sdelphij /* ctx_options */ 854296465Sdelphij SSL_CTX_set_options(ctx, ctx_options); 855109998Smarkm 856296465Sdelphij /* out_state (output of SSL handshake states to screen). */ 857296465Sdelphij if (out_state) 858296465Sdelphij cb_ssl_info_set_output(stderr); 859109998Smarkm 860296465Sdelphij /* out_verify */ 861296465Sdelphij if (out_verify > 0) { 862296465Sdelphij cb_ssl_verify_set_output(stderr); 863296465Sdelphij cb_ssl_verify_set_level(out_verify); 864296465Sdelphij } 865109998Smarkm 866296465Sdelphij /* verify_depth */ 867296465Sdelphij cb_ssl_verify_set_depth(verify_depth); 868109998Smarkm 869296465Sdelphij /* Success! (includes setting verify_mode) */ 870296465Sdelphij SSL_CTX_set_info_callback(ctx, cb_ssl_info); 871296465Sdelphij SSL_CTX_set_verify(ctx, verify_mode, cb_ssl_verify); 872296465Sdelphij ret = ctx; 873296465Sdelphij err: 874296465Sdelphij if (!ret) { 875296465Sdelphij ERR_print_errors_fp(stderr); 876296465Sdelphij if (ctx) 877296465Sdelphij SSL_CTX_free(ctx); 878296465Sdelphij } 879296465Sdelphij return ret; 880109998Smarkm} 881109998Smarkm 882109998Smarkm/*****************/ 883109998Smarkm/* Selector bits */ 884109998Smarkm/*****************/ 885109998Smarkm 886296465Sdelphijstatic void selector_sets_init(select_sets_t * s) 887109998Smarkm{ 888296465Sdelphij s->max = 0; 889296465Sdelphij FD_ZERO(&s->reads); 890296465Sdelphij FD_ZERO(&s->sends); 891296465Sdelphij FD_ZERO(&s->excepts); 892109998Smarkm} 893296465Sdelphij 894296465Sdelphijstatic void selector_init(tunala_selector_t * selector) 895109998Smarkm{ 896296465Sdelphij selector_sets_init(&selector->last_selected); 897296465Sdelphij selector_sets_init(&selector->next_select); 898109998Smarkm} 899109998Smarkm 900109998Smarkm#define SEL_EXCEPTS 0x00 901109998Smarkm#define SEL_READS 0x01 902109998Smarkm#define SEL_SENDS 0x02 903296465Sdelphijstatic void selector_add_raw_fd(tunala_selector_t * s, int fd, int flags) 904109998Smarkm{ 905296465Sdelphij FD_SET(fd, &s->next_select.excepts); 906296465Sdelphij if (flags & SEL_READS) 907296465Sdelphij FD_SET(fd, &s->next_select.reads); 908296465Sdelphij if (flags & SEL_SENDS) 909296465Sdelphij FD_SET(fd, &s->next_select.sends); 910296465Sdelphij /* Adjust "max" */ 911296465Sdelphij if (s->next_select.max < (fd + 1)) 912296465Sdelphij s->next_select.max = fd + 1; 913109998Smarkm} 914109998Smarkm 915296465Sdelphijstatic void selector_add_listener(tunala_selector_t * selector, int fd) 916109998Smarkm{ 917296465Sdelphij selector_add_raw_fd(selector, fd, SEL_READS); 918109998Smarkm} 919109998Smarkm 920296465Sdelphijstatic void selector_add_tunala(tunala_selector_t * s, tunala_item_t * t) 921109998Smarkm{ 922296465Sdelphij /* Set clean read if sm.clean_in is not full */ 923296465Sdelphij if (t->clean_read != -1) { 924296465Sdelphij selector_add_raw_fd(s, t->clean_read, 925296465Sdelphij (buffer_full(state_machine_get_buffer(&t->sm, 926296465Sdelphij SM_CLEAN_IN)) 927296465Sdelphij ? SEL_EXCEPTS : SEL_READS)); 928296465Sdelphij } 929296465Sdelphij /* Set clean send if sm.clean_out is not empty */ 930296465Sdelphij if (t->clean_send != -1) { 931296465Sdelphij selector_add_raw_fd(s, t->clean_send, 932296465Sdelphij (buffer_empty(state_machine_get_buffer(&t->sm, 933296465Sdelphij SM_CLEAN_OUT)) 934296465Sdelphij ? SEL_EXCEPTS : SEL_SENDS)); 935296465Sdelphij } 936296465Sdelphij /* Set dirty read if sm.dirty_in is not full */ 937296465Sdelphij if (t->dirty_read != -1) { 938296465Sdelphij selector_add_raw_fd(s, t->dirty_read, 939296465Sdelphij (buffer_full(state_machine_get_buffer(&t->sm, 940296465Sdelphij SM_DIRTY_IN)) 941296465Sdelphij ? SEL_EXCEPTS : SEL_READS)); 942296465Sdelphij } 943296465Sdelphij /* Set dirty send if sm.dirty_out is not empty */ 944296465Sdelphij if (t->dirty_send != -1) { 945296465Sdelphij selector_add_raw_fd(s, t->dirty_send, 946296465Sdelphij (buffer_empty(state_machine_get_buffer(&t->sm, 947296465Sdelphij SM_DIRTY_OUT)) 948296465Sdelphij ? SEL_EXCEPTS : SEL_SENDS)); 949296465Sdelphij } 950109998Smarkm} 951109998Smarkm 952296465Sdelphijstatic int selector_select(tunala_selector_t * selector) 953109998Smarkm{ 954296465Sdelphij memcpy(&selector->last_selected, &selector->next_select, 955296465Sdelphij sizeof(select_sets_t)); 956296465Sdelphij selector_sets_init(&selector->next_select); 957296465Sdelphij return select(selector->last_selected.max, 958296465Sdelphij &selector->last_selected.reads, 959296465Sdelphij &selector->last_selected.sends, 960296465Sdelphij &selector->last_selected.excepts, NULL); 961109998Smarkm} 962109998Smarkm 963296465Sdelphij/* 964296465Sdelphij * This returns -1 for error, 0 for no new connections, or 1 for success, in 965296465Sdelphij * which case *newfd is populated. 966296465Sdelphij */ 967296465Sdelphijstatic int selector_get_listener(tunala_selector_t * selector, int fd, 968296465Sdelphij int *newfd) 969109998Smarkm{ 970296465Sdelphij if (FD_ISSET(fd, &selector->last_selected.excepts)) 971296465Sdelphij return -1; 972296465Sdelphij if (!FD_ISSET(fd, &selector->last_selected.reads)) 973296465Sdelphij return 0; 974296465Sdelphij if ((*newfd = ip_accept_connection(fd)) == -1) 975296465Sdelphij return -1; 976296465Sdelphij return 1; 977109998Smarkm} 978109998Smarkm 979109998Smarkm/************************/ 980109998Smarkm/* "Tunala" world stuff */ 981109998Smarkm/************************/ 982109998Smarkm 983296465Sdelphijstatic int tunala_world_make_room(tunala_world_t * world) 984109998Smarkm{ 985296465Sdelphij unsigned int newsize; 986296465Sdelphij tunala_item_t *newarray; 987109998Smarkm 988296465Sdelphij if (world->tunnels_used < world->tunnels_size) 989296465Sdelphij return 1; 990296465Sdelphij newsize = (world->tunnels_size == 0 ? 16 : 991296465Sdelphij ((world->tunnels_size * 3) / 2)); 992296465Sdelphij if ((newarray = malloc(newsize * sizeof(tunala_item_t))) == NULL) 993296465Sdelphij return 0; 994296465Sdelphij memset(newarray, 0, newsize * sizeof(tunala_item_t)); 995296465Sdelphij if (world->tunnels_used > 0) 996296465Sdelphij memcpy(newarray, world->tunnels, 997296465Sdelphij world->tunnels_used * sizeof(tunala_item_t)); 998296465Sdelphij if (world->tunnels_size > 0) 999296465Sdelphij free(world->tunnels); 1000296465Sdelphij /* migrate */ 1001296465Sdelphij world->tunnels = newarray; 1002296465Sdelphij world->tunnels_size = newsize; 1003296465Sdelphij return 1; 1004109998Smarkm} 1005109998Smarkm 1006296465Sdelphijstatic int tunala_world_new_item(tunala_world_t * world, int fd, 1007296465Sdelphij const char *ip, unsigned short port, 1008296465Sdelphij int flipped) 1009109998Smarkm{ 1010296465Sdelphij tunala_item_t *item; 1011296465Sdelphij int newfd; 1012296465Sdelphij SSL *new_ssl = NULL; 1013109998Smarkm 1014296465Sdelphij if (!tunala_world_make_room(world)) 1015296465Sdelphij return 0; 1016296465Sdelphij if ((new_ssl = SSL_new(world->ssl_ctx)) == NULL) { 1017296465Sdelphij fprintf(stderr, "Error creating new SSL\n"); 1018296465Sdelphij ERR_print_errors_fp(stderr); 1019296465Sdelphij return 0; 1020296465Sdelphij } 1021296465Sdelphij item = world->tunnels + (world->tunnels_used++); 1022296465Sdelphij state_machine_init(&item->sm); 1023296465Sdelphij item->clean_read = item->clean_send = 1024296465Sdelphij item->dirty_read = item->dirty_send = -1; 1025296465Sdelphij if ((newfd = ip_create_connection_split(ip, port)) == -1) 1026296465Sdelphij goto err; 1027296465Sdelphij /* 1028296465Sdelphij * Which way round? If we're a server, "fd" is the dirty side and the 1029296465Sdelphij * connection we open is the clean one. For a client, it's the other way 1030296465Sdelphij * around. Unless, of course, we're "flipped" in which case everything 1031296465Sdelphij * gets reversed. :-) 1032296465Sdelphij */ 1033296465Sdelphij if ((world->server_mode && !flipped) || (!world->server_mode && flipped)) { 1034296465Sdelphij item->dirty_read = item->dirty_send = fd; 1035296465Sdelphij item->clean_read = item->clean_send = newfd; 1036296465Sdelphij } else { 1037296465Sdelphij item->clean_read = item->clean_send = fd; 1038296465Sdelphij item->dirty_read = item->dirty_send = newfd; 1039296465Sdelphij } 1040296465Sdelphij /* 1041296465Sdelphij * We use the SSL's "app_data" to indicate a call-back induced "kill" 1042296465Sdelphij */ 1043296465Sdelphij SSL_set_app_data(new_ssl, NULL); 1044296465Sdelphij if (!state_machine_set_SSL(&item->sm, new_ssl, world->server_mode)) 1045296465Sdelphij goto err; 1046296465Sdelphij return 1; 1047296465Sdelphij err: 1048296465Sdelphij tunala_world_del_item(world, world->tunnels_used - 1); 1049296465Sdelphij return 0; 1050109998Smarkm 1051109998Smarkm} 1052109998Smarkm 1053296465Sdelphijstatic void tunala_world_del_item(tunala_world_t * world, unsigned int idx) 1054109998Smarkm{ 1055296465Sdelphij tunala_item_t *item = world->tunnels + idx; 1056296465Sdelphij if (item->clean_read != -1) 1057296465Sdelphij close(item->clean_read); 1058296465Sdelphij if (item->clean_send != item->clean_read) 1059296465Sdelphij close(item->clean_send); 1060296465Sdelphij item->clean_read = item->clean_send = -1; 1061296465Sdelphij if (item->dirty_read != -1) 1062296465Sdelphij close(item->dirty_read); 1063296465Sdelphij if (item->dirty_send != item->dirty_read) 1064296465Sdelphij close(item->dirty_send); 1065296465Sdelphij item->dirty_read = item->dirty_send = -1; 1066296465Sdelphij state_machine_close(&item->sm); 1067296465Sdelphij /* OK, now we fix the item array */ 1068296465Sdelphij if (idx + 1 < world->tunnels_used) 1069296465Sdelphij /* We need to scroll entries to the left */ 1070296465Sdelphij memmove(world->tunnels + idx, 1071296465Sdelphij world->tunnels + (idx + 1), 1072296465Sdelphij (world->tunnels_used - (idx + 1)) * sizeof(tunala_item_t)); 1073296465Sdelphij world->tunnels_used--; 1074109998Smarkm} 1075109998Smarkm 1076296465Sdelphijstatic int tunala_item_io(tunala_selector_t * selector, tunala_item_t * item) 1077109998Smarkm{ 1078296465Sdelphij int c_r, c_s, d_r, d_s; /* Four boolean flags */ 1079109998Smarkm 1080296465Sdelphij /* Take ourselves out of the gene-pool if there was an except */ 1081296465Sdelphij if ((item->clean_read != -1) && FD_ISSET(item->clean_read, 1082296465Sdelphij &selector-> 1083296465Sdelphij last_selected.excepts)) 1084296465Sdelphij return 0; 1085296465Sdelphij if ((item->clean_send != -1) && FD_ISSET(item->clean_send, 1086296465Sdelphij &selector-> 1087296465Sdelphij last_selected.excepts)) 1088296465Sdelphij return 0; 1089296465Sdelphij if ((item->dirty_read != -1) && FD_ISSET(item->dirty_read, 1090296465Sdelphij &selector-> 1091296465Sdelphij last_selected.excepts)) 1092296465Sdelphij return 0; 1093296465Sdelphij if ((item->dirty_send != -1) && FD_ISSET(item->dirty_send, 1094296465Sdelphij &selector-> 1095296465Sdelphij last_selected.excepts)) 1096296465Sdelphij return 0; 1097296465Sdelphij /* Grab our 4 IO flags */ 1098296465Sdelphij c_r = c_s = d_r = d_s = 0; 1099296465Sdelphij if (item->clean_read != -1) 1100296465Sdelphij c_r = FD_ISSET(item->clean_read, &selector->last_selected.reads); 1101296465Sdelphij if (item->clean_send != -1) 1102296465Sdelphij c_s = FD_ISSET(item->clean_send, &selector->last_selected.sends); 1103296465Sdelphij if (item->dirty_read != -1) 1104296465Sdelphij d_r = FD_ISSET(item->dirty_read, &selector->last_selected.reads); 1105296465Sdelphij if (item->dirty_send != -1) 1106296465Sdelphij d_s = FD_ISSET(item->dirty_send, &selector->last_selected.sends); 1107296465Sdelphij /* If no IO has happened for us, skip needless data looping */ 1108296465Sdelphij if (!c_r && !c_s && !d_r && !d_s) 1109296465Sdelphij return 1; 1110296465Sdelphij if (c_r) 1111296465Sdelphij c_r = (buffer_from_fd(state_machine_get_buffer(&item->sm, 1112296465Sdelphij SM_CLEAN_IN), 1113296465Sdelphij item->clean_read) <= 0); 1114296465Sdelphij if (c_s) 1115296465Sdelphij c_s = (buffer_to_fd(state_machine_get_buffer(&item->sm, 1116296465Sdelphij SM_CLEAN_OUT), 1117296465Sdelphij item->clean_send) <= 0); 1118296465Sdelphij if (d_r) 1119296465Sdelphij d_r = (buffer_from_fd(state_machine_get_buffer(&item->sm, 1120296465Sdelphij SM_DIRTY_IN), 1121296465Sdelphij item->dirty_read) <= 0); 1122296465Sdelphij if (d_s) 1123296465Sdelphij d_s = (buffer_to_fd(state_machine_get_buffer(&item->sm, 1124296465Sdelphij SM_DIRTY_OUT), 1125296465Sdelphij item->dirty_send) <= 0); 1126296465Sdelphij /* If any of the flags is non-zero, that means they need closing */ 1127296465Sdelphij if (c_r) { 1128296465Sdelphij close(item->clean_read); 1129296465Sdelphij if (item->clean_send == item->clean_read) 1130296465Sdelphij item->clean_send = -1; 1131296465Sdelphij item->clean_read = -1; 1132296465Sdelphij } 1133296465Sdelphij if (c_s && (item->clean_send != -1)) { 1134296465Sdelphij close(item->clean_send); 1135296465Sdelphij if (item->clean_send == item->clean_read) 1136296465Sdelphij item->clean_read = -1; 1137296465Sdelphij item->clean_send = -1; 1138296465Sdelphij } 1139296465Sdelphij if (d_r) { 1140296465Sdelphij close(item->dirty_read); 1141296465Sdelphij if (item->dirty_send == item->dirty_read) 1142296465Sdelphij item->dirty_send = -1; 1143296465Sdelphij item->dirty_read = -1; 1144296465Sdelphij } 1145296465Sdelphij if (d_s && (item->dirty_send != -1)) { 1146296465Sdelphij close(item->dirty_send); 1147296465Sdelphij if (item->dirty_send == item->dirty_read) 1148296465Sdelphij item->dirty_read = -1; 1149296465Sdelphij item->dirty_send = -1; 1150296465Sdelphij } 1151296465Sdelphij /* 1152296465Sdelphij * This function name is attributed to the term donated by David Schwartz 1153296465Sdelphij * on openssl-dev, message-ID: 1154296465Sdelphij * <NCBBLIEPOCbmasEKBEAKEEDGLIAA.davids@webmaster.com>. :-) 1155296465Sdelphij */ 1156296465Sdelphij if (!state_machine_churn(&item->sm)) 1157296465Sdelphij /* 1158296465Sdelphij * If the SSL closes, it will also zero-out the _in buffers and will 1159296465Sdelphij * in future process just outgoing data. As and when the outgoing 1160296465Sdelphij * data has gone, it will return zero here to tell us to bail out. 1161296465Sdelphij */ 1162296465Sdelphij return 0; 1163296465Sdelphij /* Otherwise, we return zero if both sides are dead. */ 1164296465Sdelphij if (((item->clean_read == -1) || (item->clean_send == -1)) && 1165296465Sdelphij ((item->dirty_read == -1) || (item->dirty_send == -1))) 1166296465Sdelphij return 0; 1167296465Sdelphij /* 1168296465Sdelphij * If only one side closed, notify the SSL of this so it can take 1169296465Sdelphij * appropriate action. 1170296465Sdelphij */ 1171296465Sdelphij if ((item->clean_read == -1) || (item->clean_send == -1)) { 1172296465Sdelphij if (!state_machine_close_clean(&item->sm)) 1173296465Sdelphij return 0; 1174296465Sdelphij } 1175296465Sdelphij if ((item->dirty_read == -1) || (item->dirty_send == -1)) { 1176296465Sdelphij if (!state_machine_close_dirty(&item->sm)) 1177296465Sdelphij return 0; 1178296465Sdelphij } 1179296465Sdelphij return 1; 1180109998Smarkm} 1181