整理目录结构,编写CMakeLists.txt文件
This commit is contained in:
32
platform/src/Makefile
Normal file
32
platform/src/Makefile
Normal 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
342
platform/src/cert.cpp
Normal 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
70
platform/src/future.cpp
Normal 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;
|
||||
}
|
||||
|
||||
5
platform/src/io_module_kni.cpp
Normal file
5
platform/src/io_module_kni.cpp
Normal file
@@ -0,0 +1,5 @@
|
||||
#include <stdio.h>
|
||||
void* io_kni_init(const char* unix_domain_path, struct event_base * attach)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
2
platform/src/io_module_kni.h
Normal file
2
platform/src/io_module_kni.h
Normal 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
980
platform/src/privsep.cc
Normal 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: */
|
||||
|
||||
|
||||
|
||||
14
platform/src/session_cache.cpp
Normal file
14
platform/src/session_cache.cpp
Normal 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
2376
platform/src/ssl.cc
Normal file
File diff suppressed because it is too large
Load Diff
230
platform/src/ssl.h
Normal file
230
platform/src/ssl.h
Normal 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
903
platform/src/ssl_stream.cpp
Normal 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);
|
||||
}
|
||||
1
platform/src/ssl_stream.h
Normal file
1
platform/src/ssl_stream.h
Normal file
@@ -0,0 +1 @@
|
||||
|
||||
41
platform/src/tfe_main.cpp
Normal file
41
platform/src/tfe_main.cpp
Normal 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
407
platform/src/tfe_proxy.cpp
Normal 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
511
platform/src/tfe_stream.cpp
Normal 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;
|
||||
}
|
||||
|
||||
98
platform/src/tfe_stream_internal.h
Normal file
98
platform/src/tfe_stream_internal.h
Normal 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
23
platform/src/tfe_util.cpp
Normal 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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user