mirror of
https://github.com/3proxy/3proxy.git
synced 2026-11-05 17:35:49 +08:00
A fiber waiting on a socket registered it with its loop and took it off again on every wait: two epoll_ctl() calls a wait, some 39 for an exchange of the mix benchmark, about an eighth of the proxy's time. A socket is now registered once, edge-triggered for reading and writing, until it is closed, and the loop keeps what it was reported ready for until an operation finds it not to be: read, write, accept, connect, splice() from a pipe and TLS tell it so through fiberblockfunc, the close through fiberclosefunc. A wait for what a socket is known to be ready for is answered at once, with no system call and no switch; only with none of them ready does the fiber wait in its loop. Readiness is trusted once: answered again with nothing reported or found since, what was there may have been taken without its end being found, and the system is asked with poll(). epoll and kqueue only; with poll() each wait still registers its sockets. What takes readability for an end rather than reading, the control connection of a UDP association, the client waiting for a SOCKS BIND, the server between requests of a kept-alive HTTP connection, asks first with sockreadable(), a peek below any TLS layer. kqueue: the end of either side, the peer's or our own shutdown, is readable or writable, not a hang up, which poll() reports only with both: kept, it made a wait for the answer after our shutdown return at once, and sockmap give up with 93. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1363 lines
36 KiB
C
1363 lines
36 KiB
C
/*
|
|
(c) 2007-2026 by Vladimir Dubrovin <vlad@3proxy.org>
|
|
|
|
please read License Agreement
|
|
|
|
*/
|
|
|
|
#include "structures.h"
|
|
#ifdef WITH_WOLFSSL
|
|
#include <wolfssl/options.h>
|
|
#include <wolfssl/openssl/crypto.h>
|
|
#include <wolfssl/openssl/x509.h>
|
|
#include <wolfssl/openssl/pem.h>
|
|
#include <wolfssl/openssl/ssl.h>
|
|
#include <wolfssl/openssl/err.h>
|
|
#else
|
|
#include <openssl/crypto.h>
|
|
#include <openssl/x509.h>
|
|
#include <openssl/pem.h>
|
|
#include <openssl/ssl.h>
|
|
#include <openssl/err.h>
|
|
#endif
|
|
#include "proxy.h"
|
|
#include "ssl.h"
|
|
|
|
#ifndef _WIN32
|
|
#define WINAPI
|
|
#endif
|
|
|
|
#if defined(WITH_WOLFSSL) && !defined(SSL_set_mode)
|
|
#define SSL_set_mode(ssl,op) ((void)0)
|
|
#endif
|
|
|
|
#ifndef isnumber
|
|
#define isnumber(i_n_arg) ((i_n_arg>='0')&&(i_n_arg<='9'))
|
|
#endif
|
|
|
|
PROXYFUNC tcppmfunc, proxyfunc, smtppfunc, ftpprfunc;
|
|
|
|
static struct pluginlink * pl;
|
|
|
|
static struct alpn client_alpn_protos;
|
|
|
|
static int ssl_loaded = 0;
|
|
static char *certcache = NULL;
|
|
static char *srvcert = NULL;
|
|
static char *srvkey = NULL;
|
|
static char *clicert = NULL;
|
|
static char *clikey = NULL;
|
|
static char *server_ca_file = NULL;
|
|
static char *server_ca_dir = NULL;
|
|
static char *server_ca_store = NULL;
|
|
static char *server_ca_key = NULL;
|
|
static char *client_ca_file = NULL;
|
|
static char *client_ca_dir = NULL;
|
|
static char *client_ca_store = NULL;
|
|
static int mitm = 0;
|
|
static int serv = 0;
|
|
static int cli = 0;
|
|
static int client_min_proto_version = 0;
|
|
static int client_max_proto_version = 0;
|
|
static int server_min_proto_version = 0;
|
|
static int server_max_proto_version = 0;
|
|
static int client_verify = 0;
|
|
static int server_verify = 0;
|
|
static char * client_ciphersuites = NULL;
|
|
static char * server_ciphersuites = NULL;
|
|
static char * client_cipher_list = NULL;
|
|
static char * server_cipher_list = NULL;
|
|
static char * client_sni = NULL;
|
|
static int client_mode = 0;
|
|
static int ktls = 0;
|
|
|
|
|
|
struct SSLsock {
|
|
SOCKET s;
|
|
SSL_CONN conn;
|
|
/* what a read and a write which could not be done wait for, POLLIN
|
|
or POLLOUT, 0 once one is: TLS may have to write to read */
|
|
short rwant, wwant;
|
|
};
|
|
|
|
struct SSLstate {
|
|
struct SSLsock cli, srv;
|
|
struct clientparam* param;
|
|
SSL_CONFIG *config;
|
|
};
|
|
|
|
|
|
#define STATE ((struct SSLstate *)(state))
|
|
|
|
static struct SSLsock *searchSSL(void* state, SOCKET s){
|
|
if(!state || s == INVALID_SOCKET) return NULL;
|
|
if(STATE->cli.s == s) return &STATE->cli;
|
|
if(STATE->srv.s == s) return &STATE->srv;
|
|
return NULL;
|
|
}
|
|
|
|
#define SOSTATE ((struct SSLstate *)(param->sostate))
|
|
|
|
static void addSSL(
|
|
SOCKET cli_s, SSL_CONN cli_conn,
|
|
SOCKET srv_s, SSL_CONN srv_conn,
|
|
struct clientparam* param){
|
|
if(!param->sostate) return;
|
|
if (cli_s != INVALID_SOCKET){
|
|
SOSTATE->cli.s = cli_s;
|
|
SOSTATE->cli.conn = cli_conn;
|
|
SOSTATE->cli.rwant = SOSTATE->cli.wwant = 0;
|
|
}
|
|
if (srv_s != INVALID_SOCKET){
|
|
SOSTATE->srv.s = srv_s;
|
|
SOSTATE->srv.conn = srv_conn;
|
|
SOSTATE->srv.rwant = SOSTATE->srv.wwant = 0;
|
|
}
|
|
}
|
|
|
|
void delSSL(void *state, SOCKET s){
|
|
if(!state || s == INVALID_SOCKET) return;
|
|
if(STATE->cli.s == s && STATE->cli.conn) {
|
|
ssl_conn_free(STATE->cli.conn);
|
|
STATE->cli.conn = NULL;
|
|
STATE->cli.s = INVALID_SOCKET;
|
|
}
|
|
else if(STATE->srv.s == s && STATE->srv.conn) {
|
|
ssl_conn_free(STATE->srv.conn);
|
|
STATE->srv.conn = NULL;
|
|
STATE->srv.s = INVALID_SOCKET;
|
|
}
|
|
}
|
|
|
|
struct sockfuncs sso;
|
|
|
|
#ifdef _WIN32
|
|
static int WINAPI ssl_send(void *state, SOCKET s, const char *msg, int len, int flags){
|
|
#else
|
|
static ssize_t ssl_send(void *state, SOCKET s, const void *msg, size_t len, int flags){
|
|
#endif
|
|
struct SSLsock *sslq;
|
|
|
|
if ((sslq = searchSSL(state, s))){
|
|
int res, err;
|
|
if((res = ssl_write(sslq->conn, (void *)msg, len)) <= 0){
|
|
err = SSL_get_error((SSL *)((ssl_conn*)sslq->conn)->ssl, res);
|
|
if (err == SSL_ERROR_WANT_WRITE || err == SSL_ERROR_WANT_READ){
|
|
sslq->wwant = (err == SSL_ERROR_WANT_READ)? POLLIN : POLLOUT;
|
|
/* TLS found the socket not ready, past the socket functions */
|
|
if(fiberblockfunc) fiberblockfunc(s, sslq->wwant);
|
|
seterrno3(EAGAIN);
|
|
return -1;
|
|
}
|
|
else seterrno3(err);
|
|
}
|
|
else sslq->wwant = 0;
|
|
return res;
|
|
}
|
|
|
|
return sso._send(sso.state, s, msg, len, flags);
|
|
}
|
|
|
|
|
|
#ifdef _WIN32
|
|
static int WINAPI ssl_sendto(void *state, SOCKET s, const char *msg, int len, int flags, const struct sockaddr *to, int tolen){
|
|
#else
|
|
static ssize_t ssl_sendto(void *state, SOCKET s, const void *msg, size_t len, int flags, const struct sockaddr *to, SASIZETYPE tolen){
|
|
#endif
|
|
struct SSLsock *sslq;
|
|
|
|
if ((sslq = searchSSL(state, s))){
|
|
int res, err;
|
|
if((res = ssl_write(sslq->conn, (void *)msg, len)) <= 0){
|
|
err = SSL_get_error((SSL *)((ssl_conn*)sslq->conn)->ssl, res);
|
|
if (err == SSL_ERROR_WANT_WRITE || err == SSL_ERROR_WANT_READ){
|
|
sslq->wwant = (err == SSL_ERROR_WANT_READ)? POLLIN : POLLOUT;
|
|
/* TLS found the socket not ready, past the socket functions */
|
|
if(fiberblockfunc) fiberblockfunc(s, sslq->wwant);
|
|
seterrno3(EAGAIN);
|
|
return -1;
|
|
}
|
|
else seterrno3(err);
|
|
}
|
|
else sslq->wwant = 0;
|
|
return res;
|
|
}
|
|
|
|
return sso._sendto(sso.state, s, msg, len, flags, to, tolen);
|
|
}
|
|
|
|
#ifdef _WIN32
|
|
static int WINAPI ssl_recvfrom(void *state, SOCKET s, char *msg, int len, int flags, struct sockaddr *from, int *fromlen){
|
|
#else
|
|
static ssize_t ssl_recvfrom(void *state, SOCKET s, void *msg, size_t len, int flags, struct sockaddr *from, SASIZETYPE *fromlen){
|
|
#endif
|
|
struct SSLsock *sslq;
|
|
|
|
if ((sslq = searchSSL(state, s))){
|
|
int res, err;
|
|
if((res = ssl_read(sslq->conn, (void *)msg, len)) <= 0) {
|
|
err = SSL_get_error((SSL *)((ssl_conn*)sslq->conn)->ssl, res);
|
|
if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) {
|
|
sslq->rwant = (err == SSL_ERROR_WANT_WRITE)? POLLOUT : POLLIN;
|
|
/* TLS found the socket not ready, past the socket functions */
|
|
if(fiberblockfunc) fiberblockfunc(s, sslq->rwant);
|
|
seterrno3(EAGAIN);
|
|
return -1;
|
|
}
|
|
else seterrno3(err);
|
|
}
|
|
else sslq->rwant = 0;
|
|
return res;
|
|
}
|
|
return sso._recvfrom(sso.state, s, msg, len, flags, from, fromlen);
|
|
}
|
|
|
|
#ifdef _WIN32
|
|
static int WINAPI ssl_recv(void *state, SOCKET s, char *msg, int len, int flags){
|
|
#else
|
|
static ssize_t ssl_recv(void *state, SOCKET s, void *msg, size_t len, int flags){
|
|
#endif
|
|
struct SSLsock *sslq;
|
|
|
|
if ((sslq = searchSSL(state,s))){
|
|
int res, err;
|
|
if((res = ssl_read(sslq->conn, (void *)msg, len)) <= 0) {
|
|
err = SSL_get_error((SSL *)((ssl_conn*)sslq->conn)->ssl, res);
|
|
if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) {
|
|
sslq->rwant = (err == SSL_ERROR_WANT_WRITE)? POLLOUT : POLLIN;
|
|
/* TLS found the socket not ready, past the socket functions */
|
|
if(fiberblockfunc) fiberblockfunc(s, sslq->rwant);
|
|
seterrno3(EAGAIN);
|
|
return -1;
|
|
}
|
|
else seterrno3(err);
|
|
}
|
|
else sslq->rwant = 0;
|
|
return res;
|
|
}
|
|
|
|
return sso._recv(sso.state, s, msg, len, flags);
|
|
}
|
|
|
|
static int WINAPI ssl_shutdown(void *state, SOCKET s, int how){
|
|
delSSL(state, s);
|
|
return sso._shutdown(sso.state, s, how);
|
|
}
|
|
|
|
static int WINAPI ssl_closesocket(void *state, SOCKET s){
|
|
delSSL(state, s);
|
|
return sso._closesocket(sso.state, s);
|
|
}
|
|
|
|
/* Data already decrypted makes its socket readable. A read or a write TLS
|
|
could not do has its socket waited for as TLS needs it. */
|
|
#ifdef _WIN32
|
|
static int WINAPI ssl_prepoll(void *state, struct pollfd *fds, unsigned int nfds){
|
|
#else
|
|
static int ssl_prepoll(void *state, struct pollfd *fds, nfds_t nfds){
|
|
#endif
|
|
struct SSLsock *sslq = NULL;
|
|
unsigned int i;
|
|
int ret = 0;
|
|
short events;
|
|
|
|
for(i = 0; i < nfds; i++){
|
|
if((fds[i].events & POLLIN) && (sslq = searchSSL(state, fds[i].fd)) && ssl_pending(sslq->conn)){
|
|
fds[i].revents = POLLIN;
|
|
ret++;
|
|
}
|
|
else fds[i].revents = 0;
|
|
}
|
|
if(ret) return ret;
|
|
for(i = 0; i < nfds; i++){
|
|
if(!(sslq = searchSSL(state, fds[i].fd))) continue;
|
|
events = fds[i].events & ~(POLLIN|POLLOUT);
|
|
if(fds[i].events & POLLIN) events |= sslq->rwant? sslq->rwant : POLLIN;
|
|
if(fds[i].events & POLLOUT) events |= sslq->wwant? sslq->wwant : POLLOUT;
|
|
fds[i].events = events;
|
|
}
|
|
return sso._prepoll(sso.state, fds, nfds);
|
|
}
|
|
|
|
SSL_CONN ssl_handshake_to_server(SOCKET s, char * hostname, SSL_CONFIG *config, SSL_CERT *server_cert, char **errSSL)
|
|
{
|
|
int err = 0;
|
|
ssl_conn *conn;
|
|
|
|
*errSSL = NULL;
|
|
|
|
conn = (ssl_conn *)malloc(sizeof(ssl_conn));
|
|
if ( conn == NULL ){
|
|
return NULL;
|
|
}
|
|
conn->ctx = NULL;
|
|
conn->ssl = SSL_new(config->srv_ctx);
|
|
if ( conn->ssl == NULL ) {
|
|
free(conn);
|
|
return NULL;
|
|
}
|
|
if(hostname && *hostname && config->client_verify){
|
|
X509_VERIFY_PARAM *param;
|
|
|
|
param = SSL_get0_param(conn->ssl);
|
|
X509_VERIFY_PARAM_set1_host(param, hostname, strlen(hostname));
|
|
}
|
|
|
|
if(!SSL_set_fd(conn->ssl, s)){
|
|
ssl_conn_free(conn);
|
|
*errSSL = getSSLErr();
|
|
return NULL;
|
|
}
|
|
#if OPENSSL_VERSION_NUMBER >= 0x0090806fL
|
|
if(hostname && *hostname)SSL_set_tlsext_host_name(conn->ssl, hostname);
|
|
#endif
|
|
|
|
do {
|
|
struct pollfd fds[1] = {{INVALID_SOCKET}};
|
|
int sslerr;
|
|
|
|
err = SSL_connect(conn->ssl);
|
|
if (err != -1) break;
|
|
sslerr = SSL_get_error(conn->ssl, err);
|
|
if(sslerr == SSL_ERROR_WANT_READ){
|
|
fds[0].fd = s;
|
|
fds[0].events = POLLIN;
|
|
if(fiberblockfunc) fiberblockfunc(s, POLLIN);
|
|
}
|
|
else if(sslerr == SSL_ERROR_WANT_WRITE){
|
|
fds[0].fd = s;
|
|
fds[0].events = POLLOUT;
|
|
if(fiberblockfunc) fiberblockfunc(s, POLLOUT);
|
|
}
|
|
else break;
|
|
if(sockpoll(&sso, sso.state, fds, 1, config->timeout*1000) <= 0 || !(fds[0].revents & (POLLOUT|POLLIN))) break;
|
|
} while (err == -1);
|
|
|
|
if ( err != 1 ) {
|
|
*errSSL = getSSLErr();
|
|
ssl_conn_free(conn);
|
|
return NULL;
|
|
}
|
|
|
|
if(server_cert){
|
|
X509 *cert;
|
|
cert = SSL_get_peer_certificate(conn->ssl);
|
|
if(!cert) {
|
|
ssl_conn_free(conn);
|
|
return NULL;
|
|
}
|
|
|
|
*server_cert = cert;
|
|
}
|
|
|
|
return conn;
|
|
}
|
|
|
|
|
|
SSL_CONN ssl_handshake_to_client(SOCKET s, SSL_CONFIG *config, X509 *server_cert, EVP_PKEY *server_key, char** errSSL){
|
|
int err = 0;
|
|
X509 *cert;
|
|
ssl_conn *conn;
|
|
|
|
*errSSL = NULL;
|
|
|
|
conn = (ssl_conn *)malloc(sizeof(ssl_conn));
|
|
if ( conn == NULL )
|
|
return NULL;
|
|
|
|
conn->ctx = NULL;
|
|
conn->ssl = NULL;
|
|
if(!config->cli_ctx){
|
|
conn->ctx = ssl_cli_ctx(config, server_cert, server_key, errSSL);
|
|
if(!conn->ctx){
|
|
ssl_conn_free(conn);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
conn->ssl = SSL_new(config->cli_ctx?config->cli_ctx : conn->ctx);
|
|
if ( conn->ssl == NULL ) {
|
|
*errSSL = getSSLErr();
|
|
if(conn->ctx)SSL_CTX_free(conn->ctx);
|
|
free(conn);
|
|
return NULL;
|
|
}
|
|
|
|
if(!SSL_set_fd(conn->ssl, s)){
|
|
*errSSL = getSSLErr();
|
|
ssl_conn_free(conn);
|
|
return NULL;
|
|
}
|
|
|
|
do {
|
|
struct pollfd fds[1] = {{INVALID_SOCKET}};
|
|
int sslerr;
|
|
|
|
err = SSL_accept(conn->ssl);
|
|
if (err != -1) break;
|
|
sslerr = SSL_get_error(conn->ssl, err);
|
|
if(sslerr == SSL_ERROR_WANT_READ){
|
|
fds[0].fd = s;
|
|
fds[0].events = POLLIN;
|
|
if(fiberblockfunc) fiberblockfunc(s, POLLIN);
|
|
}
|
|
else if(sslerr == SSL_ERROR_WANT_WRITE){
|
|
fds[0].fd = s;
|
|
fds[0].events = POLLOUT;
|
|
if(fiberblockfunc) fiberblockfunc(s, POLLOUT);
|
|
}
|
|
else break;
|
|
if(sockpoll(&sso, sso.state, fds, 1, config->timeout*1000) <= 0 || !(fds[0].revents & (POLLOUT|POLLIN))) break;
|
|
} while (err == -1);
|
|
|
|
|
|
if ( err != 1 ) {
|
|
*errSSL = getSSLErr();
|
|
ssl_conn_free(conn);
|
|
return NULL;
|
|
}
|
|
|
|
cert = SSL_get_peer_certificate(conn->ssl);
|
|
|
|
if ( cert != NULL )
|
|
X509_free(cert);
|
|
|
|
return conn;
|
|
}
|
|
|
|
|
|
#define PCONF (((struct SSLstate *)param->sostate)->config)
|
|
|
|
/* After a handshake: read ahead, unless the kernel took receiving (kernel TLS
|
|
reads a record at a time), and say once for the service whether it took
|
|
either direction, which depends on the library, the kernel and the cipher. */
|
|
static void ssl_after_handshake(struct clientparam* param, SSL_CONN conn){
|
|
SSL *ssl = ((ssl_conn *)conn)->ssl;
|
|
int ksend = 0, krecv = 0;
|
|
|
|
SSL_set_mode(ssl, SSL_MODE_ENABLE_PARTIAL_WRITE|SSL_MODE_AUTO_RETRY);
|
|
#if defined(SSL_OP_ENABLE_KTLS) && defined(BIO_get_ktls_send) && defined(BIO_get_ktls_recv)
|
|
if(PCONF->ktls){
|
|
ksend = BIO_get_ktls_send(SSL_get_wbio(ssl));
|
|
krecv = BIO_get_ktls_recv(SSL_get_rbio(ssl));
|
|
if(!PCONF->ktls_reported){
|
|
char buf[96];
|
|
|
|
PCONF->ktls_reported = 1;
|
|
sprintf(buf, "kernel TLS: sending %s, receiving %s",
|
|
ksend? "in the kernel" : "in 3proxy", krecv? "in the kernel" : "in 3proxy");
|
|
param->srv->logfunc(param, (unsigned char *)buf);
|
|
}
|
|
}
|
|
#endif
|
|
if(!krecv) SSL_set_read_ahead(ssl, SSL_READAHEAD);
|
|
/* splice() passes the data of a TLS socket only where the kernel does TLS
|
|
for it both ways */
|
|
if(!ksend || !krecv) param->nosplice = 1;
|
|
}
|
|
|
|
#ifdef WITHSPLICE
|
|
/* Whether TLS handles the socket, and whether it has read data for it that
|
|
it has not handed out, which a splice() from the socket would not see. */
|
|
static int ssl_splicetls(struct clientparam *param, SOCKET s){
|
|
struct SSLsock *sslq = searchSSL(param->sostate, s);
|
|
|
|
if(!sslq) return 0;
|
|
return ssl_buffered(sslq->conn)? SPLICE_TLSPENDING : SPLICE_TLS;
|
|
}
|
|
#endif
|
|
|
|
SSL_CONN dosrvcon(struct clientparam* param, SSL_CERT* cert){
|
|
SSL_CONN ServerConn;
|
|
char *errSSL=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(cert && *cert == NULL) param->srv->logfunc(param, (unsigned char *)"ServerCert is NULL");
|
|
if(errSSL)param->srv->logfunc(param, (unsigned char *)errSSL);
|
|
return NULL;
|
|
}
|
|
|
|
ssl_after_handshake(param, ServerConn);
|
|
|
|
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 ) {
|
|
param->res = 8012;
|
|
param->srv->logfunc(param, (unsigned char *)"Failed to create certificate copy");
|
|
ssl_conn_free(ServerConn);
|
|
return 2;
|
|
}
|
|
|
|
ClientConn = ssl_handshake_to_client(param->clisock, PCONF, FakeCert, PCONF->server_key?PCONF->server_key:PCONF->CA_key, &errSSL);
|
|
|
|
_ssl_cert_free(FakeCert);
|
|
if ( ClientConn == NULL ) {
|
|
param->res = 8012;
|
|
param->srv->logfunc(param, (unsigned char *)"Handshake to client failed");
|
|
if(errSSL)param->srv->logfunc(param, (unsigned char *)errSSL);
|
|
ssl_conn_free(ServerConn);
|
|
return 3;
|
|
}
|
|
|
|
ssl_after_handshake(param, ClientConn);
|
|
|
|
addSSL(param->clisock, ClientConn, param->remsock, ServerConn, param);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int docli(struct clientparam* param){
|
|
|
|
SSL_CONN ServerConn;
|
|
SSL_CERT ServerCert=NULL;
|
|
unsigned char hostname[HOSTNAMELEN];
|
|
|
|
/* the name given to the server is the configured one, if any */
|
|
memcpy(hostname, param->hostname, sizeof(hostname));
|
|
if(PARAMSTR(param, hostname, PCONF->client_sni)) *param->hostname = 0;
|
|
ServerConn = dosrvcon(param, &ServerCert);
|
|
memcpy(param->hostname, hostname, sizeof(hostname));
|
|
_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;
|
|
|
|
f = BIO_new_file(fname, "r");
|
|
if(!f) return NULL;
|
|
CA_cert=PEM_read_bio_X509(f, NULL, NULL, NULL);
|
|
BIO_free(f);
|
|
return CA_cert;
|
|
}
|
|
|
|
EVP_PKEY * getKey(const char *fname){
|
|
BIO *f;
|
|
EVP_PKEY *key;
|
|
|
|
f = BIO_new_file(fname, "r");
|
|
if(!f) return NULL;
|
|
key = PEM_read_bio_PrivateKey(f, NULL, NULL, NULL);
|
|
BIO_free(f);
|
|
|
|
return key;
|
|
}
|
|
|
|
#ifdef WITH_WOLFSSL
|
|
/* wolfSSL's SSL_CTX_use_PrivateKey(EVP_PKEY*) compat is unreliable: it
|
|
* silently fails (returns 0, no error queued) for keys loaded via
|
|
* PEM_read_bio_PrivateKey. Export the key to DER and load via the
|
|
* native buffer API instead. */
|
|
static int ssl_ctx_use_pkey(SSL_CTX *ctx, EVP_PKEY *key){
|
|
unsigned char *der = NULL;
|
|
int len, rc;
|
|
len = i2d_PrivateKey(key, &der);
|
|
if(len <= 0 || !der) return 0;
|
|
rc = wolfSSL_CTX_use_PrivateKey_buffer(ctx, der, (long)len, SSL_FILETYPE_ASN1);
|
|
OPENSSL_free(der);
|
|
return rc;
|
|
}
|
|
#else
|
|
#define ssl_ctx_use_pkey(ctx, key) SSL_CTX_use_PrivateKey((ctx), (key))
|
|
#endif
|
|
|
|
|
|
SSL_CTX * ssl_cli_ctx(SSL_CONFIG *config, X509 *server_cert, EVP_PKEY *server_key, char** errSSL){
|
|
SSL_CTX *ctx;
|
|
int err = 0;
|
|
|
|
|
|
#if !defined(WITH_WOLFSSL) && OPENSSL_VERSION_NUMBER < 0x10100000L
|
|
ctx = SSL_CTX_new(SSLv23_server_method());
|
|
#else
|
|
ctx = SSL_CTX_new(TLS_server_method());
|
|
#endif
|
|
|
|
if (!ctx) {
|
|
*errSSL = getSSLErr();
|
|
return NULL;
|
|
}
|
|
|
|
if(server_cert) {
|
|
err = SSL_CTX_use_certificate(ctx, (X509 *) server_cert);
|
|
if ( err <= 0 ) {
|
|
*errSSL = getSSLErr();
|
|
SSL_CTX_free(ctx);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
/* Load the private key only when a cert is already loaded into ctx.
|
|
* wolfSSL validates the key against the cert and requires the cert
|
|
* to be present first; OpenSSL tolerates either order but is happy
|
|
* with cert-first too. When server_cert is NULL (serv path), the
|
|
* caller loads the cert chain and the key itself after this call. */
|
|
if(server_cert) {
|
|
err = ssl_ctx_use_pkey(ctx, server_key);
|
|
if ( err <= 0 ) {
|
|
*errSSL = getSSLErr();
|
|
SSL_CTX_free(ctx);
|
|
return NULL;
|
|
}
|
|
}
|
|
SSL_CTX_set_session_id_context(ctx, (const unsigned char *)"3proxy", 6);
|
|
#ifdef SSL_OP_ENABLE_KTLS
|
|
if(config->ktls) SSL_CTX_set_options(ctx, SSL_OP_ENABLE_KTLS);
|
|
#endif
|
|
if(config->server_min_proto_version)SSL_CTX_set_min_proto_version(ctx, config->server_min_proto_version);
|
|
if(config->server_max_proto_version)SSL_CTX_set_max_proto_version(ctx, config->server_max_proto_version);
|
|
if(config->server_cipher_list)SSL_CTX_set_cipher_list(ctx, config->server_cipher_list);
|
|
#if !defined(WITH_WOLFSSL) && OPENSSL_VERSION_NUMBER >= 0x10101000L
|
|
if(config->server_ciphersuites)SSL_CTX_set_ciphersuites(ctx, config->server_ciphersuites);
|
|
#elif defined(WITH_WOLFSSL)
|
|
/* wolfSSL has no separate TLS1.3 ciphersuites API; route to cipher list. */
|
|
if(config->server_ciphersuites)wolfSSL_CTX_set_cipher_list(ctx, config->server_ciphersuites);
|
|
#endif
|
|
if(config->server_verify){
|
|
if(config->server_ca_file || config->server_ca_dir){
|
|
SSL_CTX_load_verify_locations(ctx, config->server_ca_file, config->server_ca_dir);
|
|
}
|
|
#if !defined(WITH_WOLFSSL) && OPENSSL_VERSION_NUMBER >= 0x30000000L
|
|
else if(config->server_ca_store){
|
|
SSL_CTX_load_verify_store(ctx, config->server_ca_store);
|
|
}
|
|
#endif
|
|
else
|
|
SSL_CTX_set_default_verify_paths(ctx);
|
|
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT|SSL_VERIFY_CLIENT_ONCE, NULL);
|
|
}
|
|
return ctx;
|
|
}
|
|
|
|
|
|
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));
|
|
if(certcache) sc->certcache = strdup(certcache);
|
|
sc->client_min_proto_version = client_min_proto_version;
|
|
sc->client_max_proto_version = client_max_proto_version;
|
|
sc->server_min_proto_version = server_min_proto_version;
|
|
sc->server_max_proto_version = server_max_proto_version;
|
|
sc->client_mode = client_mode;
|
|
sc->ktls = ktls;
|
|
sc->timeout = srv->timeouts[CONNECT_TO];
|
|
sc->client_verify = client_verify;
|
|
sc->server_verify = server_verify;
|
|
if(client_ciphersuites) sc->client_ciphersuites = strdup(client_ciphersuites);
|
|
if(server_ciphersuites) sc->server_ciphersuites = strdup(server_ciphersuites);
|
|
if(client_cipher_list) sc->client_cipher_list = strdup(client_cipher_list);
|
|
if(server_cipher_list) sc->server_cipher_list = strdup(server_cipher_list);
|
|
if(srvkey){
|
|
sc->server_key = getKey(srvkey);
|
|
if(!sc->server_key){
|
|
fprintf(stderr, "failed to read: %s\n", srvkey);
|
|
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 =strdup(client_ca_file);
|
|
if(client_ca_dir)sc->client_ca_dir = strdup(client_ca_dir);
|
|
if(client_ca_store)sc->client_ca_store = strdup(client_ca_store);
|
|
if(server_ca_file)sc->server_ca_file = strdup(server_ca_file);
|
|
if(server_ca_dir)sc->server_ca_dir = strdup(server_ca_dir);
|
|
if(server_ca_store)sc->server_ca_store = strdup(server_ca_store);
|
|
|
|
if(client_sni)sc->client_sni=strdup(client_sni);
|
|
if(client_alpn_protos.protos_len){
|
|
sc->client_alpn_protos = client_alpn_protos;
|
|
sc->client_alpn_protos.protos = malloc(client_alpn_protos.protos_len);
|
|
if(!sc->client_alpn_protos.protos) sc->client_alpn_protos.protos_len = 0;
|
|
else memcpy(sc->client_alpn_protos.protos, client_alpn_protos.protos, client_alpn_protos.protos_len);
|
|
}
|
|
|
|
|
|
if(mitm){
|
|
if(!server_ca_file){
|
|
if(!certcache) {
|
|
return sc;
|
|
}
|
|
sprintf(fname, "%.240s3proxy.pem", certcache);
|
|
}
|
|
sc->CA_cert = getCert(server_ca_file?server_ca_file:fname);
|
|
if(!sc->CA_cert){
|
|
fprintf(stderr, "failed to read: %s\n", server_ca_file?server_ca_file:fname);
|
|
return sc;
|
|
}
|
|
if(!server_ca_key){
|
|
if(!certcache) {
|
|
return sc;
|
|
}
|
|
sprintf(fname, "%.240s3proxy.key", sc->certcache);
|
|
}
|
|
sc->CA_key = getKey(server_ca_key?server_ca_key:fname);
|
|
if(!sc->CA_key){
|
|
fprintf(stderr, "failed to read: %s\n", server_ca_key?server_ca_key:fname);
|
|
return sc;
|
|
}
|
|
if(!sc->server_key && sc->certcache){
|
|
sprintf(fname, "%.128sserver.key", sc->certcache);
|
|
sc->server_key = getKey(fname);
|
|
}
|
|
sc->mitm = 1;
|
|
}
|
|
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;
|
|
if(!sc->server_key){
|
|
return sc;
|
|
}
|
|
if(!(sc->cli_ctx = ssl_cli_ctx(sc, NULL, sc->server_key, &errSSL))){
|
|
fprintf(stderr, "failed to create context: %s\n", errSSL);
|
|
return sc;
|
|
}
|
|
if(SSL_CTX_use_certificate_chain_file(sc->cli_ctx, srvcert) != 1){
|
|
fprintf(stderr, "failed to read server cert: %s\n", srvcert);
|
|
return sc;
|
|
}
|
|
if(ssl_ctx_use_pkey(sc->cli_ctx, sc->server_key) <= 0){
|
|
fprintf(stderr, "failed to use server key\n");
|
|
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._shutdown = ssl_shutdown;
|
|
srv->so._closesocket = ssl_closesocket;
|
|
srv->so._prepoll = ssl_prepoll;
|
|
}
|
|
if(sc && (sc->mitm || sc->cli)){
|
|
#if !defined(WITH_WOLFSSL) && 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 ) {
|
|
fprintf(stderr, "failed to set client context\n");
|
|
sc->mitm = sc->cli = 0;
|
|
}
|
|
#ifdef SSL_OP_ENABLE_KTLS
|
|
if(sc->srv_ctx && sc->ktls) SSL_CTX_set_options(sc->srv_ctx, SSL_OP_ENABLE_KTLS);
|
|
#endif
|
|
if(sc->client_cert){
|
|
SSL_CTX_use_certificate(sc->srv_ctx, (X509 *) sc->client_cert);
|
|
ssl_ctx_use_pkey(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);
|
|
if(sc->client_cipher_list)SSL_CTX_set_cipher_list(sc->srv_ctx, sc->client_cipher_list);
|
|
#if !defined(WITH_WOLFSSL) && OPENSSL_VERSION_NUMBER >= 0x10101000L
|
|
if(sc->client_ciphersuites)SSL_CTX_set_ciphersuites(sc->srv_ctx, sc->client_ciphersuites);
|
|
#elif defined(WITH_WOLFSSL)
|
|
/* wolfSSL has no separate TLS1.3 ciphersuites API; route to cipher list. */
|
|
if(sc->client_ciphersuites)wolfSSL_CTX_set_cipher_list(sc->srv_ctx, sc->client_ciphersuites);
|
|
#endif
|
|
#if (defined(WITH_WOLFSSL) && defined(HAVE_ALPN) && LIBWOLFSSL_VERSION_HEX >= 0x03007000) || (!defined(WITH_WOLFSSL) && OPENSSL_VERSION_NUMBER >= 0x10200000L)
|
|
if(sc->client_alpn_protos.protos_len)SSL_CTX_set_alpn_protos(sc->srv_ctx, sc->client_alpn_protos.protos, sc->client_alpn_protos.protos_len);
|
|
#endif
|
|
if(sc->client_verify){
|
|
if(sc->client_ca_file || sc->client_ca_dir){
|
|
SSL_CTX_load_verify_locations(sc->srv_ctx, sc->client_ca_file, sc->client_ca_dir);
|
|
}
|
|
#if !defined(WITH_WOLFSSL) && OPENSSL_VERSION_NUMBER >= 0x30000000L
|
|
else if(sc->client_ca_store){
|
|
SSL_CTX_load_verify_store(sc->srv_ctx, sc->client_ca_store);
|
|
}
|
|
#endif
|
|
else
|
|
SSL_CTX_set_default_verify_paths(sc->srv_ctx);
|
|
SSL_CTX_set_verify(sc->srv_ctx, SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);
|
|
}
|
|
#ifdef WITH_WOLFSSL
|
|
else {
|
|
/* wolfSSL defaults to peer verification; OpenSSL defaults to
|
|
* SSL_VERIFY_NONE. Make the no-verify intent explicit so the
|
|
* upstream handshake (ssl_cli / ssl_mitm) succeeds without a
|
|
* trusted CA store. */
|
|
SSL_CTX_set_verify(sc->srv_ctx, SSL_VERIFY_NONE, NULL);
|
|
}
|
|
#endif
|
|
}
|
|
#ifdef WITHSPLICE
|
|
/* the data of a TLS connection passes through 3proxy, unless the kernel
|
|
does TLS for it: then each connection which got it both ways may
|
|
splice() */
|
|
#ifdef SSL_OP_ENABLE_KTLS
|
|
if(!sc || !sc->ktls)
|
|
#endif
|
|
srv->usesplice = 0;
|
|
#endif
|
|
return sc;
|
|
}
|
|
|
|
|
|
|
|
static FILTER_ACTION ssl_filter_client(void *fo, struct clientparam * param, void** fc){
|
|
struct SSLstate *ssls;
|
|
|
|
ssls = (struct SSLstate *) malloc(sizeof(struct SSLstate));
|
|
memset(ssls, 0, sizeof(struct SSLstate));
|
|
ssls->config = fo;
|
|
ssls->param = param;
|
|
param->sostate = ssls;
|
|
*fc = ssls;
|
|
if(ssls->config->serv){
|
|
SSL_CONN ClientConn;
|
|
char *err;
|
|
|
|
ClientConn = ssl_handshake_to_client(param->clisock, ssls->config, NULL, NULL, &err);
|
|
if ( ClientConn == NULL ) {
|
|
param->res = 8013;
|
|
param->srv->logfunc(param, (unsigned char *)"Handshake to client failed");
|
|
if(err)param->srv->logfunc(param, (unsigned char *)err);
|
|
return REJECT;
|
|
}
|
|
ssl_after_handshake(param, ClientConn);
|
|
addSSL(param->clisock, ClientConn, INVALID_SOCKET, NULL, param);
|
|
}
|
|
return CONTINUE;
|
|
}
|
|
|
|
static FILTER_ACTION ssl_filter_connect(void *fc, struct clientparam * param){
|
|
if(PCONF->cli && !PCONF->client_mode) {
|
|
if(docli(param)) {
|
|
return REJECT;
|
|
}
|
|
}
|
|
return PASS;
|
|
}
|
|
|
|
static FILTER_ACTION ssl_filter_afterauth(void *fc, struct clientparam * param){
|
|
if(PCONF->cli && PCONF->client_mode == 1) {
|
|
if(docli(param)) {
|
|
return REJECT;
|
|
}
|
|
}
|
|
return PASS;
|
|
}
|
|
|
|
static FILTER_ACTION ssl_filter_predata(void *fc, struct clientparam * param){
|
|
|
|
if(param->operation != HTTP_CONNECT && param->operation != CONNECT) return PASS;
|
|
if(PCONF->mitm){
|
|
if(domitm(param)) {
|
|
return REJECT;
|
|
}
|
|
if(!param->redirectfunc) param->redirectfunc = proxyfunc;
|
|
return CONTINUE;
|
|
}
|
|
else if(PCONF->cli && PCONF->client_mode == 2) {
|
|
if(docli(param)) {
|
|
return REJECT;
|
|
}
|
|
}
|
|
return PASS;
|
|
}
|
|
|
|
static FILTER_ACTION ssl_parent(struct clientparam * param){
|
|
if(PCONF->cli && PCONF->client_mode == 3) {
|
|
if(docli(param)) {
|
|
return REJECT;
|
|
}
|
|
}
|
|
return PASS;
|
|
}
|
|
|
|
static void ssl_filter_clear(void *state){
|
|
struct clientparam *param;
|
|
|
|
if(!state) return;
|
|
param = STATE->param;
|
|
free(state);
|
|
param->sostate = NULL;
|
|
}
|
|
|
|
#define CONFIG ((SSL_CONFIG *)fo)
|
|
|
|
static void ssl_filter_close(void *fo){
|
|
free(CONFIG->certcache);
|
|
if ( CONFIG->CA_cert != NULL ) {
|
|
X509_free(CONFIG->CA_cert);
|
|
}
|
|
if ( CONFIG->server_cert != NULL ) {
|
|
X509_free(CONFIG->server_cert);
|
|
}
|
|
if ( CONFIG->client_cert != NULL ) {
|
|
X509_free(CONFIG->client_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->client_key);
|
|
}
|
|
if ( CONFIG->srv_ctx != NULL ) {
|
|
SSL_CTX_free(CONFIG->srv_ctx);
|
|
}
|
|
if ( CONFIG->cli_ctx != NULL ) {
|
|
SSL_CTX_free(CONFIG->cli_ctx);
|
|
}
|
|
free(CONFIG->client_ciphersuites);
|
|
free(CONFIG->server_ciphersuites);
|
|
free(CONFIG->client_cipher_list);
|
|
free(CONFIG->server_cipher_list);
|
|
free(CONFIG->client_ca_file);
|
|
free(CONFIG->client_ca_dir);
|
|
free(CONFIG->client_ca_store);
|
|
free(CONFIG->server_ca_file);
|
|
free(CONFIG->server_ca_dir);
|
|
free(CONFIG->server_ca_store);
|
|
free(CONFIG->client_sni);
|
|
if(CONFIG->client_alpn_protos.protos_len){
|
|
free(CONFIG->client_alpn_protos.protos);
|
|
CONFIG->client_alpn_protos.protos = NULL;
|
|
CONFIG->client_alpn_protos.protos_len = 0;
|
|
}
|
|
free(fo);
|
|
}
|
|
|
|
static struct filter ssl_filter = {
|
|
NULL,
|
|
"ssl filter",
|
|
"ssl_filter",
|
|
ssl_filter_open,
|
|
ssl_filter_client,
|
|
NULL,
|
|
ssl_filter_connect,
|
|
ssl_filter_afterauth,
|
|
NULL, NULL,
|
|
ssl_filter_predata,
|
|
NULL, NULL,
|
|
ssl_filter_clear,
|
|
ssl_filter_close
|
|
};
|
|
|
|
int filterset = 0;
|
|
|
|
static void setfilters(){
|
|
filterset++;
|
|
if(filterset > 1) return;
|
|
ssl_filter.next = pl->conf->filters;
|
|
pl->conf->filters = &ssl_filter;
|
|
sso = *pl->so;
|
|
}
|
|
|
|
static void unsetfilters(){
|
|
struct filter * sf;
|
|
|
|
if(!filterset) return;
|
|
filterset--;
|
|
if(filterset > 0) return;
|
|
if(pl->conf->filters == &ssl_filter) pl->conf->filters = ssl_filter.next;
|
|
else for(sf = pl->conf->filters; sf && sf->next; sf=sf->next){
|
|
if(sf->next == &ssl_filter) {
|
|
sf->next = ssl_filter.next;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
int h_mitm(int argc, unsigned char **argv){
|
|
if(mitm) return 0;
|
|
if(serv) return 2;
|
|
mitm = 1;
|
|
setfilters();
|
|
return 0;
|
|
}
|
|
|
|
int h_nomitm(int argc, unsigned char **argv){
|
|
if(!mitm) return 0;
|
|
mitm = 0;
|
|
unsetfilters();
|
|
return 0;
|
|
}
|
|
|
|
int h_serv(int argc, unsigned char **argv){
|
|
if(serv) return 0;
|
|
if(mitm) return 2;
|
|
serv = 1;
|
|
setfilters();
|
|
return 0;
|
|
}
|
|
|
|
int h_noserv(int argc, unsigned char **argv){
|
|
if(!serv) return 0;
|
|
serv = 0;
|
|
unsetfilters();
|
|
return 0;
|
|
}
|
|
|
|
int h_cli(int argc, unsigned char **argv){
|
|
if(cli) return 0;
|
|
if(mitm) return 2;
|
|
cli = 1;
|
|
setfilters();
|
|
return 0;
|
|
}
|
|
|
|
int h_nocli(int argc, unsigned char **argv){
|
|
if(!cli) return 0;
|
|
cli = 0;
|
|
unsetfilters();
|
|
return 0;
|
|
}
|
|
|
|
|
|
int h_certcache(int argc, unsigned char **argv){
|
|
size_t len;
|
|
len = strlen((char *)argv[1]);
|
|
if(!len || (argv[1][len - 1] != '/' && argv[1][len - 1] != '\\')) return 1;
|
|
if(certcache) free(certcache);
|
|
certcache = strdup((char *)argv[1]);
|
|
return 0;
|
|
}
|
|
|
|
int h_srvcert(int argc, unsigned char **argv){
|
|
free(srvcert);
|
|
srvcert = argc > 1? strdup((char *)argv[1]) : NULL;
|
|
return 0;
|
|
}
|
|
|
|
int h_srvkey(int argc, unsigned char **argv){
|
|
free(srvkey);
|
|
srvkey = argc > 1? strdup((char *)argv[1]) : NULL;
|
|
return 0;
|
|
}
|
|
|
|
int h_clicert(int argc, unsigned char **argv){
|
|
free(clicert);
|
|
clicert = argc > 1? strdup((char *)argv[1]) : NULL;
|
|
return 0;
|
|
}
|
|
|
|
int h_clikey(int argc, unsigned char **argv){
|
|
free(clikey);
|
|
clikey = argc > 1? strdup((char *)argv[1]) : NULL;
|
|
return 0;
|
|
}
|
|
|
|
|
|
int h_client_cipher_list(int argc, unsigned char **argv){
|
|
free(client_cipher_list);
|
|
client_cipher_list = argc > 1? strdup((char *)argv[1]) : NULL;
|
|
return 0;
|
|
}
|
|
|
|
int h_server_cipher_list(int argc, unsigned char **argv){
|
|
free(server_cipher_list);
|
|
server_cipher_list = argc > 1? strdup((char *)argv[1]) : NULL;
|
|
return 0;
|
|
}
|
|
|
|
int h_client_ciphersuites(int argc, unsigned char **argv){
|
|
free(client_ciphersuites);
|
|
client_ciphersuites = argc > 1? strdup((char *)argv[1]) : NULL;
|
|
return 0;
|
|
}
|
|
|
|
int h_server_ciphersuites(int argc, unsigned char **argv){
|
|
free(server_ciphersuites);
|
|
server_ciphersuites = argc > 1? strdup((char *)argv[1]) : NULL;
|
|
return 0;
|
|
}
|
|
|
|
int h_server_ca_file(int argc, unsigned char **argv){
|
|
free(server_ca_file);
|
|
server_ca_file = argc > 1? strdup((char *)argv[1]) : NULL;
|
|
return 0;
|
|
}
|
|
|
|
int h_server_ca_key(int argc, unsigned char **argv){
|
|
free(server_ca_key);
|
|
server_ca_key = argc > 1? strdup((char *)argv[1]) : NULL;
|
|
return 0;
|
|
}
|
|
|
|
int h_client_ca_file(int argc, unsigned char **argv){
|
|
free(client_ca_file);
|
|
client_ca_file = argc > 1? strdup((char *)argv[1]) : NULL;
|
|
return 0;
|
|
}
|
|
|
|
int h_client_ca_dir(int argc, unsigned char **argv){
|
|
free(client_ca_dir);
|
|
client_ca_dir = argc > 1? strdup((char *)argv[1]) : NULL;
|
|
return 0;
|
|
}
|
|
|
|
int h_client_ca_store(int argc, unsigned char **argv){
|
|
free(client_ca_store);
|
|
client_ca_store = argc > 1? strdup((char *)argv[1]) : NULL;
|
|
return 0;
|
|
}
|
|
|
|
int h_client_sni(int argc, unsigned char **argv){
|
|
free(client_sni);
|
|
client_sni = argc > 1? strdup((char *)argv[1]) : NULL;
|
|
return 0;
|
|
}
|
|
|
|
int h_client_alpn(int argc, unsigned char **argv){
|
|
int len, i;
|
|
|
|
if(client_alpn_protos.protos_len){
|
|
free(client_alpn_protos.protos);
|
|
client_alpn_protos.protos = NULL;
|
|
client_alpn_protos.protos_len = 0;
|
|
}
|
|
if(argc <= 1) return 0;
|
|
|
|
for(len = argc - 1, i = 1; i < argc; i++){
|
|
len += strlen((char *)argv[i]);
|
|
}
|
|
|
|
if(!(client_alpn_protos.protos = malloc(len))) return 10;
|
|
|
|
for(len = 0, i = 1; i < argc; i++){
|
|
int l;
|
|
|
|
l = strlen((char *)argv[i]);
|
|
if(l >= 255) return 2;
|
|
client_alpn_protos.protos[len++] = l;
|
|
memcpy(client_alpn_protos.protos + len, argv[i], l);
|
|
len += l;
|
|
}
|
|
client_alpn_protos.protos_len = len;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int h_server_ca_dir(int argc, unsigned char **argv){
|
|
free(server_ca_dir);
|
|
server_ca_dir = argc > 1? strdup((char *)argv[1]) : NULL;
|
|
return 0;
|
|
}
|
|
|
|
int h_server_ca_store(int argc, unsigned char **argv){
|
|
free(server_ca_store);
|
|
server_ca_store = argc > 1? strdup((char *)argv[1]) : NULL;
|
|
return 0;
|
|
}
|
|
|
|
struct vermap{
|
|
char *sver;
|
|
int iver;
|
|
} versions[] = {
|
|
#ifdef SSL3_VERSION
|
|
{"SSLv3",SSL3_VERSION},
|
|
#endif
|
|
|
|
#ifdef TLS1_VERSION
|
|
{"TLSv1",TLS1_VERSION},
|
|
#endif
|
|
|
|
#ifdef TLS1_1_VERSION
|
|
{"TLSv1.1",TLS1_1_VERSION},
|
|
#endif
|
|
|
|
#ifdef TLS1_2_VERSION
|
|
{"TLSv1.2",TLS1_2_VERSION},
|
|
#endif
|
|
|
|
#ifdef TLS1_3_VERSION
|
|
{"TLSv1.3",TLS1_3_VERSION},
|
|
#endif
|
|
|
|
{NULL, 0}
|
|
};
|
|
|
|
int string_to_version(unsigned char *ver){
|
|
int i;
|
|
for (i=0; versions[i].sver; i++){
|
|
if(!strcasecmp(versions[i].sver, (char *)ver)) return versions[i].iver;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int h_client_min_proto_version(int argc, unsigned char **argv){
|
|
client_min_proto_version = argc>1? string_to_version(argv[1]) : 0;
|
|
return 0;
|
|
}
|
|
|
|
int h_client_max_proto_version(int argc, unsigned char **argv){
|
|
client_max_proto_version = argc>1? string_to_version(argv[1]) : 0;
|
|
return 0;
|
|
}
|
|
|
|
int h_server_min_proto_version(int argc, unsigned char **argv){
|
|
server_min_proto_version = argc>1? string_to_version(argv[1]) : 0;
|
|
return 0;
|
|
}
|
|
|
|
int h_server_max_proto_version(int argc, unsigned char **argv){
|
|
server_max_proto_version = argc>1? string_to_version(argv[1]) : 0;
|
|
return 0;
|
|
}
|
|
|
|
int h_client_verify(int argc, unsigned char **argv){
|
|
client_verify = 1;
|
|
return 0;
|
|
}
|
|
int h_no_client_verify(int argc, unsigned char **argv){
|
|
client_verify = 0;
|
|
return 0;
|
|
}
|
|
|
|
int h_server_verify(int argc, unsigned char **argv){
|
|
server_verify = 1;
|
|
return 0;
|
|
}
|
|
|
|
int h_client_mode(int argc, unsigned char **argv){
|
|
client_mode = 0;
|
|
if(argc <= 1) return 0;
|
|
client_mode = atoi((char *)argv[1]);
|
|
return 0;
|
|
}
|
|
|
|
|
|
int h_no_server_verify(int argc, unsigned char **argv){
|
|
server_verify = 0;
|
|
return 0;
|
|
}
|
|
|
|
int h_ktls(int argc, unsigned char **argv){
|
|
#ifdef SSL_OP_ENABLE_KTLS
|
|
ktls = 1;
|
|
#else
|
|
fprintf(stderr, "ssl_ktls: kernel TLS is not supported by this TLS library, ignored\n");
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
int h_noktls(int argc, unsigned char **argv){
|
|
ktls = 0;
|
|
return 0;
|
|
}
|
|
|
|
static struct symbol ssl_symbols[] = {
|
|
{NULL, "ssl_parent", (void *)&ssl_parent},
|
|
};
|
|
|
|
|
|
void ssl_install(void){
|
|
|
|
h_nomitm(0, NULL);
|
|
h_noserv(0, NULL);
|
|
h_nocli(0, NULL);
|
|
|
|
pl = &pluginlink;
|
|
|
|
free(certcache);
|
|
certcache = NULL;
|
|
free(srvcert);
|
|
srvcert = NULL;
|
|
free(srvkey);
|
|
srvkey = NULL;
|
|
free(clicert);
|
|
clicert = NULL;
|
|
free(clikey);
|
|
clikey = NULL;
|
|
client_min_proto_version = 0;
|
|
client_max_proto_version = 0;
|
|
server_min_proto_version = 0;
|
|
server_max_proto_version = 0;
|
|
client_verify = 0;
|
|
server_verify = 0;
|
|
client_mode = 0;
|
|
ktls = 0;
|
|
free(client_ciphersuites);
|
|
client_ciphersuites = NULL;
|
|
free(server_ciphersuites);
|
|
server_ciphersuites = NULL;
|
|
free(client_cipher_list);
|
|
client_cipher_list = NULL;
|
|
free(server_cipher_list);
|
|
server_cipher_list = NULL;
|
|
free(server_ca_file);
|
|
server_ca_file = NULL;
|
|
free(server_ca_key);
|
|
server_ca_key = NULL;
|
|
free(client_ca_file);
|
|
client_ca_file = NULL;
|
|
free(client_ca_dir);
|
|
client_ca_dir = NULL;
|
|
free(client_ca_store);
|
|
client_ca_store = NULL;
|
|
free(server_ca_dir);
|
|
server_ca_dir = NULL;
|
|
free(server_ca_store);
|
|
server_ca_store = NULL;
|
|
|
|
|
|
if(!ssl_loaded){
|
|
ssl_loaded = 1;
|
|
ssl_init();
|
|
ssl_symbols[0].next = pl->symbols.next;
|
|
pl->symbols.next = ssl_symbols;
|
|
}
|
|
|
|
#ifdef WITHSPLICE
|
|
splicetls = ssl_splicetls;
|
|
#endif
|
|
tcppmfunc = (PROXYFUNC)pl->findbyname("tcppm");
|
|
if(!tcppmfunc) return;
|
|
proxyfunc = (PROXYFUNC)pl->findbyname("proxy");
|
|
if(!proxyfunc)proxyfunc = tcppmfunc;
|
|
smtppfunc = (PROXYFUNC)pl->findbyname("smtpp");
|
|
if(!smtppfunc)smtppfunc = tcppmfunc;
|
|
ftpprfunc = (PROXYFUNC)pl->findbyname("ftppr");
|
|
if(!ftpprfunc)ftpprfunc = tcppmfunc;
|
|
|
|
}
|