From 768235920afba76f01dea6a2194d67a35b51143c Mon Sep 17 00:00:00 2001 From: Lu Date: Tue, 21 Aug 2018 16:11:50 +0800 Subject: [PATCH] =?UTF-8?q?=E6=95=B4=E7=90=86=E7=9B=AE=E5=BD=95=E7=BB=93?= =?UTF-8?q?=E6=9E=84=EF=BC=8C=E8=B0=83=E6=95=B4=E6=A1=86=E6=9E=B6=E9=83=A8?= =?UTF-8?q?=E5=88=86=E5=AE=9E=E7=8E=B0=EF=BC=8C=E5=88=9D=E6=AD=A5=E7=BC=96?= =?UTF-8?q?=E8=AF=91=E9=80=9A=E8=BF=87=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- common/CMakeLists.txt | 3 + common/include/tfe_future.h | 27 +- common/include/tfe_stream.h | 90 +- common/include/tfe_types.h | 9 +- common/include/tfe_util.h | 20 - common/include/tfe_utils.h | 17 + common/src/tfe_utils.cpp | 30 + platform/CMakeLists.txt | 9 +- platform/include/internal/cert.h | 58 +- .../internal/kni.h} | 0 platform/include/internal/sescache.h | 7 + platform/{src => include/internal}/ssl.h | 147 +- platform/include/internal/stream.h | 126 ++ platform/src/Makefile | 32 - platform/src/cert.cpp | 38 +- platform/src/future.cpp | 87 +- platform/src/{io_module_kni.cpp => kni.cpp} | 0 platform/src/main.cpp | 24 + platform/src/privsep.cc | 980 --------- platform/src/proxy.cpp | 294 +++ platform/src/session_cache.cpp | 14 - platform/src/ssl_stream.cpp | 1822 +++++++++-------- platform/src/ssl_stream.h | 1 - platform/src/tfe_main.cpp | 41 - platform/src/tfe_proxy.cpp | 407 ---- platform/src/tfe_stream.cpp | 389 ++-- platform/src/tfe_stream_internal.h | 98 - platform/src/tfe_util.cpp | 23 - 28 files changed, 1809 insertions(+), 2984 deletions(-) delete mode 100644 common/include/tfe_util.h create mode 100644 common/include/tfe_utils.h create mode 100644 common/src/tfe_utils.cpp rename platform/{src/io_module_kni.h => include/internal/kni.h} (100%) create mode 100644 platform/include/internal/sescache.h rename platform/{src => include/internal}/ssl.h (52%) create mode 100644 platform/include/internal/stream.h delete mode 100644 platform/src/Makefile rename platform/src/{io_module_kni.cpp => kni.cpp} (100%) create mode 100644 platform/src/main.cpp delete mode 100644 platform/src/privsep.cc create mode 100644 platform/src/proxy.cpp delete mode 100644 platform/src/session_cache.cpp delete mode 100644 platform/src/ssl_stream.h delete mode 100644 platform/src/tfe_main.cpp delete mode 100644 platform/src/tfe_proxy.cpp delete mode 100644 platform/src/tfe_stream_internal.h delete mode 100644 platform/src/tfe_util.cpp diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index e69de29..9bf3831 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -0,0 +1,3 @@ + +add_library(common src/tfe_stat.cpp src/tfe_utils.cpp) +target_include_directories(common PUBLIC ${CMAKE_CURRENT_LIST_DIR}/include) diff --git a/common/include/tfe_future.h b/common/include/tfe_future.h index ba7bd56..e9fc5d2 100644 --- a/common/include/tfe_future.h +++ b/common/include/tfe_future.h @@ -1,20 +1,25 @@ enum e_future_error { - FUTURE_ERROR_CANCEL, - FUTURE_ERROR_EXCEPTION, - FUTURE_ERROR_TIMEOUT + FUTURE_ERROR_CANCEL, + FUTURE_ERROR_EXCEPTION, + FUTURE_ERROR_TIMEOUT }; + struct promise; struct future; -typedef void (*future_success_cb)(void * result, void * user); -typedef void (*future_failed_cb)(enum e_future_error err, const char * what, void * user); -typedef void (*promise_ctx_destroy_cb)(struct promise* p); - -struct future* future_create(future_success_cb * cb_success, future_failed_cb * cb_failed, void * user); - -struct future* promise_to_future(struct promise* p); -struct promise* future_to_promise(struct future* f); +typedef void (future_success_cb)(void * result, void * user); +typedef void (future_failed_cb)(enum e_future_error err, const char * what, void * user); +typedef void (promise_ctx_destroy_cb)(struct promise * p); +struct future * future_create(future_success_cb * cb_success, future_failed_cb * cb_failed, void * user); +struct future * promise_to_future(struct promise * p); +struct promise * future_to_promise(struct future * f); +void future_destroy(struct future * f); +void promise_failed(struct promise * p, enum e_future_error error, const char * what); +void promise_success(struct promise * p, void * result); +void promise_set_ctx(struct promise * p, void * ctx, promise_ctx_destroy_cb * cb); +void * promise_get_ctx(struct promise * p); +void * promise_dettach_ctx(struct promise * p); diff --git a/common/include/tfe_stream.h b/common/include/tfe_stream.h index b10cf41..7e47232 100644 --- a/common/include/tfe_stream.h +++ b/common/include/tfe_stream.h @@ -1,25 +1,34 @@ +#pragma once + +#include +#include +#include + +#define TFE_STRING_MAX 2048 +#define TFE_SYMBOL_MAX 64 -#define TFE_STRING_MAX 2048 -#define TFE_SYMBOL_MAX 64 enum tfe_session_proto { - SESSION_PROTO_PLAIN=0, + SESSION_PROTO_PLAIN = 0, SESSION_PROTO_SSL, SESSION_PROTO_QUIC, SESSION_PROTO_SPDY }; + enum tfe_app_proto { APP_PROTO_HTTP1, APP_PROTO_HTTP2, - APP_PROTO_WS, //websocket - APP_PROTO_QUIC //QUIC is a protocol that cross session layer and application layer. + APP_PROTO_WS, //websocket + APP_PROTO_QUIC //QUIC is a protocol that cross session layer and application layer. }; + enum tfe_conn_dir { - CONN_DIR_DOWNSTREAM=0, //From client to proxy, aka client-side. - CONN_DIR_UPSTREAM //From proxy to server, aka server-side. + CONN_DIR_DOWNSTREAM = 0, //From client to proxy, aka client-side. + CONN_DIR_UPSTREAM //From proxy to server, aka server-side. }; + enum tfe_conn_status { CONN_STATUS_NONE, @@ -32,78 +41,79 @@ struct tfe_conn { struct layer_addr addr; enum tfe_conn_status status; - struct bufferevent *bev; -} ; + struct bufferevent * bev; +}; struct tfe_stream { - struct tfe_conn upstream; + struct tfe_conn upstream; struct tfe_conn downstream; }; - enum tfe_stream_action { ACTION_FORWARD_DATA, ACTION_DEFER_DATA, ACTION_DROP_DATA }; + enum tfe_stream_action_opt { - ACTION_OPT_FOWARD_BYTES, //value is size_t, default: forward entire data - ACTION_OPT_DEFER_TIME_TV, //value is "struct timeval " which defines in , default: time defer is not enabled - ACTION_OPT_DEFER_BYTES, //value is size_t, default: defer entire data - ACTION_OPT_DROP_BYTES //value is size_t, default: drop entire data + ACTION_OPT_FOWARD_BYTES, //value is size_t, default: forward entire data + ACTION_OPT_DEFER_TIME_TV, //value is "struct timeval " which defines in , default: time defer is not enabled + ACTION_OPT_DEFER_BYTES, //value is size_t, default: defer entire data + ACTION_OPT_DROP_BYTES //value is size_t, default: drop entire data }; + enum tfe_stream_close_reason { REASON_PASSIVE_CLOSED, REASON_ACTIVE_CLOSED, REASON_ERROR }; -int tfe_stream_action_set_opt(const struct tfe_stream* stream,enum tfe_stream_action_opt type, void* value, size_t size); + +int tfe_stream_action_set_opt(const struct tfe_stream * stream, enum tfe_stream_action_opt type, + void * value, size_t size); /* @return 0 if successful, or -1 if an error occurred */ +int tfe_stream_write(const struct tfe_stream * stream, enum tfe_conn_dir dir, const unsigned char * data, size_t len); -int tfe_stream_write(const struct tfe_stream* stream, enum tfe_conn_dir dir, const unsigned char *data, size_t len); - -struct tfe_stream_write_ctx{}; +struct tfe_stream_write_ctx; //following tfe_stream_write_xx functions are NOT thread safe, MUST be called in the stream process thread. -struct tfe_stream_write_ctx* tfe_stream_write_frag_start(const struct tfe_stream* stream, enum tfe_conn_dir dir); +struct tfe_stream_write_ctx * tfe_stream_write_frag_start(const struct tfe_stream * stream, enum tfe_conn_dir dir); /* @return 0 if successful, or -1 if an error occurred */ -int tfe_stream_write_frag(struct tfe_stream_write_ctx* w_ctx,const unsigned char *data, size_t size); -void tfe_stream_write_frag_end(struct tfe_stream_write_ctx* w_ctx); +int tfe_stream_write_frag(struct tfe_stream_write_ctx * w_ctx, const unsigned char * data, size_t size); +void tfe_stream_write_frag_end(struct tfe_stream_write_ctx * w_ctx); + //Return 1 for identify as its traffic; //Return 0 for unknown traffic; -typedef tfe_stream_action stream_open_cb_t(const struct tfe_stream* stream, unsigned int thread_id, enum tfe_conn_dir dir, const unsigned char *data, size_t len, void **pme); -typedef tfe_stream_action stream_data_cb_t(const struct tfe_stream* stream, unsigned int thread_id, enum tfe_conn_dir dir, const unsigned char *data, size_t len, void **pme); -typedef void stream_close_cb_t(const struct tfe_stream* stream, unsigned int thread_id, enum tfe_stream_close_reason reason, void **pme); +typedef tfe_stream_action stream_open_cb_t(const struct tfe_stream * stream, unsigned int thread_id, + enum tfe_conn_dir dir, const unsigned char * data, size_t len, void ** pme); -void tfe_stream_detach(const struct tfe_stream* stream); -int tfe_stream_preempt(const struct tfe_stream* stream); +typedef tfe_stream_action stream_data_cb_t(const struct tfe_stream * stream, unsigned int thread_id, + enum tfe_conn_dir dir, const unsigned char * data, size_t len, void ** pme); + +typedef void stream_close_cb_t(const struct tfe_stream * stream, unsigned int thread_id, + enum tfe_stream_close_reason reason, void ** pme); + +void tfe_stream_detach(const struct tfe_stream * stream); +int tfe_stream_preempt(const struct tfe_stream * stream); struct promise * tfe_stream_suspend(const struct tfe_stream * stream); void tfe_stream_resume(struct promisc * promisc); -int stream_shutdown(const struct tfe_stream* stream);//close both sides of the stream. -int stream_shutdown_dir(const struct tfe_stream* stream, enum tfe_conn_dir dir); -//typedef int proto_onwrite_cb_t(struct tfe_stream*, struct evbuffer *data, void **pme); +//close both sides of the stream. +int stream_shutdown(const struct tfe_stream * stream); +int stream_shutdown_dir(const struct tfe_stream * stream, enum tfe_conn_dir dir); struct tfe_plugin { char symbol[TFE_SYMBOL_MAX]; enum tfe_app_proto proto; - stream_open_cb_t* on_open; - stream_data_cb_t* on_data; - stream_close_cb_t* on_close; -// proto_onwrite_cb_t *onwrite; - + stream_open_cb_t * on_open; + stream_data_cb_t * on_data; + stream_close_cb_t * on_close; }; -int tfe_io_write(struct pxy_conn_desc* dest,int dir,struct evbuffer *data); - -int tfe_xxx_proto_init(struct tfe_plugin*m); - - diff --git a/common/include/tfe_types.h b/common/include/tfe_types.h index efb0389..7a9f6b6 100644 --- a/common/include/tfe_types.h +++ b/common/include/tfe_types.h @@ -1,9 +1,8 @@ +#pragma once + #include #include //defines struct in_addr -#define __TFE_STRING_MAX 2048 -#define MAX_FILENAME_SIZE 256 - /* network-order */ struct stream_tuple4_v4{ struct in_addr saddr; /* network order */ @@ -25,15 +24,12 @@ struct stream_tuple4_v6 in_port_t dest; /* network order */ }; - - #define GRE_TAG_LEN (4) struct layer_addr_gre { uint16_t gre_id; }; - #define VLAN_ID_MASK (0x0FFF) #define VLAN_TAG_LEN (4) struct layer_addr_vlan @@ -140,4 +136,3 @@ struct layer_addr }; uint8_t addrlen; /* 地址结构长度 */ }; - diff --git a/common/include/tfe_util.h b/common/include/tfe_util.h deleted file mode 100644 index 015ba5e..0000000 --- a/common/include/tfe_util.h +++ /dev/null @@ -1,20 +0,0 @@ -//#define ALLOC(t,n) (t *)calloc(sizeof(t),(n)) - - /* Allocates an array of objects using malloc() */ -#define ALLOC(type, number) \ - ((type *)calloc(sizeof(type), number)) - -#define log_err_printf(fmt, args...) \ - fprintf(stderr, "file %s, line %d, " fmt, \ - __FILE__, __LINE__, ##args) -#define log_dbg_printf(fmt, args...) \ - fprintf(stdout, "file %s, line %d, " fmt, \ - __FILE__, __LINE__, ##args) -#define likely(expr) __builtin_expect((expr), 1) -#define unlikely(expr) __builtin_expect((expr), 0) - -int addr_sock2layer(struct sockaddr * sock_addr, int sockaddrlen, struct layer_addr* layer_addr); -int addr_layer2sock(struct layer_addr* layer_addr,struct sockaddr * sock_addr); -char* tfe_strdup(const char* s); - - diff --git a/common/include/tfe_utils.h b/common/include/tfe_utils.h new file mode 100644 index 0000000..e4a4bc8 --- /dev/null +++ b/common/include/tfe_utils.h @@ -0,0 +1,17 @@ +//#define ALLOC(t,n) (t *)calloc(sizeof(t),(n)) + + /* Allocates an array of objects using malloc() */ +#define ALLOC(type, number) \ + ((type *)calloc(sizeof(type), number)) + +#define likely(expr) __builtin_expect((expr), 1) +#define unlikely(expr) __builtin_expect((expr), 0) + +int addr_sock_to_layer(struct sockaddr * sock_addr, int sockaddrlen, struct layer_addr * layer_addr); +int addr_layer_to_sock(struct layer_addr * layer_addr, struct sockaddr * sock_addr); +char* tfe_strdup(const char* s); + + +#define TFE_LOG_ERROR +#define TFE_LOG_INFO +#define TFE_LOG_DEBUG diff --git a/common/src/tfe_utils.cpp b/common/src/tfe_utils.cpp new file mode 100644 index 0000000..7e423fd --- /dev/null +++ b/common/src/tfe_utils.cpp @@ -0,0 +1,30 @@ + +#include +#include +#include +#include + +int addr_sock_to_layer(struct sockaddr * sock_addr, int sockaddrlen, struct layer_addr * layer_addr) +{ + return 0; +} + +int addr_layer_to_sock(struct layer_addr * layer_addr, struct sockaddr * sock_addr) +{ + int sockaddrlen=-1; + return sockaddrlen; +} + +//functioned as strdup, for dictator compatible. +char* tfe_strdup(const char* s) +{ + char*d=NULL; + if(s==NULL) + { + return NULL; + } + + d=(char*)malloc(strlen(s)+1); + memcpy(d,s,strlen(s)+1); + return d; +} diff --git a/platform/CMakeLists.txt b/platform/CMakeLists.txt index e18192c..7dad34f 100644 --- a/platform/CMakeLists.txt +++ b/platform/CMakeLists.txt @@ -1,3 +1,8 @@ -add_executable(tfe src/cert.cpp src/future.cpp src/io_module_kni.cpp src/session_cache.cpp src/ssl.cc - src/ssl_stream.cpp src/tfe_main.cpp src/tfe_proxy.cpp src/tfe_stream.cpp src/tfe_util.cpp) \ No newline at end of file +add_executable(tfe src/cert.cpp src/future.cpp src/kni.cpp src/tfe_stream.cpp src/main.cpp src/proxy.cpp) +target_include_directories(tfe PUBLIC ${CMAKE_CURRENT_LIST_DIR}/include/external) +target_include_directories(tfe PRIVATE ${CMAKE_CURRENT_LIST_DIR}/include/internal) + +target_link_libraries(tfe common) +target_link_libraries(tfe pthread dl openssl-ssl-static openssl-crypto-static pthread libevent-static + libevent-static-openssl libevent-static-pthreads MESA_handle_logger MESA_prof_load wiredcfg) diff --git a/platform/include/internal/cert.h b/platform/include/internal/cert.h index b80e019..d149811 100644 --- a/platform/include/internal/cert.h +++ b/platform/include/internal/cert.h @@ -1,62 +1,18 @@ -/*- - * SSLsplit - transparent SSL/TLS interception - * https://www.roe.ch/SSLsplit - * - * Copyright (c) 2009-2018, Daniel Roethlisberger . - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS'' - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - #ifndef CERT_H #define CERT_H -#include "attrib.h" - #include #include -typedef struct cert { - EVP_PKEY *key; - X509 *crt; - STACK_OF(X509) * chain; - pthread_mutex_t mutex; - size_t references; -} cert_t; -struct cert_mgr{}; -struct cert_mgr* cert_manager_init(const char* profile); -void cert_mgr_async_get(struct future* future, struct cert_mgr* mgr, int keyring_id, X509* origin_cert, struct event_base* evbase); +struct cert{}; +typedef struct cert cert_t; +struct cert_mgr; +struct cert_mgr * cert_manager_init(const char * profile); +void cert_mgr_async_get(struct future * future, struct cert_mgr * mgr, int keyring_id, + X509 * origin_cert, struct event_base * evbase); -cert_t * cert_new(void) MALLOC; -cert_t * cert_new_load(const char *) MALLOC; -cert_t * cert_new3(EVP_PKEY *, X509 *, STACK_OF(X509) *) MALLOC; -cert_t * cert_new3_copy(EVP_PKEY *, X509 *, STACK_OF(X509) *) MALLOC; -void cert_refcount_inc(cert_t *) NONNULL(1); -void cert_set_key(cert_t *, EVP_PKEY *) NONNULL(1); -void cert_set_crt(cert_t *, X509 *) NONNULL(1); -void cert_set_chain(cert_t *, STACK_OF(X509) *) NONNULL(1); -void cert_free(cert_t *) NONNULL(1); +void cert_free(cert_t * cert); #endif /* !CERT_H */ - -/* vim: set noet ft=c: */ diff --git a/platform/src/io_module_kni.h b/platform/include/internal/kni.h similarity index 100% rename from platform/src/io_module_kni.h rename to platform/include/internal/kni.h diff --git a/platform/include/internal/sescache.h b/platform/include/internal/sescache.h new file mode 100644 index 0000000..1118f67 --- /dev/null +++ b/platform/include/internal/sescache.h @@ -0,0 +1,7 @@ +#pragma once + +struct sess_cache; +struct sess_cache * session_cache_init(); + +void session_cache_set(struct sess_cache * cache, struct sockaddr * addr, + socklen_t addrlen, const char * sni, SSL_SESSION * session); diff --git a/platform/src/ssl.h b/platform/include/internal/ssl.h similarity index 52% rename from platform/src/ssl.h rename to platform/include/internal/ssl.h index 851b012..6712167 100644 --- a/platform/src/ssl.h +++ b/platform/include/internal/ssl.h @@ -29,8 +29,6 @@ #ifndef SSL_H #define SSL_H -#include "tfe_types.h" - #include #include #include @@ -38,31 +36,6 @@ #include #include -#if (OPENSSL_VERSION_NUMBER < 0x10000000L) && !defined(OPENSSL_NO_THREADID) -#define OPENSSL_NO_THREADID -#endif - -#if (OPENSSL_VERSION_NUMBER < 0x0090806FL) && !defined(OPENSSL_NO_TLSEXT) -#define OPENSSL_NO_TLSEXT -#endif - -/* - * ECDH is disabled when building against OpenSSL < 1.0.0e due to issues with - * thread-safety and security in server mode ephemereal ECDH cipher suites. - * http://www.openssl.org/news/secadv_20110906.txt - */ -#if (OPENSSL_VERSION_NUMBER < 0x1000005FL) && !defined(OPENSSL_NO_ECDH) -#define OPENSSL_NO_ECDH -#endif - -#if (OPENSSL_VERSION_NUMBER < 0x0090802FL) && !defined(OPENSSL_NO_ECDSA) -#define OPENSSL_NO_ECDSA -#endif - -#if (OPENSSL_VERSION_NUMBER < 0x0090802FL) && !defined(OPENSSL_NO_EC) -#define OPENSSL_NO_EC -#endif - /* * SHA0 was removed in OpenSSL 1.1.0, including OPENSSL_NO_SHA0. */ @@ -87,23 +60,6 @@ int DH_set0_pqg(DH *, BIGNUM *, BIGNUM *, BIGNUM *); #define CONST_SSL_METHOD const SSL_METHOD #endif /* >= OpensSL 1.0.0 */ -/* - * Workaround for bug in OpenSSL 0.9.8y, 1.0.0k and 1.0.1e - * http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=703031 - * http://openssl.6102.n7.nabble.com/NULL-ptr-deref-when-calling-SSL-get-certificate-with-1-0-0k-td43636.html - */ -#if (OPENSSL_VERSION_NUMBER == 0x0090819fL) || \ - (OPENSSL_VERSION_NUMBER == 0x100000bfL) || \ - (OPENSSL_VERSION_NUMBER == 0x1000105fL) -#define SSL_get_certificate(x) ssl_ssl_cert_get(x) -X509 * ssl_ssl_cert_get(SSL *); -#endif /* OpenSSL 0.9.8y or 1.0.0k or 1.0.1e */ - -#ifdef OPENSSL_NO_TLSEXT -#ifndef TLSEXT_MAXLEN_host_name -#define TLSEXT_MAXLEN_host_name 255 -#endif /* !TLSEXT_MAXLEN_host_name */ -#endif /* OPENSSL_NO_TLSEXT */ /* * SSL_OP_NO_* is used as an indication that OpenSSL is sufficiently recent @@ -159,72 +115,67 @@ X509 * ssl_ssl_cert_get(SSL *); #endif /* !HAVE_TLSV12 */ #define SSL_PROTO_SUPPORT_S SSL2_S SSL3_S TLS10_S TLS11_S TLS12_S +#define SSL_KEY_IDSZ 20 +#define SSL_X509_FPRSZ 20 + void ssl_openssl_version(void); -int ssl_init(void) WUNRES; -int ssl_reinit(void) WUNRES; +int ssl_init(void); +int ssl_reinit(void); void ssl_fini(void); -char * ssl_sha1_to_str(unsigned char *, int) NONNULL(1) MALLOC; +char * ssl_sha1_to_str(unsigned char *, int); -char * ssl_ssl_state_to_str(SSL *) NONNULL(1) MALLOC; -char * ssl_ssl_masterkey_to_str(SSL *) NONNULL(1) MALLOC; +char * ssl_ssl_state_to_str(SSL *); +char * ssl_ssl_masterkey_to_str(SSL *); -#ifndef OPENSSL_NO_DH -DH * ssl_tmp_dh_callback(SSL *, int, int) NONNULL(1) MALLOC; -DH * ssl_dh_load(const char *) NONNULL(1) MALLOC; -void ssl_dh_refcount_inc(DH *) NONNULL(1); -#endif /* !OPENSSL_NO_DH */ +DH * ssl_tmp_dh_callback(SSL *, int, int); +DH * ssl_dh_load(const char *); +void ssl_dh_refcount_inc(DH *); -#ifndef OPENSSL_NO_EC -EC_KEY * ssl_ec_by_name(const char *) MALLOC; -#endif /* !OPENSSL_NO_EC */ +EC_KEY * ssl_ec_by_name(const char *); -EVP_PKEY * ssl_key_load(const char *) NONNULL(1) MALLOC; -EVP_PKEY * ssl_key_genrsa(const int) MALLOC; -void ssl_key_refcount_inc(EVP_PKEY *) NONNULL(1); -#define SSL_KEY_IDSZ 20 -int ssl_key_identifier_sha1(EVP_PKEY *, unsigned char *) NONNULL(1,2); -char * ssl_key_identifier(EVP_PKEY *, int) NONNULL(1) MALLOC; +EVP_PKEY * ssl_key_load(const char *); +EVP_PKEY * ssl_key_genrsa(const int); -#ifndef OPENSSL_NO_TLSEXT -int ssl_x509_v3ext_add(X509V3_CTX *, X509 *, char *, char *) NONNULL(1,2,3,4); -int ssl_x509_v3ext_copy_by_nid(X509 *, X509 *, int) NONNULL(1,2); -#endif /* !OPENSSL_NO_TLSEXT */ -int ssl_x509_serial_copyrand(X509 *, X509 *) NONNULL(1,2); -X509 * ssl_x509_forge(X509 *, EVP_PKEY *, X509 *, EVP_PKEY *, - const char *, const char *) - NONNULL(1,2,3,4) MALLOC; -X509 * ssl_x509_load(const char *) NONNULL(1) MALLOC; -char * ssl_x509_subject(X509 *) NONNULL(1) MALLOC; -char * ssl_x509_subject_cn(X509 *, size_t *) NONNULL(1,2) MALLOC; -#define SSL_X509_FPRSZ 20 -int ssl_x509_fingerprint_sha1(X509 *, unsigned char *) NONNULL(1,2); -char * ssl_x509_fingerprint(X509 *, int) NONNULL(1) MALLOC; -char ** ssl_x509_names(X509 *) NONNULL(1) MALLOC; -int ssl_x509_names_match(X509 *, const char *) NONNULL(1,2); -char * ssl_x509_names_to_str(X509 *) NONNULL(1) MALLOC; -char ** ssl_x509_aias(X509 *, const int) NONNULL(1) MALLOC; -char ** ssl_x509_ocsps(X509 *) NONNULL(1) MALLOC; -int ssl_x509_is_valid(X509 *) NONNULL(1) WUNRES; -char * ssl_x509_to_str(X509 *) NONNULL(1) MALLOC; -char * ssl_x509_to_pem(X509 *) NONNULL(1) MALLOC; -void ssl_x509_refcount_inc(X509 *) NONNULL(1); +void ssl_key_refcount_inc(EVP_PKEY *); -int ssl_x509chain_load(X509 **, STACK_OF(X509) **, const char *) NONNULL(2,3); -void ssl_x509chain_use(SSL_CTX *, X509 *, STACK_OF(X509) *) NONNULL(1,2,3); +int ssl_key_identifier_sha1(EVP_PKEY *, unsigned char *); +char * ssl_key_identifier(EVP_PKEY *, int); -char * ssl_session_to_str(SSL_SESSION *) NONNULL(1) MALLOC; -int ssl_session_is_valid(SSL_SESSION *) NONNULL(1); +int ssl_x509_v3ext_add(X509V3_CTX *, X509 *, char *, char *); +int ssl_x509_v3ext_copy_by_nid(X509 *, X509 *, int); -int ssl_is_ocspreq(const unsigned char *, size_t) NONNULL(1) WUNRES; +int ssl_x509_serial_copyrand(X509 *, X509 *); +X509 * ssl_x509_forge(X509 *, EVP_PKEY *, X509 *, EVP_PKEY *, const char *, const char *); + +X509 * ssl_x509_load(const char *); +char * ssl_x509_subject(X509 *); +char * ssl_x509_subject_cn(X509 *, size_t *); + +int ssl_x509_fingerprint_sha1(X509 *, unsigned char *); +char * ssl_x509_fingerprint(X509 *, int); +char ** ssl_x509_names(X509 *); +int ssl_x509_names_match(X509 *, const char *); +char * ssl_x509_names_to_str(X509 *); +char ** ssl_x509_aias(X509 *, const int); +char ** ssl_x509_ocsps(X509 *); +int ssl_x509_is_valid(X509 *); +char * ssl_x509_to_str(X509 *); +char * ssl_x509_to_pem(X509 *); +void ssl_x509_refcount_inc(X509 *); + +int ssl_x509chain_load(X509 **, STACK_OF(X509) **, const char *); +void ssl_x509chain_use(SSL_CTX *, X509 *, STACK_OF(X509) *); + +char * ssl_session_to_str(SSL_SESSION *); +int ssl_session_is_valid(SSL_SESSION *); + +int ssl_is_ocspreq(const unsigned char *, size_t); int ssl_tls_clienthello_parse(const unsigned char *, ssize_t, int, - const unsigned char **, char **) - NONNULL(1,4) WUNRES; -int ssl_dnsname_match(const char *, size_t, const char *, size_t) - NONNULL(1,3) WUNRES; -char * ssl_wildcardify(const char *) NONNULL(1) MALLOC; + const unsigned char **, char **); + +int ssl_dnsname_match(const char *, size_t, const char *, size_t); +char * ssl_wildcardify(const char *); #endif /* !SSL_H */ - -/* vim: set noet ft=c: */ diff --git a/platform/include/internal/stream.h b/platform/include/internal/stream.h new file mode 100644 index 0000000..35d9947 --- /dev/null +++ b/platform/include/internal/stream.h @@ -0,0 +1,126 @@ +#pragma once + +#include +#include + +#include +#include +#include + +struct tfe_thread_ctx +{ + pthread_t thr; + unsigned int thread_id; + size_t load; + + struct event_base * evbase; + unsigned char running; + + struct tfe_stats stat; + struct cert_mgr * cert_mgr; + + struct sess_cache * dsess_cache; + struct sess_cache * ssess_cache; + + unsigned int nr_modules; + const struct tfe_plugin * modules; +}; + +//Downstream: comunication form client to proxy +//Upstream: communication form proxy to server +struct ssl_downstream +{ + /* server name indicated by client in SNI TLS extension */ + char * sni; + SSL * ssl; + X509 * fake_cert_ref;//? + int keyring_id; + struct future * future_sni_peek; + struct future * future_get_cert; +}; + +struct ssl_upstream +{ + X509 * orig_cert; + SSL * ssl; + struct future * conn_ssl_srv; +}; + +enum tfe_plugin_state +{ + PLUG_STATE_READONLY, + PLUG_STATE_PREEPTION, + PLUG_STATE_DETACHED +}; + +struct plugin_ctx +{ + enum tfe_plugin_state state; + void * pme; +}; + +struct tfe_stream_write_ctx +{ + struct tfe_stream_private * _stream; + enum tfe_conn_dir dir; +}; + +struct tfe_conn_private +{ + evutil_socket_t fd; + struct bufferevent * bev; + uint8_t on_writing; + uint8_t closed; + uint8_t need_shutdown; + struct tfe_stream_write_ctx w_ctx; +}; + +struct tfe_stream_private +{ + struct tfe_stream head; + enum tfe_session_proto session_type; + struct tfe_conn_private conn_upstream; + struct tfe_conn_private conn_downstream; + + union + { + struct ssl_downstream * ssl_downstream; + void * raw_downstream; + }; + + union + { + struct ssl_upstream * ssl_upstream; + void * raw_upstream; + }; + + uint8_t is_plugin_opened; + int calling_idx; + size_t forward_bytes; + size_t defere_bytes; + size_t drop_bytes; + enum tfe_app_proto app_proto; + int plugin_num; + struct plugin_ctx * plug_ctx; + unsigned char passthrough; /* 1 if SSL passthrough is active */ + + evutil_socket_t fd_downstream; + evutil_socket_t fd_upstream; + + struct tfe_thread_ctx * thrmgr_ref; + future * async_future; +}; + +struct tfe_stream_private * tfe_stream_create(evutil_socket_t fd_downstream, evutil_socket_t fd_upstream, + enum tfe_session_proto session_type, tfe_thread_ctx * thread); + +void tfe_stream_setup(struct tfe_stream_private * _stream); + +void ssl_async_connect_origin(struct future * future, evutil_socket_t fd, const char * sni, + struct event_base * evbase, struct tfe_config * opts); + +void ssl_async_peek_sni(struct future * future, evutil_socket_t fd, struct event_base * evbase); + +struct ssl_downstream * ssl_downstream_create(); +void ssl_upstream_free(struct ssl_upstream * p); +void ssl_downstream_free(struct ssl_downstream * p); diff --git a/platform/src/Makefile b/platform/src/Makefile deleted file mode 100644 index 3502f9e..0000000 --- a/platform/src/Makefile +++ /dev/null @@ -1,32 +0,0 @@ -VPATH=../inc/ -opt: OPTFLAGS = -O -export OPTFLAGS - -CC = gcc -CCC = g++ -CFLAGS = -Wall -CFLAGS += -g -fPIC -CFLAGS += $(OPTFLAGS) -MESAFramework =-lMESA_htable -lMESA_prof_load -lwiredcfg -lMESA_handle_logger -SYS_LIB =-L./ -lpthread -levent -levent_openssl -levent_pthreads -H_DIR = ./inc_internal/ -OBJS = tfe_main.o proxy.o io_module_kni.o -TARGET = tfe_3a - -.PHONY: all clean deps test opt - -all: $(TARGET) - -opt: - $(MAKE) all -.c.o: - $(CC) -c $(CFLAGS) -I $(H_DIR) $< - -.cpp.o: - $(CCC) -c $(CFLAGS) -I $(H_DIR) $< -clean: - rm -f $(TARGETSLIB) $(TARGETSO) *.o core core.* -$(TARGET): $(OBJS) - $(CC) -o $(TARGET) $(CFLAGS) $^ $(SYS_LIB) $(MESAFramework) - cp $@ ../bin - diff --git a/platform/src/cert.cpp b/platform/src/cert.cpp index 9385e63..93a0a4b 100644 --- a/platform/src/cert.cpp +++ b/platform/src/cert.cpp @@ -1,37 +1,8 @@ -/*- - * SSLsplit - transparent SSL/TLS interception - * https://www.roe.ch/SSLsplit - * - * Copyright (c) 2009-2018, Daniel Roethlisberger . - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS'' - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#include "../include/internal/cert.h" - -#include "ssl.h" - +#include #include +#include +#if 0 /* * Certificate, including private key and certificate chain. */ @@ -338,5 +309,4 @@ void cert_manager_free(cert_t * cert) cert_free(cert); return; } - -/* vim: set noet ft=c: */ +#endif diff --git a/platform/src/future.cpp b/platform/src/future.cpp index 5d86584..3a360f6 100644 --- a/platform/src/future.cpp +++ b/platform/src/future.cpp @@ -1,70 +1,83 @@ -#include "future.h" -struct promise -{ - struct future f; - void * ctx; - promise_ctx_destroy_cb* cb_ctx_destroy; -}; +#include +#include +#include + +#include +#include + struct future { - void * user; - future_success_cb * cb_success; - future_failed_cb * cb_failed; + void * user; + future_success_cb * cb_success; + future_failed_cb * cb_failed; }; -inline struct future* promise_to_future(struct promise* p) +struct promise { - return p->f; -} -inline struct promise* future_to_promise(struct future* f) + struct future f; + void * ctx; + promise_ctx_destroy_cb * cb_ctx_destroy; +}; + +struct future * promise_to_future(struct promise * p) { - return (struct promise*)f; + return &p->f; } -struct future* future_create(future_success_cb * cb_success, future_failed_cb * cb_failed, void * user) + +struct promise * future_to_promise(struct future * f) { - struct promise* p=ALLOC(struct promise, 1); - p->f.user=user; - p->f.cb_success=cb_success; - p->f.cb_failed=cb_failed; - return p->f; + return (struct promise *) f; } + +struct future * future_create(future_success_cb * cb_success, future_failed_cb * cb_failed, void * user) +{ + struct promise * p = ALLOC(struct promise, 1); + p->f.user = user; + p->f.cb_success = cb_success; + p->f.cb_failed = cb_failed; + return &p->f; +} + void future_destroy(struct future * f) { - struct promise* promise=future_to_promise(f); - if(promise->cb_ctx_destroy!=NULL) + struct promise * promise = future_to_promise(f); + if (promise->cb_ctx_destroy != NULL) { promise->cb_ctx_destroy(promise); } - memset(promise,0,sizeof(struct promise)); + + memset(promise, 0, sizeof(struct promise)); free(promise); - return; } -void promise_failed(struct promise* p, enum e_future_error error, const char* what) +void promise_failed(struct promise * p, enum e_future_error error, const char * what) { - p->f.cb_failed(error, what,p->f.user); + p->f.cb_failed(error, what, p->f.user); return; } -void promise_success(struct promise* p, void *result) + +void promise_success(struct promise * p, void * result) { p->f.cb_success(result, p->f.user); return; } -void promise_set_ctx(struct promise* p,void* ctx, promise_ctx_destroy_cb* cb) + +void promise_set_ctx(struct promise * p, void * ctx, promise_ctx_destroy_cb * cb) { - p->ctx=ctx; - p->cb_ctx_destroy=cb; + p->ctx = ctx; + p->cb_ctx_destroy = cb; return; } -void* promise_get_ctx(struct promise* p) + +void * promise_get_ctx(struct promise * p) { return p->ctx; } -void* promise_dettach_ctx(struct promise* p) + +void * promise_dettach_ctx(struct promise * p) { - void* ctx=p->ctx; - p->ctx=NULL; - p->cb_ctx_destroy=NULL; + void * ctx = p->ctx; + p->ctx = NULL; + p->cb_ctx_destroy = NULL; return ctx; } - diff --git a/platform/src/io_module_kni.cpp b/platform/src/kni.cpp similarity index 100% rename from platform/src/io_module_kni.cpp rename to platform/src/kni.cpp diff --git a/platform/src/main.cpp b/platform/src/main.cpp new file mode 100644 index 0000000..f92e192 --- /dev/null +++ b/platform/src/main.cpp @@ -0,0 +1,24 @@ +/*- +* Tango Frontend Engine (TFE) 3a +* Part of Tango Security Gateway +* +* Copyright (c) 2018-2023, MESA Lab, https://www.mesalab.cn +* All rights reserved. +*/ + +#include +#include + +#include +#include +#include +#include + +extern struct tfe_instance __g_tfe_instance; +extern struct tfe_config __g_tfe_config; + +struct tfe_instance* g_tfe_instance = &__g_tfe_instance; +struct tfe_config * g_tfe_cfg = &__g_tfe_config; + +const char* module_name="TFE"; + diff --git a/platform/src/privsep.cc b/platform/src/privsep.cc deleted file mode 100644 index da98251..0000000 --- a/platform/src/privsep.cc +++ /dev/null @@ -1,980 +0,0 @@ -/*- - * SSLsplit - transparent SSL/TLS interception - * https://www.roe.ch/SSLsplit - * - * Copyright (c) 2009-2018, Daniel Roethlisberger . - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS'' - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#include "privsep.h" - -#include "sys.h" -#include "util.h" -#include "compat.h" -#include "attrib.h" -#include "defaults.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -/* - * Privilege separation functionality. - * - * The server code has limitations on the internal functionality that can be - * used, namely only those that are initialized before forking. - */ - -/* maximal message sizes */ -#define PRIVSEP_MAX_REQ_SIZE 512 /* arbitrary limit */ -#define PRIVSEP_MAX_ANS_SIZE (1+sizeof(int)) -/* command byte */ -#define PRIVSEP_REQ_CLOSE 0 /* closing command socket */ -#define PRIVSEP_REQ_OPENFILE 1 /* open content log file */ -#define PRIVSEP_REQ_OPENFILE_P 2 /* open content log file w/mkpath */ -#define PRIVSEP_REQ_OPENSOCK 3 /* open socket and pass fd */ -#define PRIVSEP_REQ_CERTFILE 4 /* open cert file in certgendir */ -/* response byte */ -#define PRIVSEP_ANS_SUCCESS 0 /* success */ -#define PRIVSEP_ANS_UNK_CMD 1 /* unknown command */ -#define PRIVSEP_ANS_INVALID 2 /* invalid message */ -#define PRIVSEP_ANS_DENIED 3 /* request denied */ -#define PRIVSEP_ANS_SYS_ERR 4 /* system error; arg=errno */ - -/* communication with signal handler */ -static volatile sig_atomic_t received_sighup; -static volatile sig_atomic_t received_sigint; -static volatile sig_atomic_t received_sigquit; -static volatile sig_atomic_t received_sigterm; -static volatile sig_atomic_t received_sigchld; -static volatile sig_atomic_t received_sigusr1; -/* write end of pipe used for unblocking select */ -static volatile sig_atomic_t selfpipe_wrfd; - -static void -privsep_server_signal_handler(int sig) -{ - int saved_errno; - -#ifdef DEBUG_PRIVSEP_SERVER - log_dbg_printf("privsep_server_signal_handler\n"); -#endif /* DEBUG_PRIVSEP_SERVER */ - - saved_errno = errno; - switch (sig) { - case SIGHUP: - received_sighup = 1; - break; - case SIGINT: - received_sigint = 1; - break; - case SIGQUIT: - received_sigquit = 1; - break; - case SIGTERM: - received_sigterm = 1; - break; - case SIGCHLD: - received_sigchld = 1; - break; - case SIGUSR1: - received_sigusr1 = 1; - break; - } - if (selfpipe_wrfd != -1) { - ssize_t n; - -#ifdef DEBUG_PRIVSEP_SERVER - log_dbg_printf("writing to selfpipe_wrfd %i\n", selfpipe_wrfd); -#endif /* DEBUG_PRIVSEP_SERVER */ - do { - n = write(selfpipe_wrfd, "!", 1); - } while (n == -1 && errno == EINTR); - if (n == -1) { - log_err_printf("Failed to write from signal handler: " - "%s (%i)\n", strerror(errno), errno); - /* ignore error */ - } -#ifdef DEBUG_PRIVSEP_SERVER - } else { - log_dbg_printf("selfpipe_wrfd is %i - not writing\n", selfpipe_wrfd); -#endif /* DEBUG_PRIVSEP_SERVER */ - } - errno = saved_errno; -} - -static int WUNRES -privsep_server_openfile_verify(tfe_config *opts, char *fn, int mkpath) -{ - if (mkpath && !opts->contentlog_isspec) - return -1; - if (!mkpath && !opts->contentlog_isdir) - return -1; - if (strstr(fn, mkpath ? opts->contentlog_basedir - : opts->contentlog) != fn || - strstr(fn, "/../")) - return -1; - return 0; -} - -static int WUNRES -privsep_server_openfile(char *fn, int mkpath) -{ - int fd; - - if (mkpath) { - char *filedir, *fn2; - - fn2 = strdup(fn); - if (!fn2) { - log_err_printf("Could not duplicate filname: %s (%i)\n", - strerror(errno), errno); - return -1; - } - filedir = dirname(fn2); - if (!filedir) { - log_err_printf("Could not get dirname: %s (%i)\n", - strerror(errno), errno); - free(fn2); - return -1; - } - if (sys_mkpath(filedir, DFLT_DIRMODE) == -1) { - log_err_printf("Could not create directory '%s': %s (%i)\n", - filedir, strerror(errno), errno); - free(fn2); - return -1; - } - free(fn2); - } - - fd = open(fn, O_WRONLY|O_APPEND|O_CREAT, DFLT_FILEMODE); - if (fd == -1) { - log_err_printf("Failed to open '%s': %s (%i)\n", - fn, strerror(errno), errno); - return -1; - } - return fd; -} - -static int WUNRES -privsep_server_opensock_verify(tfe_config *opts, void *arg) -{ - for (proxyspec *spec = opts->spec; spec; spec = spec->next) { - if (spec == arg) - return 0; - } - return 1; -} - -static int WUNRES -privsep_server_opensock(proxyspec *spec) -{ - evutil_socket_t fd; - int on = 1; - int rv; - - fd = socket(spec->listen_addr.ss_family, SOCK_STREAM, IPPROTO_TCP); - if (fd == -1) { - log_err_printf("Error from socket(): %s (%i)\n", - strerror(errno), errno); - evutil_closesocket(fd); - return -1; - } - - rv = evutil_make_socket_nonblocking(fd); - if (rv == -1) { - log_err_printf("Error making socket nonblocking: %s (%i)\n", - strerror(errno), errno); - evutil_closesocket(fd); - return -1; - } - - rv = setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (void*)&on, sizeof(on)); - if (rv == -1) { - log_err_printf("Error from setsockopt(SO_KEEPALIVE): %s (%i)\n", - strerror(errno), errno); - evutil_closesocket(fd); - return -1; - } - - rv = evutil_make_listen_socket_reuseable(fd); - if (rv == -1) { - log_err_printf("Error from setsockopt(SO_REUSABLE): %s\n", - strerror(errno)); - evutil_closesocket(fd); - return -1; - } - - if (spec->natsocket && (spec->natsocket(fd) == -1)) { - log_err_printf("Error from spec->natsocket()\n"); - evutil_closesocket(fd); - return -1; - } - - rv = bind(fd, (struct sockaddr *)&spec->listen_addr, - spec->listen_addrlen); - if (rv == -1) { - log_err_printf("Error from bind(): %s\n", strerror(errno)); - evutil_closesocket(fd); - return -1; - } - - return fd; -} - -static int WUNRES -privsep_server_certfile_verify(tfe_config *opts, char *fn) -{ - if (!opts->certgendir) - return -1; - if (strstr(fn, opts->certgendir) != fn || strstr(fn, "/../")) - return -1; - return 0; -} - -static int WUNRES -privsep_server_certfile(char *fn) -{ - int fd; - - fd = open(fn, O_WRONLY|O_CREAT|O_EXCL, DFLT_FILEMODE); - if (fd == -1 && errno != EEXIST) { - log_err_printf("Failed to open '%s': %s (%i)\n", - fn, strerror(errno), errno); - return -1; - } - return fd; -} - -/* - * Handle a single request on a readable server socket. - * Returns 0 on success, 1 on EOF and -1 on error. - */ -static int WUNRES -privsep_server_handle_req(tfe_config *opts, int srvsock) -{ - char req[PRIVSEP_MAX_REQ_SIZE]; - char ans[PRIVSEP_MAX_ANS_SIZE]; - ssize_t n; - int mkpath = 0; - - if ((n = sys_recvmsgfd(srvsock, req, sizeof(req), - NULL)) == -1) { - if (errno == EPIPE || errno == ECONNRESET) { - /* unfriendly EOF, leave server */ - return 1; - } - log_err_printf("Failed to receive msg: %s (%i)\n", - strerror(errno), errno); - return -1; - } - if (n == 0) { - /* EOF, leave server; will not happen for SOCK_DGRAM sockets */ - return 1; - } - log_dbg_printf("Received privsep req type %02x sz %zd on srvsock %i\n", - req[0], n, srvsock); - switch (req[0]) { - case PRIVSEP_REQ_CLOSE: { - /* client indicates EOF through close message */ - return 1; - } - case PRIVSEP_REQ_OPENFILE_P: - mkpath = 1; - case PRIVSEP_REQ_OPENFILE: { - char *fn; - int fd; - - if (n < 2) { - ans[0] = PRIVSEP_ANS_INVALID; - if (sys_sendmsgfd(srvsock, ans, 1, -1) == -1) { - log_err_printf("Sending message failed: %s (%i" - ")\n", strerror(errno), errno); - return -1; - } - } - if (!(fn = malloc(n))) { - ans[0] = PRIVSEP_ANS_SYS_ERR; - *((int*)&ans[1]) = errno; - if (sys_sendmsgfd(srvsock, ans, 1 + sizeof(int), - -1) == -1) { - log_err_printf("Sending message failed: %s (%i" - ")\n", strerror(errno), errno); - return -1; - } - return 0; - } - memcpy(fn, req + 1, n - 1); - fn[n - 1] = '\0'; - if (privsep_server_openfile_verify(opts, fn, mkpath) == -1) { - free(fn); - ans[0] = PRIVSEP_ANS_DENIED; - if (sys_sendmsgfd(srvsock, ans, 1, -1) == -1) { - log_err_printf("Sending message failed: %s (%i" - ")\n", strerror(errno), errno); - return -1; - } - return 0; - } - if ((fd = privsep_server_openfile(fn, mkpath)) == -1) { - free(fn); - ans[0] = PRIVSEP_ANS_SYS_ERR; - *((int*)&ans[1]) = errno; - if (sys_sendmsgfd(srvsock, ans, 1 + sizeof(int), - -1) == -1) { - log_err_printf("Sending message failed: %s (%i" - ")\n", strerror(errno), errno); - return -1; - } - return 0; - } else { - free(fn); - ans[0] = PRIVSEP_ANS_SUCCESS; - if (sys_sendmsgfd(srvsock, ans, 1, fd) == -1) { - close(fd); - log_err_printf("Sending message failed: %s (%i" - ")\n", strerror(errno), errno); - return -1; - } - close(fd); - return 0; - } - /* not reached */ - break; - } - case PRIVSEP_REQ_OPENSOCK: { - proxyspec *arg; - int s; - - if (n != sizeof(char) + sizeof(arg)) { - ans[0] = PRIVSEP_ANS_INVALID; - if (sys_sendmsgfd(srvsock, ans, 1, -1) == -1) { - log_err_printf("Sending message failed: %s (%i" - ")\n", strerror(errno), errno); - return -1; - } - return 0; - } - arg = *(proxyspec**)(&req[1]); - if (privsep_server_opensock_verify(opts, arg) == -1) { - ans[0] = PRIVSEP_ANS_DENIED; - if (sys_sendmsgfd(srvsock, ans, 1, -1) == -1) { - log_err_printf("Sending message failed: %s (%i" - ")\n", strerror(errno), errno); - return -1; - } - return 0; - } - if ((s = privsep_server_opensock(arg)) == -1) { - ans[0] = PRIVSEP_ANS_SYS_ERR; - *((int*)&ans[1]) = errno; - if (sys_sendmsgfd(srvsock, ans, 1 + sizeof(int), - -1) == -1) { - log_err_printf("Sending message failed: %s (%i" - ")\n", strerror(errno), errno); - return -1; - } - return 0; - } else { - ans[0] = PRIVSEP_ANS_SUCCESS; - if (sys_sendmsgfd(srvsock, ans, 1, s) == -1) { - evutil_closesocket(s); - log_err_printf("Sending message failed: %s (%i" - ")\n", strerror(errno), errno); - return -1; - } - evutil_closesocket(s); - return 0; - } - /* not reached */ - break; - } - case PRIVSEP_REQ_CERTFILE: { - char *fn; - int fd; - - if (n < 2) { - ans[0] = PRIVSEP_ANS_INVALID; - if (sys_sendmsgfd(srvsock, ans, 1, -1) == -1) { - log_err_printf("Sending message failed: %s (%i" - ")\n", strerror(errno), errno); - return -1; - } - } - if (!(fn = malloc(n))) { - ans[0] = PRIVSEP_ANS_SYS_ERR; - *((int*)&ans[1]) = errno; - if (sys_sendmsgfd(srvsock, ans, 1 + sizeof(int), - -1) == -1) { - log_err_printf("Sending message failed: %s (%i" - ")\n", strerror(errno), errno); - return -1; - } - return 0; - } - memcpy(fn, req + 1, n - 1); - fn[n - 1] = '\0'; - if (privsep_server_certfile_verify(opts, fn) == -1) { - free(fn); - ans[0] = PRIVSEP_ANS_DENIED; - if (sys_sendmsgfd(srvsock, ans, 1, -1) == -1) { - log_err_printf("Sending message failed: %s (%i" - ")\n", strerror(errno), errno); - return -1; - } - return 0; - } - if ((fd = privsep_server_certfile(fn)) == -1) { - free(fn); - ans[0] = PRIVSEP_ANS_SYS_ERR; - *((int*)&ans[1]) = errno; - if (sys_sendmsgfd(srvsock, ans, 1 + sizeof(int), - -1) == -1) { - log_err_printf("Sending message failed: %s (%i" - ")\n", strerror(errno), errno); - return -1; - } - return 0; - } else { - free(fn); - ans[0] = PRIVSEP_ANS_SUCCESS; - if (sys_sendmsgfd(srvsock, ans, 1, fd) == -1) { - close(fd); - log_err_printf("Sending message failed: %s (%i" - ")\n", strerror(errno), errno); - return -1; - } - close(fd); - return 0; - } - /* not reached */ - break; - } - default: - ans[0] = PRIVSEP_ANS_UNK_CMD; - if (sys_sendmsgfd(srvsock, ans, 1, -1) == -1) { - log_err_printf("Sending message failed: %s (%i" - ")\n", strerror(errno), errno); - return -1; - } - } - return 0; -} - -/* - * Privilege separation server (main privileged monitor loop) - * - * sigpipe is the self-pipe trick pipe used for communicating signals to - * the main event loop and break out of select() without race conditions. - * srvsock[] is a dynamic array of connected privsep server sockets to serve. - * Caller is responsible for freeing memory after returning, if necessary. - * childpid is the pid of the child process to forward signals to. - * - * Returns 0 on a successful clean exit and -1 on errors. - */ -static int -privsep_server(tfe_config *opts, int sigpipe, int srvsock[], size_t nsrvsock, - pid_t childpid) -{ - int srveof[nsrvsock]; - size_t i = 0; - - for (i = 0; i < nsrvsock; i++) { - srveof[i] = 0; - } - - for (;;) { - fd_set readfds; - int maxfd, rv; - -#ifdef DEBUG_PRIVSEP_SERVER - log_dbg_printf("privsep_server select()\n"); -#endif /* DEBUG_PRIVSEP_SERVER */ - do { - FD_ZERO(&readfds); - FD_SET(sigpipe, &readfds); - maxfd = sigpipe; - for (i = 0; i < nsrvsock; i++) { - if (!srveof[i]) { - FD_SET(srvsock[i], &readfds); - maxfd = util_max(maxfd, srvsock[i]); - } - } - rv = select(maxfd + 1, &readfds, NULL, NULL, NULL); -#ifdef DEBUG_PRIVSEP_SERVER - log_dbg_printf("privsep_server woke up (1)\n"); -#endif /* DEBUG_PRIVSEP_SERVER */ - } while (rv == -1 && errno == EINTR); - if (rv == -1) { - log_err_printf("select() failed: %s (%i)\n", - strerror(errno), errno); - return -1; - } -#ifdef DEBUG_PRIVSEP_SERVER - log_dbg_printf("privsep_server woke up (2)\n"); -#endif /* DEBUG_PRIVSEP_SERVER */ - - if (FD_ISSET(sigpipe, &readfds)) { - char buf[16]; - ssize_t n; - /* first drain the signal pipe, then deal with - * all the individual signal flags */ - n = read(sigpipe, buf, sizeof(buf)); - if (n == -1) { - log_err_printf("read(sigpipe) failed:" - " %s (%i)\n", - strerror(errno), errno); - return -1; - } - if (received_sigquit) { - if (kill(childpid, SIGQUIT) == -1) { - log_err_printf("kill(%i,SIGQUIT) " - "failed: %s (%i)\n", - childpid, - strerror(errno), errno); - } - received_sigquit = 0; - } - if (received_sigterm) { - if (kill(childpid, SIGTERM) == -1) { - log_err_printf("kill(%i,SIGTERM) " - "failed: %s (%i)\n", - childpid, - strerror(errno), errno); - } - received_sigterm = 0; - } - if (received_sighup) { - if (kill(childpid, SIGHUP) == -1) { - log_err_printf("kill(%i,SIGHUP) " - "failed: %s (%i)\n", - childpid, - strerror(errno), errno); - } - received_sighup = 0; - } - if (received_sigusr1) { - if (kill(childpid, SIGUSR1) == -1) { - log_err_printf("kill(%i,SIGUSR1) " - "failed: %s (%i)\n", - childpid, - strerror(errno), errno); - } - received_sigusr1 = 0; - } - if (received_sigint) { - /* if we don't detach from the TTY, the - * child process receives SIGINT directly */ - if (opts->detach) { - if (kill(childpid, SIGINT) == -1) { - log_err_printf("kill(%i,SIGINT" - ") failed: " - "%s (%i)\n", - childpid, - strerror(errno), - errno); - } - } - received_sigint = 0; - } - if (received_sigchld) { - /* break the loop; because we are using - * SOCKET_DGRAM we don't get EOF conditions - * on the disconnected socket ends here - * unless we attempt to write or read, so - * we depend on SIGCHLD to notify us of - * our child erroring out or crashing */ - break; - } - } - - for (i = 0; i < nsrvsock; i++) { - if (FD_ISSET(srvsock[i], &readfds)) { - int rv = privsep_server_handle_req(opts, - srvsock[i]); - if (rv == -1) { - log_err_printf("Failed to handle " - "privsep req " - "on srvsock %i\n", - srvsock[i]); - return -1; - } - if (rv == 1) { -#ifdef DEBUG_PRIVSEP_SERVER - log_dbg_printf("srveof[%zu]=1\n", i); -#endif /* DEBUG_PRIVSEP_SERVER */ - srveof[i] = 1; - } - } - } - - /* - * We cannot exit as long as we need the signal handling, - * which is as long as the child process is running. - * The only way out of here is receiving SIGCHLD. - */ - } - - return 0; -} - -int -privsep_client_openfile(int clisock, const char *fn, int mkpath) -{ - char ans[PRIVSEP_MAX_ANS_SIZE]; - char req[1 + strlen(fn)]; - int fd = -1; - ssize_t n; - - req[0] = mkpath ? PRIVSEP_REQ_OPENFILE_P : PRIVSEP_REQ_OPENFILE; - memcpy(req + 1, fn, sizeof(req) - 1); - - if (sys_sendmsgfd(clisock, req, sizeof(req), -1) == -1) { - return -1; - } - - if ((n = sys_recvmsgfd(clisock, ans, sizeof(ans), &fd)) == -1) { - return -1; - } - - if (n < 1) { - errno = EINVAL; - return -1; - } - - switch (ans[0]) { - case PRIVSEP_ANS_SUCCESS: - break; - case PRIVSEP_ANS_DENIED: - errno = EACCES; - return -1; - case PRIVSEP_ANS_SYS_ERR: - if (n < (ssize_t)(1 + sizeof(int))) { - errno = EINVAL; - return -1; - } - errno = *((int*)&ans[1]); - return -1; - case PRIVSEP_ANS_UNK_CMD: - case PRIVSEP_ANS_INVALID: - default: - errno = EINVAL; - return -1; - } - - return fd; -} - -int -privsep_client_opensock(int clisock, const proxyspec *spec) -{ - char ans[PRIVSEP_MAX_ANS_SIZE]; - char req[1 + sizeof(spec)]; - int fd = -1; - ssize_t n; - - req[0] = PRIVSEP_REQ_OPENSOCK; - *((const proxyspec **)&req[1]) = spec; - - if (sys_sendmsgfd(clisock, req, sizeof(req), -1) == -1) { - return -1; - } - - if ((n = sys_recvmsgfd(clisock, ans, sizeof(ans), &fd)) == -1) { - return -1; - } - - if (n < 1) { - errno = EINVAL; - return -1; - } - - switch (ans[0]) { - case PRIVSEP_ANS_SUCCESS: - break; - case PRIVSEP_ANS_DENIED: - errno = EACCES; - return -1; - case PRIVSEP_ANS_SYS_ERR: - if (n < (ssize_t)(1 + sizeof(int))) { - errno = EINVAL; - return -1; - } - errno = *((int*)&ans[1]); - return -1; - case PRIVSEP_ANS_UNK_CMD: - case PRIVSEP_ANS_INVALID: - default: - errno = EINVAL; - return -1; - } - - return fd; -} - -int -privsep_client_certfile(int clisock, const char *fn) -{ - char ans[PRIVSEP_MAX_ANS_SIZE]; - char req[1 + strlen(fn)]; - int fd = -1; - ssize_t n; - - req[0] = PRIVSEP_REQ_CERTFILE; - memcpy(req + 1, fn, sizeof(req) - 1); - - if (sys_sendmsgfd(clisock, req, sizeof(req), -1) == -1) { - return -1; - } - - if ((n = sys_recvmsgfd(clisock, ans, sizeof(ans), &fd)) == -1) { - return -1; - } - - if (n < 1) { - errno = EINVAL; - return -1; - } - - switch (ans[0]) { - case PRIVSEP_ANS_SUCCESS: - break; - case PRIVSEP_ANS_DENIED: - errno = EACCES; - return -1; - case PRIVSEP_ANS_SYS_ERR: - if (n < (ssize_t)(1 + sizeof(int))) { - errno = EINVAL; - return -1; - } - errno = *((int*)&ans[1]); - return -1; - case PRIVSEP_ANS_UNK_CMD: - case PRIVSEP_ANS_INVALID: - default: - errno = EINVAL; - return -1; - } - - return fd; -} - -int -privsep_client_close(int clisock) -{ - char req[1]; - - req[0] = PRIVSEP_REQ_CLOSE; - - if (sys_sendmsgfd(clisock, req, sizeof(req), -1) == -1) { - close(clisock); - return -1; - } - - close(clisock); - return 0; -} - -/* - * Fork and set up privilege separated monitor process. - * Returns -1 on error before forking, 1 as parent, or 0 as child. - * The array of clisock's will get filled with nclisock privsep client - * sockets only for the child; on error and in the parent process it - * will not be touched. - */ -int -privsep_fork(tfe_config *opts, int clisock[], size_t nclisock) -{ - int selfpipev[2]; /* self-pipe trick: signal handler -> select */ - int chldpipev[2]; /* el cheapo interprocess sync early after fork */ - int sockcliv[nclisock][2]; - pid_t pid; - - received_sigquit = 0; - received_sighup = 0; - received_sigint = 0; - received_sigchld = 0; - received_sigusr1 = 0; - - if (pipe(selfpipev) == -1) { - log_err_printf("Failed to create self-pipe: %s (%i)\n", - strerror(errno), errno); - return -1; - } - log_dbg_printf("Created self-pipe [r=%i,w=%i]\n", - selfpipev[0], selfpipev[1]); - - if (pipe(chldpipev) == -1) { - log_err_printf("Failed to create chld-pipe: %s (%i)\n", - strerror(errno), errno); - return -1; - } - log_dbg_printf("Created chld-pipe [r=%i,w=%i]\n", - chldpipev[0], chldpipev[1]); - - for (size_t i = 0; i < nclisock; i++) { - if (socketpair(AF_UNIX, SOCK_DGRAM, 0, sockcliv[i]) == -1) { - log_err_printf("Failed to create socket pair %zu: " - "%s (%i)\n", i, strerror(errno), errno); - return -1; - } - log_dbg_printf("Created socketpair %zu [p=%i,c=%i]\n", - i, sockcliv[i][0], sockcliv[i][1]); - } - - pid = fork(); - if (pid == -1) { - log_err_printf("Failed to fork: %s (%i)\n", - strerror(errno), errno); - close(selfpipev[0]); - close(selfpipev[1]); - close(chldpipev[0]); - close(chldpipev[1]); - for (size_t i = 0; i < nclisock; i++) { - close(sockcliv[i][0]); - close(sockcliv[i][1]); - } - return -1; - } else if (pid == 0) { - /* child */ - close(selfpipev[0]); - close(selfpipev[1]); - for (size_t i = 0; i < nclisock; i++) - close(sockcliv[i][0]); - /* wait until parent has installed signal handlers, - * intentionally ignoring errors */ - char buf[1]; - ssize_t n; - close(chldpipev[1]); - do { - n = read(chldpipev[0], buf, sizeof(buf)); - } while (n == -1 && errno == EINTR); - close(chldpipev[0]); - - /* return the privsep client sockets */ - for (size_t i = 0; i < nclisock; i++) - clisock[i] = sockcliv[i][1]; - return 0; - } - /* parent */ - for (size_t i = 0; i < nclisock; i++) - close(sockcliv[i][1]); - selfpipe_wrfd = selfpipev[1]; - - /* close file descriptors opened by preinit's only needed in client; - * we still call the preinit's before forking in order to provide - * better user feedback and less privsep complexity */ - nat_preinit_undo(); - - /* If the child exits before the parent installs the signal handler - * here, we have a race condition; this is solved by the client - * blocking on the reading end of a pipe (chldpipev[0]). */ - if (signal(SIGHUP, privsep_server_signal_handler) == SIG_ERR) { - log_err_printf("Failed to install SIGHUP handler: %s (%i)\n", - strerror(errno), errno); - return -1; - } - if (signal(SIGINT, privsep_server_signal_handler) == SIG_ERR) { - log_err_printf("Failed to install SIGINT handler: %s (%i)\n", - strerror(errno), errno); - return -1; - } - if (signal(SIGTERM, privsep_server_signal_handler) == SIG_ERR) { - log_err_printf("Failed to install SIGTERM handler: %s (%i)\n", - strerror(errno), errno); - return -1; - } - if (signal(SIGQUIT, privsep_server_signal_handler) == SIG_ERR) { - log_err_printf("Failed to install SIGQUIT handler: %s (%i)\n", - strerror(errno), errno); - return -1; - } - if (signal(SIGUSR1, privsep_server_signal_handler) == SIG_ERR) { - log_err_printf("Failed to install SIGUSR1 handler: %s (%i)\n", - strerror(errno), errno); - return -1; - } - if (signal(SIGCHLD, privsep_server_signal_handler) == SIG_ERR) { - log_err_printf("Failed to install SIGCHLD handler: %s (%i)\n", - strerror(errno), errno); - return -1; - } - - /* unblock the child */ - close(chldpipev[0]); - close(chldpipev[1]); - - int socksrv[nclisock]; - for (size_t i = 0; i < nclisock; i++) - socksrv[i] = sockcliv[i][0]; - if (privsep_server(opts, selfpipev[0], socksrv, nclisock, pid) == -1) { - log_err_printf("Privsep server failed: %s (%i)\n", - strerror(errno), errno); - /* fall through */ - } -#ifdef DEBUG_PRIVSEP_SERVER - log_dbg_printf("privsep_server exited\n"); -#endif /* DEBUG_PRIVSEP_SERVER */ - - for (size_t i = 0; i < nclisock; i++) - close(sockcliv[i][0]); - selfpipe_wrfd = -1; /* tell signal handler not to write anymore */ - close(selfpipev[0]); - close(selfpipev[1]); - - int status; - wait(&status); - if (WIFEXITED(status)) { - if (WEXITSTATUS(status) != 0) { - log_err_printf("Child proc %lld exited with status %d\n", - (long long)pid, WEXITSTATUS(status)); - } else { - log_dbg_printf("Child proc %lld exited with status %d\n", - (long long)pid, WEXITSTATUS(status)); - } - } else if (WIFSIGNALED(status)) { - log_err_printf("Child proc %lld killed by signal %d\n", - (long long)pid, WTERMSIG(status)); - } else { - log_err_printf("Child proc %lld neither exited nor killed\n", - (long long)pid); - } - - return 1; -} - -/* vim: set noet ft=c: */ - - - diff --git a/platform/src/proxy.cpp b/platform/src/proxy.cpp new file mode 100644 index 0000000..10e2a3f --- /dev/null +++ b/platform/src/proxy.cpp @@ -0,0 +1,294 @@ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +/* + * Proxy engine, built around libevent 2.x. + */ + +#define TFE_BACKLOG_DEFAULT 20 + +const char * module_name_pxy = "TFE_PXY"; +extern struct tfe_instance * g_tfe_instance; + +__thread int __currect_thread_id; + +static void __dummy_event_handler(evutil_socket_t fd, short what, void * arg) +{ + return; +} + +/* + * Thread entry point; runs the event loop of the event base. + * Does not exit until the libevent loop is broken explicitly. + */ +static void * __tfe_thrmgr_thread_entry(void * arg) +{ + struct tfe_thread_ctx * ctx = (struct tfe_thread_ctx *) arg; + struct timeval timer_delay = {60, 0}; + + struct event * ev; + ev = event_new(ctx->evbase, -1, EV_PERSIST, __dummy_event_handler, NULL); + + if (!ev) return (void *)NULL; + + evtimer_add(ev, &timer_delay); + ctx->running = 1; + + __currect_thread_id = ctx->thread_id; + event_base_dispatch(ctx->evbase); + event_free(ev); + + return (void *)NULL; +} + +static int signals[] = {SIGTERM, SIGQUIT, SIGHUP, SIGINT, SIGPIPE, SIGUSR1}; + +struct tfe_proxy +{ + char name[TFE_SYMBOL_MAX]; + struct event_base * evbase; + struct event * sev[sizeof(signals) / sizeof(int)]; + struct event * gcev; + + struct tfe_config * opts; + void * main_logger; + + struct sess_cache * dsess_cache; + struct sess_cache * ssess_cache; + + unsigned int nr_work_threads; + struct tfe_thread_ctx * work_threads; + + unsigned int nr_modules; + struct tfe_plugin * modules; + + void * io_mod; +}; + +/* + * Signal handler for SIGTERM, SIGQUIT, SIGINT, SIGHUP, SIGPIPE and SIGUSR1. + */ +static void proxy_signal_cb(evutil_socket_t fd, short what, void * arg) +{ + tfe_proxy * ctx = (tfe_proxy *) arg; + switch (fd) + { + case SIGTERM: + case SIGQUIT: + case SIGINT: + case SIGHUP: + break; + case SIGUSR1: + break; + case SIGPIPE: + TFE_LOG_ERROR("Warning: Received SIGPIPE; ignoring.\n"); + break; + default: + TFE_LOG_ERROR("Warning: Received unexpected signal %i\n", fd); + break; + } +} + +static void proxy_gc_cb(evutil_socket_t fd, short what, void * arg) +{ + tfe_proxy * ctx = (tfe_proxy *) arg; + (void)fd; + (void)what; +} + +unsigned int select_work_thread(struct tfe_proxy * pxy) +{ + unsigned int min_thread_id = 0; + size_t min_load = pxy->work_threads[min_thread_id].load; + + for (unsigned thread_id = 1; thread_id < pxy->nr_work_threads; thread_id++) + { + if (min_load > pxy->work_threads[thread_id].load) + { + min_load = pxy->work_threads[thread_id].load; + min_thread_id = thread_id; + } + } + + pxy->work_threads[min_thread_id].load++; + return min_thread_id; +} +/* + * Callback for accept events on the socket listener bufferevent. + */ +static void io_mod_accept_cb(evutil_socket_t upstream_fd, evutil_socket_t downstream_fd, + enum tfe_session_proto session_type, void * arg) +{ + struct tfe_proxy * pxy = (struct tfe_proxy *) arg; + + unsigned int worker_tid = select_work_thread(pxy); + tfe_thread_ctx * worker_thread_ctx = &pxy->work_threads[worker_tid]; + + struct tfe_stream_private * stream = tfe_stream_create(upstream_fd, + downstream_fd, session_type, worker_thread_ctx); + + assert(stream != NULL); + return tfe_stream_setup(stream); +} + +/* + * Set up the core event loop. + * Socket clisock is the privsep client socket used for binding to ports. + * Returns ctx on success, or NULL on error. + */ +struct tfe_proxy * tfe_proxy_new(tfe_config * cfg) +{ + struct tfe_proxy * proxy = ALLOC(struct tfe_proxy, 1); + assert(proxy != NULL); + + struct timeval gc_delay = {60, 0}; + + /* adds locking, only required if accessed from separate threads */ + evthread_use_pthreads(); + event_enable_debug_mode(); + + proxy->evbase = event_base_new(); + proxy->dsess_cache = session_cache_init(); + proxy->ssess_cache = session_cache_init(); + + proxy->nr_modules = 2; + proxy->modules = ALLOC(struct tfe_plugin, proxy->nr_modules); + + proxy->modules[0].proto = APP_PROTO_HTTP1; + proxy->modules[1].proto = APP_PROTO_HTTP2; + + proxy->work_threads = ALLOC(struct tfe_thread_ctx, proxy->nr_work_threads); + + for (unsigned int i = 0; i < proxy->nr_work_threads; i++) + { + proxy->work_threads[i].thread_id = i; + proxy->work_threads[i].evbase = event_base_new(); + proxy->work_threads[i].dsess_cache = proxy->dsess_cache; + proxy->work_threads[i].ssess_cache = proxy->ssess_cache; + proxy->work_threads[i].nr_modules = proxy->nr_modules; + proxy->work_threads[i].modules = proxy->modules; + } + + //Todo: Not handle signal if have mutliple proxy instance. + for (size_t i = 0; i < (sizeof(signals) / sizeof(int)); i++) + { + proxy->sev[i] = evsignal_new(proxy->evbase, signals[i], proxy_signal_cb, proxy); + if (!proxy->sev[i]) goto error_out; + evsignal_add(proxy->sev[i], NULL); + } + + proxy->gcev = event_new(proxy->evbase, -1, EV_PERSIST, proxy_gc_cb, proxy); + if (!proxy->gcev) + goto error_out; + + evtimer_add(proxy->gcev, &gc_delay); + return proxy; + +error_out: + if (proxy->gcev) + { + event_free(proxy->gcev); + } + + for (size_t i = 0; i < (sizeof(proxy->sev) / sizeof(proxy->sev[0])); i++) + { + if (proxy->sev[i]) + { + event_free(proxy->sev[i]); + } + } + + for (typeof(proxy->nr_work_threads) i = 0; i < proxy->nr_work_threads; i++) + { + proxy->work_threads[i].thread_id = i; + event_base_free(proxy->work_threads[i].evbase); + } + + event_base_free(proxy->evbase); + + free(proxy); + return NULL; +} + +/* + * Run the event loop. Returns when the event loop is cancelled by a signal + * or on failure. + */ +void tfe_proxy_run(struct tfe_proxy * proxy) +{ + unsigned int thread_id; + for (thread_id = 0; thread_id < proxy->nr_work_threads; thread_id++) + { + if (pthread_create(&(proxy->work_threads[thread_id].thr), NULL, + __tfe_thrmgr_thread_entry, &(proxy->work_threads[thread_id]))) + { + MESA_handle_runtime_log(proxy->main_logger, RLOG_LV_FATAL, proxy->name, "pthread_create failed."); + } + + while (!proxy->work_threads[thread_id].running) + { + sched_yield(); + } + } + + event_base_dispatch(proxy->evbase); +} + +/* + * Break the loop of the proxy, causing the tfe_proxy_run to return. + */ +void proxy_loopbreak(tfe_proxy * ctx) +{ + event_base_loopbreak(ctx->evbase); +} + +/* + * Free the proxy data structures. + */ +void proxy_free(tfe_proxy * ctx) +{ +} + + +int main(int argc, char *argv[]) +{ + const char* main_profile="./conf/tfe_main.conf"; + + tfe_proxy *proxy=NULL; + void* wcfg_handle=NULL; + + //TODO: Initiate Local Cert Cache, Decryption Mirror, Field Stat, Logger and etc. + //NOTICE: Maat, Cert Store,Tango Cache are initiated in bussiness plugin. + +#if 0 + g_tfe_instance=ALLOC(struct tfe_instance,1); + proxy=tfe_proxy_new(g_tfe_cfg); +#endif + + tfe_proxy_run(proxy); + proxy_free(proxy); +} diff --git a/platform/src/session_cache.cpp b/platform/src/session_cache.cpp deleted file mode 100644 index 1db2e4a..0000000 --- a/platform/src/session_cache.cpp +++ /dev/null @@ -1,14 +0,0 @@ - -struct sess_cache -{ -}; -struct sess_cache* session_cache_init() -{ - -} - -void session_cache_set(struct sess_cache* cache, struct sockaddr *addr, socklen_t addrlen, const char* sni,SSL_SESSION* session) -{ - -} - diff --git a/platform/src/ssl_stream.cpp b/platform/src/ssl_stream.cpp index 685fc74..13c2e61 100644 --- a/platform/src/ssl_stream.cpp +++ b/platform/src/ssl_stream.cpp @@ -1,903 +1,919 @@ -#include "ssl.h" - - -/* forward declaration of OpenSSL callbacks */ -#ifndef OPENSSL_NO_TLSEXT -static int pxy_ossl_servername_cb(SSL * ssl, int * al, void * arg); -#endif /* !OPENSSL_NO_TLSEXT */ -static int pxy_ossl_sessnew_cb(SSL *, SSL_SESSION *); -static void pxy_ossl_sessremove_cb(SSL_CTX *, SSL_SESSION *); -#if OPENSSL_VERSION_NUMBER < 0x10100000L -static SSL_SESSION * pxy_ossl_sessget_cb(SSL *, unsigned char *, int, int *); -#else /* OPENSSL_VERSION_NUMBER >= 0x10100000L */ -static SSL_SESSION * pxy_ossl_sessget_cb(SSL *, const unsigned char *, int, - int *); -#endif /* OPENSSL_VERSION_NUMBER >= 0x10100000L */ - -struct peek_sni_ctx -{ - /* ssl */ - unsigned char sni_peek_retries; /* max 64 SNI parse retries */ - char* sni; - struct event* ev; - struct event_base* evbase; - -}; -void peek_sni_ctx_free(void* ctx) -{ - struct peek_sni_ctx * _ctx=(struct peek_sni_ctx *)ctx; - event_free(_ctx->ev); - _ctx->ev = NULL; - free(_ctx->sni); - _ctx->sni=NULL; - free(_ctx); - return; -} - -static void peek_sni_cb(evutil_socket_t fd, short what, void * arg) -{ - struct promise* promise=(struct promise*)arg; - struct peek_sni_ctx* ctx= (struct peek_sni_ctx*)promise->ctx; - - unsigned char buf[1024]; - ssize_t n=0; - const unsigned char * chello=NULL; - int rv=0; - - n = recv(fd, buf, sizeof(buf), MSG_PEEK); - if (n == -1) - { - log_err_printf("Error peeking on fd, aborting connection\n"); - goto promise_failed; - } - - if (n == 0) - { - goto promise_failed; - } - - rv = ssl_tls_clienthello_parse(buf, n, 0, &chello, &ctx->sni); - if ((rv == 1) && !chello) - { - log_err_printf("Peeking did not yield a (truncated) ClientHello message, aborting connection\n"); - goto promise_failed; - } - - if ((rv == 1) && chello && (ctx->sni_peek_retries++ < 50)) - { - /* ssl_tls_clienthello_parse indicates that we - * should retry later when we have more data, and we - * haven't reached the maximum retry count yet. - * Reschedule this event as timeout-only event in - * order to prevent busy looping over the read event. - * Because we only peeked at the pending bytes and - * never actually read them, fd is still ready for - * reading now. We use 25 * 0.2 s = 5 s timeout. */ - struct timeval retry_delay = {0, 100}; - - event_free(ctx->ev); - ctx->ev = event_new(ctx->evbase, fd, 0, peek_sni_cb, promise); - assert(ctx->ev!=NULL); - event_add(ctx->ev, &retry_delay); - return; - } - promise_set_ctx(promise, NULL, NULL); - promise->f.cb_success(ctx->sni,promise->f.user); - peek_sni_ctx_free(ctx); - - return; - -promise_failed: - promise->f.cb_failed(FUTURE_ERROR_EXCEPTION,"too many tries",promise->f.user); - peek_sni_ctx_free(ctx); - promise_set_ctx(promise, NULL,NULL); - return; - -} - -void ssl_async_peek_sni( struct future* future, evutil_socket_t fd, struct event_base *evbase) -{ - struct event * ev=NULL; - struct promise* p=future_to_promise(future); - struct peek_sni_ctx* ctx=ALLOC(struct peek_sni_ctx, 1); - ctx->ev = event_new(evbase, fd, EV_READ, peek_sni_cb, p); - event_add(evbase, NULL); - promise_set_ctx(p, ctx,peek_sni_ctx_free); - return; -} - -struct ssl_connect_origin_ctx -{ - struct bufferevent *bev; - SSL* ssl; - -}; -void ssl_connect_origin_ctx_free(struct ssl_connect_origin_ctx* ctx) -{ - ctx->ssl=NULL; - bufferevent_free(ctx->bev); - ctx->bev=NULL; - //Do not free bev and ssl, reserved for user. - free(ctx); -} -/* - * Callback for meta events on the up- and downstream connection bufferevents. - * Called when EOF has been reached, a connection has been made, and on errors. - */ -static void ssl_connect_origin_eventcb(struct bufferevent * bev, short events, void * arg) -{ - struct promise* promise=(struct promise*)arg; - struct ssl_connect_origin_ctx* ctx=(struct ssl_connect_origin_ctx*)promise_dettach_ctx(promise); - - struct tfe_stream_private* _stream = (struct tfe_stream_private*)arg; - - if (events & BEV_EVENT_ERROR) - { - promise->f.cb_failed(FUTURE_ERROR_EXCEPTION,"ssl connect failed",promise->f.user); - } - else - { - if (events & BEV_EVENT_CONNECTED) - { - promise->f.cb_success(ctx->bev,promise->f.user); - ctx->bev=NULL; - } - - else - { - assert(0); - } - } - ssl_connect_origin_ctx_free(ctx); - return -} - -void ssl_async_connect_origin(struct future* future, evutil_socket_t fd, const char* sni, struct event_base *evbase, struct tfe_config *opts) -{ - struct promise* p=future_to_promise(future); - struct ssl_connect_origin_ctx* ctx=ALLOC(struct ssl_connect_origin_ctx, 1); - ctx->ssl=upstream_ssl_create(opts,sni); - ctx->bev = bufferevent_openssl_socket_new(evbase, fd, ctx->ssl, BUFFEREVENT_SSL_CONNECTING, BEV_OPT_DEFER_CALLBACKS); - promise_set_ctx(p, ctx, ssl_connect_origin_ctx_free); - bufferevent_openssl_set_allow_dirty_shutdown(ctx->bev, 1); - bufferevent_setcb(ctx->bev, NULL, NULL, ssl_connect_origin_eventcb, p); - bufferevent_enable(ctx->bev, EV_READ | EV_WRITE); -} - -/* - * Dump information on a certificate to the debug log. - */ -static void pxy_debug_crt(X509 * crt) -{ - char * sj = ssl_x509_subject(crt); - if (sj) - { - log_dbg_printf("Subject DN: %s\n", sj); - free(sj); - } - - char * names = ssl_x509_names_to_str(crt); - if (names) - { - log_dbg_printf("Common Names: %s\n", names); - free(names); - } - - char * fpr; - if (!(fpr = ssl_x509_fingerprint(crt, 1))) - { - log_err_printf("Warning: Error generating X509 fingerprint\n"); - } - else - { - log_dbg_printf("Fingerprint: %s\n", fpr); - free(fpr); - } - -#ifdef DEBUG_CERTIFICATE - /* dump certificate */ - log_dbg_print_free(ssl_x509_to_str(crt)); - log_dbg_print_free(ssl_x509_to_pem(crt)); -#endif /* DEBUG_CERTIFICATE */ -} - -static void -pxy_log_connect_nonhttp(pxy_conn_ctx_t * ctx) -{ - char * msg; - int rv; - - /* - * The following ifdef's within asprintf arguments list generates - * warnings with -Wembedded-directive on some compilers. - * Not fixing the code in order to avoid more code duplication. - */ - - if (!ctx->src.ssl) - { - rv = asprintf(&msg, "%s %s %s %s %s" - #ifdef HAVE_LOCAL_PROCINFO - " %s" - #endif /* HAVE_LOCAL_PROCINFO */ - "\n", - ctx->passthrough ? "passthrough" : "tcp", - STRORDASH(ctx->srchost_str), - STRORDASH(ctx->srcport_str), - STRORDASH(ctx->dsthost_str), - STRORDASH(ctx->dstport_str) - ); - } - else - { - rv = asprintf(&msg, "%s %s %s %s %s " - "sni:%s names:%s " - "sproto:%s:%s dproto:%s:%s " - "origcrt:%s usedcrt:%s\n", - ctx->clienthello_found ? "upgrade" : "ssl", - STRORDASH(ctx->srchost_str), - STRORDASH(ctx->srcport_str), - STRORDASH(ctx->dsthost_str), - STRORDASH(ctx->dstport_str), - STRORDASH(ctx->sni), - STRORDASH(ctx->ssl_names), - SSL_get_version(ctx->src.ssl), - SSL_get_cipher(ctx->src.ssl), - SSL_get_version(ctx->dst.ssl), - SSL_get_cipher(ctx->dst.ssl), - STRORDASH(ctx->origcrtfpr), - STRORDASH(ctx->usedcrtfpr) - ); - } - if ((rv < 0) || !msg) - { - ctx->enomem = 1; - goto out; - } - if (!ctx->opts->detach) - { - log_err_printf("%s", msg); - } - if (ctx->opts->connectlog) - { - } - else - { - free(msg); - } -out: - return; -} - -static void -pxy_log_connect_http(pxy_conn_ctx_t * ctx) -{ - char * msg; - int rv; - -#ifdef DEBUG_PROXY - if (ctx->passthrough) { - log_err_printf("Warning: pxy_log_connect_http called while in " - "passthrough mode\n"); - return; - } -#endif - - - /* - * The following ifdef's within asprintf arguments list generates - * warnings with -Wembedded-directive on some compilers. - * Not fixing the code in order to avoid more code duplication. - */ - - if (!ctx->spec->ssl) - { - rv = asprintf(&msg, "http %s %s %s %s %s %s %s %s %s %s\n", - STRORDASH(ctx->srchost_str), - STRORDASH(ctx->srcport_str), - STRORDASH(ctx->dsthost_str), - STRORDASH(ctx->dstport_str), - STRORDASH(ctx->http_host), - STRORDASH(ctx->http_method), - STRORDASH(ctx->http_uri), - STRORDASH(ctx->http_status_code), - STRORDASH(ctx->http_content_length), - ctx->ocsp_denied ? " ocsp:denied" : ""); - } - else - { - rv = asprintf(&msg, "https %s %s %s %s %s %s %s %s %s " - "sni:%s names:%s " - "sproto:%s:%s dproto:%s:%s " - "origcrt:%s usedcrt:%s" - #ifdef HAVE_LOCAL_PROCINFO - " %s" - #endif /* HAVE_LOCAL_PROCINFO */ - "%s\n", - STRORDASH(ctx->srchost_str), - STRORDASH(ctx->srcport_str), - STRORDASH(ctx->dsthost_str), - STRORDASH(ctx->dstport_str), - STRORDASH(ctx->http_host), - STRORDASH(ctx->http_method), - STRORDASH(ctx->http_uri), - STRORDASH(ctx->http_status_code), - STRORDASH(ctx->http_content_length), - STRORDASH(ctx->sni), - STRORDASH(ctx->ssl_names), - SSL_get_version(ctx->src.ssl), - SSL_get_cipher(ctx->src.ssl), - SSL_get_version(ctx->dst.ssl), - SSL_get_cipher(ctx->dst.ssl), - STRORDASH(ctx->origcrtfpr), - STRORDASH(ctx->usedcrtfpr), - ctx->ocsp_denied ? " ocsp:denied" : ""); - } - if ((rv < 0) || !msg) - { - ctx->enomem = 1; - goto out; - } - if (!ctx->opts->detach) - { - log_err_printf("%s", msg); - } - if (ctx->opts->connectlog) - { - } - - else - { - free(msg); - } -out: - return; -} - -/* - * Called by OpenSSL when a new src SSL session is created. - * OpenSSL increments the refcount before calling the callback and will - * decrement it again if we return 0. Returning 1 will make OpenSSL skip - * the refcount decrementing. In other words, return 0 if we did not - * keep a pointer to the object (which we never do here). - */ -#ifdef HAVE_SSLV2 -#define MAYBE_UNUSED -#else /* !HAVE_SSLV2 */ -#define MAYBE_UNUSED UNUSED -#endif /* !HAVE_SSLV2 */ -static int -pxy_ossl_sessnew_cb(MAYBE_UNUSED SSL * ssl, SSL_SESSION * sess) -#undef MAYBE_UNUSED -{ -#ifdef DEBUG_SESSION_CACHE - log_dbg_printf("===> OpenSSL new session callback:\n"); - if (sess) { - log_dbg_print_free(ssl_session_to_str(sess)); - } else { - log_dbg_printf("(null)\n"); - } -#endif /* DEBUG_SESSION_CACHE */ -#ifdef HAVE_SSLV2 - /* Session resumption seems to fail for SSLv2 with protocol - * parsing errors, so we disable caching for SSLv2. */ - if (SSL_version(ssl) == SSL2_VERSION) { - log_err_printf("Warning: Session resumption denied to SSLv2" - "client.\n"); - return 0; - } -#endif /* HAVE_SSLV2 */ - if (sess) - { - cachemgr_ssess_set(sess); - } - return 0; -} - -/* - * Called by OpenSSL when a src SSL session should be removed. - * OpenSSL calls SSL_SESSION_free() after calling the callback; - * we do not need to free the reference here. - */ -static void -pxy_ossl_sessremove_cb(UNUSED SSL_CTX * sslctx, SSL_SESSION * sess) -{ -#ifdef DEBUG_SESSION_CACHE - log_dbg_printf("===> OpenSSL remove session callback:\n"); - if (sess) { - log_dbg_print_free(ssl_session_to_str(sess)); - } else { - log_dbg_printf("(null)\n"); - } -#endif /* DEBUG_SESSION_CACHE */ - if (sess) - { - cachemgr_ssess_del(sess); - } -} - -/* - * Called by OpenSSL when a src SSL session is requested by the client. - */ -static SSL_SESSION * -#if OPENSSL_VERSION_NUMBER < 0x10100000L -pxy_ossl_sessget_cb(UNUSED SSL * ssl, unsigned char * id, int idlen, int * copy) -#else /* OPENSSL_VERSION_NUMBER >= 0x10100000L */ -pxy_ossl_sessget_cb(UNUSED SSL *ssl, const unsigned char *id, int idlen, int *copy) -#endif /* OPENSSL_VERSION_NUMBER >= 0x10100000L */ -{ - SSL_SESSION * sess; - -#ifdef DEBUG_SESSION_CACHE - log_dbg_printf("===> OpenSSL get session callback:\n"); -#endif /* DEBUG_SESSION_CACHE */ - - *copy = 0; /* SSL should not increment reference count of session */ - sess = (SSL_SESSION *) cachemgr_ssess_get(id, idlen); - -#ifdef DEBUG_SESSION_CACHE - if (sess) { - log_dbg_print_free(ssl_session_to_str(sess)); - } -#endif /* DEBUG_SESSION_CACHE */ - - log_dbg_printf("SSL session cache: %s\n", sess ? "HIT" : "MISS"); - return sess; -} - -/* - * Set SSL_CTX options that are the same for incoming and outgoing SSL_CTX. - */ -static void -pxy_sslctx_setoptions(SSL_CTX * sslctx, tfe_config opts) -{ - SSL_CTX_set_options(sslctx, SSL_OP_ALL); -#ifdef SSL_OP_TLS_ROLLBACK_BUG - SSL_CTX_set_options(sslctx, SSL_OP_TLS_ROLLBACK_BUG); -#endif /* SSL_OP_TLS_ROLLBACK_BUG */ -#ifdef SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION - SSL_CTX_set_options(sslctx, SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION); -#endif /* SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION */ -#ifdef SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS - SSL_CTX_set_options(sslctx, SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS); -#endif /* SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS */ -#ifdef SSL_OP_NO_TICKET - SSL_CTX_set_options(sslctx, SSL_OP_NO_TICKET); -#endif /* SSL_OP_NO_TICKET */ - -#ifdef SSL_OP_NO_SSLv2 -#ifdef HAVE_SSLV2 - if (opts->no_ssl2) { -#endif /* HAVE_SSLV2 */ - SSL_CTX_set_options(sslctx, SSL_OP_NO_SSLv2); -#ifdef HAVE_SSLV2 - } -#endif /* HAVE_SSLV2 */ -#endif /* !SSL_OP_NO_SSLv2 */ -#ifdef HAVE_SSLV3 - if (opts->no_ssl3) - { - SSL_CTX_set_options(sslctx, SSL_OP_NO_SSLv3); - } -#endif /* HAVE_SSLV3 */ -#ifdef HAVE_TLSV10 - if (opts->no_tls10) - { - SSL_CTX_set_options(sslctx, SSL_OP_NO_TLSv1); - } -#endif /* HAVE_TLSV10 */ -#ifdef HAVE_TLSV11 - if (opts->no_tls11) - { - SSL_CTX_set_options(sslctx, SSL_OP_NO_TLSv1_1); - } -#endif /* HAVE_TLSV11 */ -#ifdef HAVE_TLSV12 - if (opts->no_tls12) - { - SSL_CTX_set_options(sslctx, SSL_OP_NO_TLSv1_2); - } -#endif /* HAVE_TLSV12 */ - -#ifdef SSL_OP_NO_COMPRESSION - if (!opts->sslcomp) - { - SSL_CTX_set_options(sslctx, SSL_OP_NO_COMPRESSION); - } -#endif /* SSL_OP_NO_COMPRESSION */ - - SSL_CTX_set_cipher_list(sslctx, opts->ciphers); -} - -/* - * Create and set up a new SSL_CTX instance for terminating SSL. - * Set up all the necessary callbacks, the certificate, the cert chain and key. - */ -static SSL_CTX * downstream_sslctx_create( - pxy_conn_ctx_t * ctx, X509 * crt, STACK_OF(X509) * chain, - EVP_PKEY * key, struct tfe_config* opts, void* cb_arg) -{ - SSL_CTX * sslctx = SSL_CTX_new(opts->sslmethod()); - if (!sslctx) - return NULL; - - pxy_sslctx_setoptions(sslctx, opts); - -#if OPENSSL_VERSION_NUMBER >= 0x10100000L - if (opts->sslversion) { - if (SSL_CTX_set_min_proto_version(sslctx, opts->sslversion) == 0 || - SSL_CTX_set_max_proto_version(sslctx, opts->sslversion) == 0) { - SSL_CTX_free(sslctx); - return NULL; - } - } -#endif /* OPENSSL_VERSION_NUMBER >= 0x10100000L */ - - SSL_CTX_sess_set_new_cb(sslctx, pxy_ossl_sessnew_cb); - SSL_CTX_sess_set_remove_cb(sslctx, pxy_ossl_sessremove_cb); - SSL_CTX_sess_set_get_cb(sslctx, pxy_ossl_sessget_cb); - SSL_CTX_set_session_cache_mode(sslctx, SSL_SESS_CACHE_SERVER | - SSL_SESS_CACHE_NO_INTERNAL); -#ifdef USE_SSL_SESSION_ID_CONTEXT - SSL_CTX_set_session_id_context(sslctx, (void *)(&ssl_session_context), - sizeof(ssl_session_context)); -#endif /* USE_SSL_SESSION_ID_CONTEXT */ -#ifndef OPENSSL_NO_TLSEXT - SSL_CTX_set_tlsext_servername_callback(sslctx, pxy_ossl_servername_cb); - SSL_CTX_set_tlsext_servername_arg(sslctx, ctx); -#endif /* !OPENSSL_NO_TLSEXT */ -#ifndef OPENSSL_NO_DH - if (ctx->opts->dh) - { - SSL_CTX_set_tmp_dh(sslctx, ctx->opts->dh); - } - else - { - SSL_CTX_set_tmp_dh_callback(sslctx, ssl_tmp_dh_callback); - } -#endif /* !OPENSSL_NO_DH */ -#ifndef OPENSSL_NO_ECDH - if (ctx->opts->ecdhcurve) - { - EC_KEY * ecdh = ssl_ec_by_name(ctx->opts->ecdhcurve); - SSL_CTX_set_tmp_ecdh(sslctx, ecdh); - EC_KEY_free(ecdh); - } - else - { - EC_KEY * ecdh = ssl_ec_by_name(NULL); - SSL_CTX_set_tmp_ecdh(sslctx, ecdh); - EC_KEY_free(ecdh); - } -#endif /* !OPENSSL_NO_ECDH */ - SSL_CTX_use_certificate(sslctx, crt); - SSL_CTX_use_PrivateKey(sslctx, key); - for (int i = 0; i < sk_X509_num(chain); i++) - { - X509 * c = sk_X509_value(chain, i); - ssl_x509_refcount_inc(c); /* next call consumes a reference */ - SSL_CTX_add_extra_chain_cert(sslctx, c); - } - -#ifdef DEBUG_SESSION_CACHE - if (OPTS_DEBUG(ctx->opts)) { - int mode = SSL_CTX_get_session_cache_mode(sslctx); - log_dbg_printf("SSL session cache mode: %08x\n", mode); - if (mode == SSL_SESS_CACHE_OFF) - log_dbg_printf("SSL_SESS_CACHE_OFF\n"); - if (mode & SSL_SESS_CACHE_CLIENT) - log_dbg_printf("SSL_SESS_CACHE_CLIENT\n"); - if (mode & SSL_SESS_CACHE_SERVER) - log_dbg_printf("SSL_SESS_CACHE_SERVER\n"); - if (mode & SSL_SESS_CACHE_NO_AUTO_CLEAR) - log_dbg_printf("SSL_SESS_CACHE_NO_AUTO_CLEAR\n"); - if (mode & SSL_SESS_CACHE_NO_INTERNAL_LOOKUP) - log_dbg_printf("SSL_SESS_CACHE_NO_INTERNAL_LOOKUP\n"); - if (mode & SSL_SESS_CACHE_NO_INTERNAL_STORE) - log_dbg_printf("SSL_SESS_CACHE_NO_INTERNAL_STORE\n"); - } -#endif /* DEBUG_SESSION_CACHE */ - - return sslctx; -} - -static int -pxy_srccert_write_to_gendir(pxy_conn_ctx_t * ctx, X509 * crt, int is_orig) -{ - char * fn; - int rv; - - if (!ctx->origcrtfpr) - return -1; - if (is_orig) - { - rv = asprintf(&fn, "%s/%s.crt", ctx->opts->certgendir, - ctx->origcrtfpr); - } - else - { - if (!ctx->usedcrtfpr) - return -1; - rv = asprintf(&fn, "%s/%s-%s.crt", ctx->opts->certgendir, - ctx->origcrtfpr, ctx->usedcrtfpr); - } - if (rv == -1) - { - ctx->enomem = 1; - return -1; - } - - free(fn); - return rv; -} - -static void pxy_srccert_write(pxy_conn_ctx_t * ctx) -{ - if (ctx->opts->certgen_writeall || ctx->generated_cert) - { - if (pxy_srccert_write_to_gendir(ctx, - SSL_get_certificate(ctx->src.ssl), 0) == -1) - { - log_err_printf("Failed to write used certificate\n"); - } - } - if (ctx->opts->certgen_writeall) - { - if (pxy_srccert_write_to_gendir(ctx, ctx->origcrt, 1) == -1) - { - log_err_printf("Failed to write orig certificate\n"); - } - } -} - -/* - * Create new SSL context for the incoming connection, based on the original - * destination SSL certificate. - * Returns NULL if no suitable certificate could be found. - */ - - -static SSL * downstream_ssl_create(cert_t * cert, struct tfe_config* opts) -{ - - assert(cert!=NULL); - SSL_CTX * sslctx = downstream_sslctx_create(ctx, cert->crt, cert->chain, - cert->key); - - if (!sslctx) - { - return NULL; - } - SSL * ssl = SSL_new(sslctx); - SSL_CTX_free(sslctx); /* SSL_new() increments refcount */ - if (!ssl) - { - return NULL; - } -#ifdef SSL_MODE_RELEASE_BUFFERS - /* lower memory footprint for idle connections */ - SSL_set_mode(ssl, SSL_get_mode(ssl) | SSL_MODE_RELEASE_BUFFERS); -#endif /* SSL_MODE_RELEASE_BUFFERS */ - return ssl; -} - -/* - * OpenSSL servername callback, called when OpenSSL receives a servername - * TLS extension in the clientHello. Must switch to a new SSL_CTX with - * a different certificate if we want to replace the server cert here. - * We generate a new certificate if the current one does not match the - * supplied servername. This should only happen if the original destination - * server supplies a certificate which does not match the server name we - * indicate to it. - */ -static int pxy_ossl_servername_cb(SSL * ssl, UNUSED int * al, void * arg) -{ - pxy_conn_ctx_t * ctx = (pxy_conn_ctx_t *) arg; - const char * sn; - X509 * sslcrt; - - if (!(sn = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name))) - return SSL_TLSEXT_ERR_NOACK; - - if (!ctx->sni) - { - if (OPTS_DEBUG(ctx->opts)) - { - log_dbg_printf("Warning: SNI parser yielded no " - "hostname, copying OpenSSL one: " - "[NULL] != [%s]\n", sn); - } - ctx->sni = strdup(sn); - if (!ctx->sni) - { - ctx->enomem = 1; - return SSL_TLSEXT_ERR_NOACK; - } - } - if (OPTS_DEBUG(ctx->opts)) - { - if (strcmp(sn, ctx->sni) != 0) - { - /* - * This may happen if the client resumes a session, but - * uses a different SNI hostname when resuming than it - * used when the session was created. OpenSSL - * correctly ignores the SNI in the ClientHello in this - * case, but since we have already sent the SNI onwards - * to the original destination, there is no way back. - * We log an error and hope this never happens. - */ - log_dbg_printf("Warning: SNI parser yielded different " - "hostname than OpenSSL callback for " - "the same ClientHello message: " - "[%s] != [%s]\n", ctx->sni, sn); - } - } - - /* generate a new certificate with sn as additional altSubjectName - * and replace it both in the current SSL ctx and in the cert cache */ - if (!ctx->immutable_cert && - !ssl_x509_names_match((sslcrt = SSL_get_certificate(ssl)), sn)) - { - X509 * newcrt; - SSL_CTX * newsslctx; - - if (OPTS_DEBUG(ctx->opts)) - { - log_dbg_printf("Certificate cache: UPDATE " - "(SNI mismatch)\n"); - } - newcrt = ssl_x509_forge(ctx->opts->cacrt, ctx->opts->cakey, - sslcrt, ctx->opts->key, - sn, ctx->opts->crlurl); - if (!newcrt) - { - ctx->enomem = 1; - return SSL_TLSEXT_ERR_NOACK; - } - cachemgr_fkcrt_set(ctx->origcrt, newcrt); - ctx->generated_cert = 1; - if (OPTS_DEBUG(ctx->opts)) - { - log_dbg_printf("===> Updated forged server " - "certificate:\n"); - pxy_debug_crt(newcrt); - } - if (WANT_CONNECT_LOG(ctx)) - { - if (ctx->ssl_names) - { - free(ctx->ssl_names); - } - ctx->ssl_names = ssl_x509_names_to_str(newcrt); - if (!ctx->ssl_names) - { - ctx->enomem = 1; - } - } - if (WANT_CONNECT_LOG(ctx) || ctx->opts->certgendir) - { - if (ctx->usedcrtfpr) - { - free(ctx->usedcrtfpr); - } - ctx->usedcrtfpr = ssl_x509_fingerprint(newcrt, 0); - if (!ctx->usedcrtfpr) - { - ctx->enomem = 1; - } - } - - newsslctx = downstream_sslctx_create(ctx, newcrt, ctx->opts->chain, - ctx->opts->key); - if (!newsslctx) - { - X509_free(newcrt); - ctx->enomem = 1; - return SSL_TLSEXT_ERR_NOACK; - } - SSL_set_SSL_CTX(ssl, newsslctx); /* decr's old incr new refc */ - SSL_CTX_free(newsslctx); - X509_free(newcrt); - } - else if (OPTS_DEBUG(ctx->opts)) - { - log_dbg_printf("Certificate cache: KEEP (SNI match or " - "target mode)\n"); - } - - return SSL_TLSEXT_ERR_OK; -} - -/* - * Create new SSL context for outgoing connections to the original destination. - * If hostname sni is provided, use it for Server Name Indication. - */ -static SSL* upstream_ssl_create(tfe_config*opts, const char* sni, MESA_htable_handle* dsess_cache) -{ - SSL_CTX* sslctx=NULL; - SSL* ssl=NULL; - SSL_SESSION* sess=NULL; - - sslctx = SSL_CTX_new(opts->sslmethod()); - - pxy_sslctx_setoptions(sslctx, opts); - -#if OPENSSL_VERSION_NUMBER >= 0x10100000L - if (opts->sslversion) { - if (SSL_CTX_set_min_proto_version(sslctx, opts->sslversion) == 0 || - SSL_CTX_set_max_proto_version(sslctx, opts->sslversion) == 0) { - SSL_CTX_free(sslctx); - return NULL; - } - } -#endif /* OPENSSL_VERSION_NUMBER >= 0x10100000L */ - - SSL_CTX_set_verify(sslctx, SSL_VERIFY_NONE, NULL); - - ssl = SSL_new(sslctx); - SSL_CTX_free(sslctx); /* SSL_new() increments refcount */ - if (!ssl) - { - return NULL; - } -#ifndef OPENSSL_NO_TLSEXT - if (sni) - { - SSL_set_tlsext_host_name(ssl, sni); - } -#endif /* !OPENSSL_NO_TLSEXT */ - -#ifdef SSL_MODE_RELEASE_BUFFERS - /* lower memory footprint for idle connections */ - SSL_set_mode(ssl, SSL_get_mode(ssl) | SSL_MODE_RELEASE_BUFFERS); -#endif /* SSL_MODE_RELEASE_BUFFERS */ - - //Todo: Refactor below session resumption. - /* session resuming based on remote endpoint address and port */ - sess = (SSL_SESSION *) cachemgr_dsess_get((struct sockaddr *) &ctx->addr, - ctx->addrlen, ctx->sni); /* new sess inst */ - if (sess) - { - if (OPTS_DEBUG(opts)) - { - log_dbg_printf("Attempt reuse dst SSL session\n"); - } - SSL_set_session(ssl, sess); /* increments sess refcount */ - SSL_SESSION_free(sess); - } - - return ssl; -} -/* -struct ssl_upstream* ssl_upstream_create(tfe_config*opts, const char* sni) -{ - struct ssl_upstream* upstream=NULL; - SSL* ssl=NULL; - ssl=upstream_ssl_create(opts,sni); - if(ssl==NULL) - { - return NULL; - } - upstream=ALLOC(struct ssl_upstream,1); - upstream->ssl=ssl; - return upstream; -}*/ -void ssl_upstream_free(struct ssl_upstream* p) -{ - X509_free(p->orig_cert); - //todo close ssl with a callback. - //SSL_free(ctx->ssl); -} -struct ssl_downstream* ssl_downstream_create(void) -{ - struct ssl_downstream* p=NULL; - p=ALLOC(struct ssl_downstream, 1); - return p; -} -void ssl_downstream_free(struct ssl_downstream* p) -{ - free(p->sni); - p->sni=NULL; -// X509_free(p->fake_cert); -} - +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +struct peek_sni_ctx +{ + /* ssl */ + unsigned char sni_peek_retries; /* max 64 SNI parse retries */ + char* sni; + struct event* ev; + struct event_base* evbase; + +}; + +void peek_sni_ctx_free(void* ctx) +{ + struct peek_sni_ctx * _ctx=(struct peek_sni_ctx *)ctx; + event_free(_ctx->ev); + _ctx->ev = NULL; + free(_ctx->sni); + _ctx->sni=NULL; + free(_ctx); + return; +} + +static void peek_sni_cb(evutil_socket_t fd, short what, void * arg) +{ + struct promise* promise=(struct promise*)arg; + struct peek_sni_ctx* ctx= (struct peek_sni_ctx*)promise->ctx; + + + unsigned char buf[1024]; + ssize_t n=0; + const unsigned char * chello=NULL; + int rv=0; + + n = recv(fd, buf, sizeof(buf), MSG_PEEK); + if (n == -1) + { + TFE_LOG_ERROR("Error peeking on fd, aborting connection\n"); + goto promise_failed; + } + + if (n == 0) + { + goto promise_failed; + } + + rv = ssl_tls_clienthello_parse(buf, n, 0, &chello, &ctx->sni); + if ((rv == 1) && !chello) + { + TFE_LOG_ERROR("Peeking did not yield a (truncated) ClientHello message, aborting connection\n"); + goto promise_failed; + } + + if ((rv == 1) && chello && (ctx->sni_peek_retries++ < 50)) + { + /* ssl_tls_clienthello_parse indicates that we + * should retry later when we have more data, and we + * haven't reached the maximum retry count yet. + * Reschedule this event as timeout-only event in + * order to prevent busy looping over the read event. + * Because we only peeked at the pending bytes and + * never actually read them, fd is still ready for + * reading now. We use 25 * 0.2 s = 5 s timeout. */ + struct timeval retry_delay = {0, 100}; + + event_free(ctx->ev); + ctx->ev = event_new(ctx->evbase, fd, 0, peek_sni_cb, promise); + assert(ctx->ev!=NULL); + event_add(ctx->ev, &retry_delay); + return; + } + + promise_set_ctx(promise, NULL, NULL); + promise->f.cb_success(ctx->sni,promise->f.user); + peek_sni_ctx_free(ctx); + return; + +promise_failed: + promise->f.cb_failed(FUTURE_ERROR_EXCEPTION,"too many tries",promise->f.user); + peek_sni_ctx_free(ctx); + promise_set_ctx(promise, NULL,NULL); + return; +} + +void ssl_async_peek_sni( struct future* future, evutil_socket_t fd, struct event_base *evbase) +{ + struct event * ev=NULL; + struct promise* p=future_to_promise(future); + struct peek_sni_ctx* ctx=ALLOC(struct peek_sni_ctx, 1); + ctx->ev = event_new(evbase, fd, EV_READ, peek_sni_cb, p); + event_add(evbase, NULL); + promise_set_ctx(p, ctx,peek_sni_ctx_free); + + return; +} + +struct ssl_connect_origin_ctx +{ + struct bufferevent *bev; + SSL* ssl; + +}; +void ssl_connect_origin_ctx_free(struct ssl_connect_origin_ctx* ctx) +{ + ctx->ssl=NULL; + bufferevent_free(ctx->bev); + ctx->bev=NULL; + //Do not free bev and ssl, reserved for user. + free(ctx); +} +/* + * Callback for meta events on the up- and downstream connection bufferevents. + * Called when EOF has been reached, a connection has been made, and on errors. + */ +static void ssl_connect_origin_eventcb(struct bufferevent * bev, short events, void * arg) +{ + struct promise* promise=(struct promise*)arg; + struct ssl_connect_origin_ctx* ctx=(struct ssl_connect_origin_ctx*)promise_dettach_ctx(promise); + + struct tfe_stream_private* _stream = (struct tfe_stream_private*)arg; + + if (events & BEV_EVENT_ERROR) + { + promise->f.cb_failed(FUTURE_ERROR_EXCEPTION,"ssl connect failed",promise->f.user); + } + else + { + if (events & BEV_EVENT_CONNECTED) + { + promise->f.cb_success(ctx->bev,promise->f.user); + ctx->bev=NULL; + } + + else + { + assert(0); + } + } + ssl_connect_origin_ctx_free(ctx); + return +} + +void ssl_async_connect_origin(struct future* future, evutil_socket_t fd, const char* sni, + struct event_base *evbase, struct tfe_config *opts) +{ + struct promise* p=future_to_promise(future); + struct ssl_connect_origin_ctx* ctx=ALLOC(struct ssl_connect_origin_ctx, 1); + ctx->ssl=upstream_ssl_create(opts,sni); + ctx->bev = bufferevent_openssl_socket_new(evbase, fd, ctx->ssl, BUFFEREVENT_SSL_CONNECTING, BEV_OPT_DEFER_CALLBACKS); + promise_set_ctx(p, ctx, ssl_connect_origin_ctx_free); + bufferevent_openssl_set_allow_dirty_shutdown(ctx->bev, 1); + bufferevent_setcb(ctx->bev, NULL, NULL, ssl_connect_origin_eventcb, p); + bufferevent_enable(ctx->bev, EV_READ | EV_WRITE); +} + +/* + * Dump information on a certificate to the debug log. + */ +static void pxy_debug_crt(X509 * crt) +{ + char * sj = ssl_x509_subject(crt); + if (sj) + { + log_dbg_printf("Subject DN: %s\n", sj); + free(sj); + } + + char * names = ssl_x509_names_to_str(crt); + if (names) + { + log_dbg_printf("Common Names: %s\n", names); + free(names); + } + + char * fpr; + if (!(fpr = ssl_x509_fingerprint(crt, 1))) + { + log_err_printf("Warning: Error generating X509 fingerprint\n"); + } + else + { + log_dbg_printf("Fingerprint: %s\n", fpr); + free(fpr); + } + +#ifdef DEBUG_CERTIFICATE + /* dump certificate */ + log_dbg_print_free(ssl_x509_to_str(crt)); + log_dbg_print_free(ssl_x509_to_pem(crt)); +#endif /* DEBUG_CERTIFICATE */ +} + +static void +pxy_log_connect_nonhttp(pxy_conn_ctx_t * ctx) +{ + char * msg; + int rv; + + /* + * The following ifdef's within asprintf arguments list generates + * warnings with -Wembedded-directive on some compilers. + * Not fixing the code in order to avoid more code duplication. + */ + + if (!ctx->src.ssl) + { + rv = asprintf(&msg, "%s %s %s %s %s" + #ifdef HAVE_LOCAL_PROCINFO + " %s" + #endif /* HAVE_LOCAL_PROCINFO */ + "\n", + ctx->passthrough ? "passthrough" : "tcp", + STRORDASH(ctx->srchost_str), + STRORDASH(ctx->srcport_str), + STRORDASH(ctx->dsthost_str), + STRORDASH(ctx->dstport_str) + ); + } + else + { + rv = asprintf(&msg, "%s %s %s %s %s " + "sni:%s names:%s " + "sproto:%s:%s dproto:%s:%s " + "origcrt:%s usedcrt:%s\n", + ctx->clienthello_found ? "upgrade" : "ssl", + STRORDASH(ctx->srchost_str), + STRORDASH(ctx->srcport_str), + STRORDASH(ctx->dsthost_str), + STRORDASH(ctx->dstport_str), + STRORDASH(ctx->sni), + STRORDASH(ctx->ssl_names), + SSL_get_version(ctx->src.ssl), + SSL_get_cipher(ctx->src.ssl), + SSL_get_version(ctx->dst.ssl), + SSL_get_cipher(ctx->dst.ssl), + STRORDASH(ctx->origcrtfpr), + STRORDASH(ctx->usedcrtfpr) + ); + } + if ((rv < 0) || !msg) + { + ctx->enomem = 1; + goto out; + } + if (!ctx->opts->detach) + { + log_err_printf("%s", msg); + } + if (ctx->opts->connectlog) + { + } + else + { + free(msg); + } +out: + return; +} + +static void +pxy_log_connect_http(pxy_conn_ctx_t * ctx) +{ + char * msg; + int rv; + +#ifdef DEBUG_PROXY + if (ctx->passthrough) { + log_err_printf("Warning: pxy_log_connect_http called while in " + "passthrough mode\n"); + return; + } +#endif + + + /* + * The following ifdef's within asprintf arguments list generates + * warnings with -Wembedded-directive on some compilers. + * Not fixing the code in order to avoid more code duplication. + */ + + if (!ctx->spec->ssl) + { + rv = asprintf(&msg, "http %s %s %s %s %s %s %s %s %s %s\n", + STRORDASH(ctx->srchost_str), + STRORDASH(ctx->srcport_str), + STRORDASH(ctx->dsthost_str), + STRORDASH(ctx->dstport_str), + STRORDASH(ctx->http_host), + STRORDASH(ctx->http_method), + STRORDASH(ctx->http_uri), + STRORDASH(ctx->http_status_code), + STRORDASH(ctx->http_content_length), + ctx->ocsp_denied ? " ocsp:denied" : ""); + } + else + { + rv = asprintf(&msg, "https %s %s %s %s %s %s %s %s %s " + "sni:%s names:%s " + "sproto:%s:%s dproto:%s:%s " + "origcrt:%s usedcrt:%s" + #ifdef HAVE_LOCAL_PROCINFO + " %s" + #endif /* HAVE_LOCAL_PROCINFO */ + "%s\n", + STRORDASH(ctx->srchost_str), + STRORDASH(ctx->srcport_str), + STRORDASH(ctx->dsthost_str), + STRORDASH(ctx->dstport_str), + STRORDASH(ctx->http_host), + STRORDASH(ctx->http_method), + STRORDASH(ctx->http_uri), + STRORDASH(ctx->http_status_code), + STRORDASH(ctx->http_content_length), + STRORDASH(ctx->sni), + STRORDASH(ctx->ssl_names), + SSL_get_version(ctx->src.ssl), + SSL_get_cipher(ctx->src.ssl), + SSL_get_version(ctx->dst.ssl), + SSL_get_cipher(ctx->dst.ssl), + STRORDASH(ctx->origcrtfpr), + STRORDASH(ctx->usedcrtfpr), + ctx->ocsp_denied ? " ocsp:denied" : ""); + } + if ((rv < 0) || !msg) + { + ctx->enomem = 1; + goto out; + } + if (!ctx->opts->detach) + { + log_err_printf("%s", msg); + } + if (ctx->opts->connectlog) + { + } + + else + { + free(msg); + } +out: + return; +} + +/* + * Called by OpenSSL when a new src SSL session is created. + * OpenSSL increments the refcount before calling the callback and will + * decrement it again if we return 0. Returning 1 will make OpenSSL skip + * the refcount decrementing. In other words, return 0 if we did not + * keep a pointer to the object (which we never do here). + */ +#ifdef HAVE_SSLV2 +#define MAYBE_UNUSED +#else /* !HAVE_SSLV2 */ +#define MAYBE_UNUSED UNUSED +#endif /* !HAVE_SSLV2 */ +static int +pxy_ossl_sessnew_cb(MAYBE_UNUSED SSL * ssl, SSL_SESSION * sess) +#undef MAYBE_UNUSED +{ +#ifdef DEBUG_SESSION_CACHE + log_dbg_printf("===> OpenSSL new session callback:\n"); + if (sess) { + log_dbg_print_free(ssl_session_to_str(sess)); + } else { + log_dbg_printf("(null)\n"); + } +#endif /* DEBUG_SESSION_CACHE */ +#ifdef HAVE_SSLV2 + /* Session resumption seems to fail for SSLv2 with protocol + * parsing errors, so we disable caching for SSLv2. */ + if (SSL_version(ssl) == SSL2_VERSION) { + log_err_printf("Warning: Session resumption denied to SSLv2" + "client.\n"); + return 0; + } +#endif /* HAVE_SSLV2 */ + if (sess) + { + cachemgr_ssess_set(sess); + } + return 0; +} + +/* + * Called by OpenSSL when a src SSL session should be removed. + * OpenSSL calls SSL_SESSION_free() after calling the callback; + * we do not need to free the reference here. + */ +static void +pxy_ossl_sessremove_cb(UNUSED SSL_CTX * sslctx, SSL_SESSION * sess) +{ +#ifdef DEBUG_SESSION_CACHE + log_dbg_printf("===> OpenSSL remove session callback:\n"); + if (sess) { + log_dbg_print_free(ssl_session_to_str(sess)); + } else { + log_dbg_printf("(null)\n"); + } +#endif /* DEBUG_SESSION_CACHE */ + if (sess) + { + cachemgr_ssess_del(sess); + } +} + +/* + * Called by OpenSSL when a src SSL session is requested by the client. + */ +static SSL_SESSION * +#if OPENSSL_VERSION_NUMBER < 0x10100000L +pxy_ossl_sessget_cb(UNUSED SSL * ssl, unsigned char * id, int idlen, int * copy) +#else /* OPENSSL_VERSION_NUMBER >= 0x10100000L */ +pxy_ossl_sessget_cb(UNUSED SSL *ssl, const unsigned char *id, int idlen, int *copy) +#endif /* OPENSSL_VERSION_NUMBER >= 0x10100000L */ +{ + SSL_SESSION * sess; + +#ifdef DEBUG_SESSION_CACHE + log_dbg_printf("===> OpenSSL get session callback:\n"); +#endif /* DEBUG_SESSION_CACHE */ + + *copy = 0; /* SSL should not increment reference count of session */ + sess = (SSL_SESSION *) cachemgr_ssess_get(id, idlen); + +#ifdef DEBUG_SESSION_CACHE + if (sess) { + log_dbg_print_free(ssl_session_to_str(sess)); + } +#endif /* DEBUG_SESSION_CACHE */ + + log_dbg_printf("SSL session cache: %s\n", sess ? "HIT" : "MISS"); + return sess; +} + +/* + * Set SSL_CTX options that are the same for incoming and outgoing SSL_CTX. + */ +static void +pxy_sslctx_setoptions(SSL_CTX * sslctx, tfe_config opts) +{ + SSL_CTX_set_options(sslctx, SSL_OP_ALL); +#ifdef SSL_OP_TLS_ROLLBACK_BUG + SSL_CTX_set_options(sslctx, SSL_OP_TLS_ROLLBACK_BUG); +#endif /* SSL_OP_TLS_ROLLBACK_BUG */ +#ifdef SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION + SSL_CTX_set_options(sslctx, SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION); +#endif /* SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION */ +#ifdef SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS + SSL_CTX_set_options(sslctx, SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS); +#endif /* SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS */ +#ifdef SSL_OP_NO_TICKET + SSL_CTX_set_options(sslctx, SSL_OP_NO_TICKET); +#endif /* SSL_OP_NO_TICKET */ + +#ifdef SSL_OP_NO_SSLv2 +#ifdef HAVE_SSLV2 + if (opts->no_ssl2) { +#endif /* HAVE_SSLV2 */ + SSL_CTX_set_options(sslctx, SSL_OP_NO_SSLv2); +#ifdef HAVE_SSLV2 + } +#endif /* HAVE_SSLV2 */ +#endif /* !SSL_OP_NO_SSLv2 */ +#ifdef HAVE_SSLV3 + if (opts->no_ssl3) + { + SSL_CTX_set_options(sslctx, SSL_OP_NO_SSLv3); + } +#endif /* HAVE_SSLV3 */ +#ifdef HAVE_TLSV10 + if (opts->no_tls10) + { + SSL_CTX_set_options(sslctx, SSL_OP_NO_TLSv1); + } +#endif /* HAVE_TLSV10 */ +#ifdef HAVE_TLSV11 + if (opts->no_tls11) + { + SSL_CTX_set_options(sslctx, SSL_OP_NO_TLSv1_1); + } +#endif /* HAVE_TLSV11 */ +#ifdef HAVE_TLSV12 + if (opts->no_tls12) + { + SSL_CTX_set_options(sslctx, SSL_OP_NO_TLSv1_2); + } +#endif /* HAVE_TLSV12 */ + +#ifdef SSL_OP_NO_COMPRESSION + if (!opts->sslcomp) + { + SSL_CTX_set_options(sslctx, SSL_OP_NO_COMPRESSION); + } +#endif /* SSL_OP_NO_COMPRESSION */ + + SSL_CTX_set_cipher_list(sslctx, opts->ciphers); +} + +/* + * Create and set up a new SSL_CTX instance for terminating SSL. + * Set up all the necessary callbacks, the certificate, the cert chain and key. + */ +static SSL_CTX * downstream_sslctx_create( + pxy_conn_ctx_t * ctx, X509 * crt, STACK_OF(X509) * chain, + EVP_PKEY * key, struct tfe_config* opts, void* cb_arg) +{ + SSL_CTX * sslctx = SSL_CTX_new(opts->sslmethod()); + if (!sslctx) + return NULL; + + pxy_sslctx_setoptions(sslctx, opts); + +#if OPENSSL_VERSION_NUMBER >= 0x10100000L + if (opts->sslversion) { + if (SSL_CTX_set_min_proto_version(sslctx, opts->sslversion) == 0 || + SSL_CTX_set_max_proto_version(sslctx, opts->sslversion) == 0) { + SSL_CTX_free(sslctx); + return NULL; + } + } +#endif /* OPENSSL_VERSION_NUMBER >= 0x10100000L */ + + SSL_CTX_sess_set_new_cb(sslctx, pxy_ossl_sessnew_cb); + SSL_CTX_sess_set_remove_cb(sslctx, pxy_ossl_sessremove_cb); + SSL_CTX_sess_set_get_cb(sslctx, pxy_ossl_sessget_cb); + SSL_CTX_set_session_cache_mode(sslctx, SSL_SESS_CACHE_SERVER | + SSL_SESS_CACHE_NO_INTERNAL); +#ifdef USE_SSL_SESSION_ID_CONTEXT + SSL_CTX_set_session_id_context(sslctx, (void *)(&ssl_session_context), + sizeof(ssl_session_context)); +#endif /* USE_SSL_SESSION_ID_CONTEXT */ +#ifndef OPENSSL_NO_TLSEXT + SSL_CTX_set_tlsext_servername_callback(sslctx, pxy_ossl_servername_cb); + SSL_CTX_set_tlsext_servername_arg(sslctx, ctx); +#endif /* !OPENSSL_NO_TLSEXT */ +#ifndef OPENSSL_NO_DH + if (ctx->opts->dh) + { + SSL_CTX_set_tmp_dh(sslctx, ctx->opts->dh); + } + else + { + SSL_CTX_set_tmp_dh_callback(sslctx, ssl_tmp_dh_callback); + } +#endif /* !OPENSSL_NO_DH */ +#ifndef OPENSSL_NO_ECDH + if (ctx->opts->ecdhcurve) + { + EC_KEY * ecdh = ssl_ec_by_name(ctx->opts->ecdhcurve); + SSL_CTX_set_tmp_ecdh(sslctx, ecdh); + EC_KEY_free(ecdh); + } + else + { + EC_KEY * ecdh = ssl_ec_by_name(NULL); + SSL_CTX_set_tmp_ecdh(sslctx, ecdh); + EC_KEY_free(ecdh); + } +#endif /* !OPENSSL_NO_ECDH */ + SSL_CTX_use_certificate(sslctx, crt); + SSL_CTX_use_PrivateKey(sslctx, key); + for (int i = 0; i < sk_X509_num(chain); i++) + { + X509 * c = sk_X509_value(chain, i); + ssl_x509_refcount_inc(c); /* next call consumes a reference */ + SSL_CTX_add_extra_chain_cert(sslctx, c); + } + +#ifdef DEBUG_SESSION_CACHE + if (OPTS_DEBUG(ctx->opts)) { + int mode = SSL_CTX_get_session_cache_mode(sslctx); + log_dbg_printf("SSL session cache mode: %08x\n", mode); + if (mode == SSL_SESS_CACHE_OFF) + log_dbg_printf("SSL_SESS_CACHE_OFF\n"); + if (mode & SSL_SESS_CACHE_CLIENT) + log_dbg_printf("SSL_SESS_CACHE_CLIENT\n"); + if (mode & SSL_SESS_CACHE_SERVER) + log_dbg_printf("SSL_SESS_CACHE_SERVER\n"); + if (mode & SSL_SESS_CACHE_NO_AUTO_CLEAR) + log_dbg_printf("SSL_SESS_CACHE_NO_AUTO_CLEAR\n"); + if (mode & SSL_SESS_CACHE_NO_INTERNAL_LOOKUP) + log_dbg_printf("SSL_SESS_CACHE_NO_INTERNAL_LOOKUP\n"); + if (mode & SSL_SESS_CACHE_NO_INTERNAL_STORE) + log_dbg_printf("SSL_SESS_CACHE_NO_INTERNAL_STORE\n"); + } +#endif /* DEBUG_SESSION_CACHE */ + + return sslctx; +} + +static int +pxy_srccert_write_to_gendir(pxy_conn_ctx_t * ctx, X509 * crt, int is_orig) +{ + char * fn; + int rv; + + if (!ctx->origcrtfpr) + return -1; + if (is_orig) + { + rv = asprintf(&fn, "%s/%s.crt", ctx->opts->certgendir, + ctx->origcrtfpr); + } + else + { + if (!ctx->usedcrtfpr) + return -1; + rv = asprintf(&fn, "%s/%s-%s.crt", ctx->opts->certgendir, + ctx->origcrtfpr, ctx->usedcrtfpr); + } + if (rv == -1) + { + ctx->enomem = 1; + return -1; + } + + free(fn); + return rv; +} + +static void pxy_srccert_write(pxy_conn_ctx_t * ctx) +{ + if (ctx->opts->certgen_writeall || ctx->generated_cert) + { + if (pxy_srccert_write_to_gendir(ctx, + SSL_get_certificate(ctx->src.ssl), 0) == -1) + { + log_err_printf("Failed to write used certificate\n"); + } + } + if (ctx->opts->certgen_writeall) + { + if (pxy_srccert_write_to_gendir(ctx, ctx->origcrt, 1) == -1) + { + log_err_printf("Failed to write orig certificate\n"); + } + } +} + +/* + * Create new SSL context for the incoming connection, based on the original + * destination SSL certificate. + * Returns NULL if no suitable certificate could be found. + */ + + +static SSL * downstream_ssl_create(cert_t * cert, struct tfe_config* opts) +{ + + assert(cert!=NULL); + SSL_CTX * sslctx = downstream_sslctx_create(ctx, cert->crt, cert->chain, + cert->key); + + if (!sslctx) + { + return NULL; + } + SSL * ssl = SSL_new(sslctx); + SSL_CTX_free(sslctx); /* SSL_new() increments refcount */ + if (!ssl) + { + return NULL; + } +#ifdef SSL_MODE_RELEASE_BUFFERS + /* lower memory footprint for idle connections */ + SSL_set_mode(ssl, SSL_get_mode(ssl) | SSL_MODE_RELEASE_BUFFERS); +#endif /* SSL_MODE_RELEASE_BUFFERS */ + return ssl; +} + +/* + * OpenSSL servername callback, called when OpenSSL receives a servername + * TLS extension in the clientHello. Must switch to a new SSL_CTX with + * a different certificate if we want to replace the server cert here. + * We generate a new certificate if the current one does not match the + * supplied servername. This should only happen if the original destination + * server supplies a certificate which does not match the server name we + * indicate to it. + */ +static int pxy_ossl_servername_cb(SSL * ssl, UNUSED int * al, void * arg) +{ + pxy_conn_ctx_t * ctx = (pxy_conn_ctx_t *) arg; + const char * sn; + X509 * sslcrt; + + if (!(sn = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name))) + return SSL_TLSEXT_ERR_NOACK; + + if (!ctx->sni) + { + if (OPTS_DEBUG(ctx->opts)) + { + log_dbg_printf("Warning: SNI parser yielded no " + "hostname, copying OpenSSL one: " + "[NULL] != [%s]\n", sn); + } + ctx->sni = strdup(sn); + if (!ctx->sni) + { + ctx->enomem = 1; + return SSL_TLSEXT_ERR_NOACK; + } + } + if (OPTS_DEBUG(ctx->opts)) + { + if (strcmp(sn, ctx->sni) != 0) + { + /* + * This may happen if the client resumes a session, but + * uses a different SNI hostname when resuming than it + * used when the session was created. OpenSSL + * correctly ignores the SNI in the ClientHello in this + * case, but since we have already sent the SNI onwards + * to the original destination, there is no way back. + * We log an error and hope this never happens. + */ + log_dbg_printf("Warning: SNI parser yielded different " + "hostname than OpenSSL callback for " + "the same ClientHello message: " + "[%s] != [%s]\n", ctx->sni, sn); + } + } + + /* generate a new certificate with sn as additional altSubjectName + * and replace it both in the current SSL ctx and in the cert cache */ + if (!ctx->immutable_cert && + !ssl_x509_names_match((sslcrt = SSL_get_certificate(ssl)), sn)) + { + X509 * newcrt; + SSL_CTX * newsslctx; + + if (OPTS_DEBUG(ctx->opts)) + { + log_dbg_printf("Certificate cache: UPDATE " + "(SNI mismatch)\n"); + } + newcrt = ssl_x509_forge(ctx->opts->cacrt, ctx->opts->cakey, + sslcrt, ctx->opts->key, + sn, ctx->opts->crlurl); + if (!newcrt) + { + ctx->enomem = 1; + return SSL_TLSEXT_ERR_NOACK; + } + cachemgr_fkcrt_set(ctx->origcrt, newcrt); + ctx->generated_cert = 1; + if (OPTS_DEBUG(ctx->opts)) + { + log_dbg_printf("===> Updated forged server " + "certificate:\n"); + pxy_debug_crt(newcrt); + } + if (WANT_CONNECT_LOG(ctx)) + { + if (ctx->ssl_names) + { + free(ctx->ssl_names); + } + ctx->ssl_names = ssl_x509_names_to_str(newcrt); + if (!ctx->ssl_names) + { + ctx->enomem = 1; + } + } + if (WANT_CONNECT_LOG(ctx) || ctx->opts->certgendir) + { + if (ctx->usedcrtfpr) + { + free(ctx->usedcrtfpr); + } + ctx->usedcrtfpr = ssl_x509_fingerprint(newcrt, 0); + if (!ctx->usedcrtfpr) + { + ctx->enomem = 1; + } + } + + newsslctx = downstream_sslctx_create(ctx, newcrt, ctx->opts->chain, + ctx->opts->key); + if (!newsslctx) + { + X509_free(newcrt); + ctx->enomem = 1; + return SSL_TLSEXT_ERR_NOACK; + } + SSL_set_SSL_CTX(ssl, newsslctx); /* decr's old incr new refc */ + SSL_CTX_free(newsslctx); + X509_free(newcrt); + } + else if (OPTS_DEBUG(ctx->opts)) + { + log_dbg_printf("Certificate cache: KEEP (SNI match or " + "target mode)\n"); + } + + return SSL_TLSEXT_ERR_OK; +} + +/* + * Create new SSL context for outgoing connections to the original destination. + * If hostname sni is provided, use it for Server Name Indication. + */ +static SSL* upstream_ssl_create(tfe_config*opts, const char* sni) +{ + SSL_CTX* sslctx=NULL; + SSL* ssl=NULL; + SSL_SESSION* sess=NULL; + + sslctx = SSL_CTX_new(opts->sslmethod()); + + pxy_sslctx_setoptions(sslctx, opts); + +#if OPENSSL_VERSION_NUMBER >= 0x10100000L + if (opts->sslversion) { + if (SSL_CTX_set_min_proto_version(sslctx, opts->sslversion) == 0 || + SSL_CTX_set_max_proto_version(sslctx, opts->sslversion) == 0) { + SSL_CTX_free(sslctx); + return NULL; + } + } +#endif /* OPENSSL_VERSION_NUMBER >= 0x10100000L */ + + SSL_CTX_set_verify(sslctx, SSL_VERIFY_NONE, NULL); + + ssl = SSL_new(sslctx); + SSL_CTX_free(sslctx); /* SSL_new() increments refcount */ + if (!ssl) + { + return NULL; + } +#ifndef OPENSSL_NO_TLSEXT + if (sni) + { + SSL_set_tlsext_host_name(ssl, sni); + } +#endif /* !OPENSSL_NO_TLSEXT */ + +#ifdef SSL_MODE_RELEASE_BUFFERS + /* lower memory footprint for idle connections */ + SSL_set_mode(ssl, SSL_get_mode(ssl) | SSL_MODE_RELEASE_BUFFERS); +#endif /* SSL_MODE_RELEASE_BUFFERS */ + + //Todo: Refactor below session resumption. + /* session resuming based on remote endpoint address and port */ + sess = (SSL_SESSION *) cachemgr_dsess_get((struct sockaddr *) &ctx->addr, + ctx->addrlen, ctx->sni); /* new sess inst */ + if (sess) + { + if (OPTS_DEBUG(opts)) + { + log_dbg_printf("Attempt reuse dst SSL session\n"); + } + SSL_set_session(ssl, sess); /* increments sess refcount */ + SSL_SESSION_free(sess); + } + + return ssl; +} +/* +struct ssl_upstream* ssl_upstream_create(tfe_config*opts, const char* sni) +{ + struct ssl_upstream* upstream=NULL; + SSL* ssl=NULL; + ssl=upstream_ssl_create(opts,sni); + if(ssl==NULL) + { + return NULL; + } + upstream=ALLOC(struct ssl_upstream,1); + upstream->ssl=ssl; + return upstream; +}*/ + +void ssl_upstream_free(struct ssl_upstream* p) +{ + X509_free(p->orig_cert); + //todo close ssl with a callback. + //SSL_free(ctx->ssl); +} +struct ssl_downstream* ssl_downstream_create(void) +{ + struct ssl_downstream* p=NULL; + p=ALLOC(struct ssl_downstream, 1); + return p; +} +void ssl_downstream_free(struct ssl_downstream* p) +{ + free(p->sni); + p->sni=NULL; +// X509_free(p->fake_cert); +} + diff --git a/platform/src/ssl_stream.h b/platform/src/ssl_stream.h deleted file mode 100644 index 8b13789..0000000 --- a/platform/src/ssl_stream.h +++ /dev/null @@ -1 +0,0 @@ - diff --git a/platform/src/tfe_main.cpp b/platform/src/tfe_main.cpp deleted file mode 100644 index a5c2a1a..0000000 --- a/platform/src/tfe_main.cpp +++ /dev/null @@ -1,41 +0,0 @@ -/*- -* Tango Frontend Engine (TFE) 3a -* Part of Tango Security Gateway -* -* Copyright (c) 2018-2023, MESA Lab, https://www.mesalab.cn -* All rights reserved. -*/ -#include "proxy.h" -#include "tfe_util.h" -#include "MESA_handle_logger.h" -#include "MESA_prof_load.h" -#include "wired_cfg.h" -#include -#include -struct tfe_instance* g_tfe_instance=NULL; -struct tfe_config * g_tfe_cfg=NULL; -const char* module_name="TFE"; -int main(int argc, char *argv[]) -{ - const char* main_profile="./conf/tfe_main.conf"; - - tfe_proxy *proxy=NULL; - void* wcfg_handle=NULL; - - wcfg_handle=wired_cfg_create(module_name, main_profile); - wired_cfg_init(wcfg_handle); - wired_cfg_destroy(wcfg_handle); - g_tfe_cfg=ALLOC(struct tfe_config, 1); - MESA_load_profile_uint_def(main_profile, "SYSTEM", "thread_num",&(g_tfe_cfg->thread_num),1); - MESA_load_profile_uint_def(main_profile, "SYSTEM", "use_cert_store",&(g_tfe_cfg->use_cert_store),0); - //TODO: Initiate Local Cert Cache, Decryption Mirror, Field Stat, Logger and etc. - //NOTICE: Maat, Cert Store,Tango Cache are initiated in bussiness plugin. - g_tfe_instance=ALLOC(struct tfe_instance,1); - proxy=tfe_proxy_new(g_tfe_cfg); - - //TODO: Drop privs, chroot - tfe_proxy_run(proxy); - proxy_free(proxy); - -} - diff --git a/platform/src/tfe_proxy.cpp b/platform/src/tfe_proxy.cpp deleted file mode 100644 index 5f0d927..0000000 --- a/platform/src/tfe_proxy.cpp +++ /dev/null @@ -1,407 +0,0 @@ - -#include "proxy.h" -#include "opts.h" -#include "attrib.h" -#include "tfe_util.h" -#include "tfe_stream.h" -#include "tfe_stream_internal.h" -#include "MESA_handle_logger.h" -#include "io_module_kni.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - - -/* - * Proxy engine, built around libevent 2.x. - */ - -#define TFE_BACKLOG_DEFAULT 20 -const char* module_name_pxy="TFE_PXY"; -extern struct tfe_instance* g_tfe_instance; - -__thread int __currect_thread_id; - - -void free_thread_manager(struct tfe_thread_manager_ctx* ctx) -{ - free(ctx->thr_ctx); - ctx->thr_ctx=NULL; - free(ctx); -} - -static void __dummy_event_handler( - UNUSED evutil_socket_t fd, UNUSED short what, - UNUSED void *arg) -{ - /* do nothing */ -} - -/* - * Thread entry point; runs the event loop of the event base. - * Does not exit until the libevent loop is broken explicitly. - */ -static void *__tfe_thrmgr_thread_entry(void *arg) -{ - struct tfe_thread_ctx *ctx = (struct tfe_thread_ctx *) arg; - struct timeval timer_delay = {60, 0}; - - struct event *ev; - ev = event_new(ctx->evbase, -1, EV_PERSIST, __dummy_event_handler, NULL); - - if (!ev) return NULL; - - evtimer_add(ev, &timer_delay); - ctx->running = 1; - - __currect_thread_id = ctx->thread_id; - MESA_handle_runtime_log(g_tfe_instance->main_logger, RLOG_LV_FATAL, module_name_pxy,"EventThread %d is running...", __currect_thread_id); - - event_base_dispatch(ctx->evbase); - event_free(ev); - return NULL; -} - -int tfe_thread_manager_run(struct tfe_thread_manager_ctx *ctx) -{ - unsigned int thread_id; - - for (thread_id = 0; thread_id < ctx->nr_thread; thread_id++) - { - if (pthread_create(&(ctx->thr_ctx[thread_id].thr), NULL, - __tfe_thrmgr_thread_entry, &(ctx->thr_ctx[thread_id]))) - { - MESA_handle_runtime_log(g_tfe_instance->main_logger, RLOG_LV_FATAL, module_name_pxy, "pthread_create failed."); - } - - while (!ctx->thr_ctx[thread_id].running) - { - sched_yield(); - } - } - MESA_handle_runtime_log(g_tfe_instance->main_logger, RLOG_LV_INFO, module_name_pxy,"Started %d connection handling threads\n", ctx->nr_thread); - return 0; -} - - -static int signals[] = {SIGTERM, SIGQUIT, SIGHUP, SIGINT, SIGPIPE, SIGUSR1}; - -struct tfe_proxy -{ - char name[32]; - struct event_base * evbase; - struct event * sev[sizeof(signals) / sizeof(int)]; - struct event * gcev; - struct proxy_listener_ctx * lctx; - struct tfe_config * opts; - void* main_logger; - unsigned int thread_num; - struct tfe_thread_ctx* work_threads; - void* io_mod; - cert_mgr* cert_mgr; - struct sess_cache* dsess_cache; - struct sess_cache* ssess_cache; - int module_num; - struct tfe_plugin* modules; -}; - -/* - * Signal handler for SIGTERM, SIGQUIT, SIGINT, SIGHUP, SIGPIPE and SIGUSR1. - */ -static void -proxy_signal_cb(evutil_socket_t fd, UNUSED short what, void * arg) -{ - tfe_proxy * ctx = (tfe_proxy *) arg; - if (OPTS_DEBUG(ctx->opts)) - { - log_dbg_printf("Received signal %i\n", fd); - } - - switch (fd) - { - case SIGTERM: - case SIGQUIT: - case SIGINT: - case SIGHUP: proxy_loopbreak(ctx); - break; - -#if 0 - case SIGUSR1: - if (log_reopen() == -1) { - log_err_printf("Warning: Failed to reopen logs\n"); - } else { - log_dbg_printf("Reopened log files\n"); - } - break; -#endif - - case SIGPIPE: log_err_printf("Warning: Received SIGPIPE; ignoring.\n"); - break; - default: log_err_printf("Warning: Received unexpected signal %i\n", fd); - break; - } -} - -/* - * Garbage collection handler. - */ -static void -proxy_gc_cb(UNUSED evutil_socket_t fd, UNUSED short what, void * arg) -{ - tfe_proxy * ctx = (tfe_proxy *) arg; - - if (OPTS_DEBUG(ctx->opts)) - log_dbg_printf("Garbage collecting caches started.\n"); - -// cachemgr_gc(); - - if (OPTS_DEBUG(ctx->opts)) - log_dbg_printf("Garbage collecting caches done.\n"); -} -int select_work_thread(struct tfe_proxy *pxy) -{ - - int min_thread_id = 0; - size_t min_load = pxy->work_threads[min_thread_id]->load; - - for (unsigned thread_id = 1; thread_id < pxy->thread_num; thread_id++) - { - if (min_load > pxy->work_threads[thread_id]->load) - { - min_load = pxy->work_threads[thread_id]->load; - min_thread_id = thread_id; - } - } - pxy->work_threads[min_thread_id]->load++; - return min_thread_id; -} -/* - * Callback for accept events on the socket listener bufferevent. - */ -static void io_mod_accept_cb( evutil_socket_t upstream_fd, evutil_socket_t downstream_fd, enum tfe_session_proto session_type, - struct sockaddr * peeraddr, int peeraddrlen, void * arg) -{ - struct tfe_proxy* pxy=(struct tfe_proxy* )arg; - struct tfe_stream_private* stream=NULL; - int tid=-1; - tid=select_work_thread(pxy); - stream=tfe_stream_create(upstream_fd, downstream_fd, session_type,peeraddr, peeraddrlen, pxy->work_threads+tid); - tfe_stream_setup(stream); - return; -} - -/* - * Set up the core event loop. - * Socket clisock is the privsep client socket used for binding to ports. - * Returns ctx on success, or NULL on error. - */ -struct tfe_proxy * tfe_proxy_new(tfe_config * opts) -{ - struct tfe_proxy * proxy=NULL; - size_t i = 0; - int ret=0; - - struct timeval gc_delay = {60, 0}; - - /* adds locking, only required if accessed from separate threads */ - evthread_use_pthreads(); - - if (OPTS_DEBUG(opts)) - { - event_enable_debug_mode(); - } - - proxy = ALLOC(struct tfe_proxy,1); - proxy->opts = opts; - proxy->evbase = event_base_new(); - if (!proxy->evbase) - { - log_err_printf("Error getting event base\n"); - goto error_out; - } - - if (OPTS_DEBUG(opts)) - { - proxy_debug_base(proxy->evbase); - } - switch(opts->input_io_module) - { - case IO_MOD_KNI: - strcpy(proxy->name,"KNI_PXY"); - proxy->io_mod=io_kni_init(opts->kni_path,proxy->evbase); - assert(ret>=0); - break; - case IO_MOD_TPROXY: - default: - assert(0); - break; - } - proxy->thread_num=opts->thread_num; - proxy->cert_mgr=cert_manager_init(); - proxy->dsess_cache=session_cache_init(); - proxy->ssess_cache=session_cache_init(); - - proxy->module_num=2; - proxy->modules=ALLOC(struct tfe_plugin,proxy->module_num); - proxy->modules[0].proto=APP_PROTO_HTTP1; - //todo: setup each protocol module. - //proxy->modules[0].on_read=xxx; - proxy->modules[1].proto=APP_PROTO_HTTP2; - - proxy->work_threads=ALLOC(struct tfe_thread_ctx, proxy->thread_num); - for(i=0;ithread_num;i++) - { - proxy->work_threads[i].thread_id=i; - proxy->work_threads[i].evbase = event_base_new(); - proxy->work_threads[i].opts=opts; - proxy->work_threads[i].cert_mgr=proxy->cert_mgr;//reference - proxy->work_threads[i].dsess_cache=proxy->dsess_cache; - proxy->work_threads[i].ssess_cache=proxy->ssess_cache; - proxy->work_threads[i].module_num=proxy->module_num; - proxy->work_threads[i].modules=proxy->modules - } - //Todo: Not handle signal if have mutliple proxy instance. - for (i = 0; i < (sizeof(signals) / sizeof(int)); i++) - { - proxy->sev[i] = evsignal_new(proxy->evbase, signals[i], proxy_signal_cb, proxy); - if (!proxy->sev[i]) - goto error_out; - evsignal_add(proxy->sev[i], NULL); - } - - proxy->gcev = event_new(proxy->evbase, -1, EV_PERSIST, proxy_gc_cb, proxy); - if (!proxy->gcev) - goto error_out; - - evtimer_add(proxy->gcev, &gc_delay); - return proxy; -error_out: - if (proxy->gcev) - { - event_free(proxy->gcev); - } - - for (size_t i = 0; i < (sizeof(proxy->sev) / sizeof(proxy->sev[0])); i++) - { - if (proxy->sev[i]) - { - event_free(proxy->sev[i]); - } - } - - if (proxy->lctx) - { - proxy_listener_ctx_free(proxy->lctx); - } - for(i=0;ithread_num;i++) - { - proxy->work_threads[i].thread_id=i; - event_base_free(proxy->work_threads[i].evbase); - } - - event_base_free(proxy->evbase); - - free(proxy); - return NULL; -} - -/* - * Run the event loop. Returns when the event loop is cancelled by a signal - * or on failure. - */ -void tfe_proxy_run(struct tfe_proxy * proxy) -{ - if (proxy->opts->detach) - { - event_reinit(proxy->evbase); - } -#ifndef PURIFY - if (OPTS_DEBUG(proxy->opts)) - { - event_base_dump_events(proxy->evbase, stderr); - } -#endif /* PURIFY */ - unsigned int thread_id; - - for (thread_id = 0; thread_id < proxy->thread_num; thread_id++) - { - if (pthread_create(&(proxy->work_threads[thread_id].thr), NULL, - __tfe_thrmgr_thread_entry, &(proxy->work_threads[thread_id]))) - { - MESA_handle_runtime_log(proxy->main_logger, RLOG_LV_FATAL, proxy->name, "pthread_create failed."); - } - - while (!proxy->work_threads[thread_id].running) - { - sched_yield(); - } - } - MESA_handle_runtime_log(proxy->main_logger, RLOG_LV_INFO, proxy->name,"Started %d connection handling threads\n", proxy->nr_thread); - if (OPTS_DEBUG(proxy->opts)) - { - log_dbg_printf("Starting main event loop.\n"); - } - event_base_dispatch(proxy->evbase); - if (OPTS_DEBUG(proxy->opts)) - { - log_dbg_printf("Main event loop stopped.\n"); - } -} - -/* - * Break the loop of the proxy, causing the tfe_proxy_run to return. - */ -void -proxy_loopbreak(tfe_proxy * ctx) -{ - event_base_loopbreak(ctx->evbase); -} - -/* - * Free the proxy data structures. - */ -void -proxy_free(tfe_proxy * ctx) -{ - if (ctx->gcev) - { - event_free(ctx->gcev); - } - if (ctx->lctx) - { - proxy_listener_ctx_free(ctx->lctx); - } - for (size_t i = 0; i < (sizeof(ctx->sev) / sizeof(ctx->sev[0])); i++) - { - if (ctx->sev[i]) - { - event_free(ctx->sev[i]); - } - } - if (ctx->thrmgr) - { - free_thread_manager(ctx->thrmgr); - } - if (ctx->evbase) - { - event_base_free(ctx->evbase); - } - free(ctx); -} - -/* vim: set noet ft=c: */ diff --git a/platform/src/tfe_stream.cpp b/platform/src/tfe_stream.cpp index d895898..6ba40bf 100644 --- a/platform/src/tfe_stream.cpp +++ b/platform/src/tfe_stream.cpp @@ -1,15 +1,10 @@ -#include "tfe_stream.h" -#include "tfe_util.h" -#include "opts.h" -#include "attrib.h" - - #include #include #include #include #include +#include #include #include @@ -25,17 +20,23 @@ #include #include -#define STREAM_EVBASE(s) ((s)->thrmgr_ref->evbase) +#include +#include +#include +#include +#include + +#define STREAM_EVBASE(s) ((s)->thrmgr_ref->evbase) /* * Maximum size of data to buffer per connection direction before * temporarily stopping to read data from the other end. */ -#define OUTBUF_LIMIT (1024*1024) +#define OUTBUF_LIMIT (1024*1024) /* * Print helper for logging code. */ -#define STRORDASH(x) (((x)&&*(x))?(x):"-") +#define STRORDASH(x) (((x)&&*(x))?(x):"-") /* * Context used for all server http_sessions_. @@ -44,85 +45,108 @@ static unsigned long ssl_session_context = 0x31415926; #endif /* USE_SSL_SESSION_ID_CONTEXT */ - /* forward declaration of libevent callbacks */ static void tfe_stream_readcb(struct bufferevent *, void *); static void tfe_stream_writecb(struct bufferevent *, void *); static void tfe_stream_eventcb(struct bufferevent *, short, void *); static void stream_fd_readcb(evutil_socket_t, short, void *); - -void tfe_stream_detach(const struct tfe_stream* stream) +static void tfe_stream_free(struct tfe_stream_private * stream) { - struct tfe_stream_private* _stream=(struct tfe_stream_private*)stream; - int plug_id=_stream->calling_idx; - _stream->plug_ctx[plug_id].state=PLUG_STATE_DETACHED; + struct tfe_thread_ctx * thread = stream->thrmgr_ref; + thread->load--; + + switch (stream->session_type) + { + case SESSION_PROTO_SSL: +#if 0 + ssl_upstream_free(stream->ssl_upstream); + ssl_downstream_free(stream->ssl_downstream); +#endif + thread->stat.value[SSL_NUM]--; + break; + default: break; + } + + free(stream); + thread->stat.value[STREAM_NUM]--; return; } -int tfe_stream_preempt(const struct tfe_stream* stream) + +void tfe_stream_detach(const struct tfe_stream * stream) { - struct tfe_stream_private* _stream=(struct tfe_stream_private*)stream; - int plug_id=_stream->calling_idx; - int i=0; - for(i=0;i<_stream->plugin_num;i++) + struct tfe_stream_private * _stream = (struct tfe_stream_private *) stream; + int plug_id = _stream->calling_idx; + _stream->plug_ctx[plug_id].state = PLUG_STATE_DETACHED; + return; +} + +int tfe_stream_preempt(const struct tfe_stream * stream) +{ + struct tfe_stream_private * _stream = (struct tfe_stream_private *) stream; + int plug_id = _stream->calling_idx; + int i = 0; + for (i = 0; i < _stream->plugin_num; i++) { - if(_stream->plug_ctx[i].state==PLUG_STATE_PREEPTION) + if (_stream->plug_ctx[i].state == PLUG_STATE_PREEPTION) { return -1; } } - _stream->plug_ctx[plug_id].state=PLUG_STATE_PREEPTION; + _stream->plug_ctx[plug_id].state = PLUG_STATE_PREEPTION; return 0; } -inline struct tfe_conn_private* __this_conn(struct tfe_stream_private* _stream, enum tfe_conn_dir dir) +static inline struct tfe_conn_private * __this_conn(struct tfe_stream_private * _stream, enum tfe_conn_dir dir) { - struct tfe_conn_private* this_conn=NULL; - this_conn=((dir==CONN_DIR_UPSTREAM)? &(_stream->conn_downstream):&(_stream->conn_upstream)); + struct tfe_conn_private * this_conn = NULL; + this_conn = ((dir == CONN_DIR_UPSTREAM) ? &(_stream->conn_downstream) : &(_stream->conn_upstream)); return this_conn; } -inline struct tfe_conn_private* __peer_conn(struct tfe_stream_private* _stream, enum tfe_conn_dir dir) + +static inline struct tfe_conn_private * __peer_conn(struct tfe_stream_private * _stream, enum tfe_conn_dir dir) { - struct tfe_conn_private* peer_conn=NULL; - peer_conn=(dir==CONN_DIR_UPSTREAM)? &(_stream->conn_downstream):&(_stream->conn_upstream); + struct tfe_conn_private * peer_conn = NULL; + peer_conn = (dir == CONN_DIR_UPSTREAM) ? &(_stream->conn_downstream) : &(_stream->conn_upstream); return peer_conn; } -struct tfe_stream_write_ctx* tfe_stream_write_frag_start(const struct tfe_stream* stream, enum tfe_conn_dir dir) + +struct tfe_stream_write_ctx * tfe_stream_write_frag_start(const struct tfe_stream * stream, enum tfe_conn_dir dir) { - struct tfe_stream_private* _stream=(struct tfe_stream_private*)stream; - struct tfe_conn_private* this_conn=__this_conn(_stream, dir); - struct tfe_conn_private* peer_conn=__peer_conn(_stream, dir); - if(this_conn->on_writing==1) + struct tfe_stream_private * _stream = (struct tfe_stream_private *) stream; + struct tfe_conn_private * this_conn = __this_conn(_stream, dir); + struct tfe_conn_private * peer_conn = __peer_conn(_stream, dir); + if (this_conn->on_writing == 1) { return NULL; } - this_conn->w_ctx.dir=dir; - this_conn->w_ctx._stream=_stream; - this_conn->on_writing=1; + this_conn->w_ctx.dir = dir; + this_conn->w_ctx._stream = _stream; + this_conn->on_writing = 1; bufferevent_disable(peer_conn->bev, EV_READ); return &(this_conn->w_ctx); } -int tfe_stream_write_frag(struct tfe_stream_write_ctx* w_ctx,const unsigned char *data, size_t size) +int tfe_stream_write_frag(struct tfe_stream_write_ctx * w_ctx, const unsigned char * data, size_t size) { - struct tfe_conn_private* this_conn=__this_conn(w_ctx->_stream, w_ctx->dir);; - int ret=bufferevent_write(this_conn->bev, data, size); + struct tfe_conn_private * this_conn = __this_conn(w_ctx->_stream, w_ctx->dir);; + int ret = bufferevent_write(this_conn->bev, data, size); return ret; } -void tfe_stream_write_frag_end(struct tfe_stream_write_ctx* w_ctx) +void tfe_stream_write_frag_end(struct tfe_stream_write_ctx * w_ctx) { - struct tfe_conn_private* this_conn=__this_conn(w_ctx->_stream, w_ctx->dir); - struct tfe_conn_private* peer_conn=__peer_conn(w_ctx->_stream, w_ctx->dir); - this_conn->on_writing=0; + struct tfe_conn_private * this_conn = __this_conn(w_ctx->_stream, w_ctx->dir); + struct tfe_conn_private * peer_conn = __peer_conn(w_ctx->_stream, w_ctx->dir); + this_conn->on_writing = 0; bufferevent_enable(peer_conn->bev, EV_READ); return; } -int tfe_stream_write(const struct tfe_stream* stream, enum tfe_conn_dir dir, const unsigned char *data, size_t size) +int tfe_stream_write(const struct tfe_stream * stream, enum tfe_conn_dir dir, const unsigned char * data, size_t size) { - int ret=0; - struct tfe_stream_write_ctx* wctx=tfe_stream_write_frag_start( stream, dir); - ret=tfe_stream_write_frag(wctx, data, size); + int ret = 0; + struct tfe_stream_write_ctx * wctx = tfe_stream_write_frag_start(stream, dir); + ret = tfe_stream_write_frag(wctx, data, size); tfe_stream_write_frag_end(wctx); return ret; } @@ -209,70 +233,75 @@ enum tfe_stream_action tfe_stream_call_plugin(struct tfe_stream_private* _stream static void tfe_stream_readcb(struct bufferevent * bev, void * arg) { - struct tfe_stream_private* _stream=(struct tfe_stream_private*)arg; - enum tfe_conn_dir dir=(bev == _stream->conn_downstream.bev)? CONN_DIR_UPSTREAM : CONN_DIR_DOWNSTREAM; - struct tfe_conn_private* this_conn=__this_conn(_stream, dir); - struct tfe_conn_private* peer_conn= __peer_conn(_stream, dir); + struct tfe_stream_private * _stream = (struct tfe_stream_private *) arg; + enum tfe_conn_dir dir = (bev == _stream->conn_downstream.bev) ? CONN_DIR_UPSTREAM : CONN_DIR_DOWNSTREAM; + struct tfe_conn_private * this_conn = __this_conn(_stream, dir); + struct tfe_conn_private * peer_conn = __peer_conn(_stream, dir); - int i=0,ret=0; - enum tfe_stream_action action_tmp=ACTION_FORWARD_DATA, action_final=ACTION_FORWARD_DATA; - + int i = 0, ret = 0; + enum tfe_stream_action action_tmp = ACTION_FORWARD_DATA, action_final = ACTION_FORWARD_DATA; - - const struct tfe_plugin* plugins=_stream->thrmgr_ref->modules; - struct plugin_ctx* plug_ctx=NULL; - int plug_num=_stream->thrmgr_ref->module_num; + const struct tfe_plugin * plugins = _stream->thrmgr_ref->modules; + struct plugin_ctx * plug_ctx = NULL; + int plug_num = _stream->thrmgr_ref->nr_modules; struct evbuffer * inbuf = bufferevent_get_input(bev); - struct evbuffer * outbuf = bufferevent_get_output(peer_conn->bev); - size_t contigous_len=evbuffer_get_length(inbuf),drain_size=0; - const char* contiguous_data=evbuffer_pullup(inbuf,contigous_len); - _stream->defere_bytes=0; - _stream->drop_bytes=0; - _stream->forward_bytes=0; - for(i=0;ibev); + + size_t contigous_len = evbuffer_get_length(inbuf), drain_size = 0; + const unsigned char * contiguous_data = (const unsigned char *)evbuffer_pullup(inbuf, contigous_len); + + _stream->defere_bytes = 0; + _stream->drop_bytes = 0; + _stream->forward_bytes = 0; + + for (i = 0; i < plug_num; i++) { - _stream->calling_idx=i; - plug_ctx=_stream->plug_ctx+i; - if(_stream->is_plugin_opened==0) + _stream->calling_idx = i; + plug_ctx = _stream->plug_ctx + i; + + if (_stream->is_plugin_opened == 0) { - action_tmp=plugins[i].on_open(&_stream.head, _stream->thrmgr_ref->thread_id, dir, contiguous_data,contigous_len, &(plug_ctx->pme)); - _stream->is_plugin_opened=1; + action_tmp = plugins[i].on_open(&_stream->head, _stream->thrmgr_ref->thread_id, + dir, contiguous_data, contigous_len, &(plug_ctx->pme)); + _stream->is_plugin_opened = 1; } else { - action_tmp=plugins[i].on_data(&_stream.head, _stream->thrmgr_ref->thread_id, dir, contiguous_data,contigous_len, &(plug_ctx->pme)); + action_tmp = plugins[i].on_data(&_stream->head, _stream->thrmgr_ref->thread_id, + dir, contiguous_data, contigous_len, &(plug_ctx->pme)); } - if(plug_ctx->state=PLUG_STATE_PREEPTION) + + if (plug_ctx->state == PLUG_STATE_PREEPTION) { - action_final=action_tmp; + action_final = action_tmp; } } - switch (action_final) + switch (action_final) { case ACTION_FORWARD_DATA: - if(_stream->forward_bytes>0) + if (_stream->forward_bytes > 0) { evbuffer_remove_buffer(inbuf, outbuf, _stream->forward_bytes); } else { evbuffer_add_buffer(outbuf, inbuf); - } + } break; case ACTION_DROP_DATA: - if(_stream->drop_bytes>0) + if (_stream->drop_bytes > 0) { - drain_size=_stream->drop_bytes; + drain_size = _stream->drop_bytes; } else { - drain_size= evbuffer_get_length(inbuf); + drain_size = evbuffer_get_length(inbuf); } - evbuffer_drain(inbuf,drain_size); + evbuffer_drain(inbuf, drain_size); case ACTION_DEFER_DATA: - if(_stream->defere_bytes>0) + if (_stream->defere_bytes > 0) { bufferevent_setwatermark(bev, EV_WRITE, _stream->defere_bytes, 0); } @@ -281,11 +310,12 @@ static void tfe_stream_readcb(struct bufferevent * bev, void * arg) assert(0); break; } - if(evbuffer_get_length(inbuf) != 0) + + if (evbuffer_get_length(inbuf) != 0) { bufferevent_trigger(bev, EV_READ, BEV_OPT_DEFER_CALLBACKS); } - + if (evbuffer_get_length(outbuf) >= OUTBUF_LIMIT) { /* temporarily disable data source; @@ -304,10 +334,10 @@ static void tfe_stream_readcb(struct bufferevent * bev, void * arg) */ static void tfe_stream_writecb(struct bufferevent * bev, void * arg) { - struct tfe_stream_private* _stream=(struct tfe_stream_private*)arg; - enum tfe_conn_dir dir=(bev == _stream->conn_downstream.bev)? CONN_DIR_UPSTREAM : CONN_DIR_DOWNSTREAM; - struct tfe_conn_private* this_conn=__this_conn(_stream, dir); - struct tfe_conn_private* peer_conn= __peer_conn(_stream, dir); + struct tfe_stream_private * _stream = (struct tfe_stream_private *) arg; + enum tfe_conn_dir dir = (bev == _stream->conn_downstream.bev) ? CONN_DIR_UPSTREAM : CONN_DIR_DOWNSTREAM; + struct tfe_conn_private * this_conn = __this_conn(_stream, dir); + struct tfe_conn_private * peer_conn = __peer_conn(_stream, dir); struct evbuffer * outbuf = bufferevent_get_output(bev); @@ -326,20 +356,20 @@ static void tfe_stream_writecb(struct bufferevent * bev, void * arg) */ static void tfe_stream_eventcb(struct bufferevent * bev, short events, void * arg) { - struct tfe_stream_private* _stream=(struct tfe_stream_private*)arg; - enum tfe_conn_dir dir=(bev == _stream->conn_downstream.bev)? CONN_DIR_UPSTREAM : CONN_DIR_DOWNSTREAM; - struct tfe_conn_private* this_conn=__this_conn(_stream, dir); - struct tfe_conn_private* peer_conn= __peer_conn(_stream, dir); + struct tfe_stream_private * _stream = (struct tfe_stream_private *) arg; + enum tfe_conn_dir dir = (bev == _stream->conn_downstream.bev) ? CONN_DIR_UPSTREAM : CONN_DIR_DOWNSTREAM; + struct tfe_conn_private * this_conn = __this_conn(_stream, dir); + struct tfe_conn_private * peer_conn = __peer_conn(_stream, dir); - const struct tfe_plugin* plugins=_stream->thrmgr_ref->modules; - struct plugin_ctx* plug_ctx=NULL; - int plug_num=_stream->thrmgr_ref->module_num,i=0; - enum tfe_stream_close_reason reason=REASON_PASSIVE_CLOSED; + const struct tfe_plugin * plugins = _stream->thrmgr_ref->modules; + struct plugin_ctx * plug_ctx = NULL; + int plug_num = _stream->thrmgr_ref->nr_modules, i = 0; + enum tfe_stream_close_reason reason = REASON_PASSIVE_CLOSED; if (events & BEV_EVENT_ERROR) { - this_conn->closed=1; - reason=REASON_ERROR; + this_conn->closed = 1; + reason = REASON_ERROR; goto call_plugin_close; } @@ -347,120 +377,110 @@ static void tfe_stream_eventcb(struct bufferevent * bev, short events, void * ar { //generate a 0 size read callback to notify plugins. tfe_stream_readcb(bev, arg); - this_conn->closed=1; + this_conn->closed = 1; } - if(peer_conn->closed==1&&this_conn->closed==1) + if (peer_conn->closed == 1 && this_conn->closed == 1) { - reason=REASON_PASSIVE_CLOSED; + reason = REASON_PASSIVE_CLOSED; goto call_plugin_close; } return; + call_plugin_close: - for(i=0;icalling_idx=i; - plug_ctx=_stream->plug_ctx+i; - plugins[i].on_close(&(_stream.head), _stream->thrmgr_ref->thread_id, reason, &(plug_ctx->pme)); + _stream->calling_idx = i; + plug_ctx = _stream->plug_ctx + i; + plugins[i].on_close(&(_stream->head), _stream->thrmgr_ref->thread_id, reason, &(plug_ctx->pme)); } + tfe_stream_free(_stream); return; } -static void tfe_stream_free(struct tfe_stream_private* stream) -{ - pxy_conn_ctx_t * ctx; - int by_requestor; - struct tfe_thread_ctx* thread=stream->thrmgr_ref; - thread->load--; - switch (stream->session_type) - { - case SESSION_PROTO_SSL: - ssl_upstream_free(stream->ssl_upstream); - ssl_downstream_free(stream->ssl_downstream); - thread->stat.value[SSL_NUM]--; - break; - default: - break; - } - free(stream); - thread->stat.value[STREAM_NUM]--; - return; -} - void ssl_get_cert_on_succ(void * result, void * user) { - cert_t* cert=(cert_t*)result; - struct tfe_stream_private* _stream=(struct tfe_stream_private*)user; - _stream->ssl_downstream->ssl=downstream_ssl_create(_stream, ); + cert_t * cert = (cert_t *) result; + struct tfe_stream_private * _stream = (struct tfe_stream_private *) user; + + //_stream->ssl_downstream->ssl = downstream_ssl_create(_stream); + //_stream->ssl_downstream->ssl = downstream_ssl_create(_stream); cert_free(cert); - - bufferevent_setcb(_stream->head.upstream.bev, tfe_stream_readcb, tfe_stream_writecb, tfe_stream_eventcb, _stream); - bufferevent_setcb(_stream->head.downstream.bev, tfe_stream_readcb, tfe_stream_writecb, tfe_stream_eventcb, _stream); - bufferevent_enable(_stream->head.upstream.bev, EV_READ | EV_WRITE); - bufferevent_enable(_stream->head.downstream.bev, EV_READ | EV_WRITE); - + + bufferevent_setcb(_stream->head.upstream.bev, tfe_stream_readcb, tfe_stream_writecb, tfe_stream_eventcb, _stream); + bufferevent_setcb(_stream->head.downstream.bev, tfe_stream_readcb, tfe_stream_writecb, tfe_stream_eventcb, _stream); + bufferevent_enable(_stream->head.upstream.bev, EV_READ | EV_WRITE); + bufferevent_enable(_stream->head.downstream.bev, EV_READ | EV_WRITE); future_destroy(_stream->ssl_downstream->future_get_cert); - _stream->ssl_downstream->future_get_cert=NULL; + _stream->ssl_downstream->future_get_cert = NULL; return; } + void ssl_get_cert_on_fail(enum e_future_error err, const char * what, void * user) { - //todo assert(0); } void ssl_conn_origin_on_succ(void * result, void * user) { - struct bufferevent *bev=(struct bufferevent *)result; - struct tfe_stream_private* _stream=(struct tfe_stream_private*)user; - - _stream->head.upstream.bev=bev; - _stream->ssl_upstream->ssl=bufferevent_openssl_get_ssl(bev); /* does not inc refc */ - _stream->ssl_upstream->orig_cert=SSL_get_peer_certificate(_stream->ssl_upstream->ssl); + struct bufferevent * bev = (struct bufferevent *) result; + struct tfe_stream_private * _stream = (struct tfe_stream_private *) user; - session_cache_set(_stream->thrmgr_ref->dsess_cache, struct sockaddr * addr, addrlen,_stream->ssl_downstream->sni, SSL_get0_session(_stream->ssl_upstream->ssl)); + _stream->head.upstream.bev = bev; + _stream->ssl_upstream->ssl = bufferevent_openssl_get_ssl(bev); /* does not inc refc */ + _stream->ssl_upstream->orig_cert = SSL_get_peer_certificate(_stream->ssl_upstream->ssl); - _stream->ssl_downstream->future_get_cert=future_create(ssl_get_cert_on_succ, ssl_get_cert_on_fail, _stream); - cert_mgr_async_get( _stream->ssl_downstream->future_get_cert, - _stream->thrmgr_ref->cert_mgr, - _stream->ssl_downstream->sni, - _stream->ssl_downstream->keyring_id, - _stream->ssl_upstream->orig_cert); +#if 0 + session_cache_set(_stream->thrmgr_ref->dsess_cache, _stream->ssl_downstream->sni, + SSL_get0_session(_stream->ssl_upstream->ssl)); +#endif + + _stream->ssl_downstream->future_get_cert = future_create(ssl_get_cert_on_succ, ssl_get_cert_on_fail, _stream); + +#if 0 + cert_mgr_async_get(_stream->ssl_downstream->future_get_cert, + _stream->thrmgr_ref->cert_mgr, + _stream->ssl_downstream->sni, + _stream->ssl_downstream->keyring_id, + _stream->ssl_upstream->orig_cert); +#endif future_destroy(_stream->ssl_upstream->conn_ssl_srv); - _stream->ssl_upstream->conn_ssl_srv=NULL; - return; + _stream->ssl_upstream->conn_ssl_srv = NULL; } + void ssl_conn_origin_on_fail(enum e_future_error err, const char * what, void * user) { - //todo + //TODO: assert(0); } - -void peek_sni_on_succ(void* result, void* user) +void peek_sni_on_succ(void * result, void * user) { - struct tfe_stream_private* _stream=(struct tfe_stream_private*)user; - assert(_stream->session_type==SESSION_PROTO_SSL); - _stream->ssl_downstream->sni=tfe_strdup((const char *)result); + struct tfe_stream_private * _stream = (struct tfe_stream_private *) user; + assert(_stream->session_type == SESSION_PROTO_SSL); + struct ssl_downstream * ssl_downstream = _stream->ssl_downstream; + struct ssl_upstream * ssl_upstream = _stream->ssl_upstream; + + _stream->ssl_downstream->sni = tfe_strdup((const char *) result); future_destroy(ssl_downstream->future_sni_peek); - ssl_downstream->future_sni_peek=NULL; - - _stream->ssl_upstream=ALLOC(struct ssl_upstream,1); - _stream->ssl_upstream->conn_ssl_srv=future_create(ssl_conn_origin_on_succ, ssl_conn_origin_on_fail, _stream); - ssl_async_connect_origin(_stream->ssl_upstream->conn_ssl_srv, _stream->fd_upstream, _stream->ssl_downstream->sni, _stream->thrmgr_ref->evbase, _stream->thrmgr_ref->opts); + ssl_downstream->future_sni_peek = NULL; + _stream->ssl_upstream = ALLOC(struct ssl_upstream, 1); + _stream->ssl_upstream->conn_ssl_srv = future_create(ssl_conn_origin_on_succ, ssl_conn_origin_on_fail, _stream); + ssl_async_connect_origin(_stream->ssl_upstream->conn_ssl_srv, _stream->fd_upstream, _stream->ssl_downstream->sni, + _stream->thrmgr_ref->evbase, NULL); } + void peek_sni_on_fail(enum e_future_error err, const char * what, void * user) { - //todo + //TODO: assert(0); } - /* * Callback for accept events on the socket listener bufferevent. * Called when a new incoming connection has been accepted. @@ -472,42 +492,41 @@ void peek_sni_on_fail(enum e_future_error err, const char * what, void * user) * start reading from the client while waiting on the connection to * the server to connect. */ -void tfe_stream_setup(struct tfe_stream_private* _stream) +void tfe_stream_setup(struct tfe_stream_private * _stream) { - struct future* f_sni=NULL; - tfe_thread_ctx* thread=_stream->thrmgr_ref; + struct future * f_sni = NULL; + tfe_thread_ctx * thread = _stream->thrmgr_ref; switch (_stream->session_type) { case SESSION_PROTO_SSL: // for SSL, defer dst connection setup to initial_readcb - _stream->ssl_downstream=ssl_downstream_create(); - _stream->async_future=future_create(peek_sni_on_succ, peek_sni_on_fail, _stream); - ssl_async_peek_sni(_stream->ssl_downstream->future_sni_peek, _stream->fd_downstream, _stream->thrmgr_ref->evbase); + _stream->ssl_downstream = ssl_downstream_create(); + _stream->async_future = future_create(peek_sni_on_succ, peek_sni_on_fail, _stream); + ssl_async_peek_sni(_stream->ssl_downstream->future_sni_peek, _stream->fd_downstream, + _stream->thrmgr_ref->evbase); thread->stat.value[SSL_NUM]++; - break + break; default: //todo: stream_fd_readcb(_stream->fd_downstream, 0, _stream); break; } + return; } -struct tfe_stream_private* tfe_stream_create(evutil_socket_t fd_downstream, evutil_socket_t fd_upstream, - struct sockaddr * peeraddr, int peeraddrlen, - enum tfe_session_proto session_type, tfe_thread_ctx* thread) +struct tfe_stream_private * tfe_stream_create(evutil_socket_t fd_downstream, evutil_socket_t fd_upstream, + enum tfe_session_proto session_type, tfe_thread_ctx * thread) { - struct tfe_stream_private* conn_private=NULL; - struct tfe_stream* conn_public=NULL; - conn_private= ALLOC(struct tfe_stream_private, 1); - conn_private->session_type=session_type; - conn_private->fd_downstream=fd_downstream; - conn_private->fd_upstream=fd_upstream; - conn_private->thrmgr_ref=thread; - conn_private->is_plugin_opened=0; - conn_public=&(conn_private->head); - //todo - addr_sock2layer(conn_public->downstream.addr,peeraddr,peeraddrlen); + struct tfe_stream_private * conn_private = NULL; + struct tfe_stream * conn_public = NULL; + conn_private = ALLOC(struct tfe_stream_private, 1); + conn_private->session_type = session_type; + conn_private->fd_downstream = fd_downstream; + conn_private->fd_upstream = fd_upstream; + conn_private->thrmgr_ref = thread; + conn_private->is_plugin_opened = 0; + conn_public = &(conn_private->head); thread->stat.value[STREAM_NUM]++; return conn_private; } diff --git a/platform/src/tfe_stream_internal.h b/platform/src/tfe_stream_internal.h deleted file mode 100644 index 56f276c..0000000 --- a/platform/src/tfe_stream_internal.h +++ /dev/null @@ -1,98 +0,0 @@ -#include "tfe_stream.h" -#include "ssl_stream.h" -#include "tfe_stat.h" -struct tfe_thread_ctx -{ - pthread_t thr; - unsigned int thread_id; - size_t load; - struct event_base *evbase; - unsigned char running; - struct tfe_stats stat; - tfe_config* opts; - cert_mgr cert_mgr; - struct sess_cache* dsess_cache; - struct sess_cache* ssess_cache; - const int module_num; - const struct tfe_plugin* modules; -}; - - -//Downstream: comunication form client to proxy -//Upstream: communication form proxy to server -struct ssl_downstream -{ - /* server name indicated by client in SNI TLS extension */ - char *sni; - SSL *ssl; - X509* fake_cert_ref;//? - int keyring_id; - struct future* future_sni_peek; - struct future* future_get_cert; - -}; - -struct ssl_upstream -{ - X509 *orig_cert; - SSL *ssl; - struct future* conn_ssl_srv; -}; -enum tfe_plugin_state -{ - PLUG_STATE_READONLY, - PLUG_STATE_PREEPTION, - PLUG_STATE_DETACHED -}; -struct plugin_ctx -{ - - enum tfe_plugin_state state; - void *pme; -}; -struct tfe_stream_write_ctx -{ - struct tfe_stream_private* _stream; - enum tfe_conn_dir dir; -}; -struct tfe_conn_private -{ - evutil_socket_t fd; - struct bufferevent *bev; - uint8_t on_writing; - uint8_t closed; - uint8_t need_shutdown; - struct tfe_stream_write_ctx w_ctx; -}; -struct tfe_stream_private -{ - struct tfe_stream head; - enum tfe_session_proto session_type; - struct tfe_conn_private conn_upstream; - struct tfe_conn_private conn_downstream; - union - { - struct ssl_downstream* ssl_downstream; - void* raw_downstream; - }; - union - { - struct ssl_upstream* ssl_upstream; - void* raw_upstream; - }; - uint8_t is_plugin_opened; - int calling_idx; - size_t forward_bytes; - size_t defere_bytes; - size_t drop_bytes; - enum tfe_app_proto app_proto; - int plugin_num; - struct plugin_ctx* plug_ctx; - unsigned char passthrough; /* 1 if SSL passthrough is active */ - - evutil_socket_t fd_downstream, fd_upstream; - - struct tfe_thread_ctx* thrmgr_ref; - future* async_future; -}; - diff --git a/platform/src/tfe_util.cpp b/platform/src/tfe_util.cpp deleted file mode 100644 index 884a359..0000000 --- a/platform/src/tfe_util.cpp +++ /dev/null @@ -1,23 +0,0 @@ -#include "tfe_util.h" -int addr_sock2layer(struct sockaddr * sock_addr, int sockaddrlen, struct layer_addr* layer_addr) -{ - return 0; -} -int addr_layer2sock(struct layer_addr* layer_addr,struct sockaddr * sock_addr) -{ - int sockaddrlen=-1; - return sockaddrlen; -} -//functioned as strdup, for dictator compatible. -char* tfe_strdup(const char* s) -{ - char*d=NULL; - if(s==NULL) - { - return NULL; - } - d=(char*)malloc(strlen(s)+1); - memcpy(d,s,strlen(s)+1); - return d; -} -