整理目录结构,编写CMakeLists.txt文件

This commit is contained in:
Lu
2018-08-17 20:29:38 +08:00
parent 093afcce8d
commit 86c18a15c3
68 changed files with 13 additions and 11566 deletions

32
platform/src/Makefile Normal file
View File

@@ -0,0 +1,32 @@
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

342
platform/src/cert.cpp Normal file
View File

@@ -0,0 +1,342 @@
/*-
* SSLsplit - transparent SSL/TLS interception
* https://www.roe.ch/SSLsplit
*
* Copyright (c) 2009-2018, Daniel Roethlisberger <daniel@roe.ch>.
* 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 <string.h>
/*
* Certificate, including private key and certificate chain.
*/
cert_t *
cert_new(void)
{
cert_t *c;
if (!(c = (cert_t *)malloc(sizeof(cert_t))))
return NULL;
memset(c, 0, sizeof(cert_t));
if (pthread_mutex_init(&c->mutex, NULL)) {
free(c);
return NULL;
}
c->references = 1;
return c;
}
/*
* Passed OpenSSL objects are owned by cert_t; refcount will not be
* incremented, stack will not be duplicated.
*/
cert_t *
cert_new3(EVP_PKEY *key, X509 *crt, STACK_OF(X509) *chain)
{
cert_t *c;
if (!(c = (cert_t *)malloc(sizeof(cert_t))))
return NULL;
if (pthread_mutex_init(&c->mutex, NULL)) {
free(c);
return NULL;
}
c->key = key;
c->crt = crt;
c->chain = chain;
c->references = 1;
return c;
}
/*
* Passed OpenSSL objects are copied by cert_t; crt/key refcount will be
* incremented, stack will be duplicated.
*/
cert_t *
cert_new3_copy(EVP_PKEY *key, X509 *crt, STACK_OF(X509) *chain)
{
cert_t *c;
if (!(c = (cert_t *)malloc(sizeof(cert_t))))
return NULL;
if (pthread_mutex_init(&c->mutex, NULL)) {
free(c);
return NULL;
}
c->key = key;
ssl_key_refcount_inc(c->key);
c->crt = crt;
ssl_x509_refcount_inc(c->crt);
c->chain = sk_X509_dup(chain);
for (int i = 0; i < sk_X509_num(c->chain); i++) {
ssl_x509_refcount_inc(sk_X509_value(c->chain, i));
}
c->references = 1;
return c;
}
/*
* Load cert_t from file.
*/
cert_t *
cert_new_load(const char *filename)
{
cert_t *c;
if (!(c = (cert_t *)malloc(sizeof(cert_t))))
return NULL;
memset(c, 0, sizeof(cert_t));
if (pthread_mutex_init(&c->mutex, NULL)) {
free(c);
return NULL;
}
if (ssl_x509chain_load(&c->crt, &c->chain, filename) == -1) {
free(c);
return NULL;
}
c->key = ssl_key_load(filename);
if (!c->key) {
X509_free(c->crt);
if (c->chain) {
sk_X509_pop_free(c->chain, X509_free);
}
free(c);
return NULL;
}
c->references = 1;
return c;
}
/*
* Increment reference count.
*/
void
cert_refcount_inc(cert_t *c)
{
pthread_mutex_lock(&c->mutex);
c->references++;
pthread_mutex_unlock(&c->mutex);
}
/*
* Thread-safe setter functions; they copy the value (refcounts are inc'd).
*/
void
cert_set_key(cert_t *c, EVP_PKEY *key)
{
pthread_mutex_lock(&c->mutex);
if (c->key) {
EVP_PKEY_free(c->key);
}
c->key = key;
if (c->key) {
ssl_key_refcount_inc(c->key);
}
pthread_mutex_unlock(&c->mutex);
}
void
cert_set_crt(cert_t *c, X509 *crt)
{
pthread_mutex_lock(&c->mutex);
if (c->crt) {
X509_free(c->crt);
}
c->crt = crt;
if (c->crt) {
ssl_x509_refcount_inc(c->crt);
}
pthread_mutex_unlock(&c->mutex);
}
void
cert_set_chain(cert_t *c, STACK_OF(X509) *chain)
{
pthread_mutex_lock(&c->mutex);
if (c->chain) {
sk_X509_pop_free(c->chain, X509_free);
}
if (chain) {
c->chain = sk_X509_dup(chain);
for (int i = 0; i < sk_X509_num(c->chain); i++) {
ssl_x509_refcount_inc(sk_X509_value(c->chain, i));
}
} else {
c->chain = NULL;
}
pthread_mutex_unlock(&c->mutex);
}
/*
* Free cert including internal objects.
*/
void
cert_free(cert_t *c)
{
pthread_mutex_lock(&c->mutex);
c->references--;
if (c->references) {
pthread_mutex_unlock(&c->mutex);
return;
}
pthread_mutex_unlock(&c->mutex);
pthread_mutex_destroy(&c->mutex);
if (c->key) {
EVP_PKEY_free(c->key);
}
if (c->crt) {
X509_free(c->crt);
}
if (c->chain) {
sk_X509_pop_free(c->chain, X509_free);
}
free(c);
}
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)
{
X509* orig_cert=SSL_get_peer_certificate(origssl);
//todo: need implement
cert_t * cert = NULL;
if (opts->tgcrtdir)
{
if (ctx->sni)
{
cert = (cert_t *) cachemgr_tgcrt_get(ctx->sni);
if (!cert)
{
char * wildcarded = ssl_wildcardify(ctx->sni);
if (!wildcarded)
{
ctx->enomem = 1;
return NULL;
}
cert = (cert_t *) cachemgr_tgcrt_get(wildcarded);
free(wildcarded);
}
if (cert && OPTS_DEBUG(ctx->opts))
{
log_dbg_printf("Target cert by SNI\n");
}
}
else if (ctx->origcrt)
{
char ** names = ssl_x509_names(ctx->origcrt);
for (char ** p = names; *p; p++)
{
if (!cert)
{
cert = (cert_t *) cachemgr_tgcrt_get(*p);
}
if (!cert)
{
char * wildcarded = ssl_wildcardify(*p);
if (!wildcarded)
{
ctx->enomem = 1;
}
else
{
cert = (cert_t *) (wildcarded);
free(wildcarded);
}
}
free(*p);
}
free(names);
if (ctx->enomem)
{
return NULL;
}
if (cert && OPTS_DEBUG(ctx->opts))
{
log_dbg_printf("Target cert by origcrt\n");
}
}
if (cert)
{
ctx->immutable_cert = 1;
}
}
if (!cert && ctx->origcrt && ctx->opts->key)
{
cert = cert_new();
cert->crt = (X509 *) cachemgr_fkcrt_get(ctx->origcrt);
if (cert->crt)
{
if (OPTS_DEBUG(ctx->opts)) log_dbg_printf("Certificate cache: HIT\n");
}
else
{
if (OPTS_DEBUG(ctx->opts)) log_dbg_printf("Certificate cache: MISS\n");
cert->crt = ssl_x509_forge(ctx->opts->cacrt,
ctx->opts->cakey,
ctx->origcrt,
ctx->opts->key,
NULL,
ctx->opts->crlurl);
cachemgr_fkcrt_set(ctx->origcrt, cert->crt);
}
cert_set_key(cert, ctx->opts->key);
cert_set_chain(cert, ctx->opts->chain);
ctx->generated_cert = 1;
}
if ((WANT_CONNECT_LOG(ctx) || ctx->opts->certgendir) && ctx->origcrt)
{
ctx->origcrtfpr = ssl_x509_fingerprint(ctx->origcrt, 0);
if (!ctx->origcrtfpr)
ctx->enomem = 1;
}
if ((WANT_CONNECT_LOG(ctx) || ctx->opts->certgen_writeall) &&
cert && cert->crt)
{
ctx->usedcrtfpr = ssl_x509_fingerprint(cert->crt, 0);
if (!ctx->usedcrtfpr)
ctx->enomem = 1;
}
return cert;
}
void cert_manager_free(cert_t * cert)
{
cert_free(cert);
return;
}
/* vim: set noet ft=c: */

70
platform/src/future.cpp Normal file
View File

@@ -0,0 +1,70 @@
#include "future.h"
struct promise
{
struct future f;
void * ctx;
promise_ctx_destroy_cb* cb_ctx_destroy;
};
struct future
{
void * user;
future_success_cb * cb_success;
future_failed_cb * cb_failed;
};
inline struct future* promise_to_future(struct promise* p)
{
return p->f;
}
inline struct promise* future_to_promise(struct future* 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)
{
promise->cb_ctx_destroy(promise);
}
memset(promise,0,sizeof(struct promise));
free(promise);
return;
}
void promise_failed(struct promise* p, enum e_future_error error, const char* what)
{
p->f.cb_failed(error, what,p->f.user);
return;
}
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)
{
p->ctx=ctx;
p->cb_ctx_destroy=cb;
return;
}
void* promise_get_ctx(struct promise* p)
{
return p->ctx;
}
void* promise_dettach_ctx(struct promise* p)
{
void* ctx=p->ctx;
p->ctx=NULL;
p->cb_ctx_destroy=NULL;
return ctx;
}

View File

@@ -0,0 +1,5 @@
#include <stdio.h>
void* io_kni_init(const char* unix_domain_path, struct event_base * attach)
{
return NULL;
}

View File

@@ -0,0 +1,2 @@
void* io_kni_init(const char* unix_domain_path, struct event_base * attach);

980
platform/src/privsep.cc Normal file
View File

@@ -0,0 +1,980 @@
/*-
* SSLsplit - transparent SSL/TLS interception
* https://www.roe.ch/SSLsplit
*
* Copyright (c) 2009-2018, Daniel Roethlisberger <daniel@roe.ch>.
* 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 <sys/types.h>
#include <sys/socket.h>
#include <sys/select.h>
#include <sys/wait.h>
#include <netinet/in.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <libgen.h>
#include <fcntl.h>
/*
* 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: */

View File

@@ -0,0 +1,14 @@
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)
{
}

2376
platform/src/ssl.cc Normal file

File diff suppressed because it is too large Load Diff

230
platform/src/ssl.h Normal file
View File

@@ -0,0 +1,230 @@
/*-
* SSLsplit - transparent SSL/TLS interception
* https://www.roe.ch/SSLsplit
*
* Copyright (c) 2009-2018, Daniel Roethlisberger <daniel@roe.ch>.
* 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 SSL_H
#define SSL_H
#include "tfe_types.h"
#include <openssl/opensslv.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <openssl/rand.h>
#include <openssl/x509.h>
#include <openssl/x509v3.h>
#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.
*/
#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) && !defined(OPENSSL_NO_SHA0)
#define OPENSSL_NO_SHA0
#endif
#if OPENSSL_VERSION_NUMBER < 0x10100000L
#define ASN1_STRING_get0_data(value) ASN1_STRING_data(value)
#define SSL_is_server(ssl) (ssl->type != SSL_ST_CONNECT)
#define X509_get_signature_nid(x509) (OBJ_obj2nid(x509->sig_alg->algorithm))
int DH_set0_pqg(DH *, BIGNUM *, BIGNUM *, BIGNUM *);
#endif
/*
* The constructors returning a SSL_METHOD * were changed to return
* a const SSL_METHOD * between 0.9.8 and 1.0.0.
*/
#if (OPENSSL_VERSION_NUMBER < 0x1000000fL)
#define CONST_SSL_METHOD SSL_METHOD
#else /* >= OpenSSL 1.0.0 */
#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
* to have the respective protocol implemented.
*
* OPENSSL_NO_SSL2 indicates the complete removal of SSL 2.0 support.
*
* OPENSSL_NO_SSL3 indicates that no SSL 3.0 connections will be made by
* default, but support is still present, unless OPENSSL_NO_SSL3_METHOD is
* also defined.
*/
#if defined(SSL_OP_NO_SSLv2) && !defined(OPENSSL_NO_SSL2) && \
defined(WITH_SSLV2)
#define HAVE_SSLV2
#endif /* SSL_OP_NO_SSLv2 && !OPENSSL_NO_SSL2 && WITH_SSLV2 */
#if defined(SSL_OP_NO_SSLv3) && !defined(OPENSSL_NO_SSL3_METHOD)
#define HAVE_SSLV3
#endif /* SSL_OP_NO_SSLv2 && !OPENSSL_NO_SSL3_METHOD */
#ifdef SSL_OP_NO_TLSv1
#define HAVE_TLSV10
#endif /* SSL_OP_NO_TLSv1 */
#ifdef SSL_OP_NO_TLSv1_1
#define HAVE_TLSV11
#endif /* SSL_OP_NO_TLSv1_1 */
#ifdef SSL_OP_NO_TLSv1_2
#define HAVE_TLSV12
#endif /* SSL_OP_NO_TLSv1_2 */
#ifdef HAVE_SSLV2
#define SSL2_S "ssl2 "
#else /* !HAVE_SSLV2 */
#define SSL2_S ""
#endif /* !HAVE_SSLV2 */
#ifdef HAVE_SSLV3
#define SSL3_S "ssl3 "
#else /* !HAVE_SSLV3 */
#define SSL3_S ""
#endif /* !HAVE_SSLV3 */
#ifdef HAVE_TLSV10
#define TLS10_S "tls10 "
#else /* !HAVE_TLSV10 */
#define TLS10_S ""
#endif /* !HAVE_TLSV10 */
#ifdef HAVE_TLSV11
#define TLS11_S "tls11 "
#else /* !HAVE_TLSV11 */
#define TLS11_S ""
#endif /* !HAVE_TLSV11 */
#ifdef HAVE_TLSV12
#define TLS12_S "tls12 "
#else /* !HAVE_TLSV12 */
#define TLS12_S ""
#endif /* !HAVE_TLSV12 */
#define SSL_PROTO_SUPPORT_S SSL2_S SSL3_S TLS10_S TLS11_S TLS12_S
void ssl_openssl_version(void);
int ssl_init(void) WUNRES;
int ssl_reinit(void) WUNRES;
void ssl_fini(void);
char * ssl_sha1_to_str(unsigned char *, int) NONNULL(1) MALLOC;
char * ssl_ssl_state_to_str(SSL *) NONNULL(1) MALLOC;
char * ssl_ssl_masterkey_to_str(SSL *) NONNULL(1) MALLOC;
#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 */
#ifndef OPENSSL_NO_EC
EC_KEY * ssl_ec_by_name(const char *) MALLOC;
#endif /* !OPENSSL_NO_EC */
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;
#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);
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);
char * ssl_session_to_str(SSL_SESSION *) NONNULL(1) MALLOC;
int ssl_session_is_valid(SSL_SESSION *) NONNULL(1);
int ssl_is_ocspreq(const unsigned char *, size_t) NONNULL(1) WUNRES;
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;
#endif /* !SSL_H */
/* vim: set noet ft=c: */

903
platform/src/ssl_stream.cpp Normal file
View File

@@ -0,0 +1,903 @@
#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);
}

View File

@@ -0,0 +1 @@

41
platform/src/tfe_main.cpp Normal file
View File

@@ -0,0 +1,41 @@
/*-
* 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 <string.h>
#include <stdlib.h>
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);
}

407
platform/src/tfe_proxy.cpp Normal file
View File

@@ -0,0 +1,407 @@
#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 <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <signal.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <pthread.h>
#include <event2/event.h>
#include <event2/listener.h>
#include <event2/bufferevent.h>
#include <event2/bufferevent_ssl.h>
#include <event2/buffer.h>
#include <event2/thread.h>
#include <sys/un.h>
#include <assert.h>
/*
* 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;i<proxy->thread_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;i<proxy->thread_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: */

511
platform/src/tfe_stream.cpp Normal file
View File

@@ -0,0 +1,511 @@
#include "tfe_stream.h"
#include "tfe_util.h"
#include "opts.h"
#include "attrib.h"
#include <netinet/in.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <event2/event.h>
#include <event2/listener.h>
#include <event2/bufferevent.h>
#include <event2/bufferevent_ssl.h>
#include <event2/buffer.h>
#include <event2/thread.h>
#include <event2/dns.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <openssl/rand.h>
#include <openssl/x509.h>
#include <openssl/x509v3.h>
#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)
/*
* Print helper for logging code.
*/
#define STRORDASH(x) (((x)&&*(x))?(x):"-")
/*
* Context used for all server http_sessions_.
*/
#ifdef USE_SSL_SESSION_ID_CONTEXT
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)
{
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)
{
return -1;
}
}
_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)
{
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)
{
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_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;
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)
{
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)
{
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 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;
}
/*
#define ON_OPEN_CALL 0
#define ON_DATA_CALL 1
#define ON_CLOSE_CALL 2
enum tfe_stream_action tfe_stream_call_plugin(struct tfe_stream_private* _stream, enum tfe_conn_dir dir, int what, struct evbuffer * inbuf)
{
size_t contigous_len=evbuffer_get_length(inbuf),drain_size=0;
const char* contiguous_data=evbuffer_pullup(inbuf,contigous_len);
int i=0,ret=0;
int plug_num=_stream->thrmgr_ref->module_num;
const struct tfe_plugin* plugins=_stream->thrmgr_ref->modules;
struct plugin_ctx* plug_ctx=NULL;
enum tfe_stream_action action_tmp=ACTION_FORWARD_DATA, action_final=ACTION_FORWARD_DATA;
_stream->defere_bytes=0;
_stream->drop_bytes=0;
_stream->forward_bytes=0;
switch(what)
{
case ON_OPEN_CALL:
for(i=0;i<plug_num;i++)
{
action_tmp=plugins[i].on_open(&_stream.head, _stream->thrmgr_ref->thread_id, dir, contiguous_data,contigous_len, &(plug_ctx->pme));
if(plug_ctx->state=PLUG_STATE_PREEPTION)
{
action_final=action_tmp;
}
}
break;
case ON_DATA_CALL:
for(i=0;i<plug_num;i++)
{
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)
{
action_final=action_tmp;
}
}
case ON_CLOSE_CALL:
for(i=0;i<plug_num;i++)
{
plugins[i].on_close(&_stream.head, _stream->thrmgr_ref->thread_id, dir, contiguous_data,contigous_len, &(plug_ctx->pme));
if(plug_ctx->state=PLUG_STATE_PREEPTION)
{
action_final=action_tmp;
}
}
}
for(i=0;i<plug_num;i++)
{
_stream->calling_idx=i;
switch(what)
{
}
plug_ctx=_stream->plug_ctx+i;
if(_stream->is_fisrt_read==1)
{
action_tmp=plugins[i].on_open(&_stream.head, _stream->thrmgr_ref->thread_id, dir, contiguous_data,contigous_len, &(plug_ctx->pme));
_stream->is_fisrt_read=0;
}
else
{
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)
{
action_final=action_tmp;
}
}
}
*/
/*
* Callback for read events on the up- and downstream connection bufferevents.
* Called when there is data ready in the input evbuffer.
*/
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);
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;
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;i<plug_num;i++)
{
_stream->calling_idx=i;
plug_ctx=_stream->plug_ctx+i;
if(_stream->is_fisrt_read==1)
{
action_tmp=plugins[i].on_open(&_stream.head, _stream->thrmgr_ref->thread_id, dir, contiguous_data,contigous_len, &(plug_ctx->pme));
_stream->is_fisrt_read=0;
}
else
{
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)
{
action_final=action_tmp;
}
}
switch (action_final)
{
case ACTION_FORWARD_DATA:
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)
{
drain_size=_stream->drop_bytes;
}
else
{
drain_size= evbuffer_get_length(inbuf);
}
evbuffer_drain(inbuf,drain_size);
case ACTION_DEFER_DATA:
if(_stream->defere_bytes>0)
{
bufferevent_setwatermark(bev, EV_WRITE, _stream->defere_bytes, 0);
}
break;
default:
assert(0);
break;
}
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;
* set an appropriate watermark. */
bufferevent_setwatermark(peer_conn->bev, EV_WRITE, OUTBUF_LIMIT / 2, OUTBUF_LIMIT);
bufferevent_disable(bev, EV_READ);
}
return;
}
/*
* Callback for write events on the up- and downstream connection bufferevents.
* Called when either all data from the output evbuffer has been written,
* or if the outbuf is only half full again after having been full.
*/
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 evbuffer * outbuf = bufferevent_get_output(bev);
if (peer_conn->bev && !(bufferevent_get_enabled(peer_conn->bev) & EV_READ))
{
/* data source temporarily disabled;
* re-enable and reset watermark to 0. */
bufferevent_setwatermark(bev, EV_WRITE, 0, 0);
bufferevent_enable(peer_conn->bev, EV_READ);
}
}
/*
* 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 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);
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;
if (events & BEV_EVENT_ERROR)
{
this_conn->closed=1;
reason=REASON_ERROR;
goto call_plugin_close;
}
if (events & BEV_EVENT_EOF)
{
//generate a 0 size read callback to notify plugins.
tfe_stream_readcb(bev, arg);
this_conn->closed=1;
}
if(peer_conn->closed==1&&this_conn->closed==1)
{
reason=REASON_PASSIVE_CLOSED;
goto call_plugin_close;
}
return;
call_plugin_close:
for(i=0;i<plug_num;i++)
{
_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_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(bev, EV_READ | EV_WRITE);
future_destroy(_stream->ssl_downstream->future_get_cert);
_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);
session_cache_set(_stream->thrmgr_ref->dsess_cache, struct sockaddr * addr, addrlen,_stream->ssl_downstream->sni, SSL_get0_session(_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);
future_destroy(_stream->ssl_upstream->conn_ssl_srv);
_stream->ssl_upstream->conn_ssl_srv=NULL;
return;
}
void ssl_conn_origin_on_fail(enum e_future_error err, const char * what, void * user)
{
//todo
assert(0);
}
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);
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);
}
void peek_sni_on_fail(enum e_future_error err, const char * what, void * user)
{
//todo
assert(0);
}
/*
* Callback for accept events on the socket listener bufferevent.
* Called when a new incoming connection has been accepted.
* Initiates the connection to the server. The incoming connection
* from the client is not being activated until we have a successful
* connection to the server, because we need the server's certificate
* in order to set up the SSL session to the client.
* For consistency, plain TCP works the same way, even if we could
* start reading from the client while waiting on the connection to
* the server to connect.
*/
void tfe_stream_setup(struct tfe_stream_private* _stream)
{
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);
thread->stat.value[SSL_NUM]++;
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* 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_fisrt_read=1;
conn_public=&(conn_private->head);
addr_sock2layer(conn_public->downstream.addr,peeraddr,peeraddrlen);
thread->stat.value[STREAM_NUM]++;
return;
}

View File

@@ -0,0 +1,98 @@
#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_fisrt_read;
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;
};

23
platform/src/tfe_util.cpp Normal file
View File

@@ -0,0 +1,23 @@
#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;
}