diff --git a/src/plugins/SSLPlugin/my_ssl.c b/src/plugins/SSLPlugin/my_ssl.c index 94e931c..9d24735 100644 --- a/src/plugins/SSLPlugin/my_ssl.c +++ b/src/plugins/SSLPlugin/my_ssl.c @@ -186,11 +186,19 @@ SSL_CERT ssl_copy_cert(SSL_CERT cert, SSL_CONFIG *config) SSL_CONN ssl_handshake_to_server(SOCKET s, char * hostname, SSL_CONFIG *config, SSL_CERT *server_cert, char **errSSL) { int err = 0; - X509 *cert; ssl_conn *conn; + unsigned long ul; *errSSL = NULL; +/*FIXME: support SSL_ERROR_WANT_(READ|WRITE) */ +#ifdef _WIN32 + ul = 0; + ioctlsocket(s, FIONBIO, &ul); +#else + fcntl(s,F_SETFL,0); +#endif + conn = (ssl_conn *)malloc(sizeof(ssl_conn)); if ( conn == NULL ){ return NULL; @@ -201,7 +209,7 @@ SSL_CONN ssl_handshake_to_server(SOCKET s, char * hostname, SSL_CONFIG *config, free(conn); return NULL; } - if(config->client_verify){ + if(hostname && *hostname && config->client_verify){ X509_VERIFY_PARAM *param; param = SSL_get0_param(conn->ssl); @@ -221,16 +229,23 @@ SSL_CONN ssl_handshake_to_server(SOCKET s, char * hostname, SSL_CONFIG *config, return NULL; } - cert = SSL_get_peer_certificate(conn->ssl); - if(!cert) { + if(server_cert){ + X509 *cert; + cert = SSL_get_peer_certificate(conn->ssl); + if(!cert) { ssl_conn_free(conn); return NULL; + } + + *server_cert = cert; } - /* TODO: Verify certificate */ - - *server_cert = cert; - +#ifdef _WIN32 + ul = 1; + ioctlsocket(s, FIONBIO, &ul); +#else + fcntl(s,F_SETFL,O_NONBLOCK); +#endif return conn; } @@ -245,6 +260,7 @@ SSL_CTX * ssl_cli_ctx(SSL_CONFIG *config, X509 *server_cert, EVP_PKEY *server_ke #else ctx = SSL_CTX_new(TLS_server_method()); #endif + if (!ctx) { *errSSL = ERR_error_string(ERR_get_error(), errbuf); return NULL; @@ -274,6 +290,17 @@ SSL_CONN ssl_handshake_to_client(SOCKET s, SSL_CONFIG *config, X509 *server_cert int err = 0; X509 *cert; ssl_conn *conn; + unsigned long ul; + +/*FIXME: support SSL_ERROR_WANT_(READ|WRITE)*/ + +#ifdef _WIN32 + ul = 0; + ioctlsocket(s, FIONBIO, &ul); +#else + fcntl(s,F_SETFL,0); +#endif + *errSSL = NULL; @@ -316,6 +343,12 @@ SSL_CONN ssl_handshake_to_client(SOCKET s, SSL_CONFIG *config, X509 *server_cert if ( cert != NULL ) X509_free(cert); +#ifdef _WIN32 + ul = 1; + ioctlsocket(s, FIONBIO, &ul); +#else + fcntl(s,F_SETFL,O_NONBLOCK); +#endif return conn; } diff --git a/src/plugins/SSLPlugin/my_ssl.h b/src/plugins/SSLPlugin/my_ssl.h index d8af84c..a9d058c 100644 --- a/src/plugins/SSLPlugin/my_ssl.h +++ b/src/plugins/SSLPlugin/my_ssl.h @@ -13,11 +13,14 @@ typedef void *SSL_CERT; struct ssl_config { int mitm; int serv; + int cli; char *certcache; X509 *CA_cert; X509 *server_cert; + X509 *client_cert; EVP_PKEY *CA_key; EVP_PKEY *server_key; + EVP_PKEY *client_key; SSL_CTX *cli_ctx; SSL_CTX *srv_ctx; int client_min_proto_version; diff --git a/src/plugins/SSLPlugin/ssl_plugin.c b/src/plugins/SSLPlugin/ssl_plugin.c index ef0dea5..a0ceb40 100644 --- a/src/plugins/SSLPlugin/ssl_plugin.c +++ b/src/plugins/SSLPlugin/ssl_plugin.c @@ -35,6 +35,8 @@ static int ssl_connect_timeout = 0; char *certcache = NULL; char *srvcert = NULL; char *srvkey = NULL; +char *clicert = NULL; +char *clikey = NULL; char *server_ca_file = NULL; char *server_ca_key = NULL; char *client_ca_file = NULL; @@ -42,6 +44,7 @@ char *client_ca_dir = NULL; char *client_ca_store = NULL; int mitm = 0; int serv = 0; +int cli = 0; int ssl_inited = 0; int client_min_proto_version = 0; int client_max_proto_version = 0; @@ -233,38 +236,42 @@ static int ssl_poll(void *state, struct pollfd *fds, nfds_t nfds, int timeout){ #define PCONF (((struct SSLstate *)param->sostate)->config) -int domitm(struct clientparam* param){ - SSL_CERT ServerCert=NULL, FakeCert=NULL; - SSL_CONN ServerConn, ClientConn; +SSL_CONN dosrvcon(struct clientparam* param, SSL_CERT* cert){ + SSL_CONN ServerConn; char *errSSL=NULL; unsigned long ul; -#ifdef _WIN32 - ul = 0; - ioctlsocket(param->remsock, FIONBIO, &ul); - ul = 0; - ioctlsocket(param->clisock, FIONBIO, &ul); -#else - fcntl(param->remsock,F_SETFL,0); - fcntl(param->clisock,F_SETFL,0); -#endif - if(ssl_connect_timeout){ ul = ((unsigned long)ssl_connect_timeout)*1000; setsockopt(param->remsock, SOL_SOCKET, SO_RCVTIMEO, (char *)&ul, 4); ul = ((unsigned long)ssl_connect_timeout)*1000; setsockopt(param->remsock, SOL_SOCKET, SO_SNDTIMEO, (char *)&ul, 4); } - ServerConn = ssl_handshake_to_server(param->remsock, (char *)param->hostname, PCONF, &ServerCert, &errSSL); - if ( ServerConn == NULL || ServerCert == NULL ) { + ServerConn = ssl_handshake_to_server(param->remsock, (char *)param->hostname, PCONF, cert, &errSSL); + if ( ServerConn == NULL) { if(ServerConn) ssl_conn_free(ServerConn); param->res = 8011; param->srv->logfunc(param, (unsigned char *)"SSL handshake to server failed"); if(ServerConn == NULL) param->srv->logfunc(param, (unsigned char *)"ServerConn is NULL"); - if(ServerCert == NULL) param->srv->logfunc(param, (unsigned char *)"ServerCert is NULL"); + if(cert && *cert == NULL) param->srv->logfunc(param, (unsigned char *)"ServerCert is NULL"); if(errSSL)param->srv->logfunc(param, (unsigned char *)errSSL); - return 1; + return NULL; } + + SSL_set_mode((SSL *)((ssl_conn *)ServerConn)->ssl, SSL_MODE_ENABLE_PARTIAL_WRITE|SSL_MODE_AUTO_RETRY); + SSL_set_read_ahead((SSL *)((ssl_conn *)ServerConn)->ssl, 0); + + + return ServerConn; +} + +int domitm(struct clientparam* param){ + SSL_CERT ServerCert=NULL, FakeCert=NULL; + SSL_CONN ServerConn, ClientConn; + char *errSSL=NULL; + + ServerConn = dosrvcon(param, &ServerCert); + if(!ServerConn) return 1; FakeCert = ssl_copy_cert(ServerCert, PCONF); _ssl_cert_free(ServerCert); if ( FakeCert == NULL ) { @@ -285,26 +292,28 @@ int domitm(struct clientparam* param){ return 3; } -#ifdef _WIN32 - ul = 1; - ioctlsocket(param->remsock, FIONBIO, &ul); - ul = 1; - ioctlsocket(param->clisock, FIONBIO, &ul); -#else - fcntl(param->remsock,F_SETFL,O_NONBLOCK); - fcntl(param->clisock,F_SETFL,O_NONBLOCK); -#endif - - - SSL_set_mode((SSL *)((ssl_conn *)ServerConn)->ssl, SSL_MODE_ENABLE_PARTIAL_WRITE|SSL_MODE_AUTO_RETRY); SSL_set_mode((SSL *)((ssl_conn *)ClientConn)->ssl, SSL_MODE_ENABLE_PARTIAL_WRITE|SSL_MODE_AUTO_RETRY); - SSL_set_read_ahead((SSL *)((ssl_conn *)ServerConn)->ssl, 0); SSL_set_read_ahead((SSL *)((ssl_conn *)ClientConn)->ssl, 0); + addSSL(param->clisock, ClientConn, param->remsock, ServerConn, param); return 0; } +int docli(struct clientparam* param){ + + SSL_CONN ServerConn; + SSL_CERT ServerCert=NULL; + + ServerConn = dosrvcon(param, &ServerCert); + _ssl_cert_free(ServerCert); + + if(!ServerConn) return 1; + + addSSL(INVALID_SOCKET, NULL, param->remsock, ServerConn, param); + return 0; +} + X509 * getCert (const char *fname){ BIO *f; X509 *CA_cert; @@ -336,6 +345,7 @@ static void* ssl_filter_open(void * idata, struct srvparam * srv){ char fname[256]; char *errSSL; struct ssl_config *sc; + sc = malloc(sizeof(struct ssl_config)); if(!sc) return NULL; memset(sc, 0, sizeof(struct ssl_config)); @@ -356,6 +366,13 @@ static void* ssl_filter_open(void * idata, struct srvparam * srv){ return sc; } } + if(clikey){ + sc->client_key = getKey(clikey); + if(!sc->client_key){ + fprintf(stderr, "failed to read: %s\n", clikey); + return sc; + } + } if(client_ca_file)sc->client_ca_file=client_ca_file; if(client_ca_dir)sc->client_ca_dir=client_ca_dir; if(client_ca_store)sc->client_ca_dir=client_ca_store; @@ -389,15 +406,20 @@ static void* ssl_filter_open(void * idata, struct srvparam * srv){ sc->server_key = getKey(fname); } sc->mitm = 1; - srv->so._send = ssl_send; - srv->so._recv = ssl_recv; - srv->so._sendto = ssl_sendto; - srv->so._recvfrom = ssl_recvfrom; - srv->so._closesocket = ssl_closesocket; - srv->so._poll = ssl_poll; -#ifdef WIWHSPLICE - srv->usesplice = 0; -#endif + } + if(cli){ + if(clicert){ + sc->client_cert = getCert(clicert); + if(!sc->client_cert){ + fprintf(stderr, "failed to read client cert from: %s\n", clicert); + return sc; + } + if(!sc->client_key){ + fprintf(stderr, "no client key\n"); + return sc; + } + } + sc->cli = 1; } if(serv){ if(!srvcert || !srvkey) return sc; @@ -414,24 +436,28 @@ static void* ssl_filter_open(void * idata, struct srvparam * srv){ return sc; } sc->serv = 1; + } + if(mitm || cli || serv){ srv->so._send = ssl_send; srv->so._recv = ssl_recv; srv->so._sendto = ssl_sendto; srv->so._recvfrom = ssl_recvfrom; srv->so._closesocket = ssl_closesocket; srv->so._poll = ssl_poll; -#ifdef WIWHSPLICE - srv->usesplice = 0; -#endif } - if(sc && sc->mitm){ + if(sc && (sc->mitm || sc->cli)){ #if OPENSSL_VERSION_NUMBER < 0x10100000L sc->srv_ctx = SSL_CTX_new(SSLv23_client_method()); #else sc->srv_ctx = SSL_CTX_new(TLS_client_method()); #endif if ( sc->srv_ctx == NULL ) { - sc->mitm = 0; + fprintf(stderr, "failed to set client context\n"); + sc->mitm = sc->cli = 0; + } + if(sc->client_cert){ + SSL_CTX_use_certificate(sc->srv_ctx, (X509 *) sc->client_cert); + SSL_CTX_use_PrivateKey(sc->srv_ctx, sc->client_key); } if(sc->client_min_proto_version)SSL_CTX_set_min_proto_version(sc->srv_ctx, sc->client_min_proto_version); if(sc->client_max_proto_version)SSL_CTX_set_max_proto_version(sc->srv_ctx, sc->client_max_proto_version); @@ -451,6 +477,9 @@ static void* ssl_filter_open(void * idata, struct srvparam * srv){ SSL_CTX_set_verify(sc->srv_ctx, SSL_VERIFY_PEER, verify_callback); } } +#ifdef WIWHSPLICE + srv->usesplice = 0; +#endif return sc; } @@ -469,13 +498,6 @@ static FILTER_ACTION ssl_filter_client(void *fo, struct clientparam * param, voi SSL_CONN ClientConn; char *err; -#ifdef _WIN32 - unsigned long ul = 0; - ioctlsocket(param->clisock, FIONBIO, &ul); -#else - fcntl(param->clisock,F_SETFL,0); -#endif - ClientConn = ssl_handshake_to_client(param->clisock, ssls->config, NULL, NULL, &err); if ( ClientConn == NULL ) { param->res = 8013; @@ -483,16 +505,6 @@ static FILTER_ACTION ssl_filter_client(void *fo, struct clientparam * param, voi if(err)param->srv->logfunc(param, (unsigned char *)err); return REJECT; } -#ifdef _WIN32 - { - unsigned long ul = 1; - ioctlsocket(param->clisock, FIONBIO, &ul); - } -#else - fcntl(param->clisock,F_SETFL,O_NONBLOCK); -#endif - - SSL_set_mode((SSL *)((ssl_conn *)ClientConn)->ssl, SSL_MODE_ENABLE_PARTIAL_WRITE|SSL_MODE_AUTO_RETRY); SSL_set_read_ahead((SSL *)((ssl_conn *)ClientConn)->ssl, 0); addSSL(param->clisock, ClientConn, INVALID_SOCKET, NULL, param); @@ -501,13 +513,21 @@ static FILTER_ACTION ssl_filter_client(void *fo, struct clientparam * param, voi } static FILTER_ACTION ssl_filter_predata(void *fc, struct clientparam * param){ + if(param->operation != HTTP_CONNECT && param->operation != CONNECT) return PASS; - if(!PCONF->mitm) return PASS; - if(domitm(param)) { + if(PCONF->mitm){ + if(domitm(param)) { return REJECT; + } + if(!param->redirectfunc) param->redirectfunc = proxyfunc; + return CONTINUE; } - if(!param->redirectfunc) param->redirectfunc = proxyfunc; - return CONTINUE; + else if(PCONF->cli) { + if(docli(param)) { + return REJECT; + } + } + return PASS; } @@ -525,12 +545,18 @@ static void ssl_filter_close(void *fo){ if ( CONFIG->server_cert != NULL ) { X509_free(CONFIG->server_cert); } + if ( CONFIG->client_cert != NULL ) { + X509_free(CONFIG->server_cert); + } if ( CONFIG->CA_key != NULL ) { EVP_PKEY_free(CONFIG->CA_key); } if ( CONFIG->server_key != NULL ) { EVP_PKEY_free(CONFIG->server_key); } + if ( CONFIG->client_key != NULL ) { + EVP_PKEY_free(CONFIG->server_key); + } if ( CONFIG->srv_ctx != NULL ) { SSL_CTX_free(CONFIG->srv_ctx); } @@ -607,7 +633,8 @@ static int h_serv(int argc, unsigned char **argv){ static int h_noserv(int argc, unsigned char **argv){ struct filter * sf; - if(!mitm) return 1; + if(!serv) return 1; + serv = 0; if(pl->conf->filters == &ssl_filter_serv) pl->conf->filters = ssl_filter_serv.next; else for(sf = pl->conf->filters; sf && sf->next; sf=sf->next){ if(sf->next == &ssl_filter_serv) { @@ -615,10 +642,46 @@ static int h_noserv(int argc, unsigned char **argv){ break; } } - serv = 0; return 0; } +static struct filter ssl_filter_cli = { + NULL, + "ssl filter", + "cli", + ssl_filter_open, + ssl_filter_client, + NULL, NULL, NULL, ssl_filter_predata, NULL, NULL, + ssl_filter_clear, + ssl_filter_close +}; + + +static int h_cli(int argc, unsigned char **argv){ + if(mitm) return 1; + if(cli) return 2; + ssl_filter_cli.next = pl->conf->filters; + pl->conf->filters = &ssl_filter_cli; + sso = *pl->so; + cli = 1; + return 0; +} + +static int h_nocli(int argc, unsigned char **argv){ + struct filter * sf; + if(!cli) return 1; + cli = 0; + if(pl->conf->filters == &ssl_filter_cli) pl->conf->filters = ssl_filter_cli.next; + else for(sf = pl->conf->filters; sf && sf->next; sf=sf->next){ + if(sf->next == &ssl_filter_cli) { + sf->next = ssl_filter_cli.next; + break; + } + } + return 0; +} + + static int h_certcache(int argc, unsigned char **argv){ size_t len; len = strlen((char *)argv[1]); @@ -640,6 +703,19 @@ static int h_srvkey(int argc, unsigned char **argv){ return 0; } +static int h_clicert(int argc, unsigned char **argv){ + free(clicert); + clicert = argc > 1? strdup((char *)argv[1]) : NULL; + return 0; +} + +static int h_clikey(int argc, unsigned char **argv){ + free(clikey); + clikey = argc > 1? strdup((char *)argv[1]) : NULL; + return 0; +} + + static int h_client_cipher_list(int argc, unsigned char **argv){ free(client_cipher_list); client_cipher_list = argc > 1? strdup((char *)argv[1]) : NULL; @@ -764,7 +840,7 @@ static struct commands ssl_commandhandlers[] = { {ssl_commandhandlers+1, "ssl_mitm", h_mitm, 1, 1}, {ssl_commandhandlers+2, "ssl_nomitm", h_nomitm, 1, 1}, {ssl_commandhandlers+3, "ssl_serv", h_serv, 1, 1}, - {ssl_commandhandlers+4, "ssl_noserv", h_serv, 1, 1}, + {ssl_commandhandlers+4, "ssl_noserv", h_noserv, 1, 1}, {ssl_commandhandlers+5, "ssl_server_cert", h_srvcert, 1, 2}, {ssl_commandhandlers+6, "ssl_server_key", h_srvkey, 1, 2}, {ssl_commandhandlers+7, "ssl_server_ca_file", h_server_ca_file, 1, 2}, @@ -782,6 +858,10 @@ static struct commands ssl_commandhandlers[] = { {ssl_commandhandlers+19, "ssl_server_max_proto_version", h_server_max_proto_version, 1, 2}, {ssl_commandhandlers+20, "ssl_client_verify", h_client_verify, 1, 1}, {ssl_commandhandlers+21, "ssl_client_no_verify", h_no_client_verify, 1, 1}, + {ssl_commandhandlers+22, "ssl_cli", h_cli, 1, 1}, + {ssl_commandhandlers+23, "ssl_nocli", h_nocli, 1, 1}, + {ssl_commandhandlers+24, "ssl_client_cert", h_clicert, 1, 2}, + {ssl_commandhandlers+25, "ssl_client_key", h_clikey, 1, 2}, {NULL, "ssl_certcache", h_certcache, 2, 2}, }; diff --git a/src/proxymain.c b/src/proxymain.c index e7014f6..09828b9 100644 --- a/src/proxymain.c +++ b/src/proxymain.c @@ -1414,6 +1414,8 @@ FILTER_ACTION handlepredatflt(struct clientparam *cparam){ FILTER_ACTION action; int i; + if(cparam->predatdone) return PASS; + cparam->predatdone = 1; for(i=0; inpredatfilters ;i++){ action = (*cparam->predatfilters[i]->filter->filter_predata)(cparam->predatfilters[i]->data, cparam); if(action!=CONTINUE) return action; diff --git a/src/structures.h b/src/structures.h index 34ff581..6f175f7 100644 --- a/src/structures.h +++ b/src/structures.h @@ -584,7 +584,8 @@ struct clientparam { chunked, paused, version, - connlim; + connlim, + predatdone; unsigned char *hostname, *username,