% Evil cert patch: proceed elsewhere when certificate cannot be verified % ====================================================================== % % This patch brings in the two following directives: % - evilconnect % - evilexec/evilexecargs % % When stunnel works in server mode and is asked to verify the client's % certificate, if the latter happens to be invalid the connection will % be normally shut down. When one of these options is used and the % certificate cannot be verified, stunnel redirects the "evil" % connection to another destination. % % The purpose of this feature is to cover up a suspicious SSL tunnel % aimed to evade a dictatorial proxy. % % How to apply this patch? Execute the following command in the top % stunnel directory: % patch -p1 < evil.patch % % Written by Jeremie LE HEN, jeremie at le-hen org, Jul 2009. % diff -crp stunnel-4.29/doc/stunnel.pod stunnel-4.29-evil_certs/doc/stunnel.pod *** stunnel-4.29/doc/stunnel.pod Fri Nov 20 21:48:33 2009 --- stunnel-4.29-evil_certs/doc/stunnel.pod Thu Dec 3 23:15:21 2009 *************** select engine number to read private key *** 378,383 **** --- 378,406 ---- The engines are numbered starting from 1. + =item B = [host:]port + + connect to a remote host:port when the client's certificate cannot + be verified + + This is only meaningful in server mode when I and I are used. + Otherwise it has the same properties as the I option. + + =item B = executable_path (Unix only) + + execute local inetd-type program when the client's certificate cannot + be verified + + This is only meaningful in server mode when I and I are used. + Otherwise it has the same properties as the I option. + + =item B = $0 $1 $2 ... (Unix only) + + arguments for I including program name ($0) + + Quoting is currently not supported. + Arguments are separated with arbitrary number of whitespaces. + =item B = executable_path (Unix only) execute local inetd-type program diff -crp stunnel-4.29/src/client.c stunnel-4.29-evil_certs/src/client.c *** stunnel-4.29/src/client.c Fri Oct 30 13:41:35 2009 --- stunnel-4.29-evil_certs/src/client.c Thu Dec 3 23:15:21 2009 *************** static void run_client(CLI *c) { *** 144,149 **** --- 144,150 ---- c->fd=-1; c->ssl=NULL; c->sock_bytes=c->ssl_bytes=0; + c->evil_cert=0; error=setjmp(c->err); if(!error) *************** static void init_remote(CLI *c) { *** 279,284 **** --- 280,286 ---- static void init_ssl(CLI *c) { int i, err; SSL_SESSION *old_session; + X509 *peer; if(!(c->ssl=SSL_new(c->opt->ctx))) { sslerror("SSL_new"); *************** static void init_ssl(CLI *c) { *** 329,336 **** i=SSL_accept(c->ssl); leave_critical_section(CRIT_SSL); err=SSL_get_error(c->ssl, i); ! if(err==SSL_ERROR_NONE) ! break; /* ok -> done */ if(err==SSL_ERROR_WANT_READ || err==SSL_ERROR_WANT_WRITE) { s_poll_init(&c->fds); s_poll_add(&c->fds, c->ssl_rfd->fd, --- 331,356 ---- i=SSL_accept(c->ssl); leave_critical_section(CRIT_SSL); err=SSL_get_error(c->ssl, i); ! if(err==SSL_ERROR_NONE) { ! if(c->opt->option.client) ! break; /* ok -> done */ ! if(!c->opt->option.evilremote && !c->opt->option.evilprogram) ! break; /* ok -> done */ ! peer=SSL_get_peer_certificate(c->ssl); ! if (!peer) { ! if (c->opt->verify_level!=SSL_VERIFY_NONE) { ! s_log(LOG_ERR, "No cert, proceed to evil cert endpoint"); ! c->evil_cert=1; ! } ! break; ! } ! if(SSL_get_verify_result(c->ssl)==X509_V_OK) ! break; ! sslerror("SSL_accept"); ! s_log(LOG_ERR, "Proceed to evil cert endpoint"); ! c->evil_cert=1; ! break; ! } if(err==SSL_ERROR_WANT_READ || err==SSL_ERROR_WANT_WRITE) { s_poll_init(&c->fds); s_poll_add(&c->fds, c->ssl_rfd->fd, *************** static int connect_local(CLI *c) { /* sp *** 859,865 **** sigemptyset(&newmask); sigprocmask(SIG_SETMASK, &newmask, NULL); #endif ! execvp(c->opt->execname, c->opt->execargs); ioerror(c->opt->execname); /* execv failed */ _exit(1); default: --- 879,888 ---- sigemptyset(&newmask); sigprocmask(SIG_SETMASK, &newmask, NULL); #endif ! if (c->evil_cert) ! execvp(c->opt->evilexecname, c->opt->evilexecargs); ! else ! execvp(c->opt->execname, c->opt->execargs); ioerror(c->opt->execname); /* execv failed */ _exit(1); default: *************** static int connect_remote(CLI *c) { /* c *** 935,947 **** if(c->opt->option.delayed_lookup) { resolved_list.num=0; if(!name2addrlist(&resolved_list, ! c->opt->remote_address, DEFAULT_LOOPBACK)) { s_log(LOG_ERR, "No host resolved"); longjmp(c->err, 1); } address_list=&resolved_list; } else /* use pre-resolved addresses */ ! address_list=&c->opt->remote_addr; /* try to connect each host from the list */ for(ind_try=0; ind_trynum; ind_try++) { --- 958,972 ---- if(c->opt->option.delayed_lookup) { resolved_list.num=0; if(!name2addrlist(&resolved_list, ! c->evil_cert ? c->opt->evilremote_address : ! c->opt->remote_address, DEFAULT_LOOPBACK)) { s_log(LOG_ERR, "No host resolved"); longjmp(c->err, 1); } address_list=&resolved_list; } else /* use pre-resolved addresses */ ! address_list=c->evil_cert ? &c->opt->evilremote_addr : ! &c->opt->remote_addr; /* try to connect each host from the list */ for(ind_try=0; ind_trynum; ind_try++) { diff -crp stunnel-4.29/src/options.c stunnel-4.29-evil_certs/src/options.c *** stunnel-4.29/src/options.c Fri Nov 20 21:55:12 2009 --- stunnel-4.29-evil_certs/src/options.c Thu Dec 3 23:15:21 2009 *************** static char *service_options(CMD cmd, LO *** 781,786 **** --- 781,855 ---- } #endif + /* evilconnect */ + switch(cmd) { + case CMD_INIT: + section->option.evilremote=0; + section->evilremote_address=NULL; + section->evilremote_addr.num=0; + break; + case CMD_EXEC: + if(strcasecmp(opt, "evilconnect")) + break; + section->option.evilremote=1; + section->evilremote_address=stralloc(arg); + if(!section->option.delayed_lookup && + !name2addrlist(§ion->evilremote_addr, arg, + DEFAULT_LOOPBACK)) { + s_log(LOG_RAW, "Cannot resolve '%s' - delaying DNS lookup", arg); + section->option.delayed_lookup=1; + } + return NULL; /* OK */ + case CMD_DEFAULT: + break; + case CMD_HELP: + s_log(LOG_RAW, "%-15s = [host:]port connect remote host:port " + "on evil client cert", "evilconnect"); + break; + } + + /* evilexec */ + #ifndef USE_WIN32 + switch(cmd) { + case CMD_INIT: + section->option.evilprogram=0; + section->evilexecname=NULL; + break; + case CMD_EXEC: + if(strcasecmp(opt, "evilexec")) + break; + section->option.evilprogram=1; + section->evilexecname=stralloc(arg); + return NULL; /* OK */ + case CMD_DEFAULT: + break; + case CMD_HELP: + s_log(LOG_RAW, "%-15s = file execute local inetd-type program " + "on evil client cert", "evilexec"); + break; + } + #endif + + /* evilexecargs */ + #ifndef USE_WIN32 + switch(cmd) { + case CMD_INIT: + section->evilexecargs=NULL; + break; + case CMD_EXEC: + if(strcasecmp(opt, "evilexecargs")) + break; + section->evilexecargs=argalloc(arg); + return NULL; /* OK */ + case CMD_DEFAULT: + break; + case CMD_HELP: + s_log(LOG_RAW, "%-15s = arguments for 'evilexec' (including $0)", + "evilexecargs"); + break; + } + #endif + /* exec */ #ifndef USE_WIN32 switch(cmd) { *************** static void section_validate(char *filen *** 1566,1571 **** --- 1635,1652 ---- #endif config_error(filename, line_number, "Each service section must define exactly two endpoints"); + if((section->option.evilremote || section->option.evilprogram) && + section->option.client) + config_error(filename, line_number, + "Evil cert actions have sense only in server mode"); + if((section->option.evilremote || section->option.evilprogram) && + section->verify_level==SSL_VERIFY_NONE) + config_error(filename, line_number, + "Evil cert actions are only meaningful when verify level > 0"); + if((section->option.evilremote && !section->option.remote) || + (section->option.evilprogram && !section->option.program)) + config_error(filename, line_number, + "Evil cert actions can be only specified with their counterpart"); return; /* All tests passed -- continue program execution */ } diff -crp stunnel-4.29/src/prototypes.h stunnel-4.29-evil_certs/src/prototypes.h *** stunnel-4.29/src/prototypes.h Fri Oct 30 12:14:23 2009 --- stunnel-4.29-evil_certs/src/prototypes.h Thu Dec 3 23:18:47 2009 *************** typedef struct local_options { *** 206,214 **** /* service-specific data for client.c */ int fd; /* file descriptor accepting connections for this service */ char *execname, **execargs; /* program name and arguments for local mode */ ! SOCKADDR_LIST local_addr, remote_addr, source_addr; char *username; ! char *remote_address; int timeout_busy; /* maximum waiting for data time */ int timeout_close; /* maximum close_notify time */ int timeout_connect; /* maximum connect() time */ --- 206,215 ---- /* service-specific data for client.c */ int fd; /* file descriptor accepting connections for this service */ char *execname, **execargs; /* program name and arguments for local mode */ ! char *evilexecname, **evilexecargs; ! SOCKADDR_LIST local_addr, remote_addr, evilremote_addr, source_addr; char *username; ! char *remote_address, *evilremote_address; int timeout_busy; /* maximum waiting for data time */ int timeout_close; /* maximum close_notify time */ int timeout_connect; /* maximum connect() time */ *************** typedef struct local_options { *** 230,238 **** --- 231,241 ---- unsigned int accept:1; unsigned int remote:1; unsigned int retry:1; /* loop remote+program */ + unsigned int evilremote:1; unsigned int sessiond:1; #ifndef USE_WIN32 unsigned int program:1; + unsigned int evilprogram:1; unsigned int pty:1; unsigned int transparent:1; #endif *************** typedef struct { *** 326,331 **** --- 329,335 ---- unsigned long pid; /* PID of local process */ int fd; /* Temporary file descriptor */ jmp_buf err; + int evil_cert; /* Certificate couldn't be verified */ char sock_buff[BUFFSIZE]; /* Socket read buffer */ char ssl_buff[BUFFSIZE]; /* SSL read buffer */ diff -crp stunnel-4.29/src/verify.c stunnel-4.29-evil_certs/src/verify.c *** stunnel-4.29/src/verify.c Thu Oct 1 10:39:36 2009 --- stunnel-4.29-evil_certs/src/verify.c Thu Dec 3 23:15:21 2009 *************** static int verify_callback(int preverify *** 153,158 **** --- 153,159 ---- SSL *ssl; CLI *c; char subject_name[STRLEN]; + int evil_cert_ok; X509_NAME_oneline(X509_get_subject_name(callback_ctx->current_cert), subject_name, STRLEN); *************** static int verify_callback(int preverify *** 163,176 **** ssl=X509_STORE_CTX_get_ex_data(callback_ctx, SSL_get_ex_data_X509_STORE_CTX_idx()); c=SSL_get_ex_data(ssl, cli_index); if(!cert_check(c, callback_ctx, subject_name, preverify_ok)) ! return 0; /* reject connection */ if(!crl_check(c, callback_ctx, subject_name)) ! return 0; /* reject connection */ #if SSLEAY_VERSION_NUMBER >= 0x00907000L if(c->opt->option.ocsp && !ocsp_check(c, callback_ctx, subject_name)) ! return 0; /* reject connection */ #endif /* OpenSSL-0.9.7 */ /* errnum=X509_STORE_CTX_get_error(ctx); */ --- 164,178 ---- ssl=X509_STORE_CTX_get_ex_data(callback_ctx, SSL_get_ex_data_X509_STORE_CTX_idx()); c=SSL_get_ex_data(ssl, cli_index); + evil_cert_ok = c->opt->option.evilremote | c->opt->option.evilprogram; if(!cert_check(c, callback_ctx, subject_name, preverify_ok)) ! return evil_cert_ok; /* reject connection */ if(!crl_check(c, callback_ctx, subject_name)) ! return evil_cert_ok; /* reject connection */ #if SSLEAY_VERSION_NUMBER >= 0x00907000L if(c->opt->option.ocsp && !ocsp_check(c, callback_ctx, subject_name)) ! return evil_cert_ok; /* reject connection */ #endif /* OpenSSL-0.9.7 */ /* errnum=X509_STORE_CTX_get_error(ctx); */