mirror of https://github.com/sonertari/SSLproxy
Initial import of sslsplit-0.4.2
commit
4cfdef405a
@ -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: */
|
@ -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 */
|
@ -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: */
|
@ -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: */
|
@ -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: */
|
@ -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: */
|
@ -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: */
|
@ -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: */
|
@ -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: */
|
@ -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: */
|
@ -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: */
|
@ -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: */
|
@ -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.
|
@ -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: */
|
@ -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: */
|
@ -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(¬exist, "%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…
Reference in New Issue