Initial import of sslsplit-0.4.2

pull/13/head
Daniel Roethlisberger 12 years ago
commit 4cfdef405a

19
.gitignore vendored

@ -0,0 +1,19 @@
/*.o
/*.ot
/*.dSYM
/sslsplit
/sslsplit.test
/extra/pki/dh1024.param
/extra/pki/dh512.param
/extra/pki/dsa.pem
/extra/pki/dsa.crt
/extra/pki/dsa.key
/extra/pki/dsa.param
/extra/pki/ec.pem
/extra/pki/ec.crt
/extra/pki/ec.key
/extra/pki/rsa.pem
/extra/pki/rsa.crt
/extra/pki/rsa.key
/extra/pki/session.pem
/extra/pki/targets/*

@ -0,0 +1,6 @@
USE_GNU:
@gmake $(.TARGETS)
$(.TARGETS): USE_GNU
.PHONY: USE_GNU

@ -0,0 +1,332 @@
### OpenSSL tweaking
# Define to use dubious hacks to decrease OpenSSL memory consumption.
#FEATURES+= -DUSE_FOOTPRINT_HACKS
# Define to disable server-mode SSL session caching for SSLv2 clients.
# This is needed if SSL session resumption fails with a bufferevent error:
# "illegal padding in SSL routines SSL2_READ_INTERNAL".
FEATURES+= -DDISABLE_SSLV2_SESSION_CACHE
# Define to disable server-mode SSLv2 completely, but still use SSL23 method.
#FEATURES+= -DDISABLE_SSLV2_SERVER
# Define to make SSLsplit set a session id context in server mode.
#FEATURES+= -DUSE_SSL_SESSION_ID_CONTEXT
### Debugging
# These CFLAGS are added when building from git
DEBUG_CFLAGS?= -g
#DEBUG_CFLAGS+= -Werror
# Define to remove false positives when debugging memory allocation.
# Note that you probably want to build OpenSSL with -DPURIFY too.
#FEATURES+= -DPURIFY
# Define to add proxy state machine debugging; dump state in debug mode.
#FEATURES+= -DDEBUG_PROXY
# Define to add certificate debugging; dump all certificates in debug mode.
#FEATURES+= -DDEBUG_CERTIFICATE
# Define to add SSL session cache debugging; dump all sessions in debug mode.
#FEATURES+= -DDEBUG_SESSION_CACHE
# Define to add debugging of parsing the SNI from the SSL ClientHello.
#FEATURES+= -DDEBUG_SNI_PARSER
# Define to add thread debugging; dump thread state when choosing a thread.
#FEATURES+= -DDEBUG_THREAD
### Autodetected features
# Autodetect pf
ifneq ($(wildcard /usr/include/net/pfvar.h),)
FEATURES+= -DHAVE_PF
endif
# Autodetect ipfw
ifneq ($(wildcard /sbin/ipfw),)
FEATURES+= -DHAVE_IPFW
endif
# Autodetect ipfilter
ifneq ($(wildcard /usr/include/netinet/ip_fil.h),)
FEATURES+= -DHAVE_IPFILTER
endif
# Autodetect netfilter
ifneq ($(wildcard /usr/include/linux/netfilter.h),)
FEATURES+= -DHAVE_NETFILTER
endif
# Autodetect glibc
ifneq ($(wildcard /usr/include/gnu/libc-version.h),)
CPPFLAGS+= -D_GNU_SOURCE
endif
### Variables you might need to override
PREFIX?= /usr/local
OPENSSL?= openssl
PKGCONFIG?= pkg-config
BASENAME?= basename
CAT?= cat
GREP?= grep
INSTALL?= install
MKDIR?= mkdir
SED?= sed
### Variables only used for developer targets
KHASH_URL?= https://github.com/attractivechaos/klib/raw/master/khash.h
CPPCHECK?= cppcheck
GIT?= git
WGET?= wget
BZIP2?= bzip2
LN?= ln
MAN?= man
TAR?= tar
### You should not need to touch anything below this line
TARGET:= sslsplit
PNAME:= SSLsplit
SRCS:= $(wildcard *.c)
HDRS:= $(wildcard *.h)
OBJS:= $(SRCS:.c=.o)
TSRCS:= $(wildcard *.t)
TOBJS:= $(TSRCS:.t=.ot)
TOBJS+= $(filter-out main.o,$(OBJS))
VFILE:= $(wildcard VERSION)
GITDIR:= $(wildcard .git)
ifdef VFILE
VERSION:= $(shell $(CAT) VERSION)
else
ifndef GITDIR
VERSION:= $(shell $(BASENAME) $(PWD)|\
$(GREP) $(TARGET)-|\
$(SED) 's/.*$(TARGET)-\(.*\)/\1/g')
else
VERSION:= $(shell $(GIT) describe --tags --dirty --always)
endif
CFLAGS+= $(DEBUG_CFLAGS)
endif
BUILD_DATE:= $(shell date +%Y-%m-%d)
CFLAGS+= $(PKG_CFLAGS) -pthread \
-std=c99 -Wall -Wextra -pedantic -D_FORTIFY_SOURCE=2
CPPFLAGS+= $(PKG_CPPFLAGS) $(FEATURES) -D"BNAME=\"$(TARGET)\"" \
-D"PNAME=\"$(PNAME)\"" -D"VERSION=\"$(VERSION)\"" \
-D"FEATURES=\"$(FEATURES)\"" -D"BUILD_DATE=\"$(BUILD_DATE)\""
LDFLAGS+= $(PKG_LDFLAGS) -pthread
LIBS+= $(PKG_LIBS)
# Autodetect dependencies known to pkg-config
PKGS:=
ifndef OPENSSL_BASE
PKGS+= $(shell $(PKGCONFIG) --exists openssl && echo openssl)
endif
ifndef LIBEVENT_BASE
PKGS+= $(shell $(PKGCONFIG) --exists libevent && echo libevent)
PKGS+= $(shell $(PKGCONFIG) --exists libevent_openssl \
&& echo libevent_openssl)
PKGS+= $(shell $(PKGCONFIG) --exists libevent_pthreads \
&& echo libevent_pthreads)
endif
TPKGS:=
ifndef CHECK_BASE
TPKGS+= $(shell $(PKGCONFIG) --exists check && echo check)
endif
# Autodetect dependencies not known to pkg-config
ifeq (,$(filter openssl,$(PKGS)))
OPENSSL_PAT:= include/openssl/ssl.h
ifdef OPENSSL_BASE
OPENSSL_FIND:= $(wildcard $(OPENSSL_BASE)/$(OPENSSL_PAT))
else
OPENSSL_FIND:= $(wildcard \
/opt/local/$(OPENSSL_PAT) \
/usr/local/$(OPENSSL_PAT) \
/usr/$(OPENSSL_PAT))
endif
OPENSSL_FOUND:= $(OPENSSL_FIND:/$(OPENSSL_PAT)=)
ifndef OPENSSL_FOUND
$(error OpenSSL not found; please point OPENSSL_BASE to base path)
endif
endif
ifeq (,$(filter libevent,$(PKGS)))
LIBEVENT_PAT:= include/event2/event.h
ifdef LIBEVENT_BASE
LIBEVENT_FIND:= $(wildcard $(LIBEVENT_BASE)/$(LIBEVENT_PAT))
else
LIBEVENT_FIND:= $(wildcard \
/opt/local/$(LIBEVENT_PAT) \
/usr/local/$(LIBEVENT_PAT) \
/usr/$(LIBEVENT_PAT))
endif
LIBEVENT_FOUND:=$(LIBEVENT_FIND:/$(LIBEVENT_PAT)=)
ifndef LIBEVENT_FOUND
$(error libevent 2.x not found; please point LIBEVENT_BASE to base path)
endif
endif
ifeq (,$(filter check,$(TPKGS)))
CHECK_PAT:= include/check.h
ifdef CHECK_BASE
CHECK_FIND:= $(wildcard $(CHECK_BASE)/$(CHECK_PAT))
else
CHECK_FIND:= $(wildcard \
/opt/local/$(CHECK_PAT) \
/usr/local/$(CHECK_PAT) \
/usr/$(CHECK_PAT))
endif
CHECK_FOUND:= $(CHECK_FIND:/$(CHECK_PAT)=)
ifndef CHECK_FOUND
$(warning check not found; please point CHECK_BASE to base path [optional])
endif
endif
ifdef OPENSSL_FOUND
PKG_CPPFLAGS+= -I$(OPENSSL_FOUND)/include
PKG_LDFLAGS+= -L$(OPENSSL_FOUND)/lib
PKG_LIBS+= -lssl -lcrypto -lz
endif
ifdef LIBEVENT_FOUND
PKG_CPPFLAGS+= -I$(LIBEVENT_FOUND)/include
PKG_LDFLAGS+= -L$(LIBEVENT_FOUND)/lib
PKG_LIBS+= -levent
endif
ifeq (,$(filter libevent_openssl,$(PKGS)))
PKG_LIBS+= -levent_openssl
endif
ifeq (,$(filter libevent_pthreads,$(PKGS)))
PKG_LIBS+= -levent_pthreads
endif
ifdef CHECK_FOUND
TPKG_CPPFLAGS+= -I$(CHECK_FOUND)/include
TPKG_LDFLAGS+= -L$(CHECK_FOUND)/lib
TPKG_LIBS+= -lcheck
endif
ifneq (,$(strip $(PKGS)))
PKG_CFLAGS+= $(shell $(PKGCONFIG) --cflags-only-other $(PKGS))
PKG_CPPFLAGS+= $(shell $(PKGCONFIG) --cflags-only-I $(PKGS))
PKG_LDFLAGS+= $(shell $(PKGCONFIG) --libs-only-L --libs-only-other $(PKGS))
PKG_LIBS+= $(shell $(PKGCONFIG) --libs-only-l $(PKGS))
endif
ifneq (,$(strip $(TPKGS)))
TPKG_CFLAGS+= $(shell $(PKGCONFIG) --cflags-only-other $(TPKGS))
TPKG_CPPFLAGS+= $(shell $(PKGCONFIG) --cflags-only-I $(TPKGS))
TPKG_LDFLAGS+= $(shell $(PKGCONFIG) --libs-only-L --libs-only-other $(TPKGS))
TPKG_LIBS+= $(shell $(PKGCONFIG) --libs-only-l $(TPKGS))
endif
export VERSION
export OPENSSL
export MKDIR
all: version config $(TARGET)
version:
@echo "$(PNAME) $(VERSION)"
config:
@echo "via pkg-config: $(strip $(PKGS) $(TPKGS))"
ifdef OPENSSL_FOUND
@echo "OPENSSL_BASE: $(strip $(OPENSSL_FOUND))"
endif
ifdef LIBEVENT_FOUND
@echo "LIBEVENT_BASE: $(strip $(LIBEVENT_FOUND))"
endif
ifdef CHECK_FOUND
@echo "CHECK_BASE: $(strip $(CHECK_FOUND))"
endif
@echo "Build options: $(FEATURES)"
$(TARGET): $(OBJS)
$(CC) $(LDFLAGS) -o $@ $^ $(LIBS)
%.o: %.c $(HDRS) GNUmakefile $(VFILE)
$(CC) -c $(CPPFLAGS) $(CFLAGS) -o $@ $<
extra/pki/rsa.pem:
$(MAKE) -C extra/pki
test: extra/pki/rsa.pem $(TARGET).test
$(RM) extra/pki/session.pem
$(MAKE) -C extra/pki session
./$(TARGET).test
$(TARGET).test: $(TOBJS)
$(CC) $(LDFLAGS) $(TPKG_LDFLAGS) -o $@ $^ $(LIBS) $(TPKG_LIBS)
%.ot: %.t $(HDRS) GNUmakefile $(VFILE)
$(CC) -c $(CPPFLAGS) $(TPKG_CPPFLAGS) $(CFLAGS) $(TPKG_CFLAGS) -o $@ \
-x c $<
lint:
$(CPPCHECK) --force --enable=all --error-exitcode=1 .
clean:
$(RM) -f $(TARGET) *.o $(TARGET).test *.ot *.core *~
$(RM) -rf *.dSYM
install: $(TARGET)
test -d $(PREFIX)/bin || $(MKDIR) -p $(PREFIX)/bin
test -d $(PREFIX)/share/man/man1 || \
$(MKDIR) -p $(PREFIX)/share/man/man1
$(INSTALL) -o 0 -g 0 -m 0755 $(TARGET) $(PREFIX)/bin/
$(INSTALL) -o 0 -g 0 -m 0644 $(TARGET).1 $(PREFIX)/share/man/man1/
deinstall:
$(RM) -f $(PREFIX)/bin/$(TARGET) $(PREFIX)/share/man/man1/$(TARGET).1
ifdef GITDIR
mantest:
$(RM) -f man1
$(LN) -sf . man1
$(MAN) -M . 1 $(TARGET)
$(RM) man1
fetchdeps:
$(WGET) --no-check-certificate -O- $(KHASH_URL) >khash.h
dist: $(TARGET)-$(VERSION).tar.bz2
$(TARGET)-$(VERSION).tar.bz2:
$(MKDIR) -p $(TARGET)-$(VERSION)
echo $(VERSION) >$(TARGET)-$(VERSION)/VERSION
$(GIT) archive --prefix=$(TARGET)-$(VERSION)/ HEAD \
>$(TARGET)-$(VERSION).tar
$(TAR) -f $(TARGET)-$(VERSION).tar -r $(TARGET)-$(VERSION)/VERSION
$(BZIP2) <$(TARGET)-$(VERSION).tar >$(TARGET)-$(VERSION).tar.bz2
$(RM) $(TARGET)-$(VERSION).tar
$(RM) -r $(TARGET)-$(VERSION)
disttest: $(TARGET)-$(VERSION).tar.bz2
$(BZIP2) -d < $(TARGET)-$(VERSION).tar.bz2 | $(TAR) -x -f -
cd $(TARGET)-$(VERSION) && $(MAKE) && $(MAKE) test && ./$(TARGET) -V
$(RM) -r $(TARGET)-$(VERSION)
distclean: clean
$(RM) -f $(TARGET)-*.tar.bz2
realclean: distclean
$(MAKE) -C extra/pki clean
endif
.PHONY: all config clean test lint install deinstall \
mantest fetchdeps dist disttest distclean realclean

@ -0,0 +1,75 @@
SSLsplit - transparent and scalable SSL/TLS interception
Copyright (C) 2009-2012, Daniel Roethlisberger <daniel@roe.ch>
http://www.roe.ch/SSLsplit
## Overview
SSLsplit is a tool for man-in-the-middle attacks against SSL/TLS encrypted
network connections. Connections are transparently intercepted through a
network address translation engine and redirected to SSLsplit. SSLsplit
terminates SSL/TLS and initiates a new SSL/TLS connection to the original
destination address, while logging all data transmitted.
SSLsplit supports plain TCP, plain SSL, HTTP and HTTPS connections over both
IPv4 and IPv6. For SSL and HTTPS connections, SSLsplit generates and signs
forged X509v3 certificates on-the-fly, based on the original server certificate
subject DN and subjectAltName extension. SSLsplit fully supports Server Name
Indication (SNI) and is able to work with RSA, DSA and ECDSA keys and DHE and
ECDHE cipher suites. SSLsplit can also use existing certificates of which the
private key is available, instead of generating forged ones. SSLsplit supports
NULL-prefix CN certificates.
See the manual page sslsplit(1) for details on using SSLsplit and setting up
the various NAT engines.
## Requirements
SSLsplit depends on the OpenSSL and libevent 2.x libraries.
The build depends on GNU make and a POSIX.2 environment in PATH.
The (optional) unit tests depend on check.
SSLsplit currently supports the following operating systems and NAT engines:
- FreeBSD: pf rdr, ipfw fwd, ipfilter rdr
- OpenBSD: pf rdr
- Linux: netfilter REDIRECT and TPROXY
- Mac OS X: ipfw fwd
## Installation
make
make test # optional unit tests
make install # optional install
Dependencies are autoconfigured using pkgconfig. If dependencies are not
picked up, you can specify their respective locations manually by setting
OPENSSL_BASE, LIBEVENT_BASE and/or CHECK_BASE to the respective prefixes.
You can override the default install prefix (/usr/local) by setting PREFIX.
## Development
SSLsplit is being developed on Github. For bug reports, please use the Github
issue tracker. For patch submissions, please send me pull requests.
https://github.com/droe/sslsplit
## License
SSLsplit is provided under the simplified BSD license.
SSLsplit contains components licensed under the MIT license.
See the respective source file headers for details.
## Credits
SSLsplit was inspired by ssl-mitm by Claes M. Nyberg and sslsniff by Moxie
Marlinspike, but shares no source code with them.
SSLsplit includes khash by Attractive Chaos (khash.h).

@ -0,0 +1,9 @@
- Parse some information from HTTP responses (status, size)
- Handle renego & client cert authentication more gracefully
- Separate orig cert retrieval from actual fwd address/proto config
- OCSP denial mode based on targetdir cert's OCSP servers
- CRL "denial" mode based on targetdir cert's CDPs
- Client fingerprinting: only intercept clients with headers matching regex
- Configurable and/or scriptable modification of requests and/or responses
- STARTTLS for various protocols
- Sample scripts for single file/fifo content log postprocessing

@ -0,0 +1,53 @@
/*
* SSLsplit - transparent and scalable SSL/TLS interception
* Copyright (c) 2009-2012, Daniel Roethlisberger <daniel@roe.ch>
* All rights reserved.
* http://www.roe.ch/SSLsplit
*
* 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 unmodified, 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 AUTHOR ``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 AUTHOR 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 ATTRIB_H
#define ATTRIB_H
/*
* GCC attributes.
* These serve to improve the compiler warnings or optimizations.
* They are fully optional and are automatically disabled when
* building with a non-GCC (and non-LLVM) compiler.
*/
#if !defined(__GNUC__) && !defined(__clang__)
#define __attribute__(x)
#endif
#define UNUSED __attribute__((unused))
#define NORET __attribute__((noreturn))
#define PRINTF(f,a) __attribute__((format(printf,(f),(a))))
#define SCANF(f,a) __attribute__((format(scanf,(f),(a))))
#define WUNRES __attribute__((warn_unused_result))
#define MALLOC __attribute__((malloc))
#define NONNULL(...) __attribute__((nonnull(__VA_ARGS__)))
#endif /* !ATTRIB_H */
/* vim: set noet ft=c: */

@ -0,0 +1,158 @@
/*
* SSLsplit - transparent and scalable SSL/TLS interception
* Copyright (c) 2009-2012, Daniel Roethlisberger <daniel@roe.ch>
* All rights reserved.
* http://www.roe.ch/SSLsplit
*
* 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 unmodified, 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 AUTHOR ``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 AUTHOR 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 "cache.h"
#include "log.h"
#include "khash.h"
#include <pthread.h>
/*
* Generic, thread-safe cache.
*/
/*
* Create a new cache based on the initializer callback init_cb.
*/
cache_t *
cache_new(cache_init_cb_t init_cb)
{
cache_t *cache;
if (!(cache = malloc(sizeof(cache_t))))
return NULL;
init_cb(cache);
pthread_mutex_init(&cache->mutex, NULL);
return cache;
}
/*
* Free a cache and all associated resources.
* This function is not thread-safe.
*/
void
cache_free(cache_t *cache)
{
khiter_t it;
for (it = cache->begin_cb(); it != cache->end_cb(); it++) {
if (cache->exist_cb(it)) {
cache->free_key_cb(cache->get_key_cb(it));
cache->free_val_cb(cache->get_val_cb(it));
}
}
cache->fini_cb();
pthread_mutex_destroy(&cache->mutex);
free(cache);
}
void
cache_gc(cache_t *cache)
{
khiter_t it;
cache_val_t val;
pthread_mutex_lock(&cache->mutex);
for (it = cache->begin_cb(); it != cache->end_cb(); it++) {
if (cache->exist_cb(it)) {
val = cache->get_val_cb(it);
if (!cache->unpackverify_val_cb(val, 0)) {
cache->free_val_cb(val);
cache->free_key_cb(cache->get_key_cb(it));
cache->del_cb(it);
}
}
}
pthread_mutex_unlock(&cache->mutex);
}
cache_val_t
cache_get(cache_t *cache, cache_key_t key)
{
cache_val_t rval = NULL;
cache_val_t val;
khiter_t it;
if (!key)
return NULL;
pthread_mutex_lock(&cache->mutex);
it = cache->get_cb(key);
if (it != cache->end_cb()) {
val = cache->get_val_cb(it);
if (!(rval = cache->unpackverify_val_cb(val, 1))) {
cache->free_val_cb(val);
cache->free_key_cb(cache->get_key_cb(it));
cache->del_cb(it);
}
}
cache->free_key_cb(key);
pthread_mutex_unlock(&cache->mutex);
return rval;
}
void
cache_set(cache_t *cache, cache_key_t key, cache_val_t val)
{
khiter_t it;
int ret;
if (!key || !val)
return;
pthread_mutex_lock(&cache->mutex);
it = cache->put_cb(key, &ret);
if (!ret) {
cache->free_key_cb(key);
cache->free_val_cb(cache->get_val_cb(it));
}
cache->set_val_cb(it, val);
pthread_mutex_unlock(&cache->mutex);
}
void
cache_del(cache_t *cache, cache_key_t key)
{
khiter_t it;
pthread_mutex_lock(&cache->mutex);
it = cache->get_cb(key);
if (it != cache->end_cb()) {
cache->free_val_cb(cache->get_val_cb(it));
cache->free_key_cb(cache->get_key_cb(it));
cache->del_cb(it);
}
cache->free_key_cb(key);
pthread_mutex_unlock(&cache->mutex);
}
/* vim: set noet ft=c: */

@ -0,0 +1,83 @@
/*
* SSLsplit - transparent and scalable SSL/TLS interception
* Copyright (c) 2009-2012, Daniel Roethlisberger <daniel@roe.ch>
* All rights reserved.
* http://www.roe.ch/SSLsplit
*
* 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 unmodified, 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 AUTHOR ``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 AUTHOR 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 CACHE_H
#define CACHE_H
#include "attrib.h"
#include <pthread.h>
typedef void * cache_val_t;
typedef void * cache_key_t;
typedef unsigned int cache_iter_t; /* must match khiter_t */
typedef cache_iter_t (*cache_begin_cb_t)(void);
typedef cache_iter_t (*cache_end_cb_t)(void);
typedef int (*cache_exist_cb_t)(cache_iter_t);
typedef void (*cache_del_cb_t)(cache_iter_t);
typedef cache_iter_t (*cache_get_cb_t)(cache_key_t);
typedef cache_iter_t (*cache_put_cb_t)(cache_key_t, int *);
typedef void (*cache_free_key_cb_t)(cache_key_t);
typedef void (*cache_free_val_cb_t)(cache_val_t);
typedef cache_key_t (*cache_get_key_cb_t)(cache_iter_t);
typedef cache_val_t (*cache_get_val_cb_t)(cache_iter_t);
typedef void (*cache_set_val_cb_t)(cache_iter_t, cache_val_t);
typedef cache_val_t (*cache_unpackverify_val_cb_t)(cache_val_t, int);
typedef void (*cache_fini_cb_t)(void);
typedef struct cache {
pthread_mutex_t mutex;
cache_begin_cb_t begin_cb;
cache_end_cb_t end_cb;
cache_exist_cb_t exist_cb;
cache_del_cb_t del_cb;
cache_get_cb_t get_cb;
cache_put_cb_t put_cb;
cache_free_key_cb_t free_key_cb;
cache_free_val_cb_t free_val_cb;
cache_get_key_cb_t get_key_cb;
cache_get_val_cb_t get_val_cb;
cache_set_val_cb_t set_val_cb;
cache_unpackverify_val_cb_t unpackverify_val_cb;
cache_fini_cb_t fini_cb;
} cache_t;
typedef void (*cache_init_cb_t)(struct cache *);
cache_t * cache_new(cache_init_cb_t) MALLOC;
void cache_free(cache_t *) NONNULL();
void cache_gc(cache_t *) NONNULL();
cache_val_t cache_get(cache_t *, cache_key_t) NONNULL();
void cache_set(cache_t *, cache_key_t, cache_val_t) NONNULL();
void cache_del(cache_t *, cache_key_t) NONNULL();
#endif /* !CACHE_H */
/* vim: set noet ft=c: */

@ -0,0 +1,236 @@
/*
* SSLsplit - transparent and scalable SSL/TLS interception
* Copyright (c) 2009-2012, Daniel Roethlisberger <daniel@roe.ch>
* All rights reserved.
* http://www.roe.ch/SSLsplit
*
* 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 unmodified, 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 AUTHOR ``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 AUTHOR 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 "cachedsess.h"
#include "dynbuf.h"
#include "ssl.h"
#include "khash.h"
#include <netinet/in.h>
/*
* Cache for outgoing dst connection SSL sessions.
*
* key: dynbuf_t * original destination IP address, port and SNI string
* val: dynbuf_t * ASN.1 serialized SSL_SESSION
*/
static inline khint_t
kh_dynbuf_hash_func(dynbuf_t *b)
{
khint_t *p = (khint_t *)b->buf;
khint_t h;
int rem;
if ((rem = b->sz % sizeof(khint_t))) {
memcpy(&h, b->buf + b->sz - rem, rem);
} else {
h = 0;
}
while (p < (khint_t*)(b->buf + b->sz - rem)) {
h ^= *p++;
}
return h;
}
#define kh_dynbuf_hash_equal(a, b) \
(((a)->sz == (b)->sz) && \
(memcmp((a)->buf, (b)->buf, (a)->sz) == 0))
KHASH_INIT(dynbufmap_t, dynbuf_t*, dynbuf_t*, 1, kh_dynbuf_hash_func,
kh_dynbuf_hash_equal)
static khash_t(dynbufmap_t) *dstsessmap;
static cache_iter_t
cachedsess_begin_cb(void)
{
return kh_begin(dstsessmap);
}
static cache_iter_t
cachedsess_end_cb(void)
{
return kh_end(dstsessmap);
}
static int
cachedsess_exist_cb(cache_iter_t it)
{
return kh_exist(dstsessmap, it);
}
static void
cachedsess_del_cb(cache_iter_t it)
{
kh_del(dynbufmap_t, dstsessmap, it);
}
static cache_iter_t
cachedsess_get_cb(cache_key_t key)
{
return kh_get(dynbufmap_t, dstsessmap, key);
}
static cache_iter_t
cachedsess_put_cb(cache_key_t key, int *ret)
{
return kh_put(dynbufmap_t, dstsessmap, key, ret);
}
static void
cachedsess_free_key_cb(cache_key_t key)
{
dynbuf_free(key);
}
static void
cachedsess_free_val_cb(cache_val_t val)
{
dynbuf_free(val);
}
static cache_key_t
cachedsess_get_key_cb(cache_iter_t it)
{
return kh_key(dstsessmap, it);
}
static cache_val_t
cachedsess_get_val_cb(cache_iter_t it)
{
return kh_val(dstsessmap, it);
}
static void
cachedsess_set_val_cb(cache_iter_t it, cache_val_t val)
{
kh_val(dstsessmap, it) = val;
}
static cache_val_t
cachedsess_unpackverify_val_cb(cache_val_t val, int copy)
{
dynbuf_t *valbuf = val;
SSL_SESSION *sess;
const unsigned char *p;
p = (const unsigned char *)valbuf->buf;
sess = d2i_SSL_SESSION(NULL, &p, valbuf->sz); /* increments p */
if (!sess)
return NULL;
if (!ssl_session_is_valid(sess)) {
SSL_SESSION_free(sess);
return NULL;
}
if (copy)
return sess;
SSL_SESSION_free(sess);
return ((cache_val_t)!NULL);
}
static void
cachedsess_fini_cb(void)
{
kh_destroy(dynbufmap_t, dstsessmap);
}
void
cachedsess_init_cb(cache_t *cache)
{
dstsessmap = kh_init(dynbufmap_t);
cache->begin_cb = cachedsess_begin_cb;
cache->end_cb = cachedsess_end_cb;
cache->exist_cb = cachedsess_exist_cb;
cache->del_cb = cachedsess_del_cb;
cache->get_cb = cachedsess_get_cb;
cache->put_cb = cachedsess_put_cb;
cache->free_key_cb = cachedsess_free_key_cb;
cache->free_val_cb = cachedsess_free_val_cb;
cache->get_key_cb = cachedsess_get_key_cb;
cache->get_val_cb = cachedsess_get_val_cb;
cache->set_val_cb = cachedsess_set_val_cb;
cache->unpackverify_val_cb = cachedsess_unpackverify_val_cb;
cache->fini_cb = cachedsess_fini_cb;
}
cache_key_t
cachedsess_mkkey(const struct sockaddr *addr, UNUSED const socklen_t addrlen,
const char *sni)
{
dynbuf_t tmp, *db;
short port;
size_t snilen;
switch (((struct sockaddr_storage *)addr)->ss_family) {
case AF_INET:
tmp.buf = (unsigned char *)
&((struct sockaddr_in*)addr)->sin_addr;
tmp.sz = sizeof(struct in_addr);
port = ((struct sockaddr_in*)addr)->sin_port;
break;
case AF_INET6:
tmp.buf = (unsigned char *)
&((struct sockaddr_in6*)addr)->sin6_addr;
tmp.sz = sizeof(struct in6_addr);
port = ((struct sockaddr_in6*)addr)->sin6_port;
break;
default:
return NULL;
}
snilen = sni ? strlen(sni) : 0;
if (!(db = dynbuf_new_alloc(tmp.sz + sizeof(port) + snilen)))
return NULL;
memcpy(db->buf, tmp.buf, tmp.sz);
memcpy(db->buf + tmp.sz, (char*)&port, sizeof(port));
memcpy(db->buf + tmp.sz + sizeof(port), sni, snilen);
return db;
}
cache_val_t
cachedsess_mkval(SSL_SESSION *sess)
{
dynbuf_t *db;
unsigned char *p;
size_t asn1sz;
asn1sz = i2d_SSL_SESSION(sess, NULL);
if (!asn1sz || !(db = dynbuf_new_alloc(asn1sz))) {
return NULL;
}
p = db->buf;
i2d_SSL_SESSION(sess, &p); /* updates p */
return db;
}
/* vim: set noet ft=c: */

@ -0,0 +1,47 @@
/*
* SSLsplit - transparent and scalable SSL/TLS interception
* Copyright (c) 2009-2012, Daniel Roethlisberger <daniel@roe.ch>
* All rights reserved.
* http://www.roe.ch/SSLsplit
*
* 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 unmodified, 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 AUTHOR ``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 AUTHOR 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 CACHEDSESS_H
#define CACHEDSESS_H
#include "cache.h"
#include "attrib.h"
#include <sys/types.h>
#include <sys/socket.h>
#include <openssl/ssl.h>
void cachedsess_init_cb(struct cache *) NONNULL();
cache_key_t cachedsess_mkkey(const struct sockaddr *, const socklen_t, const char *) NONNULL();
cache_val_t cachedsess_mkval(SSL_SESSION *) NONNULL();
#endif /* !CACHEDSESS_H */
/* vim: set noet ft=c: */

@ -0,0 +1,168 @@
/*
* SSLsplit - transparent and scalable SSL/TLS interception
* Copyright (c) 2009-2012, Daniel Roethlisberger <daniel@roe.ch>
* All rights reserved.
* http://www.roe.ch/SSLsplit
*
* 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 unmodified, 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 AUTHOR ``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 AUTHOR 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 <check.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <netinet/in.h>
#include "ssl.h"
#include "cachemgr.h"
#define TMP_SESS_FILE "extra/pki/session.pem"
static SSL_SESSION *
ssl_session_from_file(const char *filename)
{
SSL_SESSION *sess;
FILE *f;
f = fopen(filename, "r");
if (!f)
return NULL;
sess = PEM_read_SSL_SESSION(f, NULL, NULL, NULL);
fclose(f);
return sess;
}
static struct sockaddr_storage addr;
static socklen_t addrlen;
static char sni[] = "daniel.roe.ch";
static void
cachemgr_setup(void)
{
ssl_init();
cachemgr_init();
addrlen = sizeof(struct sockaddr_in);
memset(&addr, 0, addrlen);
addr.ss_family = AF_INET;
}
static void
cachemgr_teardown(void)
{
cachemgr_fini();
ssl_fini();
}
START_TEST(cache_dsess_01)
{
SSL_SESSION *s1, *s2;
s1 = ssl_session_from_file(TMP_SESS_FILE);
fail_unless(!!s1, "creating session failed");
fail_unless(ssl_session_is_valid(s1), "session invalid");
cachemgr_dsess_set((struct sockaddr*)&addr, addrlen, sni, s1);
s2 = cachemgr_dsess_get((struct sockaddr*)&addr, addrlen, sni);
fail_unless(!!s2, "cache returned no session");
fail_unless(s2 != s1, "cache returned same pointer");
SSL_SESSION_free(s1);
SSL_SESSION_free(s2);
}
END_TEST
START_TEST(cache_dsess_02)
{
SSL_SESSION *s1, *s2;
s1 = ssl_session_from_file(TMP_SESS_FILE);
fail_unless(!!s1, "creating session failed");
fail_unless(ssl_session_is_valid(s1), "session invalid");
s2 = cachemgr_dsess_get((struct sockaddr*)&addr, addrlen, sni);
fail_unless(s2 == NULL, "session was already in empty cache");
SSL_SESSION_free(s1);
}
END_TEST
START_TEST(cache_dsess_03)
{
SSL_SESSION *s1, *s2;
s1 = ssl_session_from_file(TMP_SESS_FILE);
fail_unless(!!s1, "creating session failed");
fail_unless(ssl_session_is_valid(s1), "session invalid");
cachemgr_dsess_set((struct sockaddr*)&addr, addrlen, sni, s1);
cachemgr_dsess_del((struct sockaddr*)&addr, addrlen, sni);
s2 = cachemgr_dsess_get((struct sockaddr*)&addr, addrlen, sni);
fail_unless(s2 == NULL, "cache returned deleted session");
SSL_SESSION_free(s1);
}
END_TEST
START_TEST(cache_dsess_04)
{
SSL_SESSION *s1, *s2;
s1 = ssl_session_from_file(TMP_SESS_FILE);
fail_unless(!!s1, "creating session failed");
fail_unless(ssl_session_is_valid(s1), "session invalid");
fail_unless(s1->references == 1, "refcount != 1");
cachemgr_dsess_set((struct sockaddr*)&addr, addrlen, sni, s1);
fail_unless(s1->references == 1, "refcount != 1");
s2 = cachemgr_dsess_get((struct sockaddr*)&addr, addrlen, sni);
fail_unless(s1->references == 1, "refcount != 1");
fail_unless(!!s2, "cache returned no session");
fail_unless(s2->references == 1, "refcount != 1");
cachemgr_dsess_set((struct sockaddr*)&addr, addrlen, sni, s1);
fail_unless(s1->references == 1, "refcount != 1");
cachemgr_dsess_del((struct sockaddr*)&addr, addrlen, sni);
fail_unless(s1->references == 1, "refcount != 1");
cachemgr_dsess_set((struct sockaddr*)&addr, addrlen, sni, s1);
fail_unless(s1->references == 1, "refcount != 1");
SSL_SESSION_free(s1);
SSL_SESSION_free(s2);
}
END_TEST
Suite *
cachedsess_suite(void)
{
Suite *s;
TCase *tc;
s = suite_create("cachedsess");
tc = tcase_create("cache_dsess");
tcase_add_checked_fixture(tc, cachemgr_setup, cachemgr_teardown);
tcase_add_test(tc, cache_dsess_01);
tcase_add_test(tc, cache_dsess_02);
tcase_add_test(tc, cache_dsess_03);
tcase_add_test(tc, cache_dsess_04);
suite_add_tcase(s, tc);
return s;
}
/* vim: set noet ft=c: */

@ -0,0 +1,183 @@
/*
* SSLsplit - transparent and scalable SSL/TLS interception
* Copyright (c) 2009-2012, Daniel Roethlisberger <daniel@roe.ch>
* All rights reserved.
* http://www.roe.ch/SSLsplit
*
* 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 unmodified, 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 AUTHOR ``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 AUTHOR 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 "cachefkcrt.h"
#include "ssl.h"
#include "khash.h"
/*
* Cache for generated fake certificates.
*
* key: char[SSL_X509_FPRSZ] fingerprint of original server cert
* val: X509 * generated fake certificate
*/
static inline khint_t
kh_x509fpr_hash_func(void *b)
{
khint_t *p = (khint_t*)(((char*)b) + SSL_X509_FPRSZ);
khint_t h = 0;
/* assumes fpr is uniformly distributed */
while (--p >= (khint_t*)b)
h ^= *p;
return h;
}
#define kh_x509fpr_hash_equal(a, b) \
(memcmp((char*)(a), (char*)(b), SSL_X509_FPRSZ) == 0)
KHASH_INIT(sha1map_t, void*, void*, 1, kh_x509fpr_hash_func,
kh_x509fpr_hash_equal)
static khash_t(sha1map_t) *certmap;
static cache_iter_t
cachefkcrt_begin_cb(void)
{
return kh_begin(certmap);
}
static cache_iter_t
cachefkcrt_end_cb(void)
{
return kh_end(certmap);
}
static int
cachefkcrt_exist_cb(cache_iter_t it)
{
return kh_exist(certmap, it);
}
static void
cachefkcrt_del_cb(cache_iter_t it)
{
kh_del(sha1map_t, certmap, it);
}
static cache_iter_t
cachefkcrt_get_cb(cache_key_t key)
{
return kh_get(sha1map_t, certmap, key);
}
static cache_iter_t
cachefkcrt_put_cb(cache_key_t key, int *ret)
{
return kh_put(sha1map_t, certmap, key, ret);
}
static void
cachefkcrt_free_key_cb(cache_key_t key)
{
free(key);
}
static void
cachefkcrt_free_val_cb(cache_val_t val)
{
X509_free(val);
}
static cache_key_t
cachefkcrt_get_key_cb(cache_iter_t it)
{
return kh_key(certmap, it);
}
static cache_val_t
cachefkcrt_get_val_cb(cache_iter_t it)
{
return kh_val(certmap, it);
}
static void
cachefkcrt_set_val_cb(cache_iter_t it, cache_val_t val)
{
kh_val(certmap, it) = val;
}
static cache_val_t
cachefkcrt_unpackverify_val_cb(cache_val_t val, int copy)
{
if (!ssl_x509_is_valid(val))
return NULL;
if (copy) {
ssl_x509_refcount_inc(val);
return val;
}
return ((cache_val_t)!NULL);
}
static void
cachefkcrt_fini_cb(void)
{
kh_destroy(sha1map_t, certmap);
}
void
cachefkcrt_init_cb(cache_t *cache)
{
certmap = kh_init(sha1map_t);
cache->begin_cb = cachefkcrt_begin_cb;
cache->end_cb = cachefkcrt_end_cb;
cache->exist_cb = cachefkcrt_exist_cb;
cache->del_cb = cachefkcrt_del_cb;
cache->get_cb = cachefkcrt_get_cb;
cache->put_cb = cachefkcrt_put_cb;
cache->free_key_cb = cachefkcrt_free_key_cb;
cache->free_val_cb = cachefkcrt_free_val_cb;
cache->get_key_cb = cachefkcrt_get_key_cb;
cache->get_val_cb = cachefkcrt_get_val_cb;
cache->set_val_cb = cachefkcrt_set_val_cb;
cache->unpackverify_val_cb = cachefkcrt_unpackverify_val_cb;
cache->fini_cb = cachefkcrt_fini_cb;
}
cache_key_t
cachefkcrt_mkkey(X509 *keycrt)
{
unsigned char *fpr;
if (!(fpr = malloc(SSL_X509_FPRSZ)))
return NULL;
ssl_x509_fingerprint_sha1(keycrt, fpr);
return fpr;
}
cache_val_t
cachefkcrt_mkval(X509 *valcrt)
{
ssl_x509_refcount_inc(valcrt);
return valcrt;
}
/* vim: set noet ft=c: */

@ -0,0 +1,44 @@
/*
* SSLsplit - transparent and scalable SSL/TLS interception
* Copyright (c) 2009-2012, Daniel Roethlisberger <daniel@roe.ch>
* All rights reserved.
* http://www.roe.ch/SSLsplit
*
* 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 unmodified, 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 AUTHOR ``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 AUTHOR 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 CACHEFKCRT_H
#define CACHEFKCRT_H
#include "cache.h"
#include "attrib.h"
#include <openssl/x509.h>
void cachefkcrt_init_cb(struct cache *) NONNULL();
cache_key_t cachefkcrt_mkkey(X509 *) NONNULL();
cache_val_t cachefkcrt_mkval(X509 *) NONNULL();
#endif /* !CACHEFKCRT_H */
/* vim: set noet ft=c: */

@ -0,0 +1,139 @@
/*
* SSLsplit - transparent and scalable SSL/TLS interception
* Copyright (c) 2009-2012, Daniel Roethlisberger <daniel@roe.ch>
* All rights reserved.
* http://www.roe.ch/SSLsplit
*
* 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 unmodified, 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 AUTHOR ``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 AUTHOR 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 <check.h>
#include <stdlib.h>
#include <unistd.h>
#include "ssl.h"
#include "cachemgr.h"
#define TESTCERT "extra/pki/rsa.crt"
static void
cachemgr_setup(void)
{
ssl_init();
cachemgr_init();
}
static void
cachemgr_teardown(void)
{
cachemgr_fini();
ssl_fini();
}
START_TEST(cache_fkcrt_01)
{
X509 *c1, *c2;
c1 = ssl_x509_load(TESTCERT);
fail_unless(!!c1, "loading certificate failed");
cachemgr_fkcrt_set(c1, c1);
c2 = cachemgr_fkcrt_get(c1);
fail_unless(c2 == c1, "cache did not return same pointer");
X509_free(c1);
X509_free(c2);
}
END_TEST
START_TEST(cache_fkcrt_02)
{
X509 *c1, *c2;
c1 = ssl_x509_load(TESTCERT);
fail_unless(!!c1, "loading certificate failed");
c2 = cachemgr_fkcrt_get(c1);
fail_unless(c2 == NULL, "certificate was already in empty cache");
X509_free(c1);
}
END_TEST
START_TEST(cache_fkcrt_03)
{
X509 *c1, *c2;
c1 = ssl_x509_load(TESTCERT);
fail_unless(!!c1, "loading certificate failed");
cachemgr_fkcrt_set(c1, c1);
cachemgr_fkcrt_del(c1);
c2 = cachemgr_fkcrt_get(c1);
fail_unless(c2 == NULL, "cache returned deleted certificate");
X509_free(c1);
}
END_TEST
START_TEST(cache_fkcrt_04)
{
X509 *c1, *c2;
c1 = ssl_x509_load(TESTCERT);
fail_unless(!!c1, "loading certificate failed");
fail_unless(c1->references == 1, "refcount != 1");
cachemgr_fkcrt_set(c1, c1);
fail_unless(c1->references == 2, "refcount != 2");
c2 = cachemgr_fkcrt_get(c1);
fail_unless(c1->references == 3, "refcount != 3");
cachemgr_fkcrt_set(c1, c1);
fail_unless(c1->references == 3, "refcount != 3");
cachemgr_fkcrt_del(c1);
fail_unless(c1->references == 2, "refcount != 2");
cachemgr_fkcrt_set(c1, c1);
fail_unless(c1->references == 3, "refcount != 3");
X509_free(c1);
fail_unless(c1->references == 2, "refcount != 2");
cachemgr_fini();
fail_unless(c1->references == 1, "refcount != 1");
X509_free(c2);
/* deliberate access of free'd X509* */
fail_unless(c1->references == 0, "refcount != 0");
cachemgr_init();
}
END_TEST
Suite *
cachefkcrt_suite(void)
{
Suite *s;
TCase *tc;
s = suite_create("cachefkcrt");
tc = tcase_create("cache_fkcrt");
tcase_add_checked_fixture(tc, cachemgr_setup, cachemgr_teardown);
tcase_add_test(tc, cache_fkcrt_01);
tcase_add_test(tc, cache_fkcrt_02);
tcase_add_test(tc, cache_fkcrt_03);
tcase_add_test(tc, cache_fkcrt_04);
suite_add_tcase(s, tc);
return s;
}
/* vim: set noet ft=c: */

@ -0,0 +1,150 @@
/*
* SSLsplit - transparent and scalable SSL/TLS interception
* Copyright (c) 2009-2012, Daniel Roethlisberger <daniel@roe.ch>
* All rights reserved.
* http://www.roe.ch/SSLsplit
*
* 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 unmodified, 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 AUTHOR ``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 AUTHOR 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 "cachemgr.h"
#include "cachefkcrt.h"
#include "cachetgcrt.h"
#include "cachessess.h"
#include "cachedsess.h"
#include "log.h"
#include "attrib.h"
#include <pthread.h>
#include <netinet/in.h>
cache_t *cachemgr_fkcrt;
cache_t *cachemgr_tgcrt;
cache_t *cachemgr_ssess;
cache_t *cachemgr_dsess;
/*
* Garbage collector thread entry point.
* Calls the _gc() method on the cache passed as argument, then returns.
*/
static void *
cachemgr_gc_thread(UNUSED void * arg)
{
cache_gc(arg);
return NULL;
}
/*
* Initialize the caches.
* The caches may be initialized before or after libevent and OpenSSL.
* Returns -1 on error, 0 on success.
*/
int
cachemgr_init(void)
{
if (!(cachemgr_fkcrt = cache_new(cachefkcrt_init_cb)))
goto out4;
if (!(cachemgr_tgcrt = cache_new(cachetgcrt_init_cb)))
goto out3;
if (!(cachemgr_ssess = cache_new(cachessess_init_cb)))
goto out2;
if (!(cachemgr_dsess = cache_new(cachedsess_init_cb)))
goto out1;
return 0;
out1:
cache_free(cachemgr_ssess);
out2:
cache_free(cachemgr_tgcrt);
out3:
cache_free(cachemgr_fkcrt);
out4:
return -1;
}
/*
* Cleanup the caches and free all memory. Since OpenSSL certificates are
* being freed, this must be done before calling the OpenSSL cleanup methods.
* Also, it is not safe to call this while cachemgr_gc() is still running.
*/
void
cachemgr_fini(void)
{
cache_free(cachemgr_dsess);
cache_free(cachemgr_ssess);
cache_free(cachemgr_tgcrt);
cache_free(cachemgr_fkcrt);
}
/*
* Garbage collect all the cache contents; free's up resources occupied by
* certificates and sessions which are no longer valid.
* This function returns after the cleanup completed and all threads are
* joined.
*/
void
cachemgr_gc(void)
{
pthread_t fkcrt_thr, dsess_thr, ssess_thr;
int rv;
/* the tgcrt cache does not need cleanup */
rv = pthread_create(&fkcrt_thr, NULL, cachemgr_gc_thread,
cachemgr_fkcrt);
if (rv) {
log_err_printf("cachemgr_gc: pthread_create failed: %s\n",
strerror(rv));
}
rv = pthread_create(&ssess_thr, NULL, cachemgr_gc_thread,
cachemgr_ssess);
if (rv) {
log_err_printf("cachemgr_gc: pthread_create failed: %s\n",
strerror(rv));
}
rv = pthread_create(&dsess_thr, NULL, cachemgr_gc_thread,
cachemgr_dsess);
if (rv) {
log_err_printf("cachemgr_gc: pthread_create failed: %s\n",
strerror(rv));
}
rv = pthread_join(fkcrt_thr, NULL);
if (rv) {
log_err_printf("cachemgr_gc: pthread_join failed: %s\n",
strerror(rv));
}
rv = pthread_join(ssess_thr, NULL);
if (rv) {
log_err_printf("cachemgr_gc: pthread_join failed: %s\n",
strerror(rv));
}
rv = pthread_join(dsess_thr, NULL);
if (rv) {
log_err_printf("cachemgr_gc: pthread_join failed: %s\n",
strerror(rv));
}
}
/* vim: set noet ft=c: */

@ -0,0 +1,83 @@
/*
* SSLsplit - transparent and scalable SSL/TLS interception
* Copyright (c) 2009-2012, Daniel Roethlisberger <daniel@roe.ch>
* All rights reserved.
* http://www.roe.ch/SSLsplit
*
* 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 unmodified, 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 AUTHOR ``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 AUTHOR 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 CACHEMGR_H
#define CACHEMGR_H
#include "cache.h"
#include "cachefkcrt.h"
#include "cachetgcrt.h"
#include "cachessess.h"
#include "cachedsess.h"
extern cache_t *cachemgr_fkcrt;
extern cache_t *cachemgr_tgcrt;
extern cache_t *cachemgr_ssess;
extern cache_t *cachemgr_dsess;
int cachemgr_init(void);
void cachemgr_fini(void);
void cachemgr_gc(void);
#define cachemgr_fkcrt_get(key) \
cache_get(cachemgr_fkcrt, cachefkcrt_mkkey(key))
#define cachemgr_fkcrt_set(key, val) \
cache_set(cachemgr_fkcrt, cachefkcrt_mkkey(key), cachefkcrt_mkval(val))
#define cachemgr_fkcrt_del(key) \
cache_del(cachemgr_fkcrt, cachefkcrt_mkkey(key))
#define cachemgr_tgcrt_get(key) \
cache_get(cachemgr_tgcrt, cachetgcrt_mkkey(key))
#define cachemgr_tgcrt_set(key, val) \
cache_set(cachemgr_tgcrt, cachetgcrt_mkkey(key), cachetgcrt_mkval(val))
#define cachemgr_tgcrt_del(key) \
cache_del(cachemgr_tgcrt, cachetgcrt_mkkey(key))
#define cachemgr_ssess_get(key, keysz) \
cache_get(cachemgr_ssess, cachessess_mkkey((key), (keysz)))
#define cachemgr_ssess_set(val) \
cache_set(cachemgr_ssess, \
cachessess_mkkey((val)->session_id, \
(val)->session_id_length), \
cachessess_mkval(val))
#define cachemgr_ssess_del(val) \
cache_del(cachemgr_ssess, \
cachessess_mkkey((val)->session_id, \
(val)->session_id_length))
#define cachemgr_dsess_get(addr, addrlen, sni) \
cache_get(cachemgr_dsess, cachedsess_mkkey((addr), (addrlen), (sni)))
#define cachemgr_dsess_set(addr, addrlen, sni, val) \
cache_set(cachemgr_dsess, cachedsess_mkkey((addr), (addrlen), (sni)), \
cachedsess_mkval(val))
#define cachemgr_dsess_del(addr, addrlen, sni) \
cache_del(cachemgr_dsess, cachedsess_mkkey((addr), (addrlen), (sni)))
#endif /* !CACHEMGR_H */
/* vim: set noet ft=c: */

@ -0,0 +1,58 @@
/*
* SSLsplit - transparent and scalable SSL/TLS interception
* Copyright (c) 2009-2012, Daniel Roethlisberger <daniel@roe.ch>
* All rights reserved.
* http://www.roe.ch/SSLsplit
*
* 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 unmodified, 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 AUTHOR ``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 AUTHOR 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 <check.h>
#include <stdlib.h>
#include <unistd.h>
#include "cachemgr.h"
#include "khash.h"
START_TEST(cache_types_01)
{
fail_unless(sizeof(cache_iter_t) == sizeof(khiter_t),
"type mismatch: cache_iter_t != khiter_t");
}
END_TEST
Suite *
cachemgr_suite(void)
{
Suite *s;
TCase *tc;
s = suite_create("cachemgr");
tc = tcase_create("cache_types");
tcase_add_test(tc, cache_types_01);
suite_add_tcase(s, tc);
return s;
}
/* vim: set noet ft=c: */

@ -0,0 +1,206 @@
/*
* SSLsplit - transparent and scalable SSL/TLS interception
* Copyright (c) 2009-2012, Daniel Roethlisberger <daniel@roe.ch>
* All rights reserved.
* http://www.roe.ch/SSLsplit
*
* 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 unmodified, 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 AUTHOR ``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 AUTHOR 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 "cachessess.h"
#include "dynbuf.h"
#include "ssl.h"
#include "khash.h"
/*
* Cache for incoming src connection SSL sessions.
*
* key: dynbuf_t * SSL session ID
* val: dynbuf_t * ASN.1 serialized SSL_SESSION
*/
static inline khint_t
kh_dynbuf_hash_func(dynbuf_t *b)
{
khint_t *p = (khint_t *)b->buf;
khint_t h;
int rem;
if ((rem = b->sz % sizeof(khint_t))) {
memcpy(&h, b->buf + b->sz - rem, rem);
} else {
h = 0;
}
while (p < (khint_t*)(b->buf + b->sz - rem)) {
h ^= *p++;
}
return h;
}
#define kh_dynbuf_hash_equal(a, b) \
(((a)->sz == (b)->sz) && \
(memcmp((a)->buf, (b)->buf, (a)->sz) == 0))
KHASH_INIT(dynbufmap_t, dynbuf_t*, dynbuf_t*, 1, kh_dynbuf_hash_func,
kh_dynbuf_hash_equal)
static khash_t(dynbufmap_t) *srcsessmap;
static cache_iter_t
cachessess_begin_cb(void)
{
return kh_begin(srcsessmap);
}
static cache_iter_t
cachessess_end_cb(void)
{
return kh_end(srcsessmap);
}
static int
cachessess_exist_cb(cache_iter_t it)
{
return kh_exist(srcsessmap, it);
}
static void
cachessess_del_cb(cache_iter_t it)
{
kh_del(dynbufmap_t, srcsessmap, it);
}
static cache_iter_t
cachessess_get_cb(cache_key_t key)
{
return kh_get(dynbufmap_t, srcsessmap, key);
}
static cache_iter_t
cachessess_put_cb(cache_key_t key, int *ret)
{
return kh_put(dynbufmap_t, srcsessmap, key, ret);
}
static void
cachessess_free_key_cb(cache_key_t key)
{
dynbuf_free(key);
}
static void
cachessess_free_val_cb(cache_val_t val)
{
dynbuf_free(val);
}
static cache_key_t
cachessess_get_key_cb(cache_iter_t it)
{
return kh_key(srcsessmap, it);
}
static cache_val_t
cachessess_get_val_cb(cache_iter_t it)
{
return kh_val(srcsessmap, it);
}
static void
cachessess_set_val_cb(cache_iter_t it, cache_val_t val)
{
kh_val(srcsessmap, it) = val;
}
static cache_val_t
cachessess_unpackverify_val_cb(cache_val_t val, int copy)
{
dynbuf_t *valbuf = val;
SSL_SESSION *sess;
const unsigned char *p;
p = (const unsigned char *)valbuf->buf;
sess = d2i_SSL_SESSION(NULL, &p, valbuf->sz); /* increments p */
if (!sess)
return NULL;
if (!ssl_session_is_valid(sess)) {
SSL_SESSION_free(sess);
return NULL;
}
if (copy)
return sess;
SSL_SESSION_free(sess);
return ((cache_val_t)!NULL);
}
static void
cachessess_fini_cb(void)
{
kh_destroy(dynbufmap_t, srcsessmap);
}
void
cachessess_init_cb(cache_t *cache)
{
srcsessmap = kh_init(dynbufmap_t);
cache->begin_cb = cachessess_begin_cb;
cache->end_cb = cachessess_end_cb;
cache->exist_cb = cachessess_exist_cb;
cache->del_cb = cachessess_del_cb;
cache->get_cb = cachessess_get_cb;
cache->put_cb = cachessess_put_cb;
cache->free_key_cb = cachessess_free_key_cb;
cache->free_val_cb = cachessess_free_val_cb;
cache->get_key_cb = cachessess_get_key_cb;
cache->get_val_cb = cachessess_get_val_cb;
cache->set_val_cb = cachessess_set_val_cb;
cache->unpackverify_val_cb = cachessess_unpackverify_val_cb;
cache->fini_cb = cachessess_fini_cb;
}
cache_key_t
cachessess_mkkey(const unsigned char *id, const size_t idlen)
{
return dynbuf_new_copy(id, idlen);
}
cache_val_t
cachessess_mkval(SSL_SESSION *sess)
{
dynbuf_t *db;
unsigned char *p;
size_t asn1sz;
asn1sz = i2d_SSL_SESSION(sess, NULL);
if (!asn1sz || !(db = dynbuf_new_alloc(asn1sz))) {
return NULL;
}
p = db->buf;
i2d_SSL_SESSION(sess, &p); /* updates p */
return db;
}
/* vim: set noet ft=c: */

@ -0,0 +1,44 @@
/*
* SSLsplit - transparent and scalable SSL/TLS interception
* Copyright (c) 2009-2012, Daniel Roethlisberger <daniel@roe.ch>
* All rights reserved.
* http://www.roe.ch/SSLsplit
*
* 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 unmodified, 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 AUTHOR ``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 AUTHOR 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 CACHESSESS_H
#define CACHESSESS_H
#include "cache.h"
#include "attrib.h"
#include <openssl/ssl.h>
void cachessess_init_cb(struct cache *) NONNULL();
cache_key_t cachessess_mkkey(const unsigned char *, const size_t) NONNULL();
cache_val_t cachessess_mkval(SSL_SESSION *) NONNULL();
#endif /* !CACHESSESS_H */
/* vim: set noet ft=c: */

@ -0,0 +1,160 @@
/*
* SSLsplit - transparent and scalable SSL/TLS interception
* Copyright (c) 2009-2012, Daniel Roethlisberger <daniel@roe.ch>
* All rights reserved.
* http://www.roe.ch/SSLsplit
*
* 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 unmodified, 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 AUTHOR ``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 AUTHOR 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 <check.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include "ssl.h"
#include "cachemgr.h"
#define TMP_SESS_FILE "extra/pki/session.pem"
static SSL_SESSION *
ssl_session_from_file(const char *filename)
{
SSL_SESSION *sess;
FILE *f;
f = fopen(filename, "r");
if (!f)
return NULL;
sess = PEM_read_SSL_SESSION(f, NULL, NULL, NULL);
fclose(f);
return sess;
}
static void
cachemgr_setup(void)
{
ssl_init();
cachemgr_init();
}
static void
cachemgr_teardown(void)
{
cachemgr_fini();
ssl_fini();
}
START_TEST(cache_ssess_01)
{
SSL_SESSION *s1, *s2;
s1 = ssl_session_from_file(TMP_SESS_FILE);
fail_unless(!!s1, "creating session failed");
fail_unless(ssl_session_is_valid(s1), "session invalid");
cachemgr_ssess_set(s1);
s2 = cachemgr_ssess_get(s1->session_id, s1->session_id_length);
fail_unless(!!s2, "cache returned no session");
fail_unless(s2 != s1, "cache returned same pointer");
SSL_SESSION_free(s1);
SSL_SESSION_free(s2);
}
END_TEST
START_TEST(cache_ssess_02)
{
SSL_SESSION *s1, *s2;
s1 = ssl_session_from_file(TMP_SESS_FILE);
fail_unless(!!s1, "creating session failed");
fail_unless(ssl_session_is_valid(s1), "session invalid");
s2 = cachemgr_ssess_get(s1->session_id, s1->session_id_length);
fail_unless(s2 == NULL, "session was already in empty cache");
SSL_SESSION_free(s1);
}
END_TEST
START_TEST(cache_ssess_03)
{
SSL_SESSION *s1, *s2;
s1 = ssl_session_from_file(TMP_SESS_FILE);
fail_unless(!!s1, "creating session failed");
fail_unless(ssl_session_is_valid(s1), "session invalid");
cachemgr_ssess_set(s1);
cachemgr_ssess_del(s1);
s2 = cachemgr_ssess_get(s1->session_id, s1->session_id_length);
fail_unless(s2 == NULL, "cache returned deleted session");
SSL_SESSION_free(s1);
}
END_TEST
START_TEST(cache_ssess_04)
{
SSL_SESSION *s1, *s2;
s1 = ssl_session_from_file(TMP_SESS_FILE);
fail_unless(!!s1, "creating session failed");
fail_unless(ssl_session_is_valid(s1), "session invalid");
fail_unless(s1->references == 1, "refcount != 1");
cachemgr_ssess_set(s1);
fail_unless(s1->references == 1, "refcount != 1");
s2 = cachemgr_ssess_get(s1->session_id, s1->session_id_length);
fail_unless(s1->references == 1, "refcount != 1");
fail_unless(!!s2, "cache returned no session");
fail_unless(s2->references == 1, "refcount != 1");
cachemgr_ssess_set(s1);
fail_unless(s1->references == 1, "refcount != 1");
cachemgr_ssess_del(s1);
fail_unless(s1->references == 1, "refcount != 1");
cachemgr_ssess_set(s1);
fail_unless(s1->references == 1, "refcount != 1");
SSL_SESSION_free(s1);
SSL_SESSION_free(s2);
}
END_TEST
Suite *
cachessess_suite(void)
{
Suite *s;
TCase *tc;
s = suite_create("cachessess");
tc = tcase_create("cache_ssess");
tcase_add_checked_fixture(tc, cachemgr_setup, cachemgr_teardown);
tcase_add_test(tc, cache_ssess_01);
tcase_add_test(tc, cache_ssess_02);
tcase_add_test(tc, cache_ssess_03);
tcase_add_test(tc, cache_ssess_04);
suite_add_tcase(s, tc);
return s;
}
/* vim: set noet ft=c: */

@ -0,0 +1,161 @@
/*
* SSLsplit - transparent and scalable SSL/TLS interception
* Copyright (c) 2009-2012, Daniel Roethlisberger <daniel@roe.ch>
* All rights reserved.
* http://www.roe.ch/SSLsplit
*
* 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 unmodified, 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 AUTHOR ``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 AUTHOR 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 "cachetgcrt.h"
#include "ssl.h"
#include "khash.h"
/*
* Cache for target cert / chain / key tuples read from configured directory.
* This cache does not need garbage collection.
*
* key: char * common name
* val: cert_t * cert / chain / key tuple
*/
KHASH_INIT(cstrmap_t, char*, void*, 1, kh_str_hash_func, kh_str_hash_equal)
static khash_t(cstrmap_t) *certmap;
static cache_iter_t
cachetgcrt_begin_cb(void)
{
return kh_begin(certmap);
}
static cache_iter_t
cachetgcrt_end_cb(void)
{
return kh_end(certmap);
}
static int
cachetgcrt_exist_cb(cache_iter_t it)
{
return kh_exist(certmap, it);
}
static void
cachetgcrt_del_cb(cache_iter_t it)
{
kh_del(cstrmap_t, certmap, it);
}
static cache_iter_t
cachetgcrt_get_cb(cache_key_t key)
{
return kh_get(cstrmap_t, certmap, key);
}
static cache_iter_t
cachetgcrt_put_cb(cache_key_t key, int *ret)
{
return kh_put(cstrmap_t, certmap, key, ret);
}
static void
cachetgcrt_free_key_cb(cache_key_t key)
{
free(key);
}
static void
cachetgcrt_free_val_cb(cache_val_t val)
{
cert_free(val);
}
static cache_key_t
cachetgcrt_get_key_cb(cache_iter_t it)
{
return kh_key(certmap, it);
}
static cache_val_t
cachetgcrt_get_val_cb(cache_iter_t it)
{
return kh_val(certmap, it);
}
static void
cachetgcrt_set_val_cb(cache_iter_t it, cache_val_t val)
{
kh_val(certmap, it) = val;
}
static cache_val_t
cachetgcrt_unpackverify_val_cb(cache_val_t val, int copy)
{
if (copy) {
cert_refcount_inc(val);
return val;
}
return ((cache_val_t)!NULL);
}
static void
cachetgcrt_fini_cb(void)
{
kh_destroy(cstrmap_t, certmap);
}
void
cachetgcrt_init_cb(cache_t *cache)
{
certmap = kh_init(cstrmap_t);
cache->begin_cb = cachetgcrt_begin_cb;
cache->end_cb = cachetgcrt_end_cb;
cache->exist_cb = cachetgcrt_exist_cb;
cache->del_cb = cachetgcrt_del_cb;
cache->get_cb = cachetgcrt_get_cb;
cache->put_cb = cachetgcrt_put_cb;
cache->free_key_cb = cachetgcrt_free_key_cb;
cache->free_val_cb = cachetgcrt_free_val_cb;
cache->get_key_cb = cachetgcrt_get_key_cb;
cache->get_val_cb = cachetgcrt_get_val_cb;
cache->set_val_cb = cachetgcrt_set_val_cb;
cache->unpackverify_val_cb = cachetgcrt_unpackverify_val_cb;
cache->fini_cb = cachetgcrt_fini_cb;
}
cache_key_t
cachetgcrt_mkkey(const char *keycn)
{
return strdup(keycn);
}
cache_val_t
cachetgcrt_mkval(cert_t *valcrt)
{
cert_refcount_inc(valcrt);
return valcrt;
}
/* vim: set noet ft=c: */

@ -0,0 +1,43 @@
/*
* SSLsplit - transparent and scalable SSL/TLS interception
* Copyright (c) 2009-2012, Daniel Roethlisberger <daniel@roe.ch>
* All rights reserved.
* http://www.roe.ch/SSLsplit
*
* 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 unmodified, 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 AUTHOR ``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 AUTHOR 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 CACHETGCRT_H
#define CACHETGCRT_H
#include "cache.h"
#include "attrib.h"
#include "cert.h"
void cachetgcrt_init_cb(struct cache *) NONNULL();
cache_key_t cachetgcrt_mkkey(const char *) NONNULL();
cache_val_t cachetgcrt_mkval(cert_t *) NONNULL();
#endif /* !CACHETGCRT_H */
/* vim: set noet ft=c: */

@ -0,0 +1,137 @@
/*
* SSLsplit - transparent and scalable SSL/TLS interception
* Copyright (c) 2009-2012, Daniel Roethlisberger <daniel@roe.ch>
* All rights reserved.
* http://www.roe.ch/SSLsplit
*
* 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 unmodified, 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 AUTHOR ``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 AUTHOR 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 <check.h>
#include <stdlib.h>
#include <unistd.h>
#include "ssl.h"
#include "cert.h"
#include "cachemgr.h"
#define TESTCERT "extra/pki/targets/daniel.roe.ch.pem"
static void
cachemgr_setup(void)
{
ssl_init();
cachemgr_init();
}
static void
cachemgr_teardown(void)
{
cachemgr_fini();
ssl_fini();
}
START_TEST(cache_tgcrt_01)
{
cert_t *c1, *c2;
c1 = cert_new_load(TESTCERT);
fail_unless(!!c1, "loading certificate failed");
cachemgr_tgcrt_set("daniel.roe.ch", c1);
c2 = cachemgr_tgcrt_get("daniel.roe.ch");
fail_unless(c2 == c1, "cache did not return same pointer");
cert_free(c1);
cert_free(c2);
}
END_TEST
START_TEST(cache_tgcrt_02)
{
cert_t *c;
c = cachemgr_tgcrt_get("daniel.roe.ch");
fail_unless(c == NULL, "certificate was already in empty cache");
}
END_TEST
START_TEST(cache_tgcrt_03)
{
cert_t *c1, *c2;
c1 = cert_new_load(TESTCERT);
fail_unless(!!c1, "loading certificate failed");
cachemgr_tgcrt_set("daniel.roe.ch", c1);
cachemgr_tgcrt_del("daniel.roe.ch");
c2 = cachemgr_tgcrt_get("daniel.roe.ch");
fail_unless(c2 == NULL, "cache returned deleted certificate");
cert_free(c1);
}
END_TEST
START_TEST(cache_tgcrt_04)
{
cert_t *c1, *c2;
c1 = cert_new_load(TESTCERT);
fail_unless(!!c1, "loading certificate failed");
fail_unless(c1->references == 1, "refcount != 1");
cachemgr_tgcrt_set("daniel.roe.ch", c1);
fail_unless(c1->references == 2, "refcount != 2");
c2 = cachemgr_tgcrt_get("daniel.roe.ch");
fail_unless(c1->references == 3, "refcount != 3");
cachemgr_tgcrt_set("daniel.roe.ch", c1);
fail_unless(c1->references == 3, "refcount != 3");
cachemgr_tgcrt_del("daniel.roe.ch");
fail_unless(c1->references == 2, "refcount != 2");
cachemgr_tgcrt_set("daniel.roe.ch", c1);
fail_unless(c1->references == 3, "refcount != 3");
cert_free(c1);
fail_unless(c1->references == 2, "refcount != 2");
cachemgr_fini();
fail_unless(c1->references == 1, "refcount != 1");
cert_free(c2);
/* deliberate access of free'd cert_t* */
fail_unless(c1->references == 0, "refcount != 0");
cachemgr_init();
}
END_TEST
Suite *
cachetgcrt_suite(void)
{
Suite *s;
TCase *tc;
s = suite_create("cachetgcrt");
tc = tcase_create("cache_tgcrt");
tcase_add_checked_fixture(tc, cachemgr_setup, cachemgr_teardown);
tcase_add_test(tc, cache_tgcrt_01);
tcase_add_test(tc, cache_tgcrt_02);
tcase_add_test(tc, cache_tgcrt_03);
tcase_add_test(tc, cache_tgcrt_04);
suite_add_tcase(s, tc);
return s;
}
/* vim: set noet ft=c: */

209
cert.c

@ -0,0 +1,209 @@
/*
* SSLsplit - transparent and scalable SSL/TLS interception
* Copyright (c) 2009-2012, Daniel Roethlisberger <daniel@roe.ch>
* All rights reserved.
* http://www.roe.ch/SSLsplit
*
* 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 unmodified, 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 AUTHOR ``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 AUTHOR 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 "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 = malloc(sizeof(cert_t))))
return NULL;
memset(c, 0, sizeof(cert_t));
c->references = 1;
pthread_mutex_init(&c->mutex, NULL);
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 = malloc(sizeof(cert_t))))
return NULL;
c->key = key;
c->crt = crt;
c->chain = chain;
c->references = 1;
pthread_mutex_init(&c->mutex, NULL);
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 = malloc(sizeof(cert_t))))
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;
pthread_mutex_init(&c->mutex, NULL);
return c;
}
/*
* Load cert_t from file.
*/
cert_t *
cert_new_load(const char *filename)
{
cert_t *c;
if (!(c = malloc(sizeof(cert_t))))
return NULL;
memset(c, 0, sizeof(cert_t));
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;
pthread_mutex_init(&c->mutex, NULL);
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);
}
/* vim: set noet ft=c: */

@ -0,0 +1,57 @@
/*
* SSLsplit - transparent and scalable SSL/TLS interception
* Copyright (c) 2009-2012, Daniel Roethlisberger <daniel@roe.ch>
* All rights reserved.
* http://www.roe.ch/SSLsplit
*
* 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 unmodified, 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 AUTHOR ``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 AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef CERT_H
#define CERT_H
#include "attrib.h"
#include <openssl/ssl.h>
#include <pthread.h>
typedef struct cert {
EVP_PKEY *key;
X509 *crt;
STACK_OF(X509) * chain;
pthread_mutex_t mutex;
size_t references;
} cert_t;
cert_t * cert_new(void) MALLOC;
cert_t * cert_new_load(const char *) MALLOC;
cert_t * cert_new3(EVP_PKEY *, X509 *, STACK_OF(X509) *) MALLOC;
cert_t * cert_new3_copy(EVP_PKEY *, X509 *, STACK_OF(X509) *) MALLOC;
void cert_refcount_inc(cert_t *) NONNULL();
void cert_set_key(cert_t *, EVP_PKEY *) NONNULL(1);
void cert_set_crt(cert_t *, X509 *) NONNULL(1);
void cert_set_chain(cert_t *, STACK_OF(X509) *) NONNULL(1);
void cert_free(cert_t *) NONNULL();
#endif /* !CERT_H */
/* vim: set noet ft=c: */

@ -0,0 +1,88 @@
/*
* SSLsplit - transparent and scalable SSL/TLS interception
* Copyright (c) 2009-2012, Daniel Roethlisberger <daniel@roe.ch>
* All rights reserved.
* http://www.roe.ch/SSLsplit
*
* 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 unmodified, 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 AUTHOR ``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 AUTHOR 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 <check.h>
#include <stdlib.h>
#include <unistd.h>
#include "ssl.h"
#include "cert.h"
#define TESTCERT "extra/pki/targets/daniel.roe.ch.pem"
START_TEST(cert_new_load_01)
{
cert_t *c;
c = cert_new_load(TESTCERT);
fail_unless(!!c, "loading PEM failed");
fail_unless(!!c->crt, "loading crt failed");
fail_unless(!!c->key, "loading key failed");
fail_unless(!!c->chain, "initializing chain stack failed");
fail_unless(sk_X509_num(c->chain) == 1, "loading chain failed");
cert_free(c);
}
END_TEST
START_TEST(cert_refcount_inc_01)
{
cert_t *c;
c = cert_new_load(TESTCERT);
fail_unless(!!c, "loading PEM failed");
fail_unless(c->references == 1, "refcount mismatch");
cert_refcount_inc(c);
fail_unless(c->references == 2, "refcount mismatch");
cert_free(c);
fail_unless(c->references == 1, "refcount mismatch");
cert_free(c);
/* deliberate access after last free() */
fail_unless(c->references == 0, "refcount mismatch");
}
END_TEST
Suite *
cert_suite(void)
{
Suite *s;
TCase *tc;
s = suite_create("cert");
tc = tcase_create("cert_new_load_01");
tcase_add_test(tc, cert_new_load_01);
suite_add_tcase(s, tc);
tc = tcase_create("cert_refcount_inc_01");
tcase_add_test(tc, cert_refcount_inc_01);
suite_add_tcase(s, tc);
return s;
}
/* vim: set noet ft=c: */

@ -0,0 +1,138 @@
/*
* SSLsplit - transparent and scalable SSL/TLS interception
* Copyright (c) 2009-2012, Daniel Roethlisberger <daniel@roe.ch>
* All rights reserved.
* http://www.roe.ch/SSLsplit
*
* 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 unmodified, 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 AUTHOR ``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 AUTHOR 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 "dynbuf.h"
#include <string.h>
#include <stdio.h>
/*
* Simple dynamic buffer, consisting of internal buffer ptr plus length.
* Dynbuf always owns the internal allocated buffer.
*/
/*
* Allocate new dynbuf; will allocate sz bytes of memory in ->buf.
*/
dynbuf_t *
dynbuf_new_alloc(size_t sz)
{
dynbuf_t *db;
if (!(db = malloc(sizeof(dynbuf_t))))
return NULL;
if (!(db->buf = malloc(sz))) {
free(db);
return NULL;
}
db->sz = sz;
return db;
}
/*
* Create new dynbuf from provided buffer, which is copied.
*/
dynbuf_t *
dynbuf_new_copy(const unsigned char *buf, const size_t sz)
{
dynbuf_t *db;
if (!(db = malloc(sizeof(dynbuf_t))))
return NULL;
if (!(db->buf = malloc(sz))) {
free(db);
return NULL;
}
memcpy(db->buf, buf, sz);
db->sz = sz;
return db;
}
/*
* Create new dynbuf by loading a file into a newly allocated internal buffer.
* The provided buffer will be freed by dynbuf_free().
*/
dynbuf_t *
dynbuf_new_file(const char *filename)
{
dynbuf_t *db;
FILE *f;
if (!(db = malloc(sizeof(dynbuf_t))))
return NULL;
f = fopen(filename, "rb");
if (!f) {
free(db);
return NULL;
}
fseek(f, 0, SEEK_END);
db->sz = ftell(f);
fseek(f, 0, SEEK_SET);
if (!(db->buf = malloc(db->sz))) {
free(db);
fclose(f);
return NULL;
}
if (fread(db->buf, db->sz, 1, f) != 1) {
free(db->buf);
free(db);
fclose(f);
return NULL;
}
fclose(f);
return db;
}
/*
* Create new dynbuf from provided, pre-allocated buffer.
* The provided buffer will be freed by dynbuf_free().
*/
dynbuf_t *
dynbuf_new(unsigned char *buf, size_t sz)
{
dynbuf_t *db;
if (!(db = malloc(sizeof(dynbuf_t))))
return NULL;
db->buf = buf;
db->sz = sz;
return db;
}
/*
* Free dynbuf including internal buffer.
*/
void
dynbuf_free(dynbuf_t *db)
{
free(db->buf);
free(db);
}
/* vim: set noet ft=c: */

@ -0,0 +1,49 @@
/*
* SSLsplit - transparent and scalable SSL/TLS interception
* Copyright (c) 2009-2012, Daniel Roethlisberger <daniel@roe.ch>
* All rights reserved.
* http://www.roe.ch/SSLsplit
*
* 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 unmodified, 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 AUTHOR ``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 AUTHOR 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 DYNBUF_H
#define DYNBUF_H
#include "attrib.h"
#include <stdlib.h>
typedef struct dynbuf {
unsigned char *buf;
size_t sz;
} dynbuf_t;
dynbuf_t * dynbuf_new(unsigned char *, size_t) MALLOC;
dynbuf_t * dynbuf_new_alloc(size_t) MALLOC;
dynbuf_t * dynbuf_new_copy(const unsigned char *, const size_t) MALLOC;
dynbuf_t * dynbuf_new_file(const char *) MALLOC;
void dynbuf_free(dynbuf_t *) NONNULL();
#endif /* !DYNBUF_H */
/* vim: set noet ft=c: */

@ -0,0 +1,92 @@
# inherited
VERSION?= unknown
OPENSSL?= openssl
MKDIR?= mkdir
# OpenSSL settings
CA_SUBJECT?= '/O=SSLsplit Root CA/CN=SSLsplit Root CA/'
CA_DAYS?= 3650
CONFIG:= x509v3ca.cnf
CONFIG_EXT:= v3_ca
all: rsa dsa ec targets
session: session.pem
dh: dh512.param dh1024.param dh2048.param
rsa: rsa.pem
dsa: dsa.pem
ec: ec.pem
dh512.param:
$(OPENSSL) dhparam -out $@ -2 512
dh1024.param:
$(OPENSSL) dhparam -out $@ -2 1024
dh2048.param:
$(OPENSSL) dhparam -out $@ -2 2048
dsa.param:
$(OPENSSL) dsaparam -out $@ 1024
dsa.key: dsa.param
$(OPENSSL) gendsa -out $@ $<
rsa.key:
$(OPENSSL) genrsa -out $@ 1024
ec.key:
$(OPENSSL) ecparam -out $@ -name prime192v1 -genkey
%.crt: %.key
$(OPENSSL) req -new -nodes -x509 -sha1 -out $@ -key $< \
-config $(CONFIG) -extensions $(CONFIG_EXT) \
-subj $(CA_SUBJECT) \
-set_serial 0 -days $(CA_DAYS)
%.pem: %.crt %.key
cat $^ >$@
targets: rsa.crt
$(MKDIR) targets
$(OPENSSL) genrsa -out targets/daniel.roe.ch.key 1024
$(OPENSSL) req -new -sha1 -subj '/C=CH/CN=daniel.roe.ch/' \
-key targets/daniel.roe.ch.key \
-out targets/daniel.roe.ch.csr
$(OPENSSL) x509 -req -sha1 -CAcreateserial -days 365 \
-CA rsa.crt -CAkey rsa.key \
-in targets/daniel.roe.ch.csr \
-out targets/daniel.roe.ch.crt
cat targets/daniel.roe.ch.crt targets/daniel.roe.ch.key rsa.crt \
>targets/daniel.roe.ch.pem
$(RM) targets/daniel.roe.ch.{key,csr,crt}
$(OPENSSL) genrsa -out targets/wildcard.roe.ch.key 1024
$(OPENSSL) req -new -sha1 -subj '/C=CH/CN=*.roe.ch/' \
-key targets/wildcard.roe.ch.key \
-out targets/wildcard.roe.ch.csr
$(OPENSSL) x509 -req -sha1 -CAcreateserial -days 365 \
-CA rsa.crt -CAkey rsa.key \
-in targets/wildcard.roe.ch.csr \
-out targets/wildcard.roe.ch.crt
cat targets/wildcard.roe.ch.crt targets/wildcard.roe.ch.key rsa.crt \
>targets/wildcard.roe.ch.pem
$(RM) targets/wildcard.roe.ch.{key,csr,crt} rsa.srl
# openssl s_server cannot be easily controlled from scripts; it is
# more robust to just connect to a real server to create a session
session.pem:
echo -e 'GET /test/SSLsplit-$(VERSION) HTTP/1.1\r\nHost:' \
'daniel.roe.ch\r\nConnection: close\r\n\r\n' | \
$(OPENSSL) s_client -connect daniel.roe.ch:443 \
-quiet -no_ign_eof -sess_out $@ >/dev/null 2>&1
test -r $@
clean:
rm -rf rsa.* dsa.* ec.* dh*.param targets *.srl session.pem
.PHONY: all clean rsa dsa ec dh dhall session

@ -0,0 +1,9 @@
[ req ]
distinguished_name = reqdn
[ reqdn ]
[ v3_ca ]
basicConstraints = CA:TRUE
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer:always

@ -0,0 +1,4 @@
#!/bin/sh
ulimit -n @@maxfds@@
export LD_LIBRARY_PATH=@@localbase@@/lib:"$LD_LIBRARY_PATH"
exec @@prefix@@/bin/sslsplit "$@"

@ -0,0 +1,548 @@
/* The MIT License
Copyright (c) 2008, 2009, 2011 by Attractive Chaos <attractor@live.co.uk>
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
/*
An example:
#include "khash.h"
KHASH_MAP_INIT_INT(32, char)
int main() {
int ret, is_missing;
khiter_t k;
khash_t(32) *h = kh_init(32);
k = kh_put(32, h, 5, &ret);
kh_value(h, k) = 10;
k = kh_get(32, h, 10);
is_missing = (k == kh_end(h));
k = kh_get(32, h, 5);
kh_del(32, h, k);
for (k = kh_begin(h); k != kh_end(h); ++k)
if (kh_exist(h, k)) kh_value(h, k) = 1;
kh_destroy(32, h);
return 0;
}
*/
/*
2011-12-29 (0.2.7):
* Minor code clean up; no actual effect.
2011-09-16 (0.2.6):
* The capacity is a power of 2. This seems to dramatically improve the
speed for simple keys. Thank Zilong Tan for the suggestion. Reference:
- http://code.google.com/p/ulib/
- http://nothings.org/computer/judy/
* Allow to optionally use linear probing which usually has better
performance for random input. Double hashing is still the default as it
is more robust to certain non-random input.
* Added Wang's integer hash function (not used by default). This hash
function is more robust to certain non-random input.
2011-02-14 (0.2.5):
* Allow to declare global functions.
2009-09-26 (0.2.4):
* Improve portability
2008-09-19 (0.2.3):
* Corrected the example
* Improved interfaces
2008-09-11 (0.2.2):
* Improved speed a little in kh_put()
2008-09-10 (0.2.1):
* Added kh_clear()
* Fixed a compiling error
2008-09-02 (0.2.0):
* Changed to token concatenation which increases flexibility.
2008-08-31 (0.1.2):
* Fixed a bug in kh_get(), which has not been tested previously.
2008-08-31 (0.1.1):
* Added destructor
*/
#ifndef __AC_KHASH_H
#define __AC_KHASH_H
/*!
@header
Generic hash table library.
*/
#define AC_VERSION_KHASH_H "0.2.6"
#include <stdlib.h>
#include <string.h>
#include <limits.h>
/* compipler specific configuration */
#if UINT_MAX == 0xffffffffu
typedef unsigned int khint32_t;
#elif ULONG_MAX == 0xffffffffu
typedef unsigned long khint32_t;
#endif
#if ULONG_MAX == ULLONG_MAX
typedef unsigned long khint64_t;
#else
typedef unsigned long long khint64_t;
#endif
#ifdef _MSC_VER
#define inline __inline
#endif
typedef khint32_t khint_t;
typedef khint_t khiter_t;
#define __ac_isempty(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&2)
#define __ac_isdel(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&1)
#define __ac_iseither(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&3)
#define __ac_set_isdel_false(flag, i) (flag[i>>4]&=~(1ul<<((i&0xfU)<<1)))
#define __ac_set_isempty_false(flag, i) (flag[i>>4]&=~(2ul<<((i&0xfU)<<1)))
#define __ac_set_isboth_false(flag, i) (flag[i>>4]&=~(3ul<<((i&0xfU)<<1)))
#define __ac_set_isdel_true(flag, i) (flag[i>>4]|=1ul<<((i&0xfU)<<1))
#ifdef KHASH_LINEAR
#define __ac_inc(k, m) 1
#else
#define __ac_inc(k, m) (((k)>>3 ^ (k)<<3) | 1) & (m)
#endif
#define __ac_fsize(m) ((m) < 16? 1 : (m)>>4)
#ifndef kroundup32
#define kroundup32(x) (--(x), (x)|=(x)>>1, (x)|=(x)>>2, (x)|=(x)>>4, (x)|=(x)>>8, (x)|=(x)>>16, ++(x))
#endif
static const double __ac_HASH_UPPER = 0.77;
#define __KHASH_TYPE(name, khkey_t, khval_t) \
typedef struct { \
khint_t n_buckets, size, n_occupied, upper_bound; \
khint32_t *flags; \
khkey_t *keys; \
khval_t *vals; \
} kh_##name##_t;
#define KHASH_DECLARE(name, khkey_t, khval_t) \
__KHASH_TYPE(name, khkey_t, khval_t) \
extern kh_##name##_t *kh_init_##name(); \
extern void kh_destroy_##name(kh_##name##_t *h); \
extern void kh_clear_##name(kh_##name##_t *h); \
extern khint_t kh_get_##name(const kh_##name##_t *h, khkey_t key); \
extern void kh_resize_##name(kh_##name##_t *h, khint_t new_n_buckets); \
extern khint_t kh_put_##name(kh_##name##_t *h, khkey_t key, int *ret); \
extern void kh_del_##name(kh_##name##_t *h, khint_t x);
#define KHASH_INIT2(name, SCOPE, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \
__KHASH_TYPE(name, khkey_t, khval_t) \
SCOPE kh_##name##_t *kh_init_##name() { \
return (kh_##name##_t*)calloc(1, sizeof(kh_##name##_t)); \
} \
SCOPE void kh_destroy_##name(kh_##name##_t *h) \
{ \
if (h) { \
free(h->keys); free(h->flags); \
free(h->vals); \
free(h); \
} \
} \
SCOPE void kh_clear_##name(kh_##name##_t *h) \
{ \
if (h && h->flags) { \
memset(h->flags, 0xaa, __ac_fsize(h->n_buckets) * sizeof(khint32_t)); \
h->size = h->n_occupied = 0; \
} \
} \
SCOPE khint_t kh_get_##name(const kh_##name##_t *h, khkey_t key) \
{ \
if (h->n_buckets) { \
khint_t inc, k, i, last, mask; \
mask = h->n_buckets - 1; \
k = __hash_func(key); i = k & mask; \
inc = __ac_inc(k, mask); last = i; /* inc==1 for linear probing */ \
while (!__ac_isempty(h->flags, i) && (__ac_isdel(h->flags, i) || !__hash_equal(h->keys[i], key))) { \
i = (i + inc) & mask; \
if (i == last) return h->n_buckets; \
} \
return __ac_iseither(h->flags, i)? h->n_buckets : i; \
} else return 0; \
} \
SCOPE void kh_resize_##name(kh_##name##_t *h, khint_t new_n_buckets) \
{ /* This function uses 0.25*n_bucktes bytes of working space instead of [sizeof(key_t+val_t)+.25]*n_buckets. */ \
khint32_t *new_flags = 0; \
khint_t j = 1; \
{ \
kroundup32(new_n_buckets); \
if (new_n_buckets < 4) new_n_buckets = 4; \
if (h->size >= (khint_t)(new_n_buckets * __ac_HASH_UPPER + 0.5)) j = 0; /* requested size is too small */ \
else { /* hash table size to be changed (shrink or expand); rehash */ \
new_flags = (khint32_t*)malloc(__ac_fsize(new_n_buckets) * sizeof(khint32_t)); \
memset(new_flags, 0xaa, __ac_fsize(new_n_buckets) * sizeof(khint32_t)); \
if (h->n_buckets < new_n_buckets) { /* expand */ \
h->keys = (khkey_t*)realloc(h->keys, new_n_buckets * sizeof(khkey_t)); \
if (kh_is_map) h->vals = (khval_t*)realloc(h->vals, new_n_buckets * sizeof(khval_t)); \
} /* otherwise shrink */ \
} \
} \
if (j) { /* rehashing is needed */ \
for (j = 0; j != h->n_buckets; ++j) { \
if (__ac_iseither(h->flags, j) == 0) { \
khkey_t key = h->keys[j]; \
khval_t val; \
khint_t new_mask; \
new_mask = new_n_buckets - 1; \
if (kh_is_map) val = h->vals[j]; \
__ac_set_isdel_true(h->flags, j); \
while (1) { /* kick-out process; sort of like in Cuckoo hashing */ \
khint_t inc, k, i; \
k = __hash_func(key); \
i = k & new_mask; \
inc = __ac_inc(k, new_mask); \
while (!__ac_isempty(new_flags, i)) i = (i + inc) & new_mask; \
__ac_set_isempty_false(new_flags, i); \
if (i < h->n_buckets && __ac_iseither(h->flags, i) == 0) { /* kick out the existing element */ \
{ khkey_t tmp = h->keys[i]; h->keys[i] = key; key = tmp; } \
if (kh_is_map) { khval_t tmp = h->vals[i]; h->vals[i] = val; val = tmp; } \
__ac_set_isdel_true(h->flags, i); /* mark it as deleted in the old hash table */ \
} else { /* write the element and jump out of the loop */ \
h->keys[i] = key; \
if (kh_is_map) h->vals[i] = val; \
break; \
} \
} \
} \
} \
if (h->n_buckets > new_n_buckets) { /* shrink the hash table */ \
h->keys = (khkey_t*)realloc(h->keys, new_n_buckets * sizeof(khkey_t)); \
if (kh_is_map) h->vals = (khval_t*)realloc(h->vals, new_n_buckets * sizeof(khval_t)); \
} \
free(h->flags); /* free the working space */ \
h->flags = new_flags; \
h->n_buckets = new_n_buckets; \
h->n_occupied = h->size; \
h->upper_bound = (khint_t)(h->n_buckets * __ac_HASH_UPPER + 0.5); \
} \
} \
SCOPE khint_t kh_put_##name(kh_##name##_t *h, khkey_t key, int *ret) \
{ \
khint_t x; \
if (h->n_occupied >= h->upper_bound) { /* update the hash table */ \
if (h->n_buckets > (h->size<<1)) kh_resize_##name(h, h->n_buckets - 1); /* clear "deleted" elements */ \
else kh_resize_##name(h, h->n_buckets + 1); /* expand the hash table */ \
} /* TODO: to implement automatically shrinking; resize() already support shrinking */ \
{ \
khint_t inc, k, i, site, last, mask = h->n_buckets - 1; \
x = site = h->n_buckets; k = __hash_func(key); i = k & mask; \
if (__ac_isempty(h->flags, i)) x = i; /* for speed up */ \
else { \
inc = __ac_inc(k, mask); last = i; \
while (!__ac_isempty(h->flags, i) && (__ac_isdel(h->flags, i) || !__hash_equal(h->keys[i], key))) { \
if (__ac_isdel(h->flags, i)) site = i; \
i = (i + inc) & mask; \
if (i == last) { x = site; break; } \
} \
if (x == h->n_buckets) { \
if (__ac_isempty(h->flags, i) && site != h->n_buckets) x = site; \
else x = i; \
} \
} \
} \
if (__ac_isempty(h->flags, x)) { /* not present at all */ \
h->keys[x] = key; \
__ac_set_isboth_false(h->flags, x); \
++h->size; ++h->n_occupied; \
*ret = 1; \
} else if (__ac_isdel(h->flags, x)) { /* deleted */ \
h->keys[x] = key; \
__ac_set_isboth_false(h->flags, x); \
++h->size; \
*ret = 2; \
} else *ret = 0; /* Don't touch h->keys[x] if present and not deleted */ \
return x; \
} \
SCOPE void kh_del_##name(kh_##name##_t *h, khint_t x) \
{ \
if (x != h->n_buckets && !__ac_iseither(h->flags, x)) { \
__ac_set_isdel_true(h->flags, x); \
--h->size; \
} \
}
#define KHASH_INIT(name, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \
KHASH_INIT2(name, static inline, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal)
/* --- BEGIN OF HASH FUNCTIONS --- */
/*! @function
@abstract Integer hash function
@param key The integer [khint32_t]
@return The hash value [khint_t]
*/
#define kh_int_hash_func(key) (khint32_t)(key)
/*! @function
@abstract Integer comparison function
*/
#define kh_int_hash_equal(a, b) ((a) == (b))
/*! @function
@abstract 64-bit integer hash function
@param key The integer [khint64_t]
@return The hash value [khint_t]
*/
#define kh_int64_hash_func(key) (khint32_t)((key)>>33^(key)^(key)<<11)
/*! @function
@abstract 64-bit integer comparison function
*/
#define kh_int64_hash_equal(a, b) ((a) == (b))
/*! @function
@abstract const char* hash function
@param s Pointer to a null terminated string
@return The hash value
*/
static inline khint_t __ac_X31_hash_string(const char *s)
{
khint_t h = *s;
if (h) for (++s ; *s; ++s) h = (h << 5) - h + *s;
return h;
}
/*! @function
@abstract Another interface to const char* hash function
@param key Pointer to a null terminated string [const char*]
@return The hash value [khint_t]
*/
#define kh_str_hash_func(key) __ac_X31_hash_string(key)
/*! @function
@abstract Const char* comparison function
*/
#define kh_str_hash_equal(a, b) (strcmp(a, b) == 0)
static inline khint_t __ac_Wang_hash(khint_t key)
{
key += ~(key << 15);
key ^= (key >> 10);
key += (key << 3);
key ^= (key >> 6);
key += ~(key << 11);
key ^= (key >> 16);
return key;
}
#define kh_int_hash_func2(k) __ac_Wang_hash((khint_t)key)
/* --- END OF HASH FUNCTIONS --- */
/* Other convenient macros... */
/*!
@abstract Type of the hash table.
@param name Name of the hash table [symbol]
*/
#define khash_t(name) kh_##name##_t
/*! @function
@abstract Initiate a hash table.
@param name Name of the hash table [symbol]
@return Pointer to the hash table [khash_t(name)*]
*/
#define kh_init(name) kh_init_##name()
/*! @function
@abstract Destroy a hash table.
@param name Name of the hash table [symbol]
@param h Pointer to the hash table [khash_t(name)*]
*/
#define kh_destroy(name, h) kh_destroy_##name(h)
/*! @function
@abstract Reset a hash table without deallocating memory.
@param name Name of the hash table [symbol]
@param h Pointer to the hash table [khash_t(name)*]
*/
#define kh_clear(name, h) kh_clear_##name(h)
/*! @function
@abstract Resize a hash table.
@param name Name of the hash table [symbol]
@param h Pointer to the hash table [khash_t(name)*]
@param s New size [khint_t]
*/
#define kh_resize(name, h, s) kh_resize_##name(h, s)
/*! @function
@abstract Insert a key to the hash table.
@param name Name of the hash table [symbol]
@param h Pointer to the hash table [khash_t(name)*]
@param k Key [type of keys]
@param r Extra return code: 0 if the key is present in the hash table;
1 if the bucket is empty (never used); 2 if the element in
the bucket has been deleted [int*]
@return Iterator to the inserted element [khint_t]
*/
#define kh_put(name, h, k, r) kh_put_##name(h, k, r)
/*! @function
@abstract Retrieve a key from the hash table.
@param name Name of the hash table [symbol]
@param h Pointer to the hash table [khash_t(name)*]
@param k Key [type of keys]
@return Iterator to the found element, or kh_end(h) is the element is absent [khint_t]
*/
#define kh_get(name, h, k) kh_get_##name(h, k)
/*! @function
@abstract Remove a key from the hash table.
@param name Name of the hash table [symbol]
@param h Pointer to the hash table [khash_t(name)*]
@param k Iterator to the element to be deleted [khint_t]
*/
#define kh_del(name, h, k) kh_del_##name(h, k)
/*! @function
@abstract Test whether a bucket contains data.
@param h Pointer to the hash table [khash_t(name)*]
@param x Iterator to the bucket [khint_t]
@return 1 if containing data; 0 otherwise [int]
*/
#define kh_exist(h, x) (!__ac_iseither((h)->flags, (x)))
/*! @function
@abstract Get key given an iterator
@param h Pointer to the hash table [khash_t(name)*]
@param x Iterator to the bucket [khint_t]
@return Key [type of keys]
*/
#define kh_key(h, x) ((h)->keys[x])
/*! @function
@abstract Get value given an iterator
@param h Pointer to the hash table [khash_t(name)*]
@param x Iterator to the bucket [khint_t]
@return Value [type of values]
@discussion For hash sets, calling this results in segfault.
*/
#define kh_val(h, x) ((h)->vals[x])
/*! @function
@abstract Alias of kh_val()
*/
#define kh_value(h, x) ((h)->vals[x])
/*! @function
@abstract Get the start iterator
@param h Pointer to the hash table [khash_t(name)*]
@return The start iterator [khint_t]
*/
#define kh_begin(h) (khint_t)(0)
/*! @function
@abstract Get the end iterator
@param h Pointer to the hash table [khash_t(name)*]
@return The end iterator [khint_t]
*/
#define kh_end(h) ((h)->n_buckets)
/*! @function
@abstract Get the number of elements in the hash table
@param h Pointer to the hash table [khash_t(name)*]
@return Number of elements in the hash table [khint_t]
*/
#define kh_size(h) ((h)->size)
/*! @function
@abstract Get the number of buckets in the hash table
@param h Pointer to the hash table [khash_t(name)*]
@return Number of buckets in the hash table [khint_t]
*/
#define kh_n_buckets(h) ((h)->n_buckets)
/* More conenient interfaces */
/*! @function
@abstract Instantiate a hash set containing integer keys
@param name Name of the hash table [symbol]
*/
#define KHASH_SET_INIT_INT(name) \
KHASH_INIT(name, khint32_t, char, 0, kh_int_hash_func, kh_int_hash_equal)
/*! @function
@abstract Instantiate a hash map containing integer keys
@param name Name of the hash table [symbol]
@param khval_t Type of values [type]
*/
#define KHASH_MAP_INIT_INT(name, khval_t) \
KHASH_INIT(name, khint32_t, khval_t, 1, kh_int_hash_func, kh_int_hash_equal)
/*! @function
@abstract Instantiate a hash map containing 64-bit integer keys
@param name Name of the hash table [symbol]
*/
#define KHASH_SET_INIT_INT64(name) \
KHASH_INIT(name, khint64_t, char, 0, kh_int64_hash_func, kh_int64_hash_equal)
/*! @function
@abstract Instantiate a hash map containing 64-bit integer keys
@param name Name of the hash table [symbol]
@param khval_t Type of values [type]
*/
#define KHASH_MAP_INIT_INT64(name, khval_t) \
KHASH_INIT(name, khint64_t, khval_t, 1, kh_int64_hash_func, kh_int64_hash_equal)
typedef const char *kh_cstr_t;
/*! @function
@abstract Instantiate a hash map containing const char* keys
@param name Name of the hash table [symbol]
*/
#define KHASH_SET_INIT_STR(name) \
KHASH_INIT(name, kh_cstr_t, char, 0, kh_str_hash_func, kh_str_hash_equal)
/*! @function
@abstract Instantiate a hash map containing const char* keys
@param name Name of the hash table [symbol]
@param khval_t Type of values [type]
*/
#define KHASH_MAP_INIT_STR(name, khval_t) \
KHASH_INIT(name, kh_cstr_t, khval_t, 1, kh_str_hash_func, kh_str_hash_equal)
#endif /* __AC_KHASH_H */

474
log.c

@ -0,0 +1,474 @@
/*
* SSLsplit - transparent and scalable SSL/TLS interception
* Copyright (c) 2009-2012, Daniel Roethlisberger <daniel@roe.ch>
* All rights reserved.
* http://www.roe.ch/SSLsplit
*
* 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 unmodified, 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 AUTHOR ``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 AUTHOR 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 "log.h"
#include "logger.h"
#include "sys.h"
#include "attrib.h"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdarg.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <syslog.h>
/*
* Centralized logging code multiplexing thread access to the logger based
* logging in separate threads. Some log types are switchable to different
* backends, such as syslog and stderr.
*/
/*
* Error log.
* Switchable between stderr and syslog.
* Uses logger thread.
*/
static logger_t *err_log = NULL;
static int err_started = 0; /* while 0, shortcut the thrqueue */
static int err_mode = LOG_ERR_MODE_STDERR;
static ssize_t
log_err_writecb(UNUSED int fd, const void *buf, size_t sz)
{
switch (err_mode) {
case LOG_ERR_MODE_STDERR:
return fwrite(buf, sz - 1, 1, stderr);
case LOG_ERR_MODE_SYSLOG:
syslog(LOG_ERR, "%s", (const char *)buf);
return 0;
}
return -1;
}
int
log_err_printf(const char *fmt, ...)
{
va_list ap;
char *buf;
int rv;
va_start(ap, fmt);
rv = vasprintf(&buf, fmt, ap);
va_end(ap);
if (rv == -1)
return -1;
if (err_started) {
return logger_write_freebuf(err_log, 0, buf, strlen(buf) + 1);
} else {
log_err_writecb(0, (unsigned char*)buf, strlen(buf) + 1);
free(buf);
}
return 0;
}
void
log_err_mode(int mode)
{
err_mode = mode;
}
/*
* Debug log. Redirects logging to error log.
* Switchable between error log or no logging.
* Uses the error log logger thread.
*/
static int dbg_mode = LOG_DBG_MODE_NONE;
int
log_dbg_write_free(void *buf, size_t sz)
{
if (dbg_mode == LOG_DBG_MODE_NONE)
return 0;
if (err_started) {
return logger_write_freebuf(err_log, 0, buf, sz);
} else {
log_err_writecb(0, buf, sz);
free(buf);
}
return 0;
}
int
log_dbg_print_free(char *s)
{
return log_dbg_write_free(s, strlen(s) + 1);
}
int
log_dbg_printf(const char *fmt, ...)
{
va_list ap;
char *buf;
int rv;
if (dbg_mode == LOG_DBG_MODE_NONE)
return 0;
va_start(ap, fmt);
rv = vasprintf(&buf, fmt, ap);
va_end(ap);
if (rv == -1)
return -1;
return log_dbg_print_free(buf);
}
void
log_dbg_mode(int mode)
{
dbg_mode = mode;
}
/*
* Connection log. Logs a one-liner to a file-based connection log.
* Uses a logger thread.
*/
logger_t *connect_log = NULL;
static int connect_fd = -1;
static int
log_connect_open(const char *logfile)
{
connect_fd = open(logfile, O_WRONLY|O_APPEND|O_CREAT, 0660);
if (connect_fd == -1) {
log_err_printf("Failed to open '%s' for writing: %s\n",
logfile, strerror(errno));
return -1;
}
return 0;
}
/*
* Do the actual write to the open connection log file descriptor.
* We prepend a timestamp here, which means that timestamps are slightly
* delayed from the time of actual logging. Since we only have second
* resolution that should not make any difference.
*/
static ssize_t
log_connect_writecb(UNUSED int fd, const void *buf, size_t sz)
{
char timebuf[32];
time_t epoch;
struct tm *utc;
size_t n;
time(&epoch);
utc = gmtime(&epoch);
n = strftime(timebuf, sizeof(timebuf), "%Y-%m-%d %H:%M:%S UTC ", utc);
if (n == 0) {
log_err_printf("Error from strftime(): buffer too small\n");
return -1;
}
if ((write(connect_fd, timebuf, n) == -1) ||
(write(connect_fd, buf, sz) == -1)) {
log_err_printf("Warning: Failed to write to connect log: %s\n",
strerror(errno));
}
return 0;
}
static void
log_connect_close(void)
{
close(connect_fd);
}
/*
* Content log.
* Logs connection content to either a single file or a directory containing
* per-connection logs.
* Uses a logger thread.
*/
logger_t *content_log = NULL;
static int content_fd = -1; /* if set, we are in single file mode */
static const char *content_basedir = NULL;
static int
log_content_open_singlefile(const char *logfile)
{
content_fd = open(logfile, O_WRONLY|O_APPEND|O_CREAT, 0660);
if (content_fd == -1) {
log_err_printf("Failed to open '%s' for writing: %s\n",
logfile, strerror(errno));
return -1;
}
return 0;
}
static int
log_content_open_logdir(const char *basedir)
{
content_basedir = basedir;
return 0;
}
static void
log_content_close_singlefile(void)
{
if (content_fd != -1) {
close(content_fd);
content_fd = -1;
}
}
void
log_content_open(log_content_ctx_t *ctx, char *srcaddr, char *dstaddr)
{
char filename[1024];
char timebuf[24];
time_t epoch;
struct tm *utc;
if (ctx->open)
return;
if (content_fd != -1) {
ctx->fd = content_fd;
asprintf(&ctx->header_in, "%s -> %s", srcaddr, dstaddr);
asprintf(&ctx->header_out, "%s -> %s", dstaddr, srcaddr);
} else {
time(&epoch);
utc = gmtime(&epoch);
strftime(timebuf, sizeof(timebuf), "%Y%m%dT%H%M%SZ", utc);
snprintf(filename, sizeof(filename), "%s/%s-%s-%s.log",
content_basedir, timebuf, srcaddr, dstaddr);
ctx->fd = open(filename, O_WRONLY|O_APPEND|O_CREAT, 0660);
if (ctx->fd == -1) {
log_err_printf("Failed to open '%s': %s\n",
filename, strerror(errno));
}
}
ctx->open = 1;
}
void
log_content_submit(log_content_ctx_t *ctx, logbuf_t *lb, int direction)
{
logbuf_t *head;
time_t epoch;
struct tm *utc;
char *header;
if (!ctx->open) {
log_err_printf("log_content_submit called on closed ctx\n");
return;
}
if (!(header = direction ? ctx->header_out : ctx->header_in))
goto out;
/* prepend size tag and newline */
head = logbuf_new_printf(lb->fd, lb, " (%zu):\n", logbuf_size(lb));
if (!head) {
log_err_printf("Failed to allocate memory\n");
logbuf_free(lb);
return;
}
lb = head;
/* prepend header */
head = logbuf_new_copy(header, strlen(header), lb->fd, lb);
if (!head) {
log_err_printf("Failed to allocate memory\n");
logbuf_free(lb);
return;
}
lb = head;
/* prepend timestamp */
head = logbuf_new_alloc(32, lb->fd, lb);
if (!head) {
log_err_printf("Failed to allocate memory\n");
logbuf_free(lb);
return;
}
lb = head;
time(&epoch);
utc = gmtime(&epoch);
lb->sz = strftime((char*)lb->buf, lb->sz, "%Y-%m-%d %H:%M:%S UTC ",
utc);
out:
lb->fd = ctx->fd;
logger_submit(content_log, lb);
}
void
log_content_close(log_content_ctx_t *ctx)
{
if (!ctx->open)
return;
if (content_fd == -1) {
logger_write_freebuf(content_log, ctx->fd, NULL, 0);
}
if (ctx->header_in) {
free(ctx->header_in);
}
if (ctx->header_out) {
free(ctx->header_out);
}
ctx->open = 0;
}
/*
* Do the actual write to the open connection log file descriptor.
* We prepend a timestamp here, which means that timestamps are slightly
* delayed from the time of actual logging. Since we only have second
* resolution that should not make any difference.
*/
static ssize_t
log_content_writecb(int fd, const void *buf, size_t sz)
{
if (!buf) {
close(fd);
return 0;
}
if (write(fd, buf, sz) == -1) {
log_err_printf("Warning: Failed to write to content log: %s\n",
strerror(errno));
return -1;
}
return 0;
}
/*
* Initialization and destruction.
*/
/*
* Log pre-init: open all log files but don't start any threads, since we may
* fork() after pre-initialization.
* Return -1 on errors, 0 otherwise.
*/
int
log_preinit(opts_t *opts)
{
if (opts->contentlog) {
if (!opts->contentlogdir) {
if (log_content_open_singlefile(opts->contentlog)
== -1)
goto out;
} else {
if (log_content_open_logdir(opts->contentlog) == -1)
goto out;
}
if (!(content_log = logger_new(log_content_writecb))) {
log_content_close_singlefile();
goto out;
}
}
if (opts->connectlog) {
if (log_connect_open(opts->connectlog) == -1)
goto out;
if (!(connect_log = logger_new(log_connect_writecb))) {
log_connect_close();
goto out;
}
}
if (!(err_log = logger_new(log_err_writecb)))
goto out;
return 0;
out:
if (content_log) {
log_content_close_singlefile();
logger_free(content_log);
}
if (connect_log) {
log_connect_close();
logger_free(connect_log);
}
return -1;
}
/*
* Log post-init: start logging threads.
* Return -1 on errors, 0 otherwise.
*/
int
log_init(UNUSED opts_t *opts)
{
if (err_log)
if (logger_start(err_log) == -1)
return -1;
err_started = 1;
if (connect_log)
if (logger_start(connect_log) == -1)
return -1;
if (content_log)
if (logger_start(content_log) == -1)
return -1;
return 0;
}
/*
* Drain and cleanup. Tell all loggers to leave, then join all logger threads,
* and finally free resources and close log files.
*/
void
log_fini(void)
{
if (content_log)
logger_leave(content_log);
if (connect_log)
logger_leave(connect_log);
logger_leave(err_log);
if (content_log)
logger_join(content_log);
if (connect_log)
logger_join(connect_log);
logger_join(err_log);
if (content_log)
logger_free(content_log);
if (connect_log)
logger_free(connect_log);
logger_free(err_log);
if (content_log)
log_content_close_singlefile();
if (connect_log)
log_connect_close();
}
/* vim: set noet ft=c: */

77
log.h

@ -0,0 +1,77 @@
/*
* SSLsplit - transparent and scalable SSL/TLS interception
* Copyright (c) 2009-2012, Daniel Roethlisberger <daniel@roe.ch>
* All rights reserved.
* http://www.roe.ch/SSLsplit
*
* 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 unmodified, 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 AUTHOR ``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 AUTHOR 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 LOG_H
#define LOG_H
#include "opts.h"
#include "logger.h"
#include "attrib.h"
int log_err_printf(const char *, ...) PRINTF(1,2);
void log_err_mode(int);
#define LOG_ERR_MODE_STDERR 0
#define LOG_ERR_MODE_SYSLOG 1
int log_dbg_printf(const char *, ...) PRINTF(1,2);
int log_dbg_print_free(char *);
int log_dbg_write_free(void *, size_t);
void log_dbg_mode(int);
#define LOG_DBG_MODE_NONE 0
#define LOG_DBG_MODE_ERRLOG 1
extern logger_t *connect_log;
#define log_connect_printf(fmt, ...) \
logger_printf(connect_log, -1, (fmt), __VA_ARGS__)
#define log_connect_print(s) \
logger_print(connect_log, -1, (s))
#define log_connect_write(buf, sz) \
logger_write(connect_log, -1, (buf), (sz))
#define log_connect_print_free(s) \
logger_print_freebuf(connect_log, -1, (s))
#define log_connect_write_free(buf, sz) \
logger_write_freebuf(connect_log, -1, (buf), (sz))
typedef struct log_content_ctx {
int open;
char *basedir;
int fd;
char *header_in;
char *header_out;
} log_content_ctx_t;
void log_content_open(log_content_ctx_t *, char *, char *) NONNULL();
void log_content_submit(log_content_ctx_t *, logbuf_t *, int) NONNULL();
void log_content_close(log_content_ctx_t *) NONNULL();
int log_preinit(opts_t *) NONNULL();
int log_init(opts_t *) NONNULL();
void log_fini(void);
#endif /* !LOG_H */
/* vim: set noet ft=c: */

@ -0,0 +1,180 @@
/*
* SSLsplit - transparent and scalable SSL/TLS interception
* Copyright (c) 2009-2012, Daniel Roethlisberger <daniel@roe.ch>
* All rights reserved.
* http://www.roe.ch/SSLsplit
*
* 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 unmodified, 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 AUTHOR ``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 AUTHOR 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 "logbuf.h"
#include <stdarg.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
/*
* Dynamic log buffer with zero-copy chaining and fd meta information.
* Logbuf always owns the internal allocated buffer.
*/
/*
* Create new logbuf from provided, pre-allocated buffer, set fd and next.
* The provided buffer will be freed by logbuf_free() if non-NULL.
*/
logbuf_t *
logbuf_new(void *buf, size_t sz, int fd, logbuf_t *next)
{
logbuf_t *lb;
if (!(lb = malloc(sizeof(logbuf_t))))
return NULL;
lb->buf = buf;
lb->sz = sz;
lb->fd = fd;
lb->next = next;
return lb;
}
/*
* Create new logbuf, allocating sz bytes into the internal buffer.
*/
logbuf_t *
logbuf_new_alloc(size_t sz, int fd, logbuf_t *next)
{
logbuf_t *lb;
if (!(lb = malloc(sizeof(logbuf_t))))
return NULL;
if (!(lb->buf = malloc(sz))) {
free(lb);
return NULL;
}
lb->sz = sz;
lb->fd = fd;
lb->next = next;
return lb;
}
/*
* Create new logbuf, copying buf into a newly allocated internal buffer.
*/
logbuf_t *
logbuf_new_copy(const void *buf, size_t sz, int fd, logbuf_t *next)
{
logbuf_t *lb;
if (!(lb = malloc(sizeof(logbuf_t))))
return NULL;
if (!(lb->buf = malloc(sz))) {
free(lb);
return NULL;
}
memcpy(lb->buf, buf, sz);
lb->sz = sz;
lb->fd = fd;
lb->next = next;
return lb;
}
/*
* Create new logbuf using printf, setting fd and next.
*/
logbuf_t *
logbuf_new_printf(int fd, logbuf_t *next, const char *fmt, ...)
{
va_list ap;
logbuf_t *lb;
if (!(lb = malloc(sizeof(logbuf_t))))
return NULL;
va_start(ap, fmt);
lb->sz = vasprintf((char**)&lb->buf, fmt, ap);
va_end(ap);
if (lb->sz == -1) {
free(lb);
return NULL;
}
lb->fd = fd;
lb->next = next;
return lb;
}
/*
* Calculate the total size of the logbuf and all chained buffers.
*/
ssize_t
logbuf_size(logbuf_t *lb)
{
ssize_t sz;
sz = lb->sz;
if (lb->next) {
sz += logbuf_size(lb->next);
}
return sz;
}
/*
* Write content of logbuf using writefunc and free all buffers.
* Returns -1 on errors and sets errno according to write().
* Returns total of bytes written by 1 .. n write() calls on success.
*/
ssize_t
logbuf_write_free(logbuf_t *lb, writefunc_t writefunc)
{
ssize_t rv1, rv2;
rv1 = writefunc(lb->fd, lb->buf, lb->sz);
free(lb->buf);
if (lb->next) {
if (rv1 == -1) {
logbuf_free(lb->next);
} else {
lb->next->fd = lb->fd;
rv2 = logbuf_write_free(lb->next, writefunc);
}
}
free(lb);
if (rv1 == -1 || rv2 == -1)
return -1;
else
return rv1 + rv2;
}
/*
* Free dynbuf including internal and chained buffers.
*/
void
logbuf_free(logbuf_t *lb)
{
if (lb->buf) {
free(lb->buf);
}
if (lb->next) {
logbuf_free(lb->next);
}
free(lb);
}
/* vim: set noet ft=c: */

@ -0,0 +1,57 @@
/*
* SSLsplit - transparent and scalable SSL/TLS interception
* Copyright (c) 2009-2012, Daniel Roethlisberger <daniel@roe.ch>
* All rights reserved.
* http://www.roe.ch/SSLsplit
*
* 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 unmodified, 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 AUTHOR ``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 AUTHOR 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 LOGBUF_H
#define LOGBUF_H
#include "attrib.h"
#include <stdlib.h>
#include <unistd.h>
typedef struct logbuf {
unsigned char *buf;
ssize_t sz;
int fd;
struct logbuf *next;
} logbuf_t;
typedef ssize_t (*writefunc_t)(int, const void *, size_t);
logbuf_t * logbuf_new(void *, size_t, int, logbuf_t *) MALLOC;
logbuf_t * logbuf_new_alloc(size_t, int, logbuf_t *) MALLOC;
logbuf_t * logbuf_new_copy(const void *, size_t, int, logbuf_t *) MALLOC;
logbuf_t * logbuf_new_printf(int, logbuf_t *, const char *, ...)
MALLOC PRINTF(3,4);
ssize_t logbuf_size(logbuf_t *) NONNULL();
ssize_t logbuf_write_free(logbuf_t *, writefunc_t) NONNULL(1);
void logbuf_free(logbuf_t *) NONNULL();
#endif /* !LOGBUF_H */
/* vim: set noet ft=c: */

@ -0,0 +1,229 @@
/*
* SSLsplit - transparent and scalable SSL/TLS interception
* Copyright (c) 2009-2012, Daniel Roethlisberger <daniel@roe.ch>
* All rights reserved.
* http://www.roe.ch/SSLsplit
*
* 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 unmodified, 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 AUTHOR ``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 AUTHOR 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 "logger.h"
#include "thrqueue.h"
#include "logbuf.h"
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
/*
* Logger for multithreaded environments. Disk writes are executed in a
* writer thread. Logging threads submit buffers to be logged by adding
* them to the thrqueue. Logging threads may block on the pthread mutex
* of the thrqueue, but not on disk writes.
*/
struct logger {
pthread_t thr;
logger_write_func_t write;
thrqueue_t *queue;
};
static void
logger_clear(logger_t *logger)
{
memset(logger, 0, sizeof(logger_t));
}
/*
* Create new logger with a specific write function callback.
* The callback will be executed in the logger's writer thread,
* not in the thread calling logger_submit().
*/
logger_t *
logger_new(logger_write_func_t writefunc)
{
logger_t *logger;
logger = malloc(sizeof(logger_t));
if (!logger)
return NULL;
logger_clear(logger);
logger->write = writefunc;
logger->queue = thrqueue_new(1024);
return logger;
}
/*
* Free the logger data structures. Caller must call logger_stop()
* or logger_leave() and logger_join() prior to freeing.
*/
void
logger_free(logger_t *logger) {
thrqueue_free(logger->queue);
free(logger);
}
/*
* Submit a buffer to be logged by the logger thread.
* Buffer will be freed after logging completes.
* Returns -1 on error, 0 on success.
*/
int
logger_submit(logger_t *logger, logbuf_t *lb)
{
thrqueue_enqueue(logger->queue, lb);
return 0;
}
/*
* Logger thread main function.
*/
static void *
logger_thread(void *arg)
{
logger_t *logger = arg;
logbuf_t *lb;
while ((lb = thrqueue_dequeue(logger->queue))) {
logbuf_write_free(lb, logger->write);
}
return NULL;
}
/*
* Start the logger's write thread.
*/
int
logger_start(logger_t *logger) {
int rv;
rv = pthread_create(&logger->thr, NULL, logger_thread, logger);
if (rv)
return -1;
return 0;
}
/*
* Tell the logger's write thread to write all pending write requests
* and then exit. Don't wait for the logger to exit.
*/
void
logger_leave(logger_t *logger) {
thrqueue_unblock_dequeue(logger->queue);
}
/*
* Wait for the logger to exit.
*/
int
logger_join(logger_t *logger) {
int rv;
rv = pthread_join(logger->thr, NULL);
if (rv)
return -1;
return 0;
}
/*
* Tell the logger's write thread to write all pending write requests
* and then exit; wait for the logger to exit.
*/
int
logger_stop(logger_t *logger) {
logger_leave(logger);
return logger_join(logger);
}
/*
* Generic print to a logger. These functions should be called by the
* actual worker thread(s) doing network I/O.
*
* _printf(), _print() and _write() copy the input buffers.
* _ncprint() and _ncwrite() will free() the input buffers.
*
* The file descriptor argument is a virtual or real system file descriptor
* used for multiplexing write requests to several files over the same
* logger. This argument is passed to the write handler as-is and is not
* interpreted or used by the logger itself in any way.
*
* All of the functions return 0 on succes, -1 on failure.
*/
int
logger_printf(logger_t *logger, int fd, const char *fmt, ...)
{
va_list ap;
logbuf_t *lb;
lb = logbuf_new(NULL, 0, fd, NULL);
if (!lb)
return -1;
va_start(ap, fmt);
lb->sz = vasprintf((char**)&lb->buf, fmt, ap);
va_end(ap);
if (lb->sz == -1) {
logbuf_free(lb);
return -1;
}
return logger_submit(logger, lb);
}
int
logger_write(logger_t *logger, int fd, const void *buf, size_t sz)
{
logbuf_t *lb;
if (!(lb = logbuf_new_copy(buf, sz, fd, NULL)))
return -1;
return logger_submit(logger, lb);
}
int
logger_print(logger_t *logger, int fd, const char *s)
{
logbuf_t *lb;
if (!(lb = logbuf_new_copy(s, strlen(s), fd, NULL)))
return -1;
return logger_submit(logger, lb);
}
int
logger_write_freebuf(logger_t *logger, int fd, void *buf, size_t sz)
{
logbuf_t *lb;
if (!(lb = logbuf_new(buf, sz, fd, NULL)))
return -1;
return logger_submit(logger, lb);
}
int
logger_print_freebuf(logger_t *logger, int fd, char *s)
{
logbuf_t *lb;
if (!(lb = logbuf_new(s, strlen(s), fd, NULL)))
return -1;
return logger_submit(logger, lb);
}
/* vim: set noet ft=c: */

@ -0,0 +1,56 @@
/*
* SSLsplit - transparent and scalable SSL/TLS interception
* Copyright (c) 2009-2012, Daniel Roethlisberger <daniel@roe.ch>
* All rights reserved.
* http://www.roe.ch/SSLsplit
*
* 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 unmodified, 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 AUTHOR ``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 AUTHOR 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 LOGGER_H
#define LOGGER_H
#include "logbuf.h"
#include "attrib.h"
#include <unistd.h>
#include <pthread.h>
typedef ssize_t (*logger_write_func_t)(int, const void *, size_t);
typedef struct logger logger_t;
logger_t * logger_new(logger_write_func_t) NONNULL();
void logger_free(logger_t *) NONNULL();
int logger_start(logger_t *) NONNULL();
void logger_leave(logger_t *) NONNULL();
int logger_join(logger_t *) NONNULL();
int logger_stop(logger_t *) NONNULL();
int logger_submit(logger_t *, logbuf_t *) NONNULL();
int logger_printf(logger_t *, int, const char *, ...) PRINTF(3,4) NONNULL(1,3);
int logger_print(logger_t *, int, const char *) NONNULL();
int logger_write(logger_t *, int, const void *, size_t) NONNULL();
int logger_print_freebuf(logger_t *, int, char *) NONNULL();
int logger_write_freebuf(logger_t *, int, void *, size_t) NONNULL(1);
#endif /* !LOGGER_H */
/* vim: set noet ft=c: */

634
main.c

@ -0,0 +1,634 @@
/*
* SSLsplit - transparent and scalable SSL/TLS interception
* Copyright (c) 2009-2012, Daniel Roethlisberger <daniel@roe.ch>
* All rights reserved.
* http://www.roe.ch/SSLsplit
*
* 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 unmodified, 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 AUTHOR ``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 AUTHOR 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.
*/
/* silence daemon(3) deprecation warning on Mac OS X */
#if __APPLE__
#define daemon xdaemon
#endif /* __APPLE__ */
#include "opts.h"
#include "proxy.h"
#include "ssl.h"
#include "nat.h"
#include "cachemgr.h"
#include "sys.h"
#include "log.h"
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>
#ifndef __BSD__
#include <getopt.h>
#endif /* !__BSD__ */
#include <event2/event.h>
#include <openssl/ssl.h>
#include <openssl/x509.h>
#if __APPLE__
#undef daemon
extern int daemon(int, int);
#endif /* __APPLE__ */
/*
* Print version information to stderr.
*/
static void
main_version(void)
{
fprintf(stderr, "%s %s (built %s)\n", PNAME, VERSION, BUILD_DATE);
fprintf(stderr, "Copyright (c) 2009-2012, "
"Daniel Roethlisberger <daniel@roe.ch>\n");
fprintf(stderr, "http://www.roe.ch/SSLsplit\n");
if (FEATURES[0]) {
fprintf(stderr, "Features: %s\n", FEATURES);
}
nat_version();
ssl_openssl_version();
fprintf(stderr, "compiled against libevent %s\n", LIBEVENT_VERSION);
fprintf(stderr, "rtlinked against libevent %s\n", event_get_version());
fprintf(stderr, "%d CPU cores detected\n", sys_get_cpu_cores());
}
/*
* Print usage to stderr.
*/
static void
main_usage(void)
{
const char *dflt, *warn;
if (!(dflt = nat_getdefaultname())) {
dflt = "n/a";
warn = "\nWarning: no supported NAT engine on this platform!\n"
"Only static and SNI proxyspecs are supported.\n";
} else {
warn = "";
}
fprintf(stderr,
"Usage: %s [options...] [proxyspecs...]\n"
" -k pemfile use CA key from pemfile to sign forged certs\n"
" -c pemfile use CA cert from pemfile to sign forged certs\n"
" -C pemfile use CA chain from pemfile (intermediate and root CA certs)\n"
" -K pemfile use key from pemfile for leaf certs (default: generate)\n"
" -t certdir use cert+chain+key PEM files from certdir to target all sites\n"
" matching the common names (non-matching: generate if CA)\n"
" -P passthrough SSL connections if they cannot be split because of\n"
" client cert auth or no matching cert and no CA (default: drop)\n"
#ifndef OPENSSL_NO_DH
" -g pemfile use DH group params from pemfile (default: keyfiles or auto)\n"
#define OPT_g "g:"
#else /* OPENSSL_NO_DH */
#define OPT_g
#endif /* !OPENSSL_NO_DH */
#ifndef OPENSSL_NO_ECDH
" -G curve use ECDH named curve (default: %s for non-RSA leafkey)\n"
#define OPT_G "G:"
#else /* OPENSSL_NO_ECDH */
#define OPT_G
#endif /* OPENSSL_NO_ECDH */
#ifdef SSL_OP_NO_COMPRESSION
" -Z disable SSL/TLS compression on all connections\n"
#define OPT_Z "Z"
#else /* !SSL_OP_NO_COMPRESSION */
#define OPT_Z
#endif /* !SSL_OP_NO_COMPRESSION */
" -s ciphers use the given OpenSSL cipher suite spec (default: ALL)\n"
" -e engine specify default NAT engine to use (default: %s)\n"
" -E list available NAT engines and exit\n"
" -u user drop privileges to user (default if run as root: nobody)\n"
" -j jaildir chroot() to jaildir (default if run as root: /var/empty)\n"
" -p pidfile write pid to pidfile (default: no pid file)\n"
" -l logfile connect log: log one line summary per connection to logfile\n"
" -L logfile content log: full data to file or named pipe (excludes -S)\n"
" -S logdir content log: full data to separate files in dir (excludes -L)\n"
" -d daemon mode: run in background, log error messages to syslog\n"
" -D debug mode: run in foreground, log debug messages on stderr\n"
" -V print version information and exit\n"
" -h print usage information and exit\n"
" proxyspec = type listenaddr+port [natengine|targetaddr+port|\"sni\"+port]\n"
" e.g. http 0.0.0.0 8080 www.roe.ch 80 # http/4; static hostname dst\n"
" https ::1 8443 2001:db8::1 443 # https/6; static address dst\n"
" https 127.0.0.1 9443 sni 443 # https/4; SNI DNS lookups\n"
" tcp 127.0.0.1 10025 # tcp/4; default NAT engine\n"
" ssl 2001:db8::2 9999 pf # ssl/6; NAT engine 'pf'\n"
"Example:\n"
" %s -k ca.key -c ca.pem -P https 127.0.0.1 8443 https ::1 8443\n"
"%s", BNAME,
#ifndef OPENSSL_NO_ECDH
SSL_EC_KEY_CURVE_DEFAULT,
#endif /* !OPENSSL_NO_ECDH */
dflt, BNAME, warn);
}
/*
* Callback to load a cert/chain/key combo from a single PEM file.
*/
static void
main_loadtgcrt(const char *filename, void *arg)
{
void **args = arg;
const char *argv0 = args[0];
opts_t *opts = args[1];
cert_t *cert;
char **names;
cert = cert_new_load(filename);
if (!cert) {
fprintf(stderr, "%s: error loading cert and key from PEM file "
"'%s'\n", argv0, filename);
exit(EXIT_FAILURE);
}
if (X509_check_private_key(cert->crt, cert->key) != 1) {
fprintf(stderr, "%s: cert does not match key in PEM file "
"'%s':\n", argv0, filename);
ERR_print_errors_fp(stderr);
exit(EXIT_FAILURE);
}
#ifdef DEBUG_CERTIFICATE
log_dbg_printf("Loaded '%s':\n", filename);
log_dbg_print_free(ssl_x509_to_str(cert->crt));
log_dbg_print_free(ssl_x509_to_pem(cert->crt));
#endif /* DEBUG_CERTIFICATE */
if (opts->debug) {
log_dbg_printf("Targets for '%s':", filename);
}
names = ssl_x509_names(cert->crt);
for (char **p = names; *p; p++) {
/* be deliberately vulnerable to NULL prefix attacks */
char *sep;
if ((sep = strchr(*p, '!'))) {
*sep = '\0';
}
if (opts->debug) {
log_dbg_printf(" '%s'", *p);
}
cachemgr_tgcrt_set(*p, cert);
free(*p);
}
if (opts->debug) {
log_dbg_printf("\n");
}
free(names);
cert_free(cert);
}
/*
* Main entry point.
*/
int
main(int argc, char *argv[])
{
const char *argv0;
int ch;
opts_t *opts;
char *natengine;
int pidfd = -1;
argv0 = argv[0];
opts = opts_new();
natengine = strdup(nat_getdefaultname());
while ((ch = getopt(argc, argv, OPT_g OPT_G OPT_Z
"k:c:C:K:t:Ps:e:Eu:j:p:l:L:S:dDVh")) != -1) {
switch (ch) {
case 'k':
if (opts->cakey)
EVP_PKEY_free(opts->cakey);
opts->cakey = ssl_key_load(optarg);
if (!opts->cakey) {
fprintf(stderr, "%s: error loading CA "
"key from '%s':\n",
argv0, optarg);
if (errno) {
fprintf(stderr, "%s\n",
strerror(errno));
} else {
ERR_print_errors_fp(stderr);
}
exit(EXIT_FAILURE);
}
if (!opts->cacrt) {
opts->cacrt = ssl_x509_load(optarg);
}
#ifndef OPENSSL_NO_DH
if (!opts->dh) {
opts->dh = ssl_dh_load(optarg);
}
#endif /* !OPENSSL_NO_DH */
break;
case 'c':
if (opts->cacrt)
X509_free(opts->cacrt);
opts->cacrt = ssl_x509_load(optarg);
if (!opts->cacrt) {
fprintf(stderr, "%s: error loading CA "
"cert from '%s':\n",
argv0, optarg);
if (errno) {
fprintf(stderr, "%s\n",
strerror(errno));
} else {
ERR_print_errors_fp(stderr);
}
exit(EXIT_FAILURE);
}
if (!opts->cakey) {
opts->cakey = ssl_key_load(optarg);
}
ssl_x509_refcount_inc(opts->cacrt);
sk_X509_insert(opts->chain, opts->cacrt, 0);
break;
case 'C':
if (ssl_x509chain_load(NULL, &opts->chain,
optarg) == -1) {
fprintf(stderr, "%s: error loading "
"chain from '%s':\n",
argv0, optarg);
if (errno) {
fprintf(stderr, "%s\n",
strerror(errno));
} else {
ERR_print_errors_fp(stderr);
}
exit(EXIT_FAILURE);
}
break;
case 'K':
if (opts->key)
EVP_PKEY_free(opts->key);
opts->key = ssl_key_load(optarg);
if (!opts->key) {
fprintf(stderr, "%s: error loading lea"
"f key from '%s':\n",
argv0, optarg);
if (errno) {
fprintf(stderr, "%s\n",
strerror(errno));
} else {
ERR_print_errors_fp(stderr);
}
exit(EXIT_FAILURE);
}
#ifndef OPENSSL_NO_DH
if (!opts->dh) {
opts->dh = ssl_dh_load(optarg);
}
#endif /* !OPENSSL_NO_DH */
break;
case 't':
if (!sys_isdir(optarg)) {
fprintf(stderr, "%s: '%s' is not a "
"directory\n",
argv0, optarg);
exit(EXIT_FAILURE);
}
if (opts->tgcrtdir)
free(opts->tgcrtdir);
opts->tgcrtdir = strdup(optarg);
break;
case 'P':
opts->passthrough = 1;
break;
#ifndef OPENSSL_NO_DH
case 'g':
if (opts->dh)
DH_free(opts->dh);
opts->dh = ssl_dh_load(optarg);
if (!opts->dh) {
fprintf(stderr, "%s: error loading DH "
"params from '%s':\n",
argv0, optarg);
if (errno) {
fprintf(stderr, "%s\n",
strerror(errno));
} else {
ERR_print_errors_fp(stderr);
}
exit(EXIT_FAILURE);
}
break;
#endif /* !OPENSSL_NO_DH */
#ifndef OPENSSL_NO_ECDH
case 'G':
{
EC_KEY *ecdh;
if (opts->ecdhcurve)
free(opts->ecdhcurve);
if (!(ecdh = ssl_ecdh_by_name(optarg))) {
fprintf(stderr, "%s: unknown curve "
"'%s'\n",
argv0, optarg);
exit(EXIT_FAILURE);
}
EC_KEY_free(ecdh);
opts->ecdhcurve = strdup(optarg);
break;
}
#endif /* !OPENSSL_NO_ECDH */
#ifdef SSL_OP_NO_COMPRESSION
case 'Z':
opts->sslcomp = 0;
break;
#endif /* SSL_OP_NO_COMPRESSION */
case 's':
if (opts->ciphers)
free(opts->ciphers);
opts->ciphers = strdup(optarg);
break;
case 'e':
free(natengine);
natengine = strdup(optarg);
break;
case 'E':
nat_list_engines();
exit(EXIT_SUCCESS);
break;
case 'u':
if (opts->dropuser)
free(opts->dropuser);
opts->dropuser = strdup(optarg);
break;
case 'p':
if (opts->pidfile)
free(opts->pidfile);
opts->pidfile = strdup(optarg);
break;
case 'j':
if (opts->jaildir)
free(opts->jaildir);
opts->jaildir = strdup(optarg);
break;
case 'l':
if (opts->connectlog)
free(opts->connectlog);
opts->connectlog = strdup(optarg);
break;
case 'L':
if (opts->contentlog)
free(opts->contentlog);
opts->contentlog = strdup(optarg);
opts->contentlogdir = 0;
break;
case 'S':
if (opts->contentlog)
free(opts->contentlog);
opts->contentlog = strdup(optarg);
opts->contentlogdir = 1;
break;
case 'd':
opts->detach = 1;
break;
case 'D':
log_dbg_mode(LOG_DBG_MODE_ERRLOG);
opts->debug = 1;
break;
case 'V':
main_version();
exit(EXIT_SUCCESS);
case 'h':
main_usage();
exit(EXIT_SUCCESS);
case '?':
exit(EXIT_FAILURE);
default:
main_usage();
exit(EXIT_FAILURE);
}
}
argc -= optind;
argv += optind;
opts->spec = proxyspec_parse(&argc, &argv, natengine);
/* usage checks */
if (opts->detach && opts->debug) {
fprintf(stderr, "%s: -d and -D are mutually exclusive.\n",
argv0);
exit(EXIT_FAILURE);
}
if (!opts->spec) {
fprintf(stderr, "%s: no proxyspec specified.\n", argv0);
exit(EXIT_FAILURE);
}
for (proxyspec_t *spec = opts->spec; spec; spec = spec->next) {
if (spec->connect_addrlen || spec->sni_port)
continue;
if (!spec->natengine) {
fprintf(stderr, "%s: no supported NAT engines "
"on this platform.\n"
"Only static addr and SNI proxyspecs "
"supported.\n", argv0);
exit(EXIT_FAILURE);
}
if (spec->listen_addr.ss_family == AF_INET6 &&
!nat_ipv6ready(spec->natengine)) {
fprintf(stderr, "%s: IPv6 not supported by '%s'\n",
argv0, spec->natengine);
exit(EXIT_FAILURE);
}
spec->natlookup = nat_getlookupcb(spec->natengine);
spec->natsocket = nat_getsocketcb(spec->natengine);
}
if (opts_has_ssl_spec(opts)) {
if ((opts->cacrt || !opts->tgcrtdir) && !opts->cakey) {
fprintf(stderr, "%s: no CA key specified (-k).\n",
argv0);
exit(EXIT_FAILURE);
}
if (opts->cakey && !opts->cacrt) {
fprintf(stderr, "%s: no CA cert specified (-c).\n",
argv0);
exit(EXIT_FAILURE);
}
if (opts->cakey && opts->cacrt &&
(X509_check_private_key(opts->cacrt, opts->cakey) != 1)) {
fprintf(stderr, "%s: CA cert does not match key.\n",
argv0);
ERR_print_errors_fp(stderr);
exit(EXIT_FAILURE);
}
}
/* dynamic defaults */
if (!opts->ciphers) {
opts->ciphers = strdup("ALL");
if (!opts->ciphers) {
fprintf(stderr, "%s: out of memory.\n", argv0);
exit(EXIT_FAILURE);
}
}
if (!opts->jaildir && (geteuid() == 0) && !opts->contentlogdir) {
opts->jaildir = strdup("/var/empty");
}
if (!opts->dropuser && !geteuid() && !getuid() &&
!opts->contentlogdir) {
opts->dropuser = strdup("nobody");
}
/* debugging */
if (opts->debug) {
main_version();
log_dbg_printf("proxyspecs:\n");
for (proxyspec_t *spec = opts->spec; spec; spec = spec->next) {
char *lbuf, *cbuf = NULL;
lbuf = sys_sockaddr_str((struct sockaddr *)
&spec->listen_addr,
spec->listen_addrlen);
if (spec->connect_addrlen) {
cbuf = sys_sockaddr_str((struct sockaddr *)
&spec->connect_addr,
spec->connect_addrlen);
}
if (spec->sni_port) {
asprintf(&cbuf, "sni %i", spec->sni_port);
}
log_dbg_printf("- %s %s %s %s\n", lbuf,
(spec->ssl ? "ssl" : "tcp"),
(spec->http ? "http" : "plain"),
(spec->natengine ? spec->natengine
: cbuf));
if (lbuf)
free(lbuf);
if (cbuf)
free(cbuf);
}
}
/* prevent multiple instances running */
if (opts->pidfile) {
pidfd = sys_pidf_open(opts->pidfile);
if (pidfd == -1) {
fprintf(stderr, "%s: cannot open PID file '%s' "
"- process already running?\n",
argv0, opts->pidfile);
exit(EXIT_FAILURE);
}
}
/*
* Initialize as much as possible before daemon() in order to be
* able to provide direct feedback to the user when failing.
*/
if (cachemgr_init() == -1) {
fprintf(stderr, "%s: failed to init cachemgr.\n", argv0);
exit(EXIT_FAILURE);
}
if (opts->debug) {
if (opts->cacrt) {
char *subj = ssl_x509_subject(opts->cacrt);
log_dbg_printf("Loaded CA: '%s'\n", subj);
free(subj);
#ifdef DEBUG_CERTIFICATE
log_dbg_print_free(ssl_x509_to_str(opts->cacrt));
log_dbg_print_free(ssl_x509_to_pem(opts->cacrt));
#endif /* DEBUG_CERTIFICATE */
} else {
log_dbg_printf("No CA loaded.\n");
}
}
if (opts_has_ssl_spec(opts) && !opts->key) {
opts->key = ssl_key_genrsa(1024);
if (!opts->key) {
fprintf(stderr, "%s: error generating RSA key:\n",
argv0);
ERR_print_errors_fp(stderr);
exit(EXIT_FAILURE);
}
if (opts->debug) {
log_dbg_printf("Generated RSA key for leaf certs.\n");
}
}
if (opts->tgcrtdir) {
const void *arg[2];
arg[0] = argv0;
arg[1] = opts;
sys_dir_eachfile(opts->tgcrtdir, main_loadtgcrt, arg);
}
if (log_preinit(opts) == -1) {
fprintf(stderr, "%s: failed to preinit logging.\n", argv0);
exit(EXIT_FAILURE);
}
if (nat_preinit() == -1) {
fprintf(stderr, "%s: failed to preinit NAT lookup.\n", argv0);
exit(EXIT_FAILURE);
}
proxy_ctx_t *proxy = proxy_new(opts);
if (!proxy) {
fprintf(stderr, "%s: error initializing proxy.\n", argv0);
exit(EXIT_FAILURE);
}
/* Drop privs, chroot, detach from TTY */
if (sys_privdrop(opts->dropuser, opts->jaildir) == -1) {
fprintf(stderr, "%s: failed to drop privileges: %s\n",
argv0, strerror(errno));
exit(EXIT_FAILURE);
}
if (opts->detach) {
if (daemon(1, 0) == -1) {
fprintf(stderr, "%s: failed to detach from TTY: %s\n",
argv0, strerror(errno));
exit(EXIT_FAILURE);
}
log_err_mode(LOG_ERR_MODE_SYSLOG);
}
if (opts->pidfile) {
sys_pidf_write(pidfd);
}
/* Post-privdrop/chroot/detach initialization, thread spawning */
if (log_init(opts) == -1) {
log_err_printf("Failed to init log facility.\n");
exit(EXIT_FAILURE);
}
if (nat_init() == -1) {
log_err_printf("Failed to init NAT state table lookup.\n");
exit(EXIT_FAILURE);
}
proxy_run(proxy);
proxy_free(proxy);
cachemgr_fini();
nat_fini();
log_fini();
if (opts->pidfile) {
sys_pidf_close(pidfd, opts->pidfile);
}
opts_free(opts);
ssl_fini();
return EXIT_SUCCESS;
}
/* vim: set noet ft=c: */

102
main.t

@ -0,0 +1,102 @@
/*
* SSLsplit - transparent and scalable SSL/TLS interception
* Copyright (c) 2009-2012, Daniel Roethlisberger <daniel@roe.ch>
* All rights reserved.
* http://www.roe.ch/SSLsplit
*
* 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 unmodified, 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 AUTHOR ``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 AUTHOR 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 "attrib.h"
#include "opts.h"
#include <check.h>
#include <stdlib.h>
#include <string.h>
Suite *
blank_suite(void)
{
Suite *s;
s = suite_create("");
return s;
}
START_TEST(build_date_01)
{
char *bd = BUILD_DATE;
fail_unless(strlen(bd) == 10, "length mismatch");
fail_unless(bd[4] == '-', "year/month separator not dash");
fail_unless(bd[7] == '-', "month/day separator not dash");
}
END_TEST
Suite *
main_suite(void)
{
Suite *s;
TCase *tc;
s = suite_create("main");
tc = tcase_create("build_date");
tcase_add_test(tc, build_date_01);
suite_add_tcase(s, tc);
return s;
}
Suite * opts_suite(void);
Suite * cert_suite(void);
Suite * cachemgr_suite(void);
Suite * cachefkcrt_suite(void);
Suite * cachetgcrt_suite(void);
Suite * cachedsess_suite(void);
Suite * cachessess_suite(void);
Suite * ssl_suite(void);
Suite * sys_suite(void);
int
main(UNUSED int argc, UNUSED char *argv[])
{
int nfail;
SRunner *sr;
sr = srunner_create(blank_suite());
srunner_add_suite(sr, main_suite());
srunner_add_suite(sr, opts_suite());
srunner_add_suite(sr, cert_suite());
srunner_add_suite(sr, cachemgr_suite());
srunner_add_suite(sr, cachefkcrt_suite());
srunner_add_suite(sr, cachetgcrt_suite());
srunner_add_suite(sr, cachedsess_suite());
srunner_add_suite(sr, cachessess_suite());
srunner_add_suite(sr, ssl_suite());
srunner_add_suite(sr, sys_suite());
srunner_run_all(sr, CK_NORMAL);
nfail = srunner_ntests_failed(sr);
srunner_free(sr);
return !nfail ? EXIT_SUCCESS : EXIT_FAILURE;
}
/* vim: set noet ft=c: */

629
nat.c

@ -0,0 +1,629 @@
/*
* SSLsplit - transparent and scalable SSL/TLS interception
* Copyright (c) 2009-2012, Daniel Roethlisberger <daniel@roe.ch>
* All rights reserved.
* http://www.roe.ch/SSLsplit
*
* 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 unmodified, 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 AUTHOR ``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 AUTHOR 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 "nat.h"
#include "log.h"
#include "attrib.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#ifdef HAVE_PF
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/fcntl.h>
#include <net/if.h>
#include <netinet/in.h>
#include <net/pfvar.h>
#include <unistd.h>
#endif /* HAVE_PF */
#ifdef HAVE_IPFILTER
#include <sys/ioctl.h>
#include <netinet/tcp.h>
#include <net/if.h>
#include <netinet/ipl.h>
#include <netinet/ip_compat.h>
#include <netinet/ip_fil.h>
#include <netinet/ip_nat.h>
#endif /* HAVE_IPFILTER */
#ifdef HAVE_NETFILTER
#include <limits.h>
#include <linux/netfilter_ipv4.h>
#include <linux/netfilter_ipv6.h>
#endif /* HAVE_NETFILTER */
/*
* Access NAT state tables in a NAT engine independant way.
* Adding support for additional NAT engines should require only
* changes in this file.
*/
/*
* pf
*/
#ifdef HAVE_PF
static int nat_pf_fd = -1;
static int
nat_pf_preinit(void)
{
nat_pf_fd = open("/dev/pf", O_RDONLY);
if (nat_pf_fd < 0) {
log_err_printf("Error opening '/dev/pf': %s\n",
strerror(errno));
return -1;
}
return 0;
}
static int
nat_pf_init(void)
{
int rv;
rv = fcntl(nat_pf_fd, F_SETFD, fcntl(nat_pf_fd, F_GETFD) | FD_CLOEXEC);
if (rv == -1) {
log_err_printf("Error setting FD_CLOEXEC on '/dev/pf': %s\n",
strerror(errno));
return -1;
}
return 0;
}
static void
nat_pf_fini(void)
{
close(nat_pf_fd);
}
static int
nat_pf_lookup_cb(struct sockaddr *dst_addr, socklen_t *dst_addrlen,
evutil_socket_t s,
struct sockaddr *src_addr, UNUSED socklen_t src_addrlen)
{
struct sockaddr_storage our_addr;
socklen_t our_addrlen;
struct pfioc_natlook nl;
our_addrlen = sizeof(struct sockaddr_storage);
if (getsockname(s, (struct sockaddr *)&our_addr, &our_addrlen) == -1) {
log_err_printf("Error from getsockname(): %s\n",
strerror(errno));
return -1;
}
memset(&nl, 0, sizeof(struct pfioc_natlook));
nl.af = src_addr->sa_family;
if (nl.af == AF_INET) {
struct sockaddr_in *src_sai = (struct sockaddr_in *)src_addr;
struct sockaddr_in *our_sai = (struct sockaddr_in *)&our_addr;
nl.saddr.v4.s_addr = src_sai->sin_addr.s_addr;
nl.sport = src_sai->sin_port;
nl.daddr.v4.s_addr = our_sai->sin_addr.s_addr;
nl.dport = our_sai->sin_port;
}
if (nl.af == AF_INET6) {
struct sockaddr_in6 *src_sai = (struct sockaddr_in6 *)src_addr;
struct sockaddr_in6 *our_sai = (struct sockaddr_in6 *)&our_addr;
memcpy(&nl.saddr.v6.s6_addr, &src_sai->sin6_addr.s6_addr, 16);
nl.sport = src_sai->sin6_port;
memcpy(&nl.daddr.v6.s6_addr, &our_sai->sin6_addr.s6_addr, 16);
nl.dport = our_sai->sin6_port;
}
nl.proto = IPPROTO_TCP;
nl.direction = PF_OUT;
if (ioctl(nat_pf_fd, DIOCNATLOOK, &nl)) {
if (errno != ENOENT) {
log_err_printf("Error from ioctl(DIOCNATLOOK): %s\n",
strerror(errno));
}
return -1;
}
if ((nl.dport == nl.rdport) &&
((nl.af == AF_INET && nl.daddr.v4.s_addr == nl.rdaddr.v4.s_addr) ||
(nl.af == AF_INET6 &&
!memcmp(nl.daddr.v6.s6_addr, nl.rdaddr.v6.s6_addr, 16)))) {
/* no destination address/port translation in place */
return -1;
}
/* copy original destination address */
if (nl.af == AF_INET) {
struct sockaddr_in *dst_sai = (struct sockaddr_in *)dst_addr;
memset(dst_sai, 0, sizeof(struct sockaddr_in));
dst_sai->sin_addr.s_addr = nl.rdaddr.v4.s_addr;
dst_sai->sin_port = nl.rdport;
dst_sai->sin_family = nl.af;
*dst_addrlen = sizeof(struct sockaddr_in);
}
if (nl.af == AF_INET6) {
struct sockaddr_in6 *dst_sai = (struct sockaddr_in6 *)dst_addr;
memset(dst_sai, 0, sizeof(struct sockaddr_in6));
memcpy(dst_sai->sin6_addr.s6_addr, nl.rdaddr.v6.s6_addr, 16);
dst_sai->sin6_port = nl.rdport;
dst_sai->sin6_family = nl.af;
*dst_addrlen = sizeof(struct sockaddr_in6);
}
return 0;
}
#endif /* HAVE_PF */
/*
* ipfilter
*/
#ifdef HAVE_IPFILTER
static int nat_ipfilter_fd = -1;
static int
nat_ipfilter_preinit(void)
{
nat_ipfilter_fd = open(IPNAT_NAME, O_RDONLY);
if (nat_ipfilter_fd < 0) {
log_err_printf("Error opening '%s': %s\n",
IPNAT_NAME, strerror(errno));
return -1;
}
return 0;
}
static int
nat_ipfilter_init(void)
{
int rv;
rv = fcntl(nat_ipfilter_fd, F_SETFD,
fcntl(nat_ipfilter_fd, F_GETFD) | FD_CLOEXEC);
if (rv == -1) {
log_err_printf("Error setting FD_CLOEXEC on '%s': %s\n",
IPNAT_NAME, strerror(errno));
return -1;
}
return 0;
}
static void
nat_ipfilter_fini(void)
{
close(nat_ipfilter_fd);
}
static int
nat_ipfilter_lookup_cb(struct sockaddr *dst_addr, socklen_t *dst_addrlen,
evutil_socket_t s,
struct sockaddr *src_addr, UNUSED socklen_t src_addrlen)
{
struct sockaddr_storage our_addr;
socklen_t our_addrlen;
struct natlookup nl;
struct ipfobj ipfo;
our_addrlen = sizeof(struct sockaddr_storage);
if (getsockname(s, (struct sockaddr *)&our_addr, &our_addrlen) == -1) {
log_err_printf("Error from getsockname(): %s\n",
strerror(errno));
return -1;
}
memset(&nl, 0, sizeof(struct natlookup));
if (src_addr->sa_family == AF_INET) {
struct sockaddr_in *src_sai = (struct sockaddr_in *)src_addr;
struct sockaddr_in *our_sai = (struct sockaddr_in *)&our_addr;
nl.nl_outip.s_addr = src_sai->sin_addr.s_addr;
nl.nl_outport = src_sai->sin_port;
nl.nl_inip.s_addr = our_sai->sin_addr.s_addr;
nl.nl_inport = our_sai->sin_port;
} else {
log_err_printf("The ipfilter NAT engine does not "
"support IPv6 state lookups\n");
return -1;
}
nl.nl_flags = IPN_TCP;
/* assuming IPv4 from here */
memset(&ipfo, 0, sizeof(struct ipfobj));
ipfo.ipfo_rev = IPFILTER_VERSION;
ipfo.ipfo_size = sizeof(struct natlookup);
ipfo.ipfo_ptr = &nl;
ipfo.ipfo_type = IPFOBJ_NATLOOKUP;
if (ioctl(nat_ipfilter_fd, SIOCGNATL, &ipfo) == -1) {
if (errno != ESRCH) {
log_err_printf("Error from ioctl(SIOCGNATL): %s\n",
strerror(errno));
}
return -1;
}
if ((nl.nl_inport == nl.nl_realport) &&
(nl.nl_inip.s_addr == nl.nl_realip.s_addr)) {
/* no destination address/port translation in place */
return -1;
}
/* copy original destination address */
struct sockaddr_in *dst_sai = (struct sockaddr_in *)dst_addr;
memset(dst_sai, 0, sizeof(struct sockaddr_in));
dst_sai->sin_addr.s_addr = nl.nl_realip.s_addr;
dst_sai->sin_port = nl.nl_realport;
dst_sai->sin_family = AF_INET;
*dst_addrlen = sizeof(struct sockaddr_in);
return 0;
}
#endif /* HAVE_IPFILTER */
/*
* netfilter, tproxy
*/
#ifdef HAVE_NETFILTER
/*
* It seems that SO_ORIGINAL_DST only works for IPv4 and that there
* is no IPv6 equivalent yet. Someone please port pf to Linux...
* We try to do something sensible for IPv6 anyway, expect it to fail.
*
* http://lists.netfilter.org/pipermail/netfilter/2007-July/069259.html
*
* It looks like TPROXY is the only way to go on Linux with IPv6.
*/
#ifndef IPV6_ORIGINAL_DST
#define IPV6_ORIGINAL_DST SO_ORIGINAL_DST
#endif /* !IPV6_ORIGINAL_DST */
#ifndef SOL_IPV6
#define SOL_IPV6 SOL_IP
#endif /* !SOL_IPV6 */
static int
nat_netfilter_lookup_cb(struct sockaddr *dst_addr, socklen_t *dst_addrlen,
evutil_socket_t s,
struct sockaddr *src_addr,
UNUSED socklen_t src_addrlen)
{
int rv;
if (src_addr->sa_family == AF_INET6) {
rv = getsockopt(s, SOL_IPV6, IPV6_ORIGINAL_DST,
dst_addr, dst_addrlen);
} else {
rv = getsockopt(s, SOL_IP, SO_ORIGINAL_DST,
dst_addr, dst_addrlen);
}
if (rv == -1) {
log_err_printf("Error from getsockopt(SO_ORIGINAL_DST): %s\n",
strerror(errno));
if (errno == ENOPROTOOPT) {
log_err_printf("Looks like your Linux kernel doesn't "
"support SO_ORIGINAL_DST for IPv6.\n");
}
}
return rv;
}
#ifdef IP_TRANSPARENT
/*
* Set the listening socket IP_TRANSPARENT. This makes the Linux IP routing
* stack omit the source address checks on output, which is needed for
* Linux TPROXY transparent proxying support.
*/
static int
nat_iptransparent_socket_cb(evutil_socket_t s)
{
int on = 1;
int rv;
rv = setsockopt(s, SOL_IP, IP_TRANSPARENT, (void*)&on, sizeof(on));
if (rv == -1) {
log_err_printf("Error from setsockopt(IP_TRANSPARENT): %s\n",
strerror(errno));
}
return rv;
}
#endif /* IP_TRANSPARENT */
#endif /* HAVE_NETFILTER */
/*
* generic
*/
/*
* Generic getsockname based implementation. This assumes that getsockname,
* by kernel magic, gives us the original destination.
*/
static int
nat_getsockname_lookup_cb(struct sockaddr *dst_addr, socklen_t *dst_addrlen,
evutil_socket_t s,
UNUSED struct sockaddr *src_addr,
UNUSED socklen_t src_addrlen)
{
if (getsockname(s, dst_addr, dst_addrlen) == -1) {
log_err_printf("Error from getsockname(): %s\n",
strerror(errno));
return -1;
}
return 0;
}
/*
* NAT engine glue code and API.
*/
typedef int (*nat_init_cb_t)(void);
typedef void (*nat_fini_cb_t)(void);
struct engine {
const char *name;
unsigned int ipv6 : 1;
unsigned int used : 1;
nat_init_cb_t preinitcb;
nat_init_cb_t initcb;
nat_fini_cb_t finicb;
nat_lookup_cb_t lookupcb;
nat_socket_cb_t socketcb;
};
struct engine engines[] = {
#ifdef HAVE_PF
{
"pf", 1, 0,
nat_pf_preinit, nat_pf_init, nat_pf_fini,
nat_pf_lookup_cb, NULL
},
#endif /* HAVE_PF */
#ifdef HAVE_IPFW
{
"ipfw", 1, 0,
NULL, NULL, NULL,
nat_getsockname_lookup_cb, NULL
},
#endif /* HAVE_IPFW */
#ifdef HAVE_IPFILTER
{
"ipfilter", 0, 0,
nat_ipfilter_preinit, nat_ipfilter_init, nat_ipfilter_fini,
nat_ipfilter_lookup_cb, NULL
},
#endif /* HAVE_IPFILTER */
#ifdef HAVE_NETFILTER
{
"netfilter", 0, 0,
NULL, NULL, NULL,
nat_netfilter_lookup_cb, NULL
},
#ifdef IP_TRANSPARENT
{
"tproxy", 1, 0,
NULL, NULL, NULL,
nat_getsockname_lookup_cb, nat_iptransparent_socket_cb
},
#endif /* IP_TRANSPARENT */
#endif /* HAVE_NETFILTER */
{
NULL, 0, 0,
NULL, NULL, NULL,
NULL, NULL
}
};
/*
* Return the name of the default NAT engine.
*/
const char *
nat_getdefaultname(void)
{
return engines[0].name;
}
/*
* Look for a NAT engine in the table and return the index if found.
* If there is no NAT engine with the given name, then the index of the
* sentinel table entry is returned.
*/
static int
nat_index(const char *name)
{
if (name)
for (int i = 0; engines[i].name; i++)
if (!strcmp(name, engines[i].name))
return i;
return ((sizeof(engines) / sizeof(struct engine)) - 1);
}
/*
* Returns 1 if the named NAT engine exists, 0 if it does not exist.
* NULL refers to the default NAT engine.
*/
int
nat_exist(const char *name)
{
if (!name)
name = engines[0].name;
return !!engines[nat_index(name)].name;
}
/*
* Returns the lookup callback of the named NAT engine and marks the NAT
* engine as used.
* NULL refers to the default NAT engine.
*/
nat_lookup_cb_t
nat_getlookupcb(const char *name)
{
int i;
if (!name)
name = engines[0].name;
i = nat_index(name);
engines[i].used = 1;
return engines[i].lookupcb;
}
/*
* Returns the socket callback of the named NAT engine.
* NULL refers to the default NAT engine.
*/
nat_socket_cb_t
nat_getsocketcb(const char *name)
{
if (!name)
name = engines[0].name;
return engines[nat_index(name)].socketcb;
}
/*
* Returns 1 if name is a NAT engine which supports IPv6.
* NULL refers to the default NAT engine.
*/
int
nat_ipv6ready(const char *name)
{
if (!name)
name = engines[0].name;
return engines[nat_index(name)].ipv6;
}
/*
* List all available NAT engines to standard output and flush.
*/
void
nat_list_engines(void)
{
for (int i = 0; engines[i].name; i++) {
fprintf(stdout, "%s%s\n", engines[i].name,
i ? "" : " (default)");
}
fflush(stdout);
}
/*
* Pre-initialize all NAT engines which were marked as used by previous calls
* to nat_getlookupcb().
*
* Privileged initialization under root privs, before dropping privs,
* before calling daemon(). Here should be initialization which needs
* to provide the user feedback on errors. This includes opening
* special device files, for which the user may not have sufficient privs.
*
* Returns -1 on failure, 0 on success.
*/
int
nat_preinit(void)
{
for (int i = 0; engines[i].preinitcb && engines[i].used; i++) {
log_dbg_printf("NAT engine preinit '%s'\n", engines[i].name);
if (engines[i].preinitcb() == -1)
return -1;
}
return 0;
}
/*
* Initialize all NAT engines which were marked as used by previous calls to
* nat_getlookupcb().
*
* Unprivileged initialization, possibly root, possibly nobody or service user.
*
* Returns -1 on failure, 0 on success.
*/
int
nat_init(void)
{
for (int i = 0; engines[i].initcb && engines[i].used; i++) {
log_dbg_printf("NAT engine init '%s'\n", engines[i].name);
if (engines[i].initcb() == -1)
return -1;
}
return 0;
}
/*
* Cleanup all NAT engines which were marked as used by previous calls to
* nat_getlookupcb().
*/
void
nat_fini(void)
{
for (int i = 0; engines[i].finicb && engines[i].used; i++) {
log_dbg_printf("NAT engine fini '%s'\n", engines[i].name);
engines[i].finicb();
}
}
/*
* Print version and option availability to standard error.
*/
void
nat_version(void)
{
fprintf(stderr, "NAT engines:");
for (int i = 0; engines[i].name; i++) {
fprintf(stderr, " %s%s", engines[i].name,
i ? "" : "*");
}
if (!engines[0].name)
fprintf(stderr, " -");
fprintf(stderr, "\n");
#ifdef HAVE_IPFILTER
fprintf(stderr, "ipfilter: version %d\n", IPFILTER_VERSION);
#endif /* HAVE_IPFILTER */
#ifdef HAVE_NETFILTER
fprintf(stderr, "netfilter: ");
#ifdef IP_TRANSPARENT
fprintf(stderr, " IP_TRANSPARENT");
#else /* !IP_TRANSPARENT */
fprintf(stderr, " !IP_TRANSPARENT");
#endif /* !IP_TRANSPARENT */
#ifdef SOL_IPV6
fprintf(stderr, " SOL_IPV6");
#else /* !SOL_IPV6 */
fprintf(stderr, " !SOL_IPV6");
#endif /* !SOL_IPV6 */
#ifdef IPV6_ORIGINAL_DST
fprintf(stderr, " IPV6_ORIGINAL_DST");
#else /* !IPV6_ORIGINAL_DST */
fprintf(stderr, " !IPV6_ORIGINAL_DST");
#endif /* !IPV6_ORIGINAL_DST */
fprintf(stderr, "\n");
#endif /* HAVE_NETFILTER */
}
/* vim: set noet ft=c: */

55
nat.h

@ -0,0 +1,55 @@
/*
* SSLsplit - transparent and scalable SSL/TLS interception
* Copyright (c) 2009-2012, Daniel Roethlisberger <daniel@roe.ch>
* All rights reserved.
* http://www.roe.ch/SSLsplit
*
* 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 unmodified, 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 AUTHOR ``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 AUTHOR 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 NAT_H
#define NAT_H
#include <sys/types.h>
#include <sys/socket.h>
#include <event2/util.h>
typedef int (*nat_lookup_cb_t)(struct sockaddr *, socklen_t *, evutil_socket_t,
struct sockaddr *, socklen_t);
typedef int (*nat_socket_cb_t)(evutil_socket_t);
int nat_exist(const char *);
nat_lookup_cb_t nat_getlookupcb(const char *);
nat_socket_cb_t nat_getsocketcb(const char *);
int nat_ipv6ready(const char *);
const char *nat_getdefaultname(void);
void nat_list_engines(void);
int nat_preinit(void);
int nat_init(void);
void nat_fini(void);
void nat_version(void);
#endif /* !NAT_H */
/* vim: set noet ft=c: */

275
opts.c

@ -0,0 +1,275 @@
/*
* SSLsplit - transparent and scalable SSL/TLS interception
* Copyright (c) 2009-2012, Daniel Roethlisberger <daniel@roe.ch>
* All rights reserved.
* http://www.roe.ch/SSLsplit
*
* 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 unmodified, 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 AUTHOR ``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 AUTHOR 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 "opts.h"
#include "sys.h"
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#ifndef OPENSSL_NO_DH
#include <openssl/dh.h>
#endif /* !OPENSSL_NO_DH */
#include <openssl/x509.h>
opts_t *
opts_new(void)
{
opts_t *opts;
opts = malloc(sizeof(opts_t));
memset(opts, 0, sizeof(opts_t));
opts->sslcomp = 1;
opts->chain = sk_X509_new_null();
return opts;
}
void
opts_free(opts_t *opts)
{
sk_X509_pop_free(opts->chain, X509_free);
if (opts->cacrt) {
X509_free(opts->cacrt);
}
if (opts->cakey) {
EVP_PKEY_free(opts->cakey);
}
if (opts->key) {
EVP_PKEY_free(opts->key);
}
#ifndef OPENSSL_NO_DH
if (opts->dh) {
DH_free(opts->dh);
}
#endif /* !OPENSSL_NO_DH */
#ifndef OPENSSL_NO_ECDH
if (opts->ecdhcurve) {
free(opts->ecdhcurve);
}
#endif /* !OPENSSL_NO_ECDH */
if (opts->spec) {
proxyspec_free(opts->spec);
}
if (opts->ciphers) {
free(opts->ciphers);
}
if (opts->tgcrtdir) {
free(opts->tgcrtdir);
}
if (opts->dropuser) {
free(opts->dropuser);
}
if (opts->jaildir) {
free(opts->jaildir);
}
if (opts->pidfile) {
free(opts->pidfile);
}
if (opts->connectlog) {
free(opts->connectlog);
}
if (opts->contentlog) {
free(opts->contentlog);
}
memset(opts, 0, sizeof(opts_t));
free(opts);
}
/*
* Return 1 if opts_t contains a proxyspec with ssl, 0 otherwise.
*/
int
opts_has_ssl_spec(opts_t *opts)
{
proxyspec_t *p = opts->spec;
while (p) {
if (p->ssl)
return 1;
p = p->next;
}
return 0;
}
/*
* Parse proxyspecs using a simple state machine.
* Returns NULL if parsing failed.
*/
proxyspec_t *
proxyspec_parse(int *argc, char **argv[], const char *natengine)
{
proxyspec_t *curspec, *spec = NULL;
char *addr;
int af;
int state = 0;
while ((*argc)--) {
switch (state) {
default:
case 0:
/* tcp | ssl | http | https */
curspec = malloc(sizeof(proxyspec_t));
memset(curspec, 0, sizeof(proxyspec_t));
curspec->next = spec;
spec = curspec;
if (!strcmp(**argv, "tcp")) {
spec->ssl = 0;
spec->http = 0;
} else
if (!strcmp(**argv, "ssl")) {
spec->ssl = 1;
spec->http = 0;
} else
if (!strcmp(**argv, "http")) {
spec->ssl = 0;
spec->http = 1;
} else
if (!strcmp(**argv, "https")) {
spec->ssl = 1;
spec->http = 1;
} else {
fprintf(stderr, "Unknown connection "
"type '%s'\n", **argv);
exit(EXIT_FAILURE);
}
state++;
break;
case 1:
/* listenaddr */
addr = **argv;
state++;
break;
case 2:
/* listenport */
if (strstr(addr, ":"))
af = AF_INET6;
else if (!strpbrk(addr, "abcdefghijklmnopqrstu"
"vwxyzABCDEFGHIJKLMNOP"
"QRSTUVWXYZ-"))
af = AF_INET;
else
af = AF_UNSPEC;
af = sys_sockaddr_parse(&spec->listen_addr,
&spec->listen_addrlen,
addr, **argv, af,
EVUTIL_AI_PASSIVE);
if (af == -1) {
exit(EXIT_FAILURE);
}
spec->natengine = strdup(natengine);
state++;
break;
case 3:
/* [ natengine | dstaddr ] */
if (!strcmp(**argv, "tcp") ||
!strcmp(**argv, "ssl") ||
!strcmp(**argv, "http") ||
!strcmp(**argv, "https")) {
/* implicit default natengine */
(*argv)--; (*argc)++; /* rewind */
state = 0;
} else
if (!strcmp(**argv, "sni")) {
free(spec->natengine);
spec->natengine = NULL;
if (!spec->ssl) {
fprintf(stderr,
"SNI hostname lookup "
"only works for ssl "
"and https proxyspecs"
"\n");
exit(EXIT_FAILURE);
}
state = 5;
} else
if (nat_exist(**argv)) {
/* natengine */
free(spec->natengine);
spec->natengine = strdup(**argv);
state = 0;
} else {
/* explicit target address */
free(spec->natengine);
spec->natengine = NULL;
addr = **argv;
state++;
}
break;
case 4:
/* dstport */
af = sys_sockaddr_parse(&spec->connect_addr,
&spec->connect_addrlen,
addr, **argv, af, 0);
if (af == -1) {
exit(EXIT_FAILURE);
}
state = 0;
break;
case 5:
/* SNI dstport */
spec->sni_port = atoi(**argv);
if (!spec->sni_port) {
fprintf(stderr, "Invalid port '%s'\n",
**argv);
exit(EXIT_FAILURE);
}
state = 0;
break;
}
(*argv)++;
}
if (state != 0 && state != 3) {
fprintf(stderr, "Incomplete proxyspec!\n");
exit(EXIT_FAILURE);
}
return spec;
}
/*
* Clear and free a proxy spec.
*/
void
proxyspec_free(proxyspec_t *spec)
{
while (spec) {
proxyspec_t *next = spec->next;
if (spec->natengine)
free(spec->natengine);
memset(spec, 0, sizeof(proxyspec_t));
free(spec);
spec = next;
}
}
/* vim: set noet ft=c: */

@ -0,0 +1,91 @@
/*
* SSLsplit - transparent and scalable SSL/TLS interception
* Copyright (c) 2009-2012, Daniel Roethlisberger <daniel@roe.ch>
* All rights reserved.
* http://www.roe.ch/SSLsplit
*
* 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 unmodified, 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 AUTHOR ``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 AUTHOR 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 OPTS_H
#define OPTS_H
#include "nat.h"
#include "ssl.h"
#include "attrib.h"
#include <sys/types.h>
#include <sys/socket.h>
typedef struct proxyspec {
unsigned int ssl : 1;
unsigned int http : 1;
struct sockaddr_storage listen_addr;
socklen_t listen_addrlen;
/* connect_addr and connect_addrlen are set: static mode;
* natlookup is set: NAT mode; natsocket /may/ be set too;
* sni_port is set, in which case we use SNI lookups */
struct sockaddr_storage connect_addr;
socklen_t connect_addrlen;
unsigned short sni_port;
char *natengine;
nat_lookup_cb_t natlookup;
nat_socket_cb_t natsocket;
struct proxyspec *next;
} proxyspec_t;
typedef struct opts {
unsigned int debug : 1;
unsigned int detach : 1;
unsigned int sslcomp : 1;
unsigned int passthrough : 1;
unsigned int contentlogdir : 1;
char *ciphers;
char *tgcrtdir;
char *dropuser;
char *jaildir;
char *pidfile;
char *connectlog;
char *contentlog;
X509 *cacrt;
EVP_PKEY *cakey;
EVP_PKEY *key;
STACK_OF(X509) *chain;
#ifndef OPENSSL_NO_DH
DH *dh;
#endif /* !OPENSSL_NO_DH */
#ifndef OPENSSL_NO_ECDH
char *ecdhcurve;
#endif /* !OPENSSL_NO_ECDH */
proxyspec_t *spec;
} opts_t;
opts_t *opts_new(void) MALLOC;
void opts_free(opts_t *) NONNULL();
int opts_has_ssl_spec(opts_t *) NONNULL();
proxyspec_t * proxyspec_parse(int *, char **[], const char *) MALLOC;
void proxyspec_free(proxyspec_t *) NONNULL();
#endif /* !OPTS_H */
/* vim: set noet ft=c: */

382
opts.t

@ -0,0 +1,382 @@
/*
* SSLsplit - transparent and scalable SSL/TLS interception
* Copyright (c) 2009-2012, Daniel Roethlisberger <daniel@roe.ch>
* All rights reserved.
* http://www.roe.ch/SSLsplit
*
* 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 unmodified, 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 AUTHOR ``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 AUTHOR 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 "attrib.h"
#include "opts.h"
#include <check.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <netinet/in.h>
static char *argv01[] = {
"https", "127.0.0.1", "10443", "127.0.0.2", "443"
};
static char *argv02[] = {
"https", "::1", "10443", "::2", "443"
};
static char *argv03[] = {
"http", "127.0.0.1", "10443", "127.0.0.2", "443"
};
static char *argv04[] = {
"ssl", "127.0.0.1", "10443", "127.0.0.2", "443"
};
static char *argv05[] = {
"tcp", "127.0.0.1", "10443", "127.0.0.2", "443"
};
static char *argv06[] = {
"https", "127.0.0.1", "10443", "sni", "443"
};
static char *argv07[] = {
"http", "127.0.0.1", "10443", "sni", "443"
};
static char *argv08[] = {
"https", "127.0.0.1", "10443", "no_such_engine"
};
static char *argv09[] = {
"https", "127.0.0.1", "10443", "127.0.0.2", "443",
"https", "::1", "10443", "::2", "443"
};
static char *argv10[] = {
"https", "127.0.0.1", "10443",
"https", "::1", "10443"
};
#define NATENGINE "pf"
START_TEST(proxyspec_parse_01)
{
proxyspec_t *spec;
int argc = 5;
char **argv = argv01;
spec = proxyspec_parse(&argc, &argv, NATENGINE);
fail_unless(!!spec, "failed to parse spec");
fail_unless(spec->ssl, "not SSL");
fail_unless(spec->http, "not HTTP");
fail_unless(spec->listen_addrlen == sizeof(struct sockaddr_in),
"not IPv4 listen addr");
fail_unless(spec->connect_addrlen == sizeof(struct sockaddr_in),
"not IPv4 connect addr");
fail_unless(!spec->sni_port, "SNI port is set");
fail_unless(!spec->natengine, "natengine is set");
fail_unless(!spec->natlookup, "natlookup() is set");
fail_unless(!spec->natsocket, "natsocket() is set");
fail_unless(!spec->next, "next is set");
proxyspec_free(spec);
}
END_TEST
START_TEST(proxyspec_parse_02)
{
proxyspec_t *spec;
int argc = 5;
char **argv = argv02;
spec = proxyspec_parse(&argc, &argv, NATENGINE);
fail_unless(!!spec, "failed to parse spec");
fail_unless(spec->ssl, "not SSL");
fail_unless(spec->http, "not HTTP");
fail_unless(spec->listen_addrlen == sizeof(struct sockaddr_in6),
"not IPv6 listen addr");
fail_unless(spec->connect_addrlen == sizeof(struct sockaddr_in6),
"not IPv6 connect addr");
fail_unless(!spec->sni_port, "SNI port is set");
fail_unless(!spec->natengine, "natengine is set");
fail_unless(!spec->natlookup, "natlookup() is set");
fail_unless(!spec->natsocket, "natsocket() is set");
fail_unless(!spec->next, "next is set");
proxyspec_free(spec);
}
END_TEST
START_TEST(proxyspec_parse_03)
{
proxyspec_t *spec;
int argc = 2;
char **argv = argv01;
close(2);
spec = proxyspec_parse(&argc, &argv, NATENGINE);
}
END_TEST
START_TEST(proxyspec_parse_04)
{
proxyspec_t *spec;
int argc = 4;
char **argv = argv01;
close(2);
spec = proxyspec_parse(&argc, &argv, NATENGINE);
}
END_TEST
START_TEST(proxyspec_parse_05)
{
proxyspec_t *spec;
int argc = 5;
char **argv = argv03;
spec = proxyspec_parse(&argc, &argv, NATENGINE);
fail_unless(!!spec, "failed to parse spec");
fail_unless(!spec->ssl, "SSL");
fail_unless(spec->http, "not HTTP");
fail_unless(spec->listen_addrlen == sizeof(struct sockaddr_in),
"not IPv4 listen addr");
fail_unless(spec->connect_addrlen == sizeof(struct sockaddr_in),
"not IPv4 connect addr");
fail_unless(!spec->sni_port, "SNI port is set");
fail_unless(!spec->natengine, "natengine is set");
fail_unless(!spec->natlookup, "natlookup() is set");
fail_unless(!spec->natsocket, "natsocket() is set");
fail_unless(!spec->next, "next is set");
proxyspec_free(spec);
}
END_TEST
START_TEST(proxyspec_parse_06)
{
proxyspec_t *spec;
int argc = 5;
char **argv = argv04;
spec = proxyspec_parse(&argc, &argv, NATENGINE);
fail_unless(!!spec, "failed to parse spec");
fail_unless(spec->ssl, "not SSL");
fail_unless(!spec->http, "HTTP");
fail_unless(spec->listen_addrlen == sizeof(struct sockaddr_in),
"not IPv4 listen addr");
fail_unless(spec->connect_addrlen == sizeof(struct sockaddr_in),
"not IPv4 connect addr");
fail_unless(!spec->sni_port, "SNI port is set");
fail_unless(!spec->natengine, "natengine is set");
fail_unless(!spec->natlookup, "natlookup() is set");
fail_unless(!spec->natsocket, "natsocket() is set");
fail_unless(!spec->next, "next is set");
proxyspec_free(spec);
}
END_TEST
START_TEST(proxyspec_parse_07)
{
proxyspec_t *spec;
int argc = 5;
char **argv = argv05;
spec = proxyspec_parse(&argc, &argv, NATENGINE);
fail_unless(!!spec, "failed to parse spec");
fail_unless(!spec->ssl, "SSL");
fail_unless(!spec->http, "HTTP");
fail_unless(spec->listen_addrlen == sizeof(struct sockaddr_in),
"not IPv4 listen addr");
fail_unless(spec->connect_addrlen == sizeof(struct sockaddr_in),
"not IPv4 connect addr");
fail_unless(!spec->sni_port, "SNI port is set");
fail_unless(!spec->natengine, "natengine is set");
fail_unless(!spec->natlookup, "natlookup() is set");
fail_unless(!spec->natsocket, "natsocket() is set");
fail_unless(!spec->next, "next is set");
proxyspec_free(spec);
}
END_TEST
START_TEST(proxyspec_parse_08)
{
proxyspec_t *spec;
int argc = 5;
char **argv = argv06;
spec = proxyspec_parse(&argc, &argv, NATENGINE);
fail_unless(!!spec, "failed to parse spec");
fail_unless(spec->ssl, "not SSL");
fail_unless(spec->http, "not HTTP");
fail_unless(spec->listen_addrlen == sizeof(struct sockaddr_in),
"not IPv4 listen addr");
fail_unless(!spec->connect_addrlen, "connect addr set");
fail_unless(spec->sni_port == 443, "SNI port is not set");
fail_unless(!spec->natengine, "natengine is set");
fail_unless(!spec->natlookup, "natlookup() is set");
fail_unless(!spec->natsocket, "natsocket() is set");
fail_unless(!spec->next, "next is set");
proxyspec_free(spec);
}
END_TEST
START_TEST(proxyspec_parse_09)
{
proxyspec_t *spec;
int argc = 5;
char **argv = argv07;
close(2);
spec = proxyspec_parse(&argc, &argv, NATENGINE);
}
END_TEST
START_TEST(proxyspec_parse_10)
{
proxyspec_t *spec;
int argc = 4;
char **argv = argv06;
close(2);
spec = proxyspec_parse(&argc, &argv, NATENGINE);
}
END_TEST
START_TEST(proxyspec_parse_11)
{
proxyspec_t *spec;
int argc = 3;
char **argv = argv08;
spec = proxyspec_parse(&argc, &argv, NATENGINE);
fail_unless(!!spec, "failed to parse spec");
fail_unless(spec->ssl, "not SSL");
fail_unless(spec->http, "not HTTP");
fail_unless(spec->listen_addrlen == sizeof(struct sockaddr_in),
"not IPv4 listen addr");
fail_unless(!spec->connect_addrlen, "connect addr set");
fail_unless(!spec->sni_port, "SNI port is set");
fail_unless(!!spec->natengine, "natengine not set");
fail_unless(!strcmp(spec->natengine, NATENGINE), "natengine mismatch");
fail_unless(!spec->natlookup, "natlookup() is set");
fail_unless(!spec->natsocket, "natsocket() is set");
fail_unless(!spec->next, "next is set");
proxyspec_free(spec);
}
END_TEST
START_TEST(proxyspec_parse_12)
{
proxyspec_t *spec;
int argc = 4;
char **argv = argv08;
close(2);
spec = proxyspec_parse(&argc, &argv, NATENGINE);
}
END_TEST
START_TEST(proxyspec_parse_13)
{
proxyspec_t *spec;
int argc = 10;
char **argv = argv09;
spec = proxyspec_parse(&argc, &argv, NATENGINE);
fail_unless(!!spec, "failed to parse spec");
fail_unless(spec->ssl, "not SSL");
fail_unless(spec->http, "not HTTP");
fail_unless(spec->listen_addrlen == sizeof(struct sockaddr_in6),
"not IPv6 listen addr");
fail_unless(spec->connect_addrlen == sizeof(struct sockaddr_in6),
"not IPv6 connect addr");
fail_unless(!spec->sni_port, "SNI port is set");
fail_unless(!spec->natengine, "natengine is set");
fail_unless(!spec->natlookup, "natlookup() is set");
fail_unless(!spec->natsocket, "natsocket() is set");
fail_unless(!!spec->next, "next is not set");
fail_unless(spec->next->ssl, "not SSL");
fail_unless(spec->next->http, "not HTTP");
fail_unless(spec->next->listen_addrlen == sizeof(struct sockaddr_in),
"not IPv4 listen addr");
fail_unless(spec->next->connect_addrlen == sizeof(struct sockaddr_in),
"not IPv4 connect addr");
fail_unless(!spec->next->sni_port, "SNI port is set");
fail_unless(!spec->next->natengine, "natengine is set");
fail_unless(!spec->next->natlookup, "natlookup() is set");
fail_unless(!spec->next->natsocket, "natsocket() is set");
proxyspec_free(spec);
}
END_TEST
START_TEST(proxyspec_parse_14)
{
proxyspec_t *spec;
int argc = 6;
char **argv = argv10;
spec = proxyspec_parse(&argc, &argv, NATENGINE);
fail_unless(!!spec, "failed to parse spec");
fail_unless(spec->ssl, "not SSL");
fail_unless(spec->http, "not HTTP");
fail_unless(spec->listen_addrlen == sizeof(struct sockaddr_in6),
"not IPv6 listen addr");
fail_unless(!spec->connect_addrlen, "connect addr set");
fail_unless(!spec->sni_port, "SNI port is set");
fail_unless(!!spec->natengine, "natengine not set");
fail_unless(!strcmp(spec->natengine, NATENGINE), "natengine mismatch");
fail_unless(!spec->natlookup, "natlookup() is set");
fail_unless(!spec->natsocket, "natsocket() is set");
fail_unless(!!spec->next, "next is not set");
fail_unless(spec->next->ssl, "not SSL");
fail_unless(spec->next->http, "not HTTP");
fail_unless(spec->next->listen_addrlen == sizeof(struct sockaddr_in),
"not IPv4 listen addr");
fail_unless(!spec->next->connect_addrlen, "connect addr set");
fail_unless(!spec->next->sni_port, "SNI port is set");
fail_unless(!!spec->next->natengine, "natengine not set");
fail_unless(!strcmp(spec->next->natengine, NATENGINE),
"natengine mismatch");
fail_unless(!spec->next->natlookup, "natlookup() is set");
fail_unless(!spec->next->natsocket, "natsocket() is set");
proxyspec_free(spec);
}
END_TEST
Suite *
opts_suite(void)
{
Suite *s;
TCase *tc;
s = suite_create("opts");
tc = tcase_create("proxyspec_parse");
tcase_add_test(tc, proxyspec_parse_01);
tcase_add_test(tc, proxyspec_parse_02);
tcase_add_exit_test(tc, proxyspec_parse_03, EXIT_FAILURE);
tcase_add_exit_test(tc, proxyspec_parse_04, EXIT_FAILURE);
tcase_add_test(tc, proxyspec_parse_05);
tcase_add_test(tc, proxyspec_parse_06);
tcase_add_test(tc, proxyspec_parse_07);
tcase_add_test(tc, proxyspec_parse_08);
tcase_add_exit_test(tc, proxyspec_parse_09, EXIT_FAILURE);
tcase_add_exit_test(tc, proxyspec_parse_10, EXIT_FAILURE);
tcase_add_test(tc, proxyspec_parse_11);
tcase_add_exit_test(tc, proxyspec_parse_12, EXIT_FAILURE);
tcase_add_test(tc, proxyspec_parse_13);
tcase_add_test(tc, proxyspec_parse_14);
suite_add_tcase(s, tc);
return s;
}
/* vim: set noet ft=c: */

@ -0,0 +1,410 @@
/*
* SSLsplit - transparent and scalable SSL/TLS interception
* Copyright (c) 2009-2012, Daniel Roethlisberger <daniel@roe.ch>
* All rights reserved.
* http://www.roe.ch/SSLsplit
*
* 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 unmodified, 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 AUTHOR ``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 AUTHOR 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 "proxy.h"
#include "pxythrmgr.h"
#include "pxyconn.h"
#include "cachemgr.h"
#include "opts.h"
#include "log.h"
#include "attrib.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 <event2/event.h>
#include <event2/listener.h>
#include <event2/bufferevent.h>
#include <event2/bufferevent_ssl.h>
#include <event2/buffer.h>
#include <event2/thread.h>
/*
* Proxy engine, built around libevent 2.x.
*/
static int signals[] = { SIGQUIT, SIGHUP, SIGINT, SIGPIPE };
struct proxy_ctx {
pxy_thrmgr_ctx_t *thrmgr;
struct event_base *evbase;
struct event *sev[sizeof(signals)/sizeof(int)];
struct event *gcev;
struct proxy_listener_ctx *lctx;
opts_t *opts;
};
/*
* Listener context.
*/
typedef struct proxy_listener_ctx {
pxy_thrmgr_ctx_t *thrmgr;
proxyspec_t *spec;
opts_t *opts;
struct evconnlistener *evcl;
struct proxy_listener_ctx *next;
} proxy_listener_ctx_t;
static proxy_listener_ctx_t *
proxy_listener_ctx_new(pxy_thrmgr_ctx_t *thrmgr, proxyspec_t *spec,
opts_t *opts) MALLOC;
static proxy_listener_ctx_t *
proxy_listener_ctx_new(pxy_thrmgr_ctx_t *thrmgr, proxyspec_t *spec,
opts_t *opts)
{
proxy_listener_ctx_t *ctx = malloc(sizeof(proxy_listener_ctx_t));
if (!ctx)
return NULL;
memset(ctx, 0, sizeof(proxy_listener_ctx_t));
ctx->thrmgr = thrmgr;
ctx->spec = spec;
ctx->opts = opts;
return ctx;
}
static void
proxy_listener_ctx_free(proxy_listener_ctx_t *ctx) NONNULL();
static void
proxy_listener_ctx_free(proxy_listener_ctx_t *ctx)
{
if (ctx->evcl) {
evconnlistener_free(ctx->evcl);
}
if (ctx->next) {
proxy_listener_ctx_free(ctx->next);
}
free(ctx);
}
/*
* Callback for accept events on the socket listener bufferevent.
*/
static void
proxy_listener_acceptcb(UNUSED struct evconnlistener *listener,
evutil_socket_t fd,
struct sockaddr *peeraddr, int peeraddrlen,
void *arg)
{
proxy_listener_ctx_t *cfg = arg;
pxy_conn_setup(fd, peeraddr, peeraddrlen, cfg->thrmgr,
cfg->spec, cfg->opts);
}
/*
* Callback for error events on the socket listener bufferevent.
*/
static void
proxy_listener_errorcb(struct evconnlistener *listener, UNUSED void *ctx)
{
struct event_base *evbase = evconnlistener_get_base(listener);
int err = EVUTIL_SOCKET_ERROR();
log_err_printf("Error %d on listener: %s\n", err,
evutil_socket_error_to_string(err));
event_base_loopbreak(evbase);
}
/*
* Dump a description of an evbase to debugging code.
*/
static void
proxy_debug_base(const struct event_base *ev_base)
{
log_dbg_printf("Using libevent backend '%s'\n",
event_base_get_method(ev_base));
enum event_method_feature f;
f = event_base_get_features(ev_base);
log_dbg_printf("Event base supports: edge %s, O(1) %s, anyfd %s\n",
((f & EV_FEATURE_ET) ? "yes" : "no"),
((f & EV_FEATURE_O1) ? "yes" : "no"),
((f & EV_FEATURE_FDS) ? "yes" : "no"));
}
/*
* Set up the listener for a single proxyspec and add it to evbase.
* Returns the proxy_listener_ctx_t pointer if successful, NULL otherwise.
*/
static proxy_listener_ctx_t *
proxy_listener_setup(struct event_base *evbase, pxy_thrmgr_ctx_t *thrmgr,
proxyspec_t *spec, opts_t *opts)
{
proxy_listener_ctx_t *plc;
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\n",
strerror(errno));
evutil_closesocket(fd);
return NULL;
}
rv = evutil_make_socket_nonblocking(fd);
if (rv == -1) {
log_err_printf("Error making socket nonblocking: %s\n",
strerror(errno));
evutil_closesocket(fd);
return NULL;
}
rv = setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (void*)&on, sizeof(on));
if (rv == -1) {
log_err_printf("Error from setsockopt(SO_KEEPALIVE): %s\n",
strerror(errno));
evutil_closesocket(fd);
return NULL;
}
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 NULL;
}
if (spec->natsocket && (spec->natsocket(fd) == -1)) {
log_err_printf("Error from spec->natsocket()\n");
evutil_closesocket(fd);
return NULL;
}
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 NULL;
}
plc = proxy_listener_ctx_new(thrmgr, spec, opts);
if (!plc) {
log_err_printf("Error creating listener context\n");
evutil_closesocket(fd);
return NULL;
}
plc->evcl = evconnlistener_new(evbase, proxy_listener_acceptcb,
plc, LEV_OPT_CLOSE_ON_FREE, 1024, fd);
if (!plc->evcl) {
log_err_printf("Error creating evconnlistener: %s\n",
strerror(errno));
proxy_listener_ctx_free(plc);
evutil_closesocket(fd);
return NULL;
}
evconnlistener_set_error_cb(plc->evcl, proxy_listener_errorcb);
return plc;
}
/*
* Signal handler for SIGQUIT, SIGINT, SIGHUP and SIGPIPE.
*/
static void
proxy_signal_cb(evutil_socket_t fd, UNUSED short what, void *arg)
{
proxy_ctx_t *ctx = arg;
if (ctx->opts->debug) {
log_dbg_printf("Received signal %i\n", fd);
}
if (fd == SIGPIPE) {
log_err_printf("Warning: Received SIGPIPE; ignoring.\n");
} else {
event_base_loopbreak(ctx->evbase);
}
}
/*
* Garbace collection handler.
*/
static void
proxy_gc_cb(UNUSED evutil_socket_t fd, UNUSED short what, void *arg)
{
proxy_ctx_t *ctx = arg;
if (ctx->opts->debug)
log_dbg_printf("Garbage collecting caches started.\n");
cachemgr_gc();
if (ctx->opts->debug)
log_dbg_printf("Garbage collecting caches done.\n");
}
/*
* Set up the core event loop.
* Returns ctx on success, or NULL on error.
*/
proxy_ctx_t *
proxy_new(opts_t *opts)
{
proxy_listener_ctx_t *head;
proxy_ctx_t *ctx;
/* adds locking, only required if accessed from separate threads */
evthread_use_pthreads();
#ifndef PURIFY
if (opts->debug) {
event_enable_debug_mode();
}
#endif /* PURIFY */
ctx = malloc(sizeof(proxy_ctx_t));
if (!ctx) {
log_err_printf("Error allocating memory\n");
goto leave0;
}
memset(ctx, 0, sizeof(proxy_ctx_t));
ctx->opts = opts;
ctx->evbase = event_base_new();
if (!ctx->evbase) {
log_err_printf("Error getting event base\n");
goto leave1;
}
if (opts->debug) {
proxy_debug_base(ctx->evbase);
}
ctx->thrmgr = pxy_thrmgr_new(opts);
if (!ctx->thrmgr) {
log_err_printf("Error creating thread manager\n");
goto leave1b;
}
head = ctx->lctx = NULL;
for (proxyspec_t *spec = opts->spec; spec; spec = spec->next) {
head = proxy_listener_setup(ctx->evbase, ctx->thrmgr,
spec, opts);
if (!head)
goto leave2;
head->next = ctx->lctx;
ctx->lctx = head;
}
for (size_t i = 0; i < (sizeof(signals) / sizeof(int)); i++) {
ctx->sev[i] = evsignal_new(ctx->evbase, signals[i],
proxy_signal_cb, ctx);
if (!ctx->sev[i])
goto leave3;
evsignal_add(ctx->sev[i], NULL);
}
struct timeval gc_delay = {60, 0};
ctx->gcev = event_new(ctx->evbase, -1, EV_PERSIST, proxy_gc_cb, ctx);
if (!ctx->gcev)
goto leave4;
evtimer_add(ctx->gcev, &gc_delay);
return ctx;
leave4:
if (ctx->gcev) {
event_free(ctx->gcev);
}
leave3:
for (size_t i = 0; i < (sizeof(ctx->sev) / sizeof(ctx->sev[0])); i++) {
if (ctx->sev[i]) {
event_free(ctx->sev[i]);
}
}
leave2:
if (ctx->lctx) {
proxy_listener_ctx_free(ctx->lctx);
}
pxy_thrmgr_free(ctx->thrmgr);
leave1b:
event_base_free(ctx->evbase);
leave1:
free(ctx);
leave0:
return NULL;
}
/*
* Run the event loop. Returns when the event loop is cancelled by a signal.
*/
void
proxy_run(proxy_ctx_t *ctx)
{
if (ctx->opts->detach) {
event_reinit(ctx->evbase);
}
#ifndef PURIFY
if (ctx->opts->debug) {
event_base_dump_events(ctx->evbase, stderr);
}
#endif /* PURIFY */
if (ctx->opts->debug) {
log_dbg_printf("Starting main event loop.\n");
}
event_base_dispatch(ctx->evbase);
if (ctx->opts->debug) {
log_dbg_printf("Main event loop stopped.\n");
}
}
/*
* Free the proxy data structures.
*/
void
proxy_free(proxy_ctx_t *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++) {
event_free(ctx->sev[i]);
}
pxy_thrmgr_free(ctx->thrmgr);
event_base_free(ctx->evbase);
free(ctx);
}
/* vim: set noet ft=c: */

@ -0,0 +1,43 @@
/*
* SSLsplit - transparent and scalable SSL/TLS interception
* Copyright (c) 2009-2012, Daniel Roethlisberger <daniel@roe.ch>
* All rights reserved.
* http://www.roe.ch/SSLsplit
*
* 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 unmodified, 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 AUTHOR ``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 AUTHOR 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 PROXY_H
#define PROXY_H
#include "opts.h"
#include "attrib.h"
typedef struct proxy_ctx proxy_ctx_t;
proxy_ctx_t * proxy_new(opts_t *) NONNULL();
void proxy_run(proxy_ctx_t *) NONNULL();
void proxy_free(proxy_ctx_t *) NONNULL();
#endif /* !PROXY_H */
/* vim: set noet ft=c: */

File diff suppressed because it is too large Load Diff

@ -0,0 +1,47 @@
/*
* SSLsplit - transparent and scalable SSL/TLS interception
* Copyright (c) 2009-2012, Daniel Roethlisberger <daniel@roe.ch>
* All rights reserved.
* http://www.roe.ch/SSLsplit
*
* 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 unmodified, 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 AUTHOR ``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 AUTHOR 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 PXYCONN_H
#define PXYCONN_H
#include "opts.h"
#include "attrib.h"
#include "pxythrmgr.h"
#include <sys/types.h>
#include <sys/socket.h>
#include <event2/event.h>
#include <event2/util.h>
void pxy_conn_setup(evutil_socket_t, struct sockaddr *, int,
pxy_thrmgr_ctx_t *, proxyspec_t *, opts_t *) NONNULL();
#endif /* !PXYCONN_H */
/* vim: set noet ft=c: */

@ -0,0 +1,178 @@
/*
* SSLsplit - transparent and scalable SSL/TLS interception
* Copyright (c) 2009-2012, Daniel Roethlisberger <daniel@roe.ch>
* All rights reserved.
* http://www.roe.ch/SSLsplit
*
* 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 unmodified, 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 AUTHOR ``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 AUTHOR 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 "pxysslshut.h"
#include "log.h"
#include "attrib.h"
#include <stdlib.h>
#include <errno.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
/*
* Cleanly shut down an SSL socket. Libevent currently has no support for
* cleanly shutting down an SSL socket so we work around that by using a
* low-level event. This works for recent versions of OpenSSL. OpenSSL
* with the older SSL_shutdown() semantics, not exposing WANT_READ/WRITE
* may or may not work.
*/
/*
* SSL shutdown context.
*/
typedef struct pxy_ssl_shutdown_ctx {
struct event_base *evbase;
struct event *ev;
SSL *ssl;
unsigned int retries;
} pxy_ssl_shutdown_ctx_t;
static pxy_ssl_shutdown_ctx_t *
pxy_ssl_shutdown_ctx_new(struct event_base *evbase, SSL *ssl)
{
pxy_ssl_shutdown_ctx_t *ctx;
ctx = malloc(sizeof(pxy_ssl_shutdown_ctx_t));
if (!ctx)
return NULL;
ctx->evbase = evbase;
ctx->ssl = ssl;
ctx->ev = NULL;
ctx->retries = 0;
return ctx;
}
static void
pxy_ssl_shutdown_ctx_free(pxy_ssl_shutdown_ctx_t *ctx)
{
free(ctx);
}
/*
* The shutdown socket event handler. This is either
* scheduled as a timeout-only event, or as a fd read or
* fd write event, depending on whether SSL_shutdown()
* indicates it needs read or write on the socket.
*/
static void
pxy_ssl_shutdown_cb(evutil_socket_t fd, UNUSED short what, void *arg)
{
pxy_ssl_shutdown_ctx_t *ctx = arg;
struct timeval retry_delay = {0, 100};
SSL_CTX *sslctx;
short want = 0;
int rv, sslerr;
if (ctx->ev) {
event_free(ctx->ev);
ctx->ev = NULL;
}
/*
* Use the new (post-2008) semantics for SSL_shutdown() on a
* non-blocking socket. SSL_shutdown() returns -1 and WANT_READ
* if the other end's close notify was not received yet, and
* WANT_WRITE it could not write our own close notify.
*
* This is a good collection of recent and relevant documents:
* http://bugs.python.org/issue8108
*/
rv = SSL_shutdown(ctx->ssl);
if (rv == 1)
goto complete;
if (rv != -1) {
goto retry;
}
switch ((sslerr = SSL_get_error(ctx->ssl, rv))) {
case SSL_ERROR_WANT_READ:
want = EV_READ;
goto retry;
case SSL_ERROR_WANT_WRITE:
want = EV_WRITE;
goto retry;
case SSL_ERROR_ZERO_RETURN:
goto retry;
case SSL_ERROR_SYSCALL:
goto complete;
default:
log_err_printf("Unhandled SSL_shutdown() "
"error %i. Closing fd.\n", sslerr);
goto complete;
}
goto complete;
retry:
if (ctx->retries++ >= 50) {
log_err_printf("Failed to shutdown SSL connection cleanly: "
"Max retries reached. Closing fd.\n");
goto complete;
}
ctx->ev = event_new(ctx->evbase, fd, want, pxy_ssl_shutdown_cb, ctx);
if (ctx->ev) {
event_add(ctx->ev, want ? NULL : &retry_delay);
return;
}
log_err_printf("Failed to shutdown SSL connection cleanly: "
"Cannot create event. Closing fd.\n");
complete:
sslctx = SSL_get_SSL_CTX(ctx->ssl);
SSL_free(ctx->ssl);
SSL_CTX_free(sslctx);
evutil_closesocket(fd);
pxy_ssl_shutdown_ctx_free(ctx);
}
/*
* Cleanly shutdown an SSL session on file descriptor fd using low-level
* file descriptor readiness events on event base evbase.
* Guarantees that SSL and the corresponding SSL_CTX are freed and the
* socket is closed, eventually, or in the case of fatal errors, immediately.
*/
void
pxy_ssl_shutdown(struct event_base *evbase, SSL *ssl, evutil_socket_t fd)
{
pxy_ssl_shutdown_ctx_t *sslshutctx;
sslshutctx = pxy_ssl_shutdown_ctx_new(evbase, ssl);
if (!sslshutctx) {
SSL_CTX *sslctx = SSL_get_SSL_CTX(ssl);
SSL_free(ssl);
SSL_CTX_free(sslctx);
evutil_closesocket(fd);
return;
}
pxy_ssl_shutdown_cb(fd, 0, sslshutctx);
}
/* vim: set noet ft=c: */

@ -0,0 +1,42 @@
/*
* SSLsplit - transparent and scalable SSL/TLS interception
* Copyright (c) 2009-2012, Daniel Roethlisberger <daniel@roe.ch>
* All rights reserved.
* http://www.roe.ch/SSLsplit
*
* 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 unmodified, 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 AUTHOR ``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 AUTHOR 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 PXYSSLSHUT_H
#define PXYSSLSHUT_H
#include "attrib.h"
#include <openssl/ssl.h>
#include <event2/event.h>
#include <event2/util.h>
void pxy_ssl_shutdown(struct event_base *, SSL *, evutil_socket_t) NONNULL();
#endif /* !PXYSSLSHUT_H */
/* vim: set noet ft=c: */

@ -0,0 +1,236 @@
/*
* SSLsplit - transparent and scalable SSL/TLS interception
* Copyright (c) 2009-2012, Daniel Roethlisberger <daniel@roe.ch>
* All rights reserved.
* http://www.roe.ch/SSLsplit
*
* 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 unmodified, 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 AUTHOR ``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 AUTHOR 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 "pxythrmgr.h"
#include "sys.h"
#include "log.h"
#include <pthread.h>
/*
* Proxy thread manager: manages the connection handling worker threads
* and the per-thread resources (i.e. event bases). The load is shared
* across num_cpu * 2 connection handling threads, using the number of
* currently assigned connections as the sole metric.
*
* The attach and detach functions are thread-safe.
*/
typedef struct pxy_thr_ctx {
pthread_t thr;
size_t load;
struct event_base *evbase;
struct evdns_base *dnsbase;
} pxy_thr_ctx_t;
struct pxy_thrmgr_ctx {
int num_thr;
pxy_thr_ctx_t **thr;
pthread_mutex_t mutex;
};
/*
* Dummy recurring timer event to prevent the event loops from exiting when
* they run out of events.
*/
static void
pxy_thrmgr_timer_cb(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 *
pxy_thrmgr_thr(void *arg)
{
pxy_thr_ctx_t *ctx = arg;
struct timeval timer_delay = {60, 0};
struct event *ev;
ev = event_new(ctx->evbase, -1, EV_PERSIST, pxy_thrmgr_timer_cb, NULL);
if (!ev)
return NULL;
evtimer_add(ev, &timer_delay);
event_base_dispatch(ctx->evbase);
event_free(ev);
return NULL;
}
/*
* Create new thread manager and start threads.
*/
pxy_thrmgr_ctx_t *
pxy_thrmgr_new(UNUSED opts_t *opts)
{
pxy_thrmgr_ctx_t *ctx;
int idx = -1;
if (!(ctx = malloc(sizeof(pxy_thrmgr_ctx_t))))
return NULL;
memset(ctx, 0, sizeof(pxy_thrmgr_ctx_t));
ctx->num_thr = 2 * sys_get_cpu_cores();
if (!(ctx->thr = malloc(ctx->num_thr * sizeof(void*))))
goto leave;
for (idx = 0; idx < ctx->num_thr; idx++) {
if (!(ctx->thr[idx] = malloc(sizeof(pxy_thr_ctx_t))))
goto leave;
ctx->thr[idx]->evbase = event_base_new();
if (!ctx->thr[idx]->evbase)
goto leave;
ctx->thr[idx]->dnsbase = evdns_base_new(
ctx->thr[idx]->evbase, 1);
if (!ctx->thr[idx]->dnsbase)
goto leave;
ctx->thr[idx]->load = 0;
}
for (idx = 0; idx < ctx->num_thr; idx++) {
if (pthread_create(&ctx->thr[idx]->thr, NULL,
pxy_thrmgr_thr, ctx->thr[idx]))
goto leave_thr;
}
log_dbg_printf("Started %d connection handling threads\n",
ctx->num_thr);
pthread_mutex_init(&ctx->mutex, NULL);
return ctx;
leave_thr:
idx--;
while (idx >= 0) {
pthread_cancel(ctx->thr[idx]->thr);
pthread_join(ctx->thr[idx]->thr, NULL);
idx--;
}
idx = ctx->num_thr;
leave:
while (idx >= 0) {
if (ctx->thr[idx]) {
if (ctx->thr[idx]->dnsbase) {
evdns_base_free(ctx->thr[idx]->dnsbase, 0);
}
if (ctx->thr[idx]->evbase) {
event_base_free(ctx->thr[idx]->evbase);
}
free(ctx->thr[idx]);
}
idx--;
}
if (ctx->thr)
free(ctx->thr);
free(ctx);
return NULL;
}
/*
* Destroy the event manager and stop all threads.
*/
void
pxy_thrmgr_free(pxy_thrmgr_ctx_t *ctx)
{
pthread_mutex_destroy(&ctx->mutex);
for (int idx = 0; idx < ctx->num_thr; idx++) {
event_base_loopbreak(ctx->thr[idx]->evbase);
}
for (int idx = 0; idx < ctx->num_thr; idx++) {
pthread_join(ctx->thr[idx]->thr, NULL);
}
for (int idx = 0; idx < ctx->num_thr; idx++) {
evdns_base_free(ctx->thr[idx]->dnsbase, 0);
event_base_free(ctx->thr[idx]->evbase);
free(ctx->thr[idx]);
}
free(ctx->thr);
free(ctx);
}
/*
* Attach a new connection to a thread. Chooses the thread with the fewest
* currently active connections, returns the appropriate event bases.
* Returns the index of the chosen thread (for passing to _detach later).
* This function cannot fail.
*/
int
pxy_thrmgr_attach(pxy_thrmgr_ctx_t *ctx, struct event_base **evbase,
struct evdns_base **dnsbase)
{
int thridx;
size_t minload;
thridx = 0;
pthread_mutex_lock(&ctx->mutex);
minload = ctx->thr[thridx]->load;
#ifdef DEBUG_THREAD
log_dbg_printf("===> Proxy connection handler thread status:\n"
"thr[%d]: %zu\n", thridx, minload);
#endif /* DEBUG_THREAD */
for (int idx = 1; idx < ctx->num_thr; idx++) {
#ifdef DEBUG_THREAD
log_dbg_printf("thr[%d]: %zu\n", idx, ctx->thr[idx]->load);
#endif /* DEBUG_THREAD */
if (minload > ctx->thr[idx]->load) {
minload = ctx->thr[idx]->load;
thridx = idx;
}
}
*evbase = ctx->thr[thridx]->evbase;
*dnsbase = ctx->thr[thridx]->dnsbase;
ctx->thr[thridx]->load++;
pthread_mutex_unlock(&ctx->mutex);
#ifdef DEBUG_THREAD
log_dbg_printf("thridx: %d\n", thridx);
#endif /* DEBUG_THREAD */
return thridx;
}
/*
* Detach a connection from a thread by index.
* This function cannot fail.
*/
void
pxy_thrmgr_detach(pxy_thrmgr_ctx_t *ctx, int thridx)
{
pthread_mutex_lock(&ctx->mutex);
ctx->thr[thridx]->load--;
pthread_mutex_unlock(&ctx->mutex);
}
/* vim: set noet ft=c: */

@ -0,0 +1,52 @@
/*
* SSLsplit - transparent and scalable SSL/TLS interception
* Copyright (c) 2009-2012, Daniel Roethlisberger <daniel@roe.ch>
* All rights reserved.
* http://www.roe.ch/SSLsplit
*
* 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 unmodified, 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 AUTHOR ``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 AUTHOR 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 PXYTHRMGR_H
#define PXYTHRMGR_H
#include "opts.h"
#include "attrib.h"
#include <sys/types.h>
#include <sys/socket.h>
#include <event2/event.h>
#include <event2/dns.h>
typedef struct pxy_thrmgr_ctx pxy_thrmgr_ctx_t;
pxy_thrmgr_ctx_t * pxy_thrmgr_new(opts_t *);
void pxy_thrmgr_free(pxy_thrmgr_ctx_t *);
int pxy_thrmgr_attach(pxy_thrmgr_ctx_t *, struct event_base **,
struct evdns_base **);
void pxy_thrmgr_detach(pxy_thrmgr_ctx_t *, int);
#endif /* !PXYTHRMGR_H */
/* vim: set noet ft=c: */

1565
ssl.c

File diff suppressed because it is too large Load Diff

111
ssl.h

@ -0,0 +1,111 @@
/*
* SSLsplit - transparent and scalable SSL/TLS interception
* Copyright (c) 2009-2012, Daniel Roethlisberger <daniel@roe.ch>
* All rights reserved.
* http://www.roe.ch/SSLsplit
*
* 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 unmodified, 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 AUTHOR ``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 AUTHOR 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 "attrib.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
#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
void ssl_openssl_version(void);
int ssl_init(void);
void ssl_fini(void);
#ifndef OPENSSL_NO_DH
DH * ssl_tmp_dh_callback(SSL *, int, int) NONNULL();
DH * ssl_dh_load(const char *) NONNULL();
void ssl_dh_refcount_inc(DH *) NONNULL();
#endif /* !OPENSSL_NO_DH */
#ifndef OPENSSL_NO_ECDH
#define SSL_EC_KEY_CURVE_DEFAULT "prime256v1"
EC_KEY * ssl_ecdh_by_name(const char *);
#endif /* !OPENSSL_NO_ECDH */
EVP_PKEY * ssl_key_load(const char *) NONNULL();
EVP_PKEY * ssl_key_genrsa(const int);
void ssl_key_refcount_inc(EVP_PKEY *) NONNULL();
#ifndef OPENSSL_NO_TLSEXT
int ssl_x509_v3ext_add(X509V3_CTX *, X509 *, char *, char *) NONNULL();
int ssl_x509_v3ext_copy_by_nid(X509 *, X509 *, int) NONNULL();
#endif /* !OPENSSL_NO_TLSEXT */
int ssl_x509_serial_copyrand(X509 *, X509 *) NONNULL();
X509 * ssl_x509_forge(X509 *, EVP_PKEY *, X509 *, const char *, EVP_PKEY *) \
NONNULL(1,2,3,5) MALLOC;
X509 * ssl_x509_load(const char *) NONNULL() MALLOC;
char * ssl_x509_subject(X509 *) NONNULL() MALLOC;
char * ssl_x509_subject_cn(X509 *, size_t *) NONNULL() MALLOC;
#define SSL_X509_FPRSZ 20
int ssl_x509_fingerprint_sha1(X509 *, unsigned char *) NONNULL();
int ssl_x509_names_match(X509 *, const char *) NONNULL();
char * ssl_x509_names_to_str(X509 *, size_t) NONNULL() MALLOC;
char ** ssl_x509_names(X509 *) NONNULL() MALLOC;
int ssl_x509_is_valid(X509 *) NONNULL();
char * ssl_x509_to_str(X509 *) NONNULL() MALLOC;
char * ssl_x509_to_pem(X509 *) NONNULL() MALLOC;
void ssl_x509_refcount_inc(X509 *) NONNULL();
int ssl_x509chain_load(X509 **, STACK_OF(X509) **, const char *) NONNULL(2,3);
void ssl_x509chain_use(SSL_CTX *, X509 *, STACK_OF(X509) *) NONNULL();
char * ssl_session_to_str(SSL_SESSION *) NONNULL() MALLOC;
int ssl_session_is_valid(SSL_SESSION *) NONNULL();
#ifndef OPENSSL_NO_TLSEXT
char * ssl_tls_clienthello_parse_sni(const unsigned char *, ssize_t *) \
NONNULL() MALLOC;
#endif /* !OPENSSL_NO_TLSEXT */
int ssl_dnsname_match(const char *, size_t, const char *, size_t) NONNULL();
char * ssl_wildcardify(const char *) NONNULL() MALLOC;
#endif /* !SSL_H */
/* vim: set noet ft=c: */

450
ssl.t

@ -0,0 +1,450 @@
/*
* SSLsplit - transparent and scalable SSL/TLS interception
* Copyright (c) 2009-2012, Daniel Roethlisberger <daniel@roe.ch>
* All rights reserved.
* http://www.roe.ch/SSLsplit
*
* 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 unmodified, 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 AUTHOR ``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 AUTHOR 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 <check.h>
#include "ssl.h"
static char wildcard1[] = "*.example.org";
static char wildcard2[] = "www.*.example.org";
static char wildcard3[] = "*.*.org";
static char wildcard4[] = "www*.example.org";
static char wildcard5[] = "*";
static char wildcard6[] = "*.xn--r-1ga.ch";
static char wildcard7[] = "xn--r-1ga*.xn--r-1ga.ch";
static char wildcard8[] = "xn--r-1ga.*.xn--r-1ga.ch";
static char name1[] = "www.example.org";
static char name2[] = "www.example.com";
static char name3[] = "example.org";
static char name4[] = "www.example.org.co.uk";
static char name5[] = "test.www.example.org";
static char name6[] = "www.test.example.org";
static char name7[] = "wwwtest.example.org";
static char name8[] = "ch";
static char name9[] = "www.xn--r-1ga.ch";
static char name10[] = "xn--r-1ga.xn--r-1ga.ch";
static char name11[] = "";
START_TEST(ssl_wildcardify_01)
{
char *wc = ssl_wildcardify(name1);
fail_unless(!strcmp(wc, wildcard1), "mismatch for 'www.example.org'");
free(wc);
}
END_TEST
START_TEST(ssl_wildcardify_02)
{
char *wc = ssl_wildcardify(name8);
fail_unless(!strcmp(wc, wildcard5), "mismatch for 'ch'");
free(wc);
}
END_TEST
START_TEST(ssl_wildcardify_03)
{
char *wc = ssl_wildcardify(name11);
fail_unless(!strcmp(wc, wildcard5), "mismatch for ''");
free(wc);
}
END_TEST
START_TEST(ssl_dnsname_match_01)
{
fail_unless(
ssl_dnsname_match(name1, sizeof(name1) - 1,
name1, sizeof(name1) - 1),
"Hostname does not match itself");
}
END_TEST
START_TEST(ssl_dnsname_match_02)
{
fail_unless(
!ssl_dnsname_match(name1, sizeof(name1) - 1,
name2, sizeof(name2) - 1),
"Hostname matches hostname with different TLD");
}
END_TEST
START_TEST(ssl_dnsname_match_03)
{
fail_unless(
ssl_dnsname_match(wildcard1, sizeof(wildcard1) - 1,
name1, sizeof(name1) - 1),
"Regular wildcard does not match");
}
END_TEST
START_TEST(ssl_dnsname_match_04)
{
fail_unless(
!ssl_dnsname_match(wildcard1, sizeof(wildcard1) - 1,
name2, sizeof(name2) - 1),
"Regular wildcard matches other TLD");
}
END_TEST
START_TEST(ssl_dnsname_match_05)
{
fail_unless(
!ssl_dnsname_match(wildcard1, sizeof(wildcard1) - 1,
name3, sizeof(name3) - 1),
"Regular wildcard matches upper level domain");
}
END_TEST
START_TEST(ssl_dnsname_match_06)
{
fail_unless(
!ssl_dnsname_match(wildcard1, sizeof(wildcard1) - 1,
name4, sizeof(name4) - 1),
"Regular wildcard matches despite added suffix");
}
END_TEST
START_TEST(ssl_dnsname_match_07)
{
fail_unless(
!ssl_dnsname_match(wildcard1, sizeof(wildcard1) - 1,
name5, sizeof(name5) - 1),
"Regular wildcard matches two elements");
}
END_TEST
START_TEST(ssl_dnsname_match_08)
{
fail_unless(
!ssl_dnsname_match(wildcard2, sizeof(wildcard2) - 1,
name6, sizeof(name6) - 1),
"Wildcard matches in non-leftmost element");
}
END_TEST
START_TEST(ssl_dnsname_match_09)
{
fail_unless(
!ssl_dnsname_match(wildcard3, sizeof(wildcard3) - 1,
name5, sizeof(name5) - 1),
"Multiple wildcard matches");
}
END_TEST
START_TEST(ssl_dnsname_match_10)
{
fail_unless(
!ssl_dnsname_match(wildcard4, sizeof(wildcard4) - 1,
name7, sizeof(name7) - 1),
"Partial label wildcard matches");
}
END_TEST
START_TEST(ssl_dnsname_match_11)
{
fail_unless(
!ssl_dnsname_match(wildcard5, sizeof(wildcard5) - 1,
name1, sizeof(name1) - 1),
"Global wildcard * matches fqdn");
}
END_TEST
START_TEST(ssl_dnsname_match_12)
{
fail_unless(
ssl_dnsname_match(wildcard5, sizeof(wildcard5) - 1,
name8, sizeof(name8) - 1),
"Global wildcard * does not match TLD");
}
END_TEST
START_TEST(ssl_dnsname_match_13)
{
fail_unless(
ssl_dnsname_match(wildcard6, sizeof(wildcard6) - 1,
name9, sizeof(name9) - 1),
"IDN wildcard does not match");
}
END_TEST
START_TEST(ssl_dnsname_match_14)
{
fail_unless(
ssl_dnsname_match(wildcard6, sizeof(wildcard6) - 1,
name10, sizeof(name10) - 1),
"IDN wildcard does not match IDN element");
}
END_TEST
START_TEST(ssl_dnsname_match_15)
{
fail_unless(
!ssl_dnsname_match(wildcard7, sizeof(wildcard7) - 1,
name10, sizeof(name10) - 1),
"Illegal IDN wildcard matches");
}
END_TEST
START_TEST(ssl_dnsname_match_16)
{
fail_unless(
!ssl_dnsname_match(wildcard8, sizeof(wildcard8) - 1,
name10, sizeof(name10) - 1),
"Illegal IDN wildcard matches IDN element");
}
END_TEST
#ifndef OPENSSL_NO_TLSEXT
static unsigned char clienthello01[] =
"\x80\x67\x01\x03\x00\x00\x4e\x00\x00\x00\x10\x01\x00\x80\x03\x00"
"\x80\x07\x00\xc0\x06\x00\x40\x02\x00\x80\x04\x00\x80\x00\x00\x39"
"\x00\x00\x38\x00\x00\x35\x00\x00\x33\x00\x00\x32\x00\x00\x04\x00"
"\x00\x05\x00\x00\x2f\x00\x00\x16\x00\x00\x13\x00\xfe\xff\x00\x00"
"\x0a\x00\x00\x15\x00\x00\x12\x00\xfe\xfe\x00\x00\x09\x00\x00\x64"
"\x00\x00\x62\x00\x00\x03\x00\x00\x06\xa8\xb8\x93\xbb\x90\xe9\x2a"
"\xa2\x4d\x6d\xcc\x1c\xe7\x2a\x80\x21";
/* SSL 2.0, no TLS extensions */
static unsigned char clienthello02[] =
"\x16\x03\x00\x00\x73\x01\x00\x00\x6f\x03\x00\x00\x34\x01\x1e\x67"
"\x3a\xfa\xce\xd9\x51\xba\xe4\xfc\x64\x95\x03\x82\x63\x0f\xe3\x39"
"\x6b\xc7\xbd\x2b\xe5\x51\x37\x23\x48\x5b\xfb\x20\xa3\xca\xad\x46"
"\x95\x5d\x64\xbb\x33\xec\xb5\x12\x91\x21\xa3\x50\xd2\xc0\xc5\xf6"
"\x67\xc3\xcc\x9e\xc0\x4a\x71\x1b\x92\xdc\x58\x55\x00\x28\x00\x39"
"\x00\x38\x00\x35\x00\x33\x00\x32\x00\x04\x00\x05\x00\x2f\x00\x16"
"\x00\x13\xfe\xff\x00\x0a\x00\x15\x00\x12\xfe\xfe\x00\x09\x00\x64"
"\x00\x62\x00\x03\x00\x06\x01\x00";
/* SSL 3.0, no TLS extensions */
static unsigned char clienthello03[] =
"\x16\x03\x01\x00\x9b\x01\x00\x00\x97\x03\x01\x4b\x99\x46\xac\x38"
"\x08\xbb\xa7\x1c\x9b\xea\x79\xc5\xd6\x70\x3d\xed\x20\x80\x60\xb4"
"\x7e\xb5\x07\x13\xcf\x9a\x1c\xec\x6f\x64\xe5\x00\x00\x46\xc0\x0a"
"\xc0\x09\xc0\x07\xc0\x08\xc0\x13\xc0\x14\xc0\x11\xc0\x12\xc0\x04"
"\xc0\x05\xc0\x02\xc0\x03\xc0\x0e\xc0\x0f\xc0\x0c\xc0\x0d\x00\x2f"
"\x00\x05\x00\x04\x00\x35\x00\x0a\x00\x09\x00\x03\x00\x08\x00\x06"
"\x00\x32\x00\x33\x00\x38\x00\x39\x00\x16\x00\x15\x00\x14\x00\x13"
"\x00\x12\x00\x11\x01\x00\x00\x28\x00\x00\x00\x12\x00\x10\x00\x00"
"\x0d\x31\x39\x32\x2e\x31\x36\x38\x2e\x31\x30\x30\x2e\x34\x00\x0a"
"\x00\x08\x00\x06\x00\x17\x00\x18\x00\x19\x00\x0b\x00\x02\x01\x00";
/* TLS 1.0, SNI extension with hostname "192.168.100.4";
* Note: IP addresses are not legal values */
static unsigned char clienthello04[] =
"\x16\x03\x01\x00\x6c\x01\x00\x00\x68\x03\x01\x4a\x9d\x49\x75\xb2"
"\x7e\xf9\xbc\xc3\x76\xac\x19\x78\xfb\x6a\xee\x50\x55\x5e\x35\x4c"
"\xca\xf2\x21\x15\xf3\x8a\x2a\xfc\xb5\x35\xed\x00\x00\x28\x00\x39"
"\x00\x38\x00\x35\x00\x16\x00\x13\x00\x0a\x00\x33\x00\x32\x00\x2f"
"\x00\x07\x00\x05\x00\x04\x00\x15\x00\x12\x00\x09\x00\x14\x00\x11"
"\x00\x08\x00\x06\x00\x03\x01\x00\x00\x17\x00\x00\x00\x0f\x00\x0d"
"\x00\x00\x0a\x6b\x61\x6d\x65\x73\x68\x2e\x63\x6f\x6d\x00\x23\x00"
"\x00";
/* TLS 1.0, SNI extension with hostname "kamesh.com" */
static unsigned char clienthello05[] =
"\x16\x03\x03\x01\x7d\x01\x00\x01\x79\x03\x03\x4f\x7f\x27\xd0\x76"
"\x5f\xc1\x3b\xba\x73\xd5\x07\x8b\xd9\x79\xf9\x51\xd4\xce\x7d\x9a"
"\xdb\xdf\xf8\x4e\x95\x86\x38\x61\xdd\x84\x2a\x00\x00\xca\xc0\x30"
"\xc0\x2c\xc0\x28\xc0\x24\xc0\x14\xc0\x0a\xc0\x22\xc0\x21\x00\xa3"
"\x00\x9f\x00\x6b\x00\x6a\x00\x39\x00\x38\x00\x88\x00\x87\xc0\x19"
"\xc0\x20\x00\xa7\x00\x6d\x00\x3a\x00\x89\xc0\x32\xc0\x2e\xc0\x2a"
"\xc0\x26\xc0\x0f\xc0\x05\x00\x9d\x00\x3d\x00\x35\x00\x84\xc0\x12"
"\xc0\x08\xc0\x1c\xc0\x1b\x00\x16\x00\x13\xc0\x17\xc0\x1a\x00\x1b"
"\xc0\x0d\xc0\x03\x00\x0a\xc0\x2f\xc0\x2b\xc0\x27\xc0\x23\xc0\x13"
"\xc0\x09\xc0\x1f\xc0\x1e\x00\xa2\x00\x9e\x00\x67\x00\x40\x00\x33"
"\x00\x32\x00\x9a\x00\x99\x00\x45\x00\x44\xc0\x18\xc0\x1d\x00\xa6"
"\x00\x6c\x00\x34\x00\x9b\x00\x46\xc0\x31\xc0\x2d\xc0\x29\xc0\x25"
"\xc0\x0e\xc0\x04\x00\x9c\x00\x3c\x00\x2f\x00\x96\x00\x41\x00\x07"
"\xc0\x11\xc0\x07\xc0\x16\x00\x18\xc0\x0c\xc0\x02\x00\x05\x00\x04"
"\x00\x15\x00\x12\x00\x1a\x00\x09\x00\x14\x00\x11\x00\x19\x00\x08"
"\x00\x06\x00\x17\x00\x03\x00\xff\x02\x01\x00\x00\x85\x00\x00\x00"
"\x12\x00\x10\x00\x00\x0d\x64\x61\x6e\x69\x65\x6c\x2e\x72\x6f\x65"
"\x2e\x63\x68\x00\x0b\x00\x04\x03\x00\x01\x02\x00\x0a\x00\x34\x00"
"\x32\x00\x0e\x00\x0d\x00\x19\x00\x0b\x00\x0c\x00\x18\x00\x09\x00"
"\x0a\x00\x16\x00\x17\x00\x08\x00\x06\x00\x07\x00\x14\x00\x15\x00"
"\x04\x00\x05\x00\x12\x00\x13\x00\x01\x00\x02\x00\x03\x00\x0f\x00"
"\x10\x00\x11\x00\x23\x00\x00\x00\x0d\x00\x22\x00\x20\x06\x01\x06"
"\x02\x06\x03\x05\x01\x05\x02\x05\x03\x04\x01\x04\x02\x04\x03\x03"
"\x01\x03\x02\x03\x03\x02\x01\x02\x02\x02\x03\x01\x01\x00\x0f\x00"
"\x01\x01";
/* TLS 1.2, SNI extension with hostname "daniel.roe.ch" */
START_TEST(ssl_tls_clienthello_parse_sni_01)
{
ssize_t sz;
char *sni;
sz = sizeof(clienthello01) - 1;
sni = ssl_tls_clienthello_parse_sni(clienthello01, &sz);
fail_unless(sni == NULL, "sni not null but should be");
fail_unless(sz != -1, "size is -1 but should not");
}
END_TEST
START_TEST(ssl_tls_clienthello_parse_sni_02)
{
ssize_t sz;
char *sni;
sz = sizeof(clienthello02) - 1;
sni = ssl_tls_clienthello_parse_sni(clienthello02, &sz);
fail_unless(sni == NULL, "sni not null but should be");
fail_unless(sz != -1, "size is -1 but should not");
}
END_TEST
START_TEST(ssl_tls_clienthello_parse_sni_03)
{
ssize_t sz;
char *sni;
sz = sizeof(clienthello03) - 1;
sni = ssl_tls_clienthello_parse_sni(clienthello03, &sz);
fail_unless(sni && !strcmp(sni, "192.168.100.4"),
"sni not '192.168.100.4' but should be");
fail_unless(sz != -1, "size is -1 but should not");
}
END_TEST
START_TEST(ssl_tls_clienthello_parse_sni_04)
{
ssize_t sz;
char *sni;
sz = sizeof(clienthello04) - 1;
sni = ssl_tls_clienthello_parse_sni(clienthello04, &sz);
fail_unless(sni && !strcmp(sni, "kamesh.com"),
"sni not 'kamesh.com' but should be");
fail_unless(sz != -1, "size is -1 but should not");
}
END_TEST
START_TEST(ssl_tls_clienthello_parse_sni_05)
{
ssize_t sz;
char *sni;
for (size_t i = 0; i < sizeof(clienthello04) - 1; i++) {
sz = (ssize_t)i;
sni = ssl_tls_clienthello_parse_sni(clienthello04, &sz);
fail_unless(sni == NULL, "sni not null but should be");
fail_unless(sz == -1, "size is not -1 but should be");
}
}
END_TEST
START_TEST(ssl_tls_clienthello_parse_sni_06)
{
ssize_t sz;
char *sni;
sz = sizeof(clienthello05) - 1;
sni = ssl_tls_clienthello_parse_sni(clienthello05, &sz);
fail_unless(sni && !strcmp(sni, "daniel.roe.ch"),
"sni not 'daniel.roe.ch' but should be");
fail_unless(sz != -1, "size is -1 but should not");
}
END_TEST
START_TEST(ssl_tls_clienthello_parse_sni_07)
{
ssize_t sz;
char *sni;
for (size_t i = 0; i < sizeof(clienthello05) - 1; i++) {
sz = (ssize_t)i;
sni = ssl_tls_clienthello_parse_sni(clienthello05, &sz);
fail_unless(sni == NULL, "sni not null but should be");
fail_unless(sz == -1, "size is not -1 but should be");
}
}
END_TEST
#endif /* !OPENSSL_NO_TLSEXT */
START_TEST(ssl_features_01)
{
int have_threads = 0;
#ifdef OPENSSL_THREADS
have_threads = 1;
#endif /* OPENSSL_THREADS */
fail_unless(have_threads, "!OPENSSL_THREADS: no threading support");
}
END_TEST
Suite *
ssl_suite(void)
{
Suite *s;
TCase *tc;
s = suite_create("ssl");
tc = tcase_create("ssl_wildcardify");
tcase_add_test(tc, ssl_wildcardify_01);
tcase_add_test(tc, ssl_wildcardify_02);
tcase_add_test(tc, ssl_wildcardify_03);
suite_add_tcase(s, tc);
tc = tcase_create("ssl_dnsname_match");
tcase_add_test(tc, ssl_dnsname_match_01);
tcase_add_test(tc, ssl_dnsname_match_02);
tcase_add_test(tc, ssl_dnsname_match_03);
tcase_add_test(tc, ssl_dnsname_match_04);
tcase_add_test(tc, ssl_dnsname_match_05);
tcase_add_test(tc, ssl_dnsname_match_06);
tcase_add_test(tc, ssl_dnsname_match_07);
tcase_add_test(tc, ssl_dnsname_match_08);
tcase_add_test(tc, ssl_dnsname_match_09);
tcase_add_test(tc, ssl_dnsname_match_10);
tcase_add_test(tc, ssl_dnsname_match_11);
tcase_add_test(tc, ssl_dnsname_match_12);
tcase_add_test(tc, ssl_dnsname_match_13);
tcase_add_test(tc, ssl_dnsname_match_14);
tcase_add_test(tc, ssl_dnsname_match_15);
tcase_add_test(tc, ssl_dnsname_match_16);
suite_add_tcase(s, tc);
#ifndef OPENSSL_NO_TLSEXT
tc = tcase_create("ssl_tls_clienthello_parse_sni");
tcase_add_test(tc, ssl_tls_clienthello_parse_sni_01);
tcase_add_test(tc, ssl_tls_clienthello_parse_sni_02);
tcase_add_test(tc, ssl_tls_clienthello_parse_sni_03);
tcase_add_test(tc, ssl_tls_clienthello_parse_sni_04);
tcase_add_test(tc, ssl_tls_clienthello_parse_sni_05);
tcase_add_test(tc, ssl_tls_clienthello_parse_sni_06);
tcase_add_test(tc, ssl_tls_clienthello_parse_sni_07);
suite_add_tcase(s, tc);
#endif /* !OPENSSL_NO_TLSEXT */
tc = tcase_create("ssl_features");
tcase_add_test(tc, ssl_features_01);
suite_add_tcase(s, tc);
return s;
}
/* vim: set noet ft=c: */

@ -0,0 +1,487 @@
.\" SSLsplit - transparent and scalable SSL/TLS interception
.\" Copyright (c) 2009-2012, Daniel Roethlisberger <daniel@roe.ch>
.\" All rights reserved.
.\" http://www.roe.ch/SSLsplit
.\"
.\" 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 unmodified, 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 AUTHOR ``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 AUTHOR 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.
.\"
.TH SSLSPLIT 1 "1 April 2012"
.SH NAME
sslsplit \-\- transparent and scalable SSL/TLS interception
.SH SYNOPSIS
.na
.B sslsplit
[\fB-PZdDgGseujplLS\fP]
\fB-k\fP \fIpem\fP \fB-c\fP \fIpem\fP [\fB-C\fP \fIpem\fP] \
[\fB-K\fP \fIpem\fP]
\fIspecs\fP
.br
.B sslsplit
[\fB-PZdDgGseujplLS\fP]
\fB-t\fP \fIdir\fP
\fIspecs\fP
.br
.B sslsplit
[\fB-PZdDgGseujplLS\fP]
\fB-t\fP \fIdir\fP
\fB-k\fP \fIpem\fP \fB-c\fP \fIpem\fP [\fB-C\fP \fIpem\fP] \
[\fB-K\fP \fIpem\fP]
\fIspecs\fP
.br
.B sslsplit -E
.br
.B sslsplit -V
.br
.B sslsplit -h
.br
.ad
.SH DESCRIPTION
SSLsplit is a tool for man-in-the-middle attacks against SSL/TLS encrypted
network connections. Connections are transparently intercepted through a
network address translation engine and redirected to SSLsplit. SSLsplit
terminates SSL/TLS and initiates a new SSL/TLS connection to the original
destination address, while logging all data transmitted.
.LP
SSLsplit supports plain TCP, plain SSL, HTTP and HTTPS connections over both
IPv4 and IPv6. For SSL and HTTPS connections, SSLsplit generates and signs
forged X509v3 certificates on-the-fly, based on the original server certificate
subject DN and subjectAltName extension. SSLsplit fully supports Server Name
Indication (SNI) and is able to work with RSA, DSA and ECDSA keys and DHE and
ECDHE cipher suites. SSLsplit can also use existing certificates of which the
private key is available, instead of generating forged ones. SSLsplit supports
NULL-prefix CN certificates.
.LP
SSLsplit supports a number of NAT engines, static forwarding and SNI DNS
lookups to determine the original destination of redirected connections
(see NAT ENGINES and PROXY SPECIFICATIONS below).
.LP
To actually implement an attack, you also need to redirect the traffic to the
system running \fBsslsplit\fP. Your options include running \fBsslsplit\fP on
a legitimate router, ARP spoofing, ND spoofing, DNS poisoning, deploying a
rogue access point (e.g. using hostap mode), physical recabling, malicious VLAN
reconfiguration or route injection, /etc/hosts modification and so on.
SSLsplit does not implement the actual traffic redirection.
.SH OPTIONS
.TP
.B \-c \fIpemfile\fP
Use CA certificate from \fIpemfile\fP to sign certificates forged on-the-fly.
If \fIpemfile\fP also contains the matching CA private key, it is also loaded,
otherwise it must be provided with \fB-k\fP.
If \fB-t\fP is also given, SSLsplit will only forge a certificate if there is
no matching certificate in the provided certificate directory.
.TP
.B \-C \fIpemfile\fP
Use CA certificates from \fIpemfile\fP as extra certificates in the certificate
chain. This is needed if the CA given with \fB-k\fP and \fB-c\fP is a sub-CA,
in which case any intermediate CA certificates and the root CA certificate must
be included in the certificate chain.
.TP
.B \-d
Detach from TTY and run as a daemon, logging error messages to syslog instead
of standard error.
.TP
.B \-D
Run in debug mode, log lots of debugging information to standard error. This
also forces foreground mode and cannot be used with \fB-d\fP.
.TP
.B \-e \fIengine\fP
Use \fIengine\fP as the default NAT engine for \fIspecs\fP without
explicit NAT engine, static destination address or SNI mode.
\fIengine\fP can be any of the NAT engines supported by the system, as
returned by \fB-E\fP.
.TP
.B \-E
List all supported NAT engines available on the system and exit. See
NAT ENGINES for a list of NAT engines currently supported by SSLsplit.
.TP
.B \-g \fIpemfile\fP
Use Diffie-Hellman group parameters from \fIpemfile\fP for Ephemereal
Diffie-Hellman (EDH/DHE) cipher suites. If \fB-g\fP is not given, SSLsplit
first tries to load DH parameters from the key files given by \fB-K\fP and
\fB-k\fP. If no DH parameters are found in the key files, built-in 512 or 1024
bit group parameters are automatically used iff a non-RSA private key is given
with \fB-K\fP.
This is because DSA/DSS private keys can by themselves only be used for signing
and thus require DH to exchange an SSL/TLS session key.
If \fB-g\fP is given, the parameters from the given \fIpemfile\fP will always
be used, even with RSA private keys (within the cipher suites available in
OpenSSL).
The \fB-g\fP option is only available if SSLsplit was built against a version
of OpenSSL which supports Diffie-Hellman cipher suites.
.TP
.B \-G \fIcurve\fP
Use the named \fIcurve\fP for Ephemereal Elliptic Curve Diffie-Hellman (EECDH)
cipher suites. If \fB-G\fP is not given, the curve \fBprime256v1\fP is used
automatically iff a non-RSA private key is given with \fB-K\fP.
This is because ECDSA/ECDSS private keys can by themselves only be used for
signing and thus require ECDH to exchange an SSL/TLS session key.
If \fB-G\fP is given, the named \fIcurve\fP will always be used, even with RSA
private keys (within the cipher suites available in OpenSSL).
The \fB-G\fP option is only available if SSLsplit was built against a version
of OpenSSL which supports Elliptic Curve Diffie-Hellman cipher suites.
.TP
.B \-h
Display help on usage and exit.
.TP
.B \-j \fIjaildir\fP
Change the root directory to \fIjaildir\fP using chroot(2) after opening files.
If \fB-j\fP is not given, SSLsplit will automatically change root directory to
\fI/var/empty\fP if run as root and \fB-S\fP is not used.
.TP
.B \-k \fIpemfile\fP
Use CA private key from \fIpemfile\fP to sign certificates forged on-the-fly.
If \fIpemfile\fP also contains the matching CA certificate, it is also loaded,
otherwise it must be provided with \fB-c\fP.
If \fB-t\fP is also given, SSLsplit will only forge a certificate if there is
no matching certificate in the provided certificate directory.
.TP
.B \-K \fIpemfile\fP
Use private key from \fIpemfile\fP for certificates forged on-the-fly.
If \fB-K\fP is not given, SSLsplit will generate a random 1024-bit RSA key.
.TP
.B \-l \fIlogfile\fP
Log connections to \fIlogfile\fP in a single line per connection format,
including addresses and ports and some HTTP and SSL information, if available.
.TP
.B \-L \fIlogfile\fP
Log connection content to \fIlogfile\fP. The content log will contain a
parsable log format with transmitted data, prepended with headers identifying
the connection and the data length of each logged segment.
.TP
.B \-p \fIpidfile\fP
Write the process ID to \fIpidfile\fP and refuse to run if the \fIpidfile\fP
is already in use by another process.
.TP
.B \-P
Passthrough SSL/TLS connections which cannot be split instead of dropping them.
Connections cannot be split if \fB-c\fP and \fB-k\fP are not given and the
site does not match any certificate loaded using \fB-t\fP, or if the connection
to the original server gives SSL/TLS errors. Specifically, this happens if the
site requests a client certificate. Passthrough with \fB-P\fP results in
uninterrupted service for the clients, while dropping is the more secure
alternative if unmonitored connections must be prevented.
.TP
.B \-s \fIciphers\fP
Use OpenSSL \fIciphers\fP specification for both server and client SSL/TLS
connections. If \fB-s\fP is not given, a cipher list of \fBALL\fP is used.
Normally, SSL/TLS implementations choose the most secure cipher suites, not the
fastest ones. By specifying an appropriate OpenSSL cipher list, the set of
cipher suites can be limited to fast algorithms, or \fBNULL\fP cipher suites
can be added. Not that for connections to be successful, the SSLsplit cipher
suites must include at least one cipher suite supported by both the client and
the server of each connection.
See ciphers(1) for details on how to construct OpenSSL cipher lists.
.TP
.B \-S \fIlogdir\fP
Log connection content to separate log files under \fIlogdir\fP. For each
connection, a log file will be written, which will contain both directions of
data as transmitted. Information about the connection will be contained in
the filename only.
If \fB-S\fP is used with \fB-j\fP, \fIlogdir\fP is relative to \fIjaildir\fP.
If \fB-S\fP is used with \fB-u\fP, \fIlogdir\fP must be writable by \fIuser\fP.
.TP
.B \-t \fIcertdir\fP
Use private key, certificate and certificate chain from PEM files in
\fIcertdir\fP for sites matching the respective common names, instead of
using certificates forged on-the-fly. A single PEM file must contain a
single private key, a single certificate and optionally intermediate and
root CA certificates to use as certificate chain.
If \fB-c\fP and \fB-k\fP are also given, certificates will be forged
on-the-fly for sites matching none of the certificates loaded from
\fIcertdir\fP.
Otherwise, connections matching no certificate will be dropped, or if
\fB-P\fP is given, passed through without splitting SSL/TLS.
.TP
.B \-u
Drop privileges after opening sockets and files by setting the real,
effective and stored user IDs to \fIuser\fP and loading the appropriate
primary and ancillary groups. If \fB-u\fP is not given, SSLsplit will drop
privileges to the stored UID if EUID != UID (setuid bit scenario), or to
\fBnobody\fP if running with full \fBroot\fP privileges (EUID == UID == 0)
and \fB-S\fP is not used.
.TP
.B \-V
Display version and compiled features information and exit.
.TP
.B \-Z
Disable SSL/TLS compression on all connections. This is useful if your
limiting factor is CPU, not network bandwidth.
The \fB-Z\fP option is only available if SSLsplit was built against a version
of OpenSSL which supports disabling compression.
.SH "PROXY SPECIFICATIONS"
Proxy specifications (\fIspecs\fP) consist of the connection type, listen
address and static forward address or address resolution mechanism (NAT engine,
SNI DNS lookup):
.LP
.na
\fBhttps\fP \fIlistenaddr port\fP
[\fInat-engine\fP|\fIfwdaddr port\fP|\fBsni\fP \fIport\fP]
.br
\fBssl\fP \fIlistenaddr port\fP
[\fInat-engine\fP|\fIfwdaddr port\fP|\fBsni\fP \fIport\fP]
.br
\fBhttp\fP \fIlistenaddr port\fP
[\fInat-engine\fP|\fIfwdaddr port\fP]
.br
\fBtcp\fP \fIlistenaddr port\fP
[\fInat-engine\fP|\fIfwdaddr port\fP]
.ad
.TP
.I listenaddr port
IPv4 or IPv6 address and port or service name to listen on. This is the
address and port where the NAT engine should redirect connections to.
.TP
.I nat-engine
NAT engine to query for determining the original destination address and port
of transparently redirected connections.
If no engine is given, the default engine is used, unless overridden with
\fB-e\fP. When using a NAT engine, \fBsslsplit\fP needs to run on the same
system as the NAT rules redirecting the traffic to \fBsslsplit\fP.
See NAT ENGINES for a list of supported NAT engines.
.TP
.I fwdaddr port
Static destination address, IPv4 or IPv6, with port or service name. When this
is used, connections are forwarded to the given server address and port.
.TP
\fBsni\fP \fIport\fP
Use the Server Name Indication (SNI) hostname sent by the client in the
ClientHello SSL/TLS message to determine the IP address of the server to
connect to. This only works for \fBssl\fP and \fBhttps\fP \fIspecs\fP and
needs a port or service name as an argument.
This is the only way to redirect traffic transparently using NAT rules and run
\fBsslsplit\fP on a different system than the NAT engine.
.LP
.SH "NAT ENGINES"
SSLsplit currently supports the following NAT engines:
.TP
.B pf
OpenBSD packet filter (pf), also available on FreeBSD and NetBSD.
Fully supported, including IPv6.
Assuming inbound interface \fBem0\fP:
.LP
.RS
.nf
\fBrdr pass on em0 proto tcp from 2001:db8::/64 to any port 80 \\
-> ::1 port 10080\fP
\fBrdr pass on em0 proto tcp from 2001:db8::/64 to any port 443 \\
-> ::1 port 10443\fP
\fBrdr pass on em0 proto tcp from 192.0.2.0/24 to any port 80 \\
-> 127.0.0.1 port 10080\fP
\fBrdr pass on em0 proto tcp from 192.0.2.0/24 to any port 443 \\
-> 127.0.0.1 port 10443\fP
.fi
.RE
.TP
.B ipfw
FreeBSD IP firewall (IPFW), also available on Mac OS X.
Fully supported on FreeBSD, including IPv6.
Only supports IPv4 on Mac OS X due to the ancient version of IPFW included.
.LP
.RS
.nf
\fBipfw add fwd ::1,10080 tcp from 2001:db8::/64 to any 80\fP
\fBipfw add fwd ::1,10443 tcp from 2001:db8::/64 to any 443\fP
\fBipfw add fwd 127.0.0.1,10080 tcp from 192.0.2.0/24 to any 80\fP
\fBipfw add fwd 127.0.0.1,10443 tcp from 192.0.2.0/24 to any 443\fP
.fi
.RE
.TP
.B ipfilter
IPFilter (ipfilter, ipf), available on many systems, including FreeBSD, NetBSD,
Linux and Solaris.
Only supports IPv4 due to limitations in the SIOCGNATL ioctl(2) interface.
Assuming inbound interface \fBbge0\fP:
.LP
.RS
.nf
\fBrdr bge0 0.0.0.0/0 port 80 -> 127.0.0.1 port 10080\fP
\fBrdr bge0 0.0.0.0/0 port 443 -> 127.0.0.1 port 10443\fP
.fi
.RE
.TP
.B netfilter
Linux netfilter using the iptables REDIRECT target.
Only supports IPv4 due to limitations in the SO_ORIGINAL_DST getsockopt(2)
interface.
.LP
.RS
.nf
\fBiptables -t nat -A PREROUTING -s 192.0.2.0/24 \\
-p tcp --dport 80 \\
-j REDIRECT --to-ports 10080\fP
\fBiptables -t nat -A PREROUTING -s 192.0.2.0/24 \\
-p tcp --dport 443 \\
-j REDIRECT --to-ports 10443\fP
.fi
.RE
.TP
.B tproxy
Linux netfilter using the iptables TPROXY target together with routing
table magic to allow non-local traffic to originate on local sockets.
Fully supported, including IPv6.
.LP
.RS
.nf
\fBip -f inet6 rule add fwmark 1 lookup 100\fP
\fBip -f inet6 route add local default dev lo table 100\fP
\fBip6tables -t mangle -N DIVERT\fP
\fBip6tables -t mangle -A DIVERT -j MARK --set-mark 1\fP
\fBip6tables -t mangle -A DIVERT -j ACCEPT\fP
\fBip6tables -t mangle -A PREROUTING -p tcp -m socket -j DIVERT\fP
\fBip6tables -t mangle -A PREROUTING -s 2001:db8::/64 \\
-p tcp --dport 80 \\
-j TPROXY --tproxy-mark 0x1/0x1 --on-port 10080\fP
\fBip6tables -t mangle -A PREROUTING -s 2001:db8::/64 \\
-p tcp --dport 443 \\
-j TPROXY --tproxy-mark 0x1/0x1 --on-port 10443\fP
\fBip -f inet rule add fwmark 1 lookup 100\fP
\fBip -f inet route add local default dev lo table 100\fP
\fBiptables -t mangle -N DIVERT\fP
\fBiptables -t mangle -A DIVERT -j MARK --set-mark 1\fP
\fBiptables -t mangle -A DIVERT -j ACCEPT\fP
\fBiptables -t mangle -A PREROUTING -p tcp -m socket -j DIVERT\fP
\fBiptables -t mangle -A PREROUTING -s 192.0.2.0/24 \\
-p tcp --dport 80 \\
-j TPROXY --tproxy-mark 0x1/0x1 --on-port 10080\fP
\fBiptables -t mangle -A PREROUTING -s 192.0.2.0/24 \\
-p tcp --dport 443 \\
-j TPROXY --tproxy-mark 0x1/0x1 --on-port 10443\fP
.fi
.LP
Note that return path filtering (rp_filter) also needs to be disabled on
interfaces which handle TPROXY redirected traffic.
.RE
.SH EXAMPLES
Matching the above NAT engine configuration samples, intercept HTTP and HTTPS
over IPv4 and IPv6 using forged certificates with CA private key \fBca.key\fP
and certificate \fBca.crt\fP, logging connections to \fBconnect.log\fP and
connection data into separate files under \fB/tmp\fP (add \fB-e\fP
\fInat-engine\fP to select the appropriate engine if multiple engines are
available on your system):
.LP
.HS
.nf
\fBsslsplit -k ca.key -c ca.crt -l connect.log -L /tmp \\
https ::1 10443 https 127.0.0.1 10443 \\
http ::1 10080 http 127.0.0.1 10080\fP
.fi
.RE
.LP
Intercepting IMAP/IMAPS using the same settings:
.LP
.HS
.nf
\fBsslsplit -k ca.key -c ca.crt -l connect.log -L /tmp \\
ssl ::1 10993 ssl 127.0.0.1 10993 \\
tcp ::1 10143 tcp 127.0.0.1 10143\fP
.fi
.RE
.LP
A more targetted setup, HTTPS only, using certificate/chain/key files from
\fB/path/to/cert.d\fP and statically redirecting to \fBwww.example.org\fP
instead of querying a NAT engine:
.LP
.HS
.nf
\fBsslsplit -t /path/to/cert.d -l connect.log -L /tmp \\
https ::1 10443 www.example.org 443 \\
https 127.0.0.1 10443 www.example.org 443\fP
.fi
.RE
.LP
The original example, but using SSL options optimized for speed by disabling
compression and selecting only fast block cipher cipher suites and using a
precomputed private key \fBleaf.key\fP for the forged certificates
(most significant speed increase is gained by choosing fast algorithms and
small keysizes for the CA and leaf private keys; check \fBopenssl speed\fP for
algorithm performance on your system):
.LP
.HS
.nf
\fBsslsplit -Z -s NULL:RC4:AES128 -K leaf.key \\
-k ca.key -c ca.crt -l connect.log -L /tmp \\
https ::1 10443 https 127.0.0.1 10443 \\
http ::1 10080 http 127.0.0.1 10080\fP
.fi
.RE
.LP
The original example, but running as a daemon under user \fBsslsplit\fP and
writing a PID file:
.LP
.HS
.nf
\fBsslsplit -d -p /var/run/sslsplit.pid -u sslsplit \\
-k ca.key -c ca.crt -l connect.log -L /tmp \\
https ::1 10443 https 127.0.0.1 10443 \\
http ::1 10080 http 127.0.0.1 10080\fP
.fi
.RE
.LP
To generate a CA private key \fBca.key\fP and certificate \fBca.crt\fP using
OpenSSL:
.LP
.HS
.nf
\fBcat >x509v3ca.cnf <<'EOF'\fP
[ req ]
distinguished_name = reqdn
[ reqdn ]
[ v3_ca ]
basicConstraints = CA:TRUE
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer:always
\fBEOF\fP
\fBopenssl genrsa -out ca.key 1024\fP
\fBopenssl req -new -nodes -x509 -sha1 -out ca.crt -key ca.key \\
-config x509v3ca.cnf -extensions v3_ca \\
-subj '/O=SSLsplit Root CA/CN=SSLsplit Root CA/' \\
-set_serial 0 -days 3650\fP
.fi
.SH SCALABILITY
SSLsplit is scalable to a relatively high number of listeners and connections
due to a multithreaded, event based architecture based on libevent, taking
advantage of platform specific select() replacements such as kqueue. The main
thread handles the listeners and signalling, while a number of worker threads
equal to twice the number of CPU cores is used for handling the actual
connections in separate event bases, including the CPU-intensive SSL/TLS
handling.
.LP
Care has been taken to choose scalable data structures for caching certificates
and SSL sessions. Logging is implemented in separate disk writer threads to
ensure that socket event handling threads don't have to block on disk I/O.
SSLsplit uses SSL session caching on both ends to minimize the amount of full
SSL handshakes, but even then, the limiting factor in handling SSL connections
are the actual bignum computations.
.SH "SEE ALSO"
openssl(1), ciphers(1), speed(1),
pf(4), ipfw(8), iptables(8), ip6tables(8), ip(8),
hostapd(8), arpspoof(8), parasite6(8), yersinia(8)
.SH AUTHORS
Daniel Roethlisberger <daniel@roe.ch>
.SH BUGS
Session resumption does not work for SSLv2-only clients. As a workaround,
clients attepmting to resume a session will always be given a new one and thus
require a full handshake on every connection, resulting in degraded performance
with SSLv2 clients. However, SSLv2-only clients should be rare these days.

327
sys.c

@ -0,0 +1,327 @@
/*
* SSLsplit - transparent and scalable SSL/TLS interception
* Copyright (c) 2009-2012, Daniel Roethlisberger <daniel@roe.ch>
* All rights reserved.
* http://www.roe.ch/SSLsplit
*
* 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 unmodified, 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 AUTHOR ``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 AUTHOR 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 "sys.h"
#include "log.h"
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/file.h>
#include <netinet/in.h>
#include <netdb.h>
#include <fcntl.h>
#include <pwd.h>
#include <grp.h>
#include <fts.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#ifndef _SC_NPROCESSORS_ONLN
#include <sys/sysctl.h>
#endif /* !_SC_NPROCESSORS_ONLN */
#include <event2/util.h>
/*
* Permanently drop from root privileges to an unprivileged user account.
* Sets the real, effective and stored user and group ID and the list of
* ancillary groups. This is only safe if the effective user ID is 0.
* If username is unset and the effective uid != uid, drop privs to uid.
* This is to support setuid bit configurations.
* If jaildir is set, also chroot to jaildir after reading system files
* but before dropping privileges.
* Returns 0 on success, -1 on failure.
*/
int
sys_privdrop(const char *username, const char *jaildir)
{
struct passwd *pw = NULL;
int ret = -1;
if (username) {
if (!(pw = getpwnam(username))) {
log_err_printf("Failed to getpwnam user '%s': %s\n",
username, strerror(errno));
goto error;
}
if (initgroups(username, pw->pw_gid) == -1) {
log_err_printf("Failed to initgroups user '%s': %s\n",
username, strerror(errno));
goto error;
}
}
if (jaildir) {
if (chroot(jaildir) == -1) {
log_err_printf("Failed to chroot to '%s': %s\n",
jaildir, strerror(errno));
goto error;
}
if (chdir("/") == -1) {
log_err_printf("Failed to chdir to '/': %s\n",
strerror(errno));
goto error;
}
}
if (username) {
if (setgid(pw->pw_gid) == -1) {
log_err_printf("Failed to setgid to %i: %s\n",
pw->pw_gid, strerror(errno));
goto error;
}
if (setuid(pw->pw_uid) == -1) {
log_err_printf("Failed to setuid to %i: %s\n",
pw->pw_uid, strerror(errno));
goto error;
}
} else if (getuid() != geteuid()) {
if (setuid(getuid()) == -1) {
log_err_printf("Failed to setuid(getuid()): %s\n",
strerror(errno));
goto error;
}
}
ret = 0;
error:
if (pw) {
endpwent();
}
return ret;
}
/*
* Open and lock process ID file fn.
* Returns open file descriptor on success or -1 on errors.
*/
int
sys_pidf_open(const char *fn)
{
int fd;
if ((fd = open(fn, O_RDWR|O_CREAT, 0640)) == -1) {
log_err_printf("Failed to open '%s': %s\n", fn,
strerror(errno));
return -1;
}
if (flock(fd, LOCK_EX|LOCK_NB) == -1) {
log_err_printf("Failed to lock '%s': %s\n", fn,
strerror(errno));
close(fd);
return -1;
}
return fd;
}
/*
* Write process ID to open process ID file descriptor fd.
* Returns 0 on success, -1 on errors.
*/
int
sys_pidf_write(int fd)
{
char pidbuf[4*sizeof(pid_t)];
int rv;
rv = snprintf(pidbuf, sizeof(pidbuf), "%d\n", getpid());
if (rv == -1 || rv >= (int)sizeof(pidbuf))
return -1;
write(fd, pidbuf, strlen(pidbuf));
fsync(fd);
fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC);
return 0;
}
/*
* Close and remove open process ID file before quitting.
*/
void
sys_pidf_close(int fd, const char *fn)
{
unlink(fn);
close(fd);
}
/*
* Parse an ascii host/IP and port tuple into a sockaddr_storage.
* On success, returns address family and fills in addr, addrlen.
* Exit and don't return on errors.
*/
int
sys_sockaddr_parse(struct sockaddr_storage *addr, socklen_t *addrlen,
char *naddr, char *nport, int af, int flags)
{
struct evutil_addrinfo hints;
struct evutil_addrinfo *ai;
int rv;
memset(&hints, 0, sizeof(hints));
hints.ai_family = af;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
hints.ai_flags = EVUTIL_AI_ADDRCONFIG | flags;
rv = evutil_getaddrinfo(naddr, nport, &hints, &ai);
if (rv != 0) {
log_err_printf("Cannot resolve address '%s' port '%s': %s\n",
naddr, nport, gai_strerror(rv));
return -1;
}
memcpy(addr, ai->ai_addr, ai->ai_addrlen);
*addrlen = ai->ai_addrlen;
af = ai->ai_family;
freeaddrinfo(ai);
return af;
}
/*
* Converts an IPv4/IPv6 sockaddr into a printable string representation.
* Returns an allocated buffer which must be freed by caller, or NULL on error.
*/
char *
sys_sockaddr_str(struct sockaddr *addr, socklen_t addrlen)
{
char host[INET6_ADDRSTRLEN], serv[6];
char *buf;
int rv;
size_t bufsz;
bufsz = sizeof(host) + sizeof(serv) + 3;
buf = malloc(bufsz);
if (!buf) {
log_err_printf("Cannot allocate memory\n");
return NULL;
}
rv = getnameinfo(addr, addrlen, host, sizeof(host), serv, sizeof(serv),
NI_NUMERICHOST | NI_NUMERICSERV);
if (rv != 0) {
log_err_printf("Cannot get nameinfo for socket address: %s\n",
gai_strerror(rv));
free(buf);
return NULL;
}
snprintf(buf, bufsz, "[%s]:%s", host, serv);
return buf;
}
/*
* Returns 1 if path points to an existing directory node in the filesystem.
* Returns 0 if path is NULL, does not exist, or points to a file of some kind.
*/
int
sys_isdir(const char *path)
{
struct stat s;
if (stat(path, &s) == -1)
return 0;
if (s.st_mode & S_IFDIR)
return 1;
return 0;
}
/*
* Iterate over all files in a directory hierarchy, calling the callback
* cb for each file, passing the filename and arg as arguments. Files and
* directories beginning with a dot are skipped, symlinks are followed.
*/
int
sys_dir_eachfile(const char *dirname, sys_dir_eachfile_cb_t cb, void *arg)
{
FTS *tree;
FTSENT *node;
char * paths[2];
paths[1] = NULL;
paths[0] = strdup(dirname);
if (!paths[0])
return -1;
tree = fts_open(paths, FTS_NOCHDIR | FTS_LOGICAL, NULL);
if (!tree) {
log_err_printf("Cannot open directory '%s': %s\n",
dirname, strerror(errno));
return -1;
}
while ((node = fts_read(tree))) {
if (node->fts_level > 0 && node->fts_name[0] == '.')
fts_set(tree, node, FTS_SKIP);
else if (node->fts_info & FTS_F) {
cb(node->fts_path, arg);
}
}
if (errno) {
log_err_printf("Error reading directory entry: %s\n",
strerror(errno));
return -1;
}
fts_close(tree);
free(paths[0]);
return 0;
}
/*
* Portably get the number of CPU cores online in the system.
*/
uint32_t
sys_get_cpu_cores(void)
{
#ifdef _SC_NPROCESSORS_ONLN
return sysconf(_SC_NPROCESSORS_ONLN);
#else /* !_SC_NPROCESSORS_ONLN */
int mib[2];
uint32_t n;
size_t len = sizeof(n);
mib[0] = CTL_HW;
mib[1] = HW_AVAILCPU;
sysctl(mib, sizeof(mib)/sizeof(int), &n, &len, NULL, 0);
if (n < 1) {
mib[1] = HW_NCPU;
sysctl(mib, sizeof(mib)/sizeof(int), &n, &len, NULL, 0);
if (n < 1) {
n = 1;
}
}
return n;
#endif /* !_SC_NPROCESSORS_ONLN */
}
/* vim: set noet ft=c: */

57
sys.h

@ -0,0 +1,57 @@
/*
* SSLsplit - transparent and scalable SSL/TLS interception
* Copyright (c) 2009-2012, Daniel Roethlisberger <daniel@roe.ch>
* All rights reserved.
* http://www.roe.ch/SSLsplit
*
* 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 unmodified, 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 AUTHOR ``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 AUTHOR 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 SYS_H
#define SYS_H
#include "attrib.h"
#include <sys/types.h>
#include <sys/socket.h>
#include <stdint.h>
int sys_privdrop(const char *, const char *);
int sys_pidf_open(const char *) NONNULL();
int sys_pidf_write(int);
void sys_pidf_close(int, const char *) NONNULL(2);
int sys_sockaddr_parse(struct sockaddr_storage *, socklen_t *,
char *, char *, int, int) NONNULL(1,2,3,4);
char * sys_sockaddr_str(struct sockaddr *, socklen_t) NONNULL(1);
int sys_isdir(const char *) NONNULL();
typedef void (*sys_dir_eachfile_cb_t)(const char *, void *) NONNULL(1);
int sys_dir_eachfile(const char *, sys_dir_eachfile_cb_t, void *) NONNULL(1,2);
uint32_t sys_get_cpu_cores(void);
#endif /* !SYS_H */
/* vim: set noet ft=c: */

140
sys.t

@ -0,0 +1,140 @@
/*
* SSLsplit - transparent and scalable SSL/TLS interception
* Copyright (c) 2009-2012, Daniel Roethlisberger <daniel@roe.ch>
* All rights reserved.
* http://www.roe.ch/SSLsplit
*
* 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 unmodified, 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 AUTHOR ``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 AUTHOR 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 <check.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/stat.h>
#include "sys.h"
static char basedir[] = "/tmp/" BNAME ".test.XXXXXX";
static char *file, *lfile, *dir, *ldir, *notexist;
static void
sys_isdir_setup(void)
{
if (!mkdtemp(basedir)) {
perror("mkdtemp");
exit(EXIT_FAILURE);
}
asprintf(&file, "%s/file", basedir);
asprintf(&lfile, "%s/lfile", basedir);
asprintf(&dir, "%s/dir", basedir);
asprintf(&ldir, "%s/ldir", basedir);
asprintf(&notexist, "%s/DOES_NOT_EXIST", basedir);
if (!file || !lfile || !dir || !ldir || !notexist) {
perror("asprintf");
exit(EXIT_FAILURE);
}
close(open(file, O_CREAT|O_WRONLY|O_APPEND, 0600));
symlink(file, lfile);
mkdir(dir, 0700);
symlink(dir, ldir);
}
static void
sys_isdir_teardown(void)
{
unlink(lfile);
unlink(file);
unlink(ldir);
rmdir(dir);
rmdir(basedir);
free(lfile);
free(file);
free(ldir);
free(dir);
free(notexist);
}
START_TEST(sys_isdir_01)
{
fail_unless(sys_isdir(dir), "Directory !isdir");
}
END_TEST
START_TEST(sys_isdir_02)
{
fail_unless(sys_isdir(ldir), "Symlink dir !isdir");
}
END_TEST
START_TEST(sys_isdir_03)
{
fail_unless(!sys_isdir(notexist), "Not-exist isdir");
}
END_TEST
START_TEST(sys_isdir_04)
{
fail_unless(!sys_isdir(file), "File isdir");
}
END_TEST
START_TEST(sys_isdir_05)
{
fail_unless(!sys_isdir(lfile), "Symlink file isdir");
}
END_TEST
START_TEST(sys_get_cpu_cores_01)
{
fail_unless(sys_get_cpu_cores() >= 1, "Number of CPU cores < 1");
}
END_TEST
Suite *
sys_suite(void)
{
Suite *s;
TCase *tc;
s = suite_create("sys");
tc = tcase_create("sys_isdir");
tcase_add_unchecked_fixture(tc, sys_isdir_setup, sys_isdir_teardown);
tcase_add_test(tc, sys_isdir_01);
tcase_add_test(tc, sys_isdir_02);
tcase_add_test(tc, sys_isdir_03);
tcase_add_test(tc, sys_isdir_04);
tcase_add_test(tc, sys_isdir_05);
suite_add_tcase(s, tc);
tc = tcase_create("sys_get_cpu_cores");
tcase_add_test(tc, sys_get_cpu_cores_01);
suite_add_tcase(s, tc);
return s;
}
/* vim: set noet ft=c: */

@ -0,0 +1,215 @@
/*
* SSLsplit - transparent and scalable SSL/TLS interception
* Copyright (c) 2009-2012, Daniel Roethlisberger <daniel@roe.ch>
* All rights reserved.
* http://www.roe.ch/SSLsplit
*
* 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 unmodified, 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 AUTHOR ``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 AUTHOR 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 "thrqueue.h"
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
/*
* Threadsafe, bounded-size queue based on pthreads mutex and conds.
* Both enqueue and dequeue are available in a blocking and non-blocking
* version.
*/
struct thrqueue {
void **data;
size_t sz, n;
size_t in, out;
unsigned int block_enqueue : 1;
unsigned int block_dequeue : 1;
pthread_mutex_t mutex;
pthread_cond_t notempty;
pthread_cond_t notfull;
};
/*
* Create a new thread-safe queue of size sz.
*/
thrqueue_t *
thrqueue_new(size_t sz)
{
thrqueue_t *queue;
if (!(queue = malloc(sizeof(thrqueue_t))))
return NULL;
if (!(queue->data = malloc(sz * sizeof(void*)))) {
free(queue);
return NULL;
}
queue->sz = sz;
queue->n = 0;
queue->in = 0;
queue->out = 0;
queue->block_enqueue = 1;
queue->block_dequeue = 1;
pthread_mutex_init(&queue->mutex, NULL);
pthread_cond_init(&queue->notempty, NULL);
pthread_cond_init(&queue->notfull, NULL);
return queue;
}
/*
* Free all resources associated with queue.
* The caller must ensure that there are no threads still
* using the queue when it is free'd.
*/
void
thrqueue_free(thrqueue_t *queue)
{
free(queue->data);
pthread_mutex_destroy(&queue->mutex);
pthread_cond_destroy(&queue->notempty);
pthread_cond_destroy(&queue->notfull);
free(queue);
}
/*
* Enqueue an item into the queue. Will block if the queue is full.
* If enqueue has been switched to non-blocking mode, never blocks
* but instead returns NULL if queue is full.
* Returns enqueued item on success.
*/
void *
thrqueue_enqueue(thrqueue_t *queue, void *item)
{
pthread_mutex_lock(&queue->mutex);
while (queue->n == queue->sz) {
if (!queue->block_enqueue) {
pthread_mutex_unlock(&queue->mutex);
return NULL;
}
pthread_cond_wait(&queue->notfull, &queue->mutex);
}
queue->data[queue->in++] = item;
queue->in %= queue->sz;
queue->n++;
pthread_mutex_unlock(&queue->mutex);
pthread_cond_broadcast(&queue->notempty);
return item;
}
/*
* Non-blocking enqueue. Never blocks.
* Returns NULL if the queue is full.
* Returns the enqueued item on success.
*/
void *
thrqueue_enqueue_nb(thrqueue_t *queue, void *item)
{
pthread_mutex_lock(&queue->mutex);
if (queue->n == queue->sz) {
pthread_mutex_unlock(&queue->mutex);
return NULL;
}
queue->data[queue->in++] = item;
queue->in %= queue->sz;
queue->n++;
pthread_mutex_unlock(&queue->mutex);
pthread_cond_signal(&queue->notempty);
return item;
}
/*
* Dequeue an item from the queue. Will block if the queue is empty.
* If dequeue has been switched to non-blocking mode, never blocks
* but instead returns NULL if queue is empty.
* Returns dequeued item on success.
*/
void *
thrqueue_dequeue(thrqueue_t *queue)
{
void *item;
pthread_mutex_lock(&queue->mutex);
while (queue->n == 0) {
if (!queue->block_dequeue) {
pthread_mutex_unlock(&queue->mutex);
return NULL;
}
pthread_cond_wait(&queue->notempty, &queue->mutex);
}
item = queue->data[queue->out++];
queue->out %= queue->sz;
queue->n--;
pthread_mutex_unlock(&queue->mutex);
pthread_cond_signal(&queue->notfull);
return item;
}
/*
* Non-blocking dequeue. Never blocks.
* Returns NULL if the queue is empty.
* Returns the dequeued item on success.
*/
void *
thrqueue_dequeue_nb(thrqueue_t *queue)
{
void *item;
pthread_mutex_lock(&queue->mutex);
if (queue->n == 0) {
pthread_mutex_unlock(&queue->mutex);
return NULL;
}
item = queue->data[queue->out++];
queue->out %= queue->sz;
queue->n--;
pthread_mutex_unlock(&queue->mutex);
pthread_cond_signal(&queue->notfull);
return item;
}
/*
* Permanently make all enqueue operations on queue non-blocking and wake
* up all threads currently waiting for the queue to become not full.
* This is to allow threads to finish their work on the queue on application
* shutdown, but not be blocked forever.
*/
void
thrqueue_unblock_enqueue(thrqueue_t *queue)
{
queue->block_enqueue = 0;
pthread_cond_broadcast(&queue->notfull);
}
/*
* Permanently make all dequeue operations on queue non-blocking and wake
* up all threads currently waiting for the queue to become not empty.
* This is to allow threads to finish their work on the queue on application
* shutdown, but not be blocked forever.
*/
void
thrqueue_unblock_dequeue(thrqueue_t *queue)
{
queue->block_dequeue = 0;
pthread_cond_broadcast(&queue->notempty);
}
/* vim: set noet ft=c: */

@ -0,0 +1,50 @@
/*
* SSLsplit - transparent and scalable SSL/TLS interception
* Copyright (c) 2009-2012, Daniel Roethlisberger <daniel@roe.ch>
* All rights reserved.
* http://www.roe.ch/SSLsplit
*
* 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 unmodified, 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 AUTHOR ``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 AUTHOR 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 THRQUEUE_H
#define THRQUEUE_H
#include "attrib.h"
#include <unistd.h>
typedef struct thrqueue thrqueue_t;
thrqueue_t * thrqueue_new(size_t) MALLOC;
void thrqueue_free(thrqueue_t *) NONNULL();
void * thrqueue_enqueue(thrqueue_t *, void *) NONNULL(1);
void * thrqueue_enqueue_nb(thrqueue_t *, void *) NONNULL(1);
void * thrqueue_dequeue(thrqueue_t *) NONNULL(1);
void * thrqueue_dequeue_nb(thrqueue_t *) NONNULL(1);
void thrqueue_unblock_enqueue(thrqueue_t *) NONNULL(1);
void thrqueue_unblock_dequeue(thrqueue_t *) NONNULL(1);
#endif /* !THRQUEUE_H */
/* vim: set noet ft=c: */
Loading…
Cancel
Save