mirror of https://github.com/sonertari/SSLproxy
Add lp listening program and testproxy tests under extra
parent
c54cb627a1
commit
e8f9f216a9
@ -0,0 +1,6 @@
|
||||
all:
|
||||
@gmake $(.TARGETS)
|
||||
|
||||
$(.TARGETS): all
|
||||
|
||||
.PHONY: all
|
@ -0,0 +1,423 @@
|
||||
### Variable overrides
|
||||
|
||||
# You can change many aspects of the build behaviour without modifying this
|
||||
# make file simply by setting environment variables.
|
||||
#
|
||||
# Dependencies and features are auto-detected, but can be overridden:
|
||||
#
|
||||
# LIBEVENT_BASE Prefix of libevent library and headers to build against
|
||||
# PKGCONFIG Name/path of pkg-config program to use for auto-detection
|
||||
# PCFLAGS Additional pkg-config flags
|
||||
# XNU_VERSION Version of included XNU headers to build against (OS X only)
|
||||
# FEATURES Enable optional or force-enable undetected features (see below)
|
||||
#
|
||||
# Where and how to install to:
|
||||
#
|
||||
# PREFIX Prefix to install under (default /usr/local)
|
||||
# DESTDIR Destination root under which prefix is located (default /)
|
||||
# MANDIR Subdir of PREFIX that contains man section dirs
|
||||
# INSTALLUID UID to use for installed files if installing as root
|
||||
# INSTALLGID GID to use for installed files if installing as root
|
||||
#
|
||||
# Standard compiler variables are respected, e.g.:
|
||||
#
|
||||
# CC Compiler, e.g. for cross-compiling, ccache or ccc-analyzer
|
||||
# CFLAGS Additional compiler flags, e.g. optimization flags
|
||||
# CPPFLAGS Additional pre-processor flags
|
||||
# LDFLAGS Additional linker flags
|
||||
# LIBS Additional libraries to link against
|
||||
# SOURCE_DATE_EPOCH Set to epoch time to make the build reproducible
|
||||
#
|
||||
# On macOS, the following build environment variables are respected:
|
||||
#
|
||||
# DEVELOPER_DIR Override Xcode Command Line Developer Tools directory
|
||||
# MACOSX_VERSION_MIN Minimal version of macOS to target, e.g. 10.11
|
||||
# SDK SDK name to build against, e.g. macosx, macosx10.11
|
||||
#
|
||||
# Examples:
|
||||
#
|
||||
# Build against custom installed libraries under /opt:
|
||||
# % LIBEVENT_BASE=/opt/libevent make
|
||||
#
|
||||
# Create a statically linked binary:
|
||||
# % PCFLAGS='--static' CFLAGS='-static' LDFLAGS='-static' make
|
||||
#
|
||||
# Build a macOS binary for El Capitan using the default SDK from Xcode 7.3.1:
|
||||
# % MACOSX_VERSION_MIN=10.11 DEVELOPER_DIR=/Applications/Xcode-7.3.1.app/Contents/Developer make
|
||||
|
||||
|
||||
### Debugging
|
||||
|
||||
# These flags are added to CFLAGS iff building from a git repo.
|
||||
DEBUG_CFLAGS?= -g
|
||||
#DEBUG_CFLAGS+= -Werror
|
||||
|
||||
# Define to remove false positives when debugging memory allocation.
|
||||
#FEATURES+= -DPURIFY
|
||||
|
||||
# Define to add proxy state machine debugging; dump state in debug mode.
|
||||
#FEATURES+= -DDEBUG_PROXY
|
||||
|
||||
# Define to add thread debugging; dump thread state when choosing a thread.
|
||||
#FEATURES+= -DDEBUG_THREAD
|
||||
|
||||
# Define to add privilege separation server event loop debugging.
|
||||
#FEATURES+= -DDEBUG_PRIVSEP_SERVER
|
||||
|
||||
# Define to add diagnostic output for debugging option parsing.
|
||||
#FEATURES+= -DDEBUG_OPTS
|
||||
|
||||
|
||||
### Mac OS X header selection
|
||||
|
||||
# First, try to use the exact XNU version reported by the kernel. If they
|
||||
# are not available, try to look up a suitable XNU version that we have
|
||||
# headers for based on the OS X release reported by sw_vers. Then as a last
|
||||
# resort, fall back to the latest version of XNU that we have headers for,
|
||||
# which may or may not work, depending on if there were API or ABI changes
|
||||
# in the DIOCNATLOOK ioctl interface to the NAT state table in the kernel.
|
||||
#
|
||||
# Note that you can override the XNU headers used by defining XNU_VERSION.
|
||||
|
||||
ifeq ($(shell uname),Darwin)
|
||||
include Mk/xcode.mk
|
||||
ifneq ($(wildcard /usr/include/libproc.h),)
|
||||
FEATURES+= -DHAVE_DARWIN_LIBPROC
|
||||
endif
|
||||
OSX_VERSION= $(shell sw_vers -productVersion)
|
||||
ifneq ($(XNU_VERSION),)
|
||||
XNU_METHOD= override
|
||||
XNU_HAVE= $(shell uname -a|sed 's/^.*root:xnu-//g'|sed 's/~.*$$//')
|
||||
else
|
||||
XNU_METHOD= uname
|
||||
XNU_VERSION= $(shell uname -a|sed 's/^.*root:xnu-//g'|sed 's/~.*$$//')
|
||||
XNU_HAVE:= $(XNU_VERSION)
|
||||
endif
|
||||
ifeq ($(wildcard xnu/xnu-$(XNU_VERSION)),)
|
||||
XNU_METHOD= sw_vers
|
||||
XNU_VERSION= $(shell awk '/^XNU_RELS.*\# $(OSX_VERSION)$$/ {print $$2}' xnu/GNUmakefile)
|
||||
endif
|
||||
ifeq ($(wildcard xnu/xnu-$(XNU_VERSION)),)
|
||||
XNU_METHOD= fallback
|
||||
XNU_VERSION= $(shell awk '/^XNU_RELS/ {print $$2}' xnu/GNUmakefile|tail -1)
|
||||
endif
|
||||
ifneq ($(wildcard xnu/xnu-$(XNU_VERSION)),)
|
||||
FEATURES+= -DHAVE_PF
|
||||
PKG_CPPFLAGS+= -I./xnu/xnu-$(XNU_VERSION)
|
||||
BUILD_INFO+= OSX:$(OSX_VERSION) XNU:$(XNU_VERSION):$(XNU_METHOD):$(XNU_HAVE)
|
||||
endif
|
||||
endif
|
||||
|
||||
|
||||
### Autodetected features
|
||||
|
||||
# Autodetect pf
|
||||
ifneq ($(wildcard /usr/include/net/pfvar.h),)
|
||||
FEATURES+= -DHAVE_PF
|
||||
# OpenBSD 4.7+ and FreeBSD 9.0+ also include ipfw-style divert-to in pf
|
||||
FEATURES+= -DHAVE_IPFW
|
||||
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
|
||||
|
||||
|
||||
### Variables you might need to override
|
||||
|
||||
PREFIX?= /usr/local
|
||||
MANDIR?= share/man
|
||||
EXAMPLESDIR?= share/examples
|
||||
|
||||
INSTALLUID?= 0
|
||||
INSTALLGID?= 0
|
||||
BINUID?= $(INSTALLUID)
|
||||
BINGID?= $(INSTALLGID)
|
||||
BINMODE?= 0755
|
||||
CNFUID?= $(INSTALLUID)
|
||||
CNFGID?= $(INSTALLGID)
|
||||
CNFMODE?= 0644
|
||||
MANUID?= $(INSTALLUID)
|
||||
MANGID?= $(INSTALLGID)
|
||||
MANMODE?= 0644
|
||||
EXAMPLESMODE?= 0444
|
||||
ifeq ($(shell id -u),0)
|
||||
BINOWNERFLAGS?= -o $(BINUID) -g $(BINGID)
|
||||
CNFOWNERFLAGS?= -o $(CNFUID) -g $(CNFGID)
|
||||
MANOWNERFLAGS?= -o $(MANUID) -g $(MANGID)
|
||||
else
|
||||
BINOWNERFLAGS?=
|
||||
CNFOWNERFLAGS?=
|
||||
MANOWNERFLAGS?=
|
||||
endif
|
||||
|
||||
PKGCONFIG?= $(shell command -v pkg-config||echo false)
|
||||
ifeq ($(PKGCONFIG),false)
|
||||
$(warning pkg-config not found - guessing paths/flags for dependencies)
|
||||
endif
|
||||
|
||||
BASENAME?= basename
|
||||
CAT?= cat
|
||||
CHECKNR?= checknr
|
||||
CUT?= cut
|
||||
GREP?= grep
|
||||
INSTALL?= install
|
||||
MKDIR?= mkdir
|
||||
SED?= sed
|
||||
SORT?= sort
|
||||
|
||||
|
||||
### Variables only used for developer targets
|
||||
|
||||
GPGSIGNKEY?= 0xE1520675375F5E35
|
||||
|
||||
CPPCHECK?= cppcheck
|
||||
GPG?= gpg
|
||||
GIT?= git
|
||||
WGET?= wget
|
||||
|
||||
BZIP2?= bzip2
|
||||
COL?= col
|
||||
LN?= ln
|
||||
MAN?= man
|
||||
TAR?= tar
|
||||
|
||||
|
||||
### You should not need to touch anything below this line
|
||||
|
||||
PKGLABEL:= Lp
|
||||
PKGNAME:= lp
|
||||
TARGET:= $(PKGNAME)
|
||||
SRCS:= $(filter-out $(wildcard *.t.c),$(wildcard *.c))
|
||||
HDRS:= $(wildcard *.h)
|
||||
OBJS:= $(SRCS:.c=.o)
|
||||
MKFS= $(wildcard GNUmakefile Mk/*.mk)
|
||||
FEATURES:= $(sort $(FEATURES))
|
||||
|
||||
include Mk/buildinfo.mk
|
||||
VERSION:= $(BUILD_VERSION)
|
||||
ifdef GITDIR
|
||||
CFLAGS+= $(DEBUG_CFLAGS)
|
||||
endif
|
||||
|
||||
# Autodetect dependencies known to pkg-config
|
||||
PKGS:=
|
||||
ifndef LIBEVENT_BASE
|
||||
PKGS+= $(shell $(PKGCONFIG) $(PCFLAGS) --exists libevent \
|
||||
&& echo libevent)
|
||||
PKGS+= $(shell $(PKGCONFIG) $(PCFLAGS) --exists libevent_pthreads \
|
||||
&& echo libevent_pthreads)
|
||||
endif
|
||||
|
||||
# Function: Generate list of base paths to search when locating packages
|
||||
# $1 packagename
|
||||
bases= /usr/local/opt/$(1) \
|
||||
/opt/local \
|
||||
/usr/local \
|
||||
/usr
|
||||
|
||||
# Function: Locate base path for a package we depend on
|
||||
# $1 packagename, $2 pattern suffix, $3 override path(s)
|
||||
locate= $(subst /$(2),,$(word 1,$(wildcard \
|
||||
$(addsuffix /$(2),$(if $(3),$(3),$(call bases,$(1)))))))
|
||||
|
||||
# Autodetect dependencies not known to pkg-config
|
||||
ifeq (,$(filter libevent,$(PKGS)))
|
||||
LIBEVENT_FOUND:=$(call locate,libevent,include/event2/event.h,$(LIBEVENT_BASE))
|
||||
ifndef LIBEVENT_FOUND
|
||||
$(error dependency 'libevent 2.x' not found; \
|
||||
install it or point LIBEVENT_BASE to base path)
|
||||
endif
|
||||
endif
|
||||
|
||||
ifdef LIBEVENT_FOUND
|
||||
PKG_CPPFLAGS+= -I$(LIBEVENT_FOUND)/include
|
||||
PKG_LDFLAGS+= -L$(LIBEVENT_FOUND)/lib
|
||||
PKG_LIBS+= -levent
|
||||
endif
|
||||
ifeq (,$(filter libevent_pthreads,$(PKGS)))
|
||||
PKG_LIBS+= -levent_pthreads
|
||||
endif
|
||||
|
||||
ifneq (,$(strip $(PKGS)))
|
||||
PKG_CFLAGS+= $(shell $(PKGCONFIG) $(PCFLAGS) --cflags-only-other $(PKGS))
|
||||
PKG_CPPFLAGS+= $(shell $(PKGCONFIG) $(PCFLAGS) --cflags-only-I $(PKGS))
|
||||
PKG_LDFLAGS+= $(shell $(PKGCONFIG) $(PCFLAGS) --libs-only-L \
|
||||
--libs-only-other $(PKGS))
|
||||
PKG_LIBS+= $(shell $(PKGCONFIG) $(PCFLAGS) --libs-only-l $(PKGS))
|
||||
endif
|
||||
|
||||
CPPDEFS+= -D_GNU_SOURCE \
|
||||
-D"PKGLABEL=\"$(PKGLABEL)\""
|
||||
CPPCHECKFLAGS+= $(CPPDEFS)
|
||||
|
||||
ifneq (ccc-analyzer,$(notdir $(CC)))
|
||||
PKG_CPPFLAGS:= $(subst -I,-isystem,$(PKG_CPPFLAGS))
|
||||
endif
|
||||
|
||||
CFLAGS+= $(PKG_CFLAGS) \
|
||||
-std=c99 -Wall -Wextra -pedantic \
|
||||
-D_FORTIFY_SOURCE=2 -fstack-protector-all
|
||||
CPPFLAGS+= $(PKG_CPPFLAGS) $(CPPDEFS) $(FEATURES)
|
||||
LDFLAGS+= $(PKG_LDFLAGS)
|
||||
LIBS+= $(PKG_LIBS)
|
||||
|
||||
ifneq ($(shell uname),Darwin)
|
||||
CFLAGS+= -pthread
|
||||
LDFLAGS+= -pthread
|
||||
endif
|
||||
|
||||
# _FORTIFY_SOURCE requires -O on Linux
|
||||
ifeq (,$(findstring -O,$(CFLAGS)))
|
||||
CFLAGS+= -O2
|
||||
endif
|
||||
|
||||
export VERSION
|
||||
export MKDIR
|
||||
export WGET
|
||||
|
||||
ifndef MAKE_RESTARTS
|
||||
$(info ------------------------------------------------------------------------------)
|
||||
$(info $(PKGLABEL) $(VERSION))
|
||||
$(info ------------------------------------------------------------------------------)
|
||||
$(info Report bugs at https://github.com/sonertari/SSLproxy/issues/new)
|
||||
$(info Please supply this header for diagnostics when reporting build issues)
|
||||
$(info Before reporting bugs, make sure to try the latest develop branch first:)
|
||||
$(info % git clone -b develop https://github.com/sonertari/SSLproxy.git)
|
||||
$(info ------------------------------------------------------------------------------)
|
||||
$(info Via pkg-config: $(strip $(PKGS)))
|
||||
ifdef LIBEVENT_FOUND
|
||||
$(info LIBEVENT_BASE: $(strip $(LIBEVENT_FOUND)))
|
||||
endif
|
||||
$(info Build options: $(FEATURES))
|
||||
$(info Build info: $(BUILD_INFO))
|
||||
ifeq ($(shell uname),Darwin)
|
||||
$(info OSX_VERSION: $(OSX_VERSION))
|
||||
$(info XNU_VERSION: $(XNU_VERSION) ($(XNU_METHOD), have $(XNU_HAVE)))
|
||||
endif
|
||||
$(info uname -a: $(shell uname -a))
|
||||
$(info ------------------------------------------------------------------------------)
|
||||
endif
|
||||
|
||||
all: $(TARGET)
|
||||
|
||||
$(TARGET): $(OBJS)
|
||||
$(CC) $(LDFLAGS) -o $@ $^ $(LIBS)
|
||||
|
||||
build.o: CPPFLAGS+=$(BUILD_CPPFLAGS)
|
||||
build.o: build.c FORCE
|
||||
|
||||
%.o: %.c $(HDRS) $(MKFS)
|
||||
$(CC) -c $(CPPFLAGS) $(CFLAGS) -o $@ $<
|
||||
|
||||
clean:
|
||||
$(RM) -f $(TARGET) *.o .*.o *.core *~
|
||||
$(RM) -rf *.dSYM
|
||||
|
||||
install: $(TARGET)
|
||||
test -d $(DESTDIR)$(PREFIX)/bin || $(MKDIR) -p $(DESTDIR)$(PREFIX)/bin
|
||||
test -d $(DESTDIR)$(PREFIX)/$(MANDIR)/man1 || \
|
||||
$(MKDIR) -p $(DESTDIR)$(PREFIX)/$(MANDIR)/man1
|
||||
test -d $(DESTDIR)$(PREFIX)/$(MANDIR)/man5 || \
|
||||
$(MKDIR) -p $(DESTDIR)$(PREFIX)/$(MANDIR)/man5
|
||||
test -d $(DESTDIR)$(PREFIX)/$(EXAMPLESDIR)/$(TARGET) || \
|
||||
$(MKDIR) -p $(DESTDIR)$(PREFIX)/$(EXAMPLESDIR)/$(TARGET)
|
||||
$(INSTALL) $(BINOWNERFLAGS) -m $(BINMODE) \
|
||||
$(TARGET) $(DESTDIR)$(PREFIX)/bin/
|
||||
$(INSTALL) $(MANOWNERFLAGS) -m $(MANMODE) \
|
||||
$(TARGET).1 $(DESTDIR)$(PREFIX)/$(MANDIR)/man1/
|
||||
$(INSTALL) $(MANOWNERFLAGS) -m $(MANMODE) \
|
||||
$(TARGET).conf.5 $(DESTDIR)$(PREFIX)/$(MANDIR)/man5/
|
||||
$(INSTALL) $(MANOWNERFLAGS) -m $(EXAMPLESMODE) \
|
||||
$(TARGET).conf $(DESTDIR)$(PREFIX)/$(EXAMPLESDIR)/$(TARGET)/
|
||||
|
||||
deinstall:
|
||||
$(RM) -f $(DESTDIR)$(PREFIX)/bin/$(TARGET) $(DESTDIR)$(PREFIX)/$(MANDIR)/man1/$(TARGET).1 \
|
||||
$(DESTDIR)$(PREFIX)/$(MANDIR)/man5/$(TARGET).conf.5
|
||||
$(RM) -rf $(DESTDIR)$(PREFIX)/$(EXAMPLESDIR)/$(TARGET)/
|
||||
|
||||
ifdef GITDIR
|
||||
lint:
|
||||
$(CPPCHECK) $(CPPCHECKFLAGS) --force --enable=all --error-exitcode=1 .
|
||||
|
||||
manlint: $(TARGET).1 $(TARGET).conf.5
|
||||
$(CHECKNR) $(TARGET).1
|
||||
|
||||
mantest: $(TARGET).1 $(TARGET).conf.5
|
||||
$(RM) -f man1 man5
|
||||
$(LN) -sf . man1
|
||||
$(LN) -sf . man5
|
||||
$(MAN) -M . 1 $(TARGET)
|
||||
$(MAN) -M . 5 $(TARGET).conf
|
||||
$(RM) man1 man5
|
||||
|
||||
copyright: *.c *.h *.1 *.5
|
||||
Mk/bin/copyright.py $^
|
||||
|
||||
$(PKGNAME)-$(VERSION).1.txt: $(TARGET).1
|
||||
$(RM) -f man1
|
||||
$(LN) -sf . man1
|
||||
$(MAN) -M . 1 $(TARGET) | $(COL) -b >$@
|
||||
$(RM) man1
|
||||
|
||||
$(PKGNAME)-$(VERSION).conf.5.txt: $(TARGET).conf.5
|
||||
$(RM) -f man5
|
||||
$(LN) -sf . man5
|
||||
$(MAN) -M . 5 $(TARGET).conf | $(COL) -b >$@
|
||||
$(RM) man5
|
||||
|
||||
man: $(PKGNAME)-$(VERSION).1.txt $(PKGNAME)-$(VERSION).conf.5.txt
|
||||
|
||||
manclean:
|
||||
$(RM) -f $(PKGNAME)-*.1.txt $(PKGNAME)-*.conf.5.txt
|
||||
|
||||
fetchdeps:
|
||||
$(MAKE) -C xnu fetch
|
||||
|
||||
dist: $(PKGNAME)-$(VERSION).tar.bz2 $(PKGNAME)-$(VERSION).tar.bz2.asc
|
||||
|
||||
%.asc: %
|
||||
$(GPG) -u $(GPGSIGNKEY) --armor --output $@ --detach-sig $<
|
||||
|
||||
$(PKGNAME)-$(VERSION).tar.bz2:
|
||||
$(MKDIR) -p $(PKGNAME)-$(VERSION)
|
||||
echo $(VERSION) >$(PKGNAME)-$(VERSION)/VERSION
|
||||
$(OPENSSL) dgst -sha1 -r *.[hc] | $(SORT) -k 2 \
|
||||
>$(PKGNAME)-$(VERSION)/HASHES
|
||||
$(GIT) archive --prefix=$(PKGNAME)-$(VERSION)/ HEAD \
|
||||
>$(PKGNAME)-$(VERSION).tar
|
||||
$(TAR) -f $(PKGNAME)-$(VERSION).tar -r $(PKGNAME)-$(VERSION)/VERSION
|
||||
$(TAR) -f $(PKGNAME)-$(VERSION).tar -r $(PKGNAME)-$(VERSION)/HASHES
|
||||
$(BZIP2) <$(PKGNAME)-$(VERSION).tar >$(PKGNAME)-$(VERSION).tar.bz2
|
||||
$(RM) $(PKGNAME)-$(VERSION).tar
|
||||
$(RM) -r $(PKGNAME)-$(VERSION)
|
||||
|
||||
disttest: $(PKGNAME)-$(VERSION).tar.bz2 $(PKGNAME)-$(VERSION).tar.bz2.asc
|
||||
$(GPG) --verify $<.asc $<
|
||||
$(BZIP2) -d < $< | $(TAR) -x -f -
|
||||
cd $(PKGNAME)-$(VERSION) && $(MAKE) && $(MAKE) test && ./$(TARGET) -V
|
||||
$(RM) -r $(PKGNAME)-$(VERSION)
|
||||
|
||||
distclean:
|
||||
$(RM) -f $(PKGNAME)-*.tar.bz2*
|
||||
|
||||
realclean: distclean manclean clean
|
||||
endif
|
||||
|
||||
FORCE:
|
||||
|
||||
.PHONY: all config clean buildtest travis lint \
|
||||
install deinstall copyright manlint mantest man manclean fetchdeps \
|
||||
dist disttest distclean realclean
|
||||
|
@ -0,0 +1,85 @@
|
||||
# in: PKGNAME
|
||||
# in: FEATURES (optional)
|
||||
# in: BUILD_INFO (optional)
|
||||
# in: OPENSSL (optional)
|
||||
# in: OPENSSL_FOUND (optional)
|
||||
# in: SOURCE_DATE_EPOCH (optional)
|
||||
|
||||
ifndef PKGNAME
|
||||
$(error PKGNAME not defined)
|
||||
endif
|
||||
|
||||
ifndef OPENSSL
|
||||
ifdef OPENSSL_FOUND
|
||||
OPENSSL= $(OPENSSL_FOUND)/bin/openssl
|
||||
else
|
||||
OPENSSL= openssl
|
||||
endif
|
||||
endif
|
||||
|
||||
BASENAME?= basename
|
||||
CUT?= cut
|
||||
DATE?= date
|
||||
DIFF?= diff
|
||||
GIT?= git
|
||||
GREP?= grep
|
||||
RM?= rm
|
||||
SED?= sed
|
||||
SORT?= sort
|
||||
TR?= tr
|
||||
WC?= wc
|
||||
|
||||
GITDIR:= $(wildcard .git)
|
||||
VERSION_FILE:= $(wildcard VERSION)
|
||||
HASHES_FILE:= $(wildcard HASHES)
|
||||
NEWS_FILE:= $(firstword $(wildcard NEWS*))
|
||||
|
||||
ifdef GITDIR
|
||||
BUILD_VERSION:= $(shell $(GIT) describe --tags --dirty --always)
|
||||
BUILD_INFO+= V:GIT
|
||||
else
|
||||
ifdef VERSION_FILE
|
||||
BUILD_VERSION:= $(shell $(CAT) VERSION)
|
||||
BUILD_INFO+= V:FILE
|
||||
else
|
||||
BUILD_VERSION:= $(shell $(BASENAME) $(PWD)|\
|
||||
$(GREP) $(PKGNAME)-|\
|
||||
$(SED) 's/.*$(PKGNAME)-\(.*\)/\1/g')
|
||||
BUILD_INFO+= V:DIR
|
||||
endif
|
||||
ifdef HASHES_FILE
|
||||
BUILD_INFO+= HDIFF:$(shell $(OPENSSL) dgst -sha1 -r *.[hc]|\
|
||||
$(SORT) -k 2 >HASHES~;\
|
||||
$(DIFF) -u HASHES HASHES~|\
|
||||
$(GREP) '^-[^-]'|$(WC) -l|$(TR) -d ' ';\
|
||||
$(RM) HASHES~)
|
||||
endif
|
||||
ifdef NEWS_FILE
|
||||
NEWS_SHA:= $(shell $(OPENSSL) dgst -sha1 -r $(NEWS_FILE) |\
|
||||
$(CUT) -c -7)
|
||||
BUILD_INFO+= N:$(NEWS_SHA)
|
||||
endif
|
||||
endif # GITDIR
|
||||
|
||||
ifdef SOURCE_DATE_EPOCH
|
||||
BUILD_DATE:= $(shell \
|
||||
$(DATE) -u -d "@$(SOURCE_DATE_EPOCH)" "+%Y-%m-%d" 2>/dev/null||\
|
||||
$(DATE) -u -r "$(SOURCE_DATE_EPOCH)" "+%Y-%m-%d" 2>/dev/null||\
|
||||
$(DATE) -u "+%Y-%m-%d")
|
||||
else
|
||||
BUILD_DATE:= $(shell date +%Y-%m-%d)
|
||||
endif
|
||||
BUILD_CPPFLAGS+=-D"BUILD_PKGNAME=\"$(PKGNAME)\"" \
|
||||
-D"BUILD_VERSION=\"$(BUILD_VERSION)\"" \
|
||||
-D"BUILD_DATE=\"$(BUILD_DATE)\"" \
|
||||
-D"BUILD_INFO=\"$(BUILD_INFO)\"" \
|
||||
-D"BUILD_FEATURES=\"$(FEATURES)\""
|
||||
|
||||
# out: NEWS_FILE
|
||||
# out: NEWS_SHA
|
||||
# out: VERSION_FILE
|
||||
# out: GITDIR
|
||||
# out: BUILD_VERSION
|
||||
# out: BUILD_DATE
|
||||
# out: BUILD_INFO
|
||||
# out: BUILD_CPPFLAGS
|
@ -0,0 +1,42 @@
|
||||
# macOS Xcode and SDK selection makefile
|
||||
# Authored 2018, Daniel Roethlisberger
|
||||
# Provided under the Unlicense
|
||||
# https://github.com/droe/example.kext
|
||||
|
||||
# DEVELOPER_DIR override Xcode Command Line Developer Tools directory
|
||||
# MACOSX_VERSION_MIN minimal version of macOS to target, e.g. 10.11
|
||||
# SDK SDK name to build against (e.g. macosx, macosx10.11, ...);
|
||||
# for kernel extensions, use macosx$(MACOSX_VERSION_MIN)
|
||||
|
||||
# target specific macOS min version
|
||||
ifdef MACOSX_VERSION_MIN
|
||||
CFLAGS+= -mmacosx-version-min=$(MACOSX_VERSION_MIN)
|
||||
LDFLAGS+= -mmacosx-version-min=$(MACOSX_VERSION_MIN)
|
||||
endif
|
||||
|
||||
# select specific Xcode
|
||||
ifdef DEVELOPER_DIR
|
||||
ifndef SDK
|
||||
SDK:= macosx
|
||||
endif
|
||||
else
|
||||
DEVELOPER_DIR:= $(shell xcode-select -p)
|
||||
endif
|
||||
|
||||
# activate the selected Xcode and SDK
|
||||
ifdef SDK
|
||||
SDKPATH:= $(shell DEVELOPER_DIR="$(DEVELOPER_DIR)" xcrun -find -sdk $(SDK) --show-sdk-path||echo none)
|
||||
ifeq "$(SDKPATH)" "none"
|
||||
$(error SDK not found)
|
||||
endif
|
||||
CPPFLAGS+= -isysroot $(SDKPATH)
|
||||
LDFLAGS+= -isysroot $(SDKPATH)
|
||||
CC:= $(shell DEVELOPER_DIR="$(DEVELOPER_DIR)" xcrun -find -sdk $(SDK) cc||echo false)
|
||||
CXX:= $(shell DEVELOPER_DIR="$(DEVELOPER_DIR)" xcrun -find -sdk $(SDK) c++||echo false)
|
||||
CODESIGN:= $(shell DEVELOPER_DIR="$(DEVELOPER_DIR)" xcrun -find -sdk $(SDK) codesign||echo false)
|
||||
else
|
||||
CC?= cc
|
||||
CXX?= c++
|
||||
CODESIGN?= codesign
|
||||
endif
|
||||
|
@ -0,0 +1,73 @@
|
||||
/*
|
||||
* SSLsplit - transparent SSL/TLS interception
|
||||
* https://www.roe.ch/SSLsplit
|
||||
*
|
||||
* Copyright (c) 2009-2018, Daniel Roethlisberger <daniel@roe.ch>.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef ATTRIB_H
|
||||
#define ATTRIB_H
|
||||
|
||||
/*
|
||||
* GCC attributes and built-ins for improved compile-time error checking
|
||||
* and performance optimization.
|
||||
*
|
||||
* All of these are fully optional and are automatically disabled on non-GCC
|
||||
* and non-LLVM/clang compilers.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Attributes.
|
||||
* These serve to improve the compiler warnings or optimizations.
|
||||
*/
|
||||
|
||||
#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)) WUNRES
|
||||
#define NONNULL(...) __attribute__((nonnull(__VA_ARGS__)))
|
||||
#define PURE __attribute__((pure))
|
||||
|
||||
/*
|
||||
* Branch prediction macros.
|
||||
* These serve to tell the compiler which of the branches is more likely.
|
||||
*/
|
||||
|
||||
#if !defined(__GNUC__) && !defined(__clang__)
|
||||
#define likely(expr) (expr)
|
||||
#define unlikely(expr) (expr)
|
||||
#else
|
||||
#define likely(expr) __builtin_expect((expr), 1)
|
||||
#define unlikely(expr) __builtin_expect((expr), 0)
|
||||
#endif
|
||||
|
||||
#endif /* !ATTRIB_H */
|
||||
|
||||
/* vim: set noet ft=c: */
|
@ -0,0 +1,41 @@
|
||||
/*-
|
||||
* SSLsplit - transparent SSL/TLS interception
|
||||
* https://www.roe.ch/SSLsplit
|
||||
*
|
||||
* Copyright (c) 2009-2018, Daniel Roethlisberger <daniel@roe.ch>.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "build.h"
|
||||
|
||||
/*
|
||||
* Volatile build-time information which can change between make runs.
|
||||
*/
|
||||
|
||||
const char *build_pkgname = BUILD_PKGNAME;
|
||||
const char *build_version = BUILD_VERSION;
|
||||
const char *build_date = BUILD_DATE;
|
||||
const char *build_info = BUILD_INFO;
|
||||
const char *build_features = BUILD_FEATURES;
|
||||
|
||||
/* vim: set noet ft=c: */
|
@ -0,0 +1,40 @@
|
||||
/*-
|
||||
* SSLsplit - transparent SSL/TLS interception
|
||||
* https://www.roe.ch/SSLsplit
|
||||
*
|
||||
* Copyright (c) 2009-2018, Daniel Roethlisberger <daniel@roe.ch>.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef BUILD_H
|
||||
#define BUILD_H
|
||||
|
||||
extern const char *build_pkgname;
|
||||
extern const char *build_version;
|
||||
extern const char *build_date;
|
||||
extern const char *build_info;
|
||||
extern const char *build_features;
|
||||
|
||||
#endif /* !BUILD_H */
|
||||
|
||||
/* vim: set noet ft=c: */
|
@ -0,0 +1,62 @@
|
||||
/*-
|
||||
* SSLsplit - transparent SSL/TLS interception
|
||||
* https://www.roe.ch/SSLsplit
|
||||
*
|
||||
* Copyright (c) 2009-2018, Daniel Roethlisberger <daniel@roe.ch>.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef DEFAULTS_H
|
||||
#define DEFAULTS_H
|
||||
|
||||
/*
|
||||
* Defaults for convenient tweaking or patching.
|
||||
*/
|
||||
|
||||
/*
|
||||
* User to drop privileges to by default. This user needs to be allowed to
|
||||
* create outbound TCP connections, and in some configurations, perform DNS
|
||||
* resolution.
|
||||
*
|
||||
* Packagers may want to use a specific service user account instead of
|
||||
* overloading nobody with yet another use case. Using nobody for source
|
||||
* builds makes sense because chances are high that it exists. Good practice
|
||||
* is to create a dedicated user for sslsplit.
|
||||
*
|
||||
* Make sure to also patch the manual page if you patch this.
|
||||
*/
|
||||
#define DFLT_DROPUSER "nobody"
|
||||
|
||||
/*
|
||||
* Default file and directory modes for newly created files and directories
|
||||
* created as part of e.g. logging. The default is to use full permissions
|
||||
* subject to the system's umask, as is the default for system utilities.
|
||||
* Use a more restrictive mode for the PID file.
|
||||
*/
|
||||
#define DFLT_DIRMODE 0777
|
||||
#define DFLT_FILEMODE 0666
|
||||
#define DFLT_PIDFMODE 0644
|
||||
|
||||
#endif /* !DEFAULTS_H */
|
||||
|
||||
/* vim: set noet ft=c: */
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,104 @@
|
||||
/*-
|
||||
* SSLsplit - transparent SSL/TLS interception
|
||||
* https://www.roe.ch/SSLsplit
|
||||
*
|
||||
* Copyright (c) 2009-2018, Daniel Roethlisberger <daniel@roe.ch>.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef LOG_H
|
||||
#define LOG_H
|
||||
|
||||
#include "opts.h"
|
||||
#include "proxy.h"
|
||||
#include "logger.h"
|
||||
#include "attrib.h"
|
||||
|
||||
int log_err_printf(const char *, ...) PRINTF(1,2);
|
||||
int log_err_level_printf(int, const char *, ...) PRINTF(2,3);
|
||||
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_level_printf(int, const char *, ...) PRINTF(2,3);
|
||||
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
|
||||
#define LOG_DBG_MODE_FINE 2
|
||||
#define LOG_DBG_MODE_FINER 3
|
||||
#define LOG_DBG_MODE_FINEST 4
|
||||
|
||||
extern logger_t *masterkey_log;
|
||||
#define log_masterkey_printf(fmt, ...) \
|
||||
logger_printf(masterkey_log, NULL, 0, (fmt), __VA_ARGS__)
|
||||
#define log_masterkey_print(s) \
|
||||
logger_print(masterkey_log, NULL, 0, (s))
|
||||
#define log_masterkey_write(buf, sz) \
|
||||
logger_write(masterkey_log, NULL, 0, (buf), (sz))
|
||||
#define log_masterkey_print_free(s) \
|
||||
logger_print_freebuf(masterkey_log, NULL, 0, (s))
|
||||
#define log_masterkey_write_free(buf, sz) \
|
||||
logger_write_freebuf(masterkey_log, NULL, 0, (buf), (sz))
|
||||
|
||||
extern logger_t *connect_log;
|
||||
#define log_connect_printf(fmt, ...) \
|
||||
logger_printf(connect_log, NULL, 0, (fmt), __VA_ARGS__)
|
||||
#define log_connect_print(s) \
|
||||
logger_print(connect_log, NULL, 0, (s))
|
||||
#define log_connect_write(buf, sz) \
|
||||
logger_write(connect_log, NULL, 0, (buf), (sz))
|
||||
#define log_connect_print_free(s) \
|
||||
logger_print_freebuf(connect_log, NULL, 0, (s))
|
||||
#define log_connect_write_free(buf, sz) \
|
||||
logger_write_freebuf(connect_log, NULL, 0, (buf), (sz))
|
||||
|
||||
int log_stats(const char *);
|
||||
int log_conn(const char *);
|
||||
|
||||
typedef struct log_content_ctx log_content_ctx_t;
|
||||
struct log_content_file_ctx;
|
||||
struct log_content_ctx {
|
||||
struct log_content_file_ctx *file;
|
||||
};
|
||||
int log_content_open(log_content_ctx_t *, opts_t *,
|
||||
char *, char *, char *, char *,
|
||||
char *, char *, char *) NONNULL(1,2) WUNRES;
|
||||
int log_content_submit(log_content_ctx_t *, logbuf_t *, int)
|
||||
NONNULL(1,2) WUNRES;
|
||||
int log_content_close(log_content_ctx_t *, int) NONNULL(1) WUNRES;
|
||||
int log_content_split_pathspec(const char *, char **,
|
||||
char **) NONNULL(1,2,3) WUNRES;
|
||||
|
||||
int log_preinit(opts_t *) NONNULL(1) WUNRES;
|
||||
void log_preinit_undo(void);
|
||||
int log_init(opts_t *, proxy_ctx_t *, int[3]) NONNULL(1,2) WUNRES;
|
||||
void log_fini(void);
|
||||
int log_reopen(void) WUNRES;
|
||||
void log_exceptcb(void);
|
||||
|
||||
#endif /* !LOG_H */
|
||||
|
||||
/* vim: set noet ft=c: */
|
@ -0,0 +1,276 @@
|
||||
/*-
|
||||
* SSLsplit - transparent SSL/TLS interception
|
||||
* https://www.roe.ch/SSLsplit
|
||||
*
|
||||
* Copyright (c) 2009-2018, Daniel Roethlisberger <daniel@roe.ch>.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "logbuf.h"
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
|
||||
/*
|
||||
* Dynamic log buffer with zero-copy chaining, generic void * file handle
|
||||
* and ctl for status control flags.
|
||||
* 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, and by
|
||||
* logbuf_new() in case it fails returning NULL.
|
||||
*/
|
||||
logbuf_t *
|
||||
logbuf_new(int level, void *buf, size_t sz, logbuf_t *next)
|
||||
{
|
||||
logbuf_t *lb;
|
||||
|
||||
if (!(lb = malloc(sizeof(logbuf_t)))) {
|
||||
if (buf)
|
||||
free(buf);
|
||||
return NULL;
|
||||
}
|
||||
lb->prio = level;
|
||||
lb->buf = buf;
|
||||
lb->sz = sz;
|
||||
if (next) {
|
||||
lb->fh = next->fh;
|
||||
lb->ctl = next->ctl;
|
||||
lb->next = next;
|
||||
} else {
|
||||
lb->fh = NULL;
|
||||
lb->ctl = 0;
|
||||
lb->next = NULL;
|
||||
}
|
||||
return lb;
|
||||
}
|
||||
|
||||
/*
|
||||
* Create new logbuf, allocating sz bytes into the internal buffer.
|
||||
*/
|
||||
logbuf_t *
|
||||
logbuf_new_alloc(size_t sz, 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;
|
||||
if (next) {
|
||||
lb->fh = next->fh;
|
||||
lb->ctl = next->ctl;
|
||||
lb->next = next;
|
||||
} else {
|
||||
lb->fh = NULL;
|
||||
lb->ctl = 0;
|
||||
lb->next = NULL;
|
||||
}
|
||||
return lb;
|
||||
}
|
||||
|
||||
/*
|
||||
* Create new logbuf, copying buf into a newly allocated internal buffer.
|
||||
*/
|
||||
logbuf_t *
|
||||
logbuf_new_copy(const void *buf, size_t sz, 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;
|
||||
if (next) {
|
||||
lb->fh = next->fh;
|
||||
lb->ctl = next->ctl;
|
||||
lb->next = next;
|
||||
} else {
|
||||
lb->fh = NULL;
|
||||
lb->ctl = 0;
|
||||
lb->next = NULL;
|
||||
}
|
||||
return lb;
|
||||
}
|
||||
|
||||
/*
|
||||
* Create new logbuf using printf.
|
||||
*/
|
||||
logbuf_t *
|
||||
logbuf_new_printf(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 < 0) {
|
||||
free(lb);
|
||||
return NULL;
|
||||
}
|
||||
if (next) {
|
||||
lb->fh = next->fh;
|
||||
lb->ctl = next->ctl;
|
||||
lb->next = next;
|
||||
} else {
|
||||
lb->fh = NULL;
|
||||
lb->ctl = 0;
|
||||
lb->next = NULL;
|
||||
}
|
||||
return lb;
|
||||
}
|
||||
|
||||
/*
|
||||
* Create new logbuf from lb. If combine is set, combine all the buffer
|
||||
* segments into a single contiguous one. Otherwise, copy segment by segment.
|
||||
*/
|
||||
logbuf_t *
|
||||
logbuf_new_deepcopy(logbuf_t *lb, int combine)
|
||||
{
|
||||
logbuf_t *lbnew;
|
||||
unsigned char *p;
|
||||
|
||||
if (!lb)
|
||||
return NULL;
|
||||
|
||||
if (combine) {
|
||||
lbnew = logbuf_new_alloc(logbuf_size(lb), NULL);
|
||||
if (!lbnew)
|
||||
return NULL;
|
||||
lbnew->fh = lb->fh;
|
||||
lbnew->ctl = lb->ctl;
|
||||
p = lbnew->buf;
|
||||
while (lb) {
|
||||
memcpy(p, lb->buf, lb->sz);
|
||||
p += lb->sz;
|
||||
lb = lb->next;
|
||||
}
|
||||
} else {
|
||||
lbnew = logbuf_new_copy(lb->buf, lb->sz, NULL);
|
||||
if (!lbnew)
|
||||
return NULL;
|
||||
lbnew->fh = lb->fh;
|
||||
lbnew->ctl = lb->ctl;
|
||||
lbnew->next = logbuf_new_deepcopy(lb->next, 0);
|
||||
}
|
||||
return lbnew;
|
||||
}
|
||||
|
||||
logbuf_t *
|
||||
logbuf_make_contiguous(logbuf_t *lb) {
|
||||
unsigned char *p;
|
||||
logbuf_t *lbtmp;
|
||||
|
||||
if (!lb)
|
||||
return NULL;
|
||||
if (!lb->next)
|
||||
return lb;
|
||||
p = realloc(lb->buf, logbuf_size(lb));
|
||||
if (!p)
|
||||
return NULL;
|
||||
lb->buf = p;
|
||||
lbtmp = lb;
|
||||
p += lbtmp->sz;
|
||||
while ((lbtmp = lbtmp->next)) {
|
||||
memcpy(p, lbtmp->buf, lbtmp->sz);
|
||||
lb->sz += lbtmp->sz;
|
||||
p += lbtmp->sz;
|
||||
}
|
||||
logbuf_free(lb->next);
|
||||
lb->next = NULL;
|
||||
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 = 0;
|
||||
rv1 = writefunc(lb->prio, lb->fh, lb->ctl, lb->buf, lb->sz);
|
||||
if (lb->buf) {
|
||||
free(lb->buf);
|
||||
}
|
||||
if (lb->next) {
|
||||
if (rv1 == -1) {
|
||||
logbuf_free(lb->next);
|
||||
} else {
|
||||
lb->next->fh = lb->fh;
|
||||
rv2 = logbuf_write_free(lb->next, writefunc);
|
||||
}
|
||||
}
|
||||
free(lb);
|
||||
if (rv1 == -1 || rv2 == -1)
|
||||
return -1;
|
||||
else
|
||||
return rv1 + rv2;
|
||||
}
|
||||
|
||||
/*
|
||||
* Free logbuf 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,71 @@
|
||||
/*-
|
||||
* SSLsplit - transparent SSL/TLS interception
|
||||
* https://www.roe.ch/SSLsplit
|
||||
*
|
||||
* Copyright (c) 2009-2018, Daniel Roethlisberger <daniel@roe.ch>.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef LOGBUF_H
|
||||
#define LOGBUF_H
|
||||
|
||||
#include "attrib.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
typedef struct logbuf {
|
||||
int prio;
|
||||
unsigned char *buf;
|
||||
ssize_t sz;
|
||||
void *fh;
|
||||
unsigned long ctl;
|
||||
struct logbuf *next;
|
||||
} logbuf_t;
|
||||
|
||||
typedef ssize_t (*writefunc_t)(int, void *, unsigned long, const void *, size_t);
|
||||
|
||||
logbuf_t * logbuf_new(int, void *, size_t, logbuf_t *) MALLOC;
|
||||
logbuf_t * logbuf_new_alloc(size_t, logbuf_t *) MALLOC;
|
||||
logbuf_t * logbuf_new_copy(const void *, size_t, logbuf_t *) MALLOC;
|
||||
logbuf_t * logbuf_new_printf(logbuf_t *, const char *, ...) MALLOC PRINTF(2,3);
|
||||
logbuf_t * logbuf_new_deepcopy(logbuf_t *, int) MALLOC;
|
||||
logbuf_t * logbuf_make_contiguous(logbuf_t *) WUNRES;
|
||||
ssize_t logbuf_size(logbuf_t *) NONNULL(1) WUNRES;
|
||||
ssize_t logbuf_write_free(logbuf_t *, writefunc_t) NONNULL(1);
|
||||
void logbuf_free(logbuf_t *) NONNULL(1);
|
||||
|
||||
#define logbuf_ctl_clear(x) (x)->ctl = 0
|
||||
#define logbuf_ctl_set(x, y) (x)->ctl |= (y)
|
||||
#define logbuf_ctl_unset(x, y) (x)->ctl &= ~(y)
|
||||
#define logbuf_ctl_isset(x, y) (!!((x)->ctl & (y)))
|
||||
|
||||
#define LBFLAG_REOPEN (1 << 0) /* logger */
|
||||
#define LBFLAG_OPEN (1 << 1) /* logger */
|
||||
#define LBFLAG_CLOSE (1 << 2) /* logger */
|
||||
#define LBFLAG_IS_REQ (1 << 3) /* pcap/mirror content log */
|
||||
#define LBFLAG_IS_RESP (1 << 4) /* pcap/mirror content log */
|
||||
|
||||
#endif /* !LOGBUF_H */
|
||||
|
||||
/* vim: set noet ft=c: */
|
@ -0,0 +1,361 @@
|
||||
/*
|
||||
* SSLsplit - transparent SSL/TLS interception
|
||||
* https://www.roe.ch/SSLsplit
|
||||
*
|
||||
* Copyright (c) 2009-2018, Daniel Roethlisberger <daniel@roe.ch>.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "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_reopen_func_t reopen;
|
||||
logger_open_func_t open;
|
||||
logger_close_func_t close;
|
||||
logger_prep_func_t prep;
|
||||
logger_write_func_t write;
|
||||
logger_except_func_t except;
|
||||
thrqueue_t *queue;
|
||||
};
|
||||
|
||||
static void
|
||||
logger_clear(logger_t *logger)
|
||||
{
|
||||
memset(logger, 0, sizeof(logger_t));
|
||||
}
|
||||
|
||||
/*
|
||||
* Create new logger with a set of specific function callbacks:
|
||||
*
|
||||
* reopenfunc: handle SIGHUP for the log by reopening all open files across
|
||||
* multiple connections
|
||||
* openfunc: open a new log for a new connection
|
||||
* closefunc: close a log for a connection
|
||||
* writefunc: write a single logbuf to the log
|
||||
* prepfunc: prepare a log buffer before adding it to the logbuffer's queue
|
||||
* exceptfunc: called after failed callback operations
|
||||
*
|
||||
* All callbacks except prepfunc will be executed in the logger's writer
|
||||
* thread, not in the thread calling logger_submit(). Prepfunc will be called
|
||||
* in the thread calling logger_submit().
|
||||
*/
|
||||
logger_t *
|
||||
logger_new(logger_reopen_func_t reopenfunc, logger_open_func_t openfunc,
|
||||
logger_close_func_t closefunc, logger_write_func_t writefunc,
|
||||
logger_prep_func_t prepfunc, logger_except_func_t exceptfunc)
|
||||
{
|
||||
logger_t *logger;
|
||||
|
||||
logger = malloc(sizeof(logger_t));
|
||||
if (!logger)
|
||||
return NULL;
|
||||
logger_clear(logger);
|
||||
logger->reopen = reopenfunc;
|
||||
logger->open = openfunc;
|
||||
logger->close = closefunc;
|
||||
logger->write = writefunc;
|
||||
logger->prep = prepfunc;
|
||||
logger->except = exceptfunc;
|
||||
logger->queue = NULL;
|
||||
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) {
|
||||
if (logger->queue) {
|
||||
thrqueue_free(logger->queue);
|
||||
}
|
||||
free(logger);
|
||||
}
|
||||
|
||||
/*
|
||||
* Submit a buffer to be logged by the logger thread.
|
||||
* Calls the prep callback from within the calling tread before submission.
|
||||
* Buffer guaranteed to be freed after logging completes or on failure.
|
||||
* Returns -1 on error, 0 on success (including logging a NULL logbuf, which
|
||||
* is a no-op).
|
||||
*/
|
||||
int
|
||||
logger_submit(logger_t *logger, void *fh, unsigned long prepflags,
|
||||
logbuf_t *lb)
|
||||
{
|
||||
if (lb) {
|
||||
lb->fh = fh;
|
||||
logbuf_ctl_clear(lb);
|
||||
}
|
||||
if (logger->prep)
|
||||
lb = logger->prep(fh, prepflags, lb);
|
||||
/* If we got passed lb == NULL and prep callback did not replace it
|
||||
* with an actual log buffer, stop here. */
|
||||
if (!lb)
|
||||
return 0;
|
||||
if (thrqueue_enqueue(logger->queue, lb)) {
|
||||
return 0;
|
||||
} else {
|
||||
logbuf_free(lb);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Submit a log reopen event to the logger thread.
|
||||
*/
|
||||
int
|
||||
logger_reopen(logger_t *logger)
|
||||
{
|
||||
logbuf_t *lb;
|
||||
|
||||
if (!logger->reopen)
|
||||
return 0;
|
||||
|
||||
if (!(lb = logbuf_new(0, NULL, 0, NULL)))
|
||||
return -1;
|
||||
logbuf_ctl_set(lb, LBFLAG_REOPEN);
|
||||
return thrqueue_enqueue(logger->queue, lb) ? 0 : -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Submit a file open event to the logger thread.
|
||||
* fh is the file handle; an opaque unique address identifying the new file.
|
||||
* If no open callback is configured, returns successfully.
|
||||
* Returns 0 on success, -1 on failure.
|
||||
*/
|
||||
int
|
||||
logger_open(logger_t *logger, void *fh)
|
||||
{
|
||||
logbuf_t *lb;
|
||||
|
||||
if (!logger->open)
|
||||
return 0;
|
||||
|
||||
if (!(lb = logbuf_new(0, NULL, 0, NULL)))
|
||||
return -1;
|
||||
lb->fh = fh;
|
||||
logbuf_ctl_set(lb, LBFLAG_OPEN);
|
||||
return thrqueue_enqueue(logger->queue, lb) ? 0 : -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Submit a file close event to the logger thread.
|
||||
* If no close callback is configured, returns successfully.
|
||||
* Returns 0 on success, -1 on failure.
|
||||
*/
|
||||
int
|
||||
logger_close(logger_t *logger, void *fh, unsigned long ctl)
|
||||
{
|
||||
logbuf_t *lb;
|
||||
|
||||
if (!logger->close)
|
||||
return 0;
|
||||
|
||||
if (!(lb = logbuf_new(0, NULL, 0, NULL)))
|
||||
return -1;
|
||||
lb->fh = fh;
|
||||
lb->ctl = ctl;
|
||||
logbuf_ctl_set(lb, LBFLAG_CLOSE);
|
||||
return thrqueue_enqueue(logger->queue, lb) ? 0 : -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Logger thread main function.
|
||||
*/
|
||||
static void *
|
||||
logger_thread(void *arg)
|
||||
{
|
||||
logger_t *logger = arg;
|
||||
logbuf_t *lb;
|
||||
int e = 0;
|
||||
|
||||
while ((lb = thrqueue_dequeue(logger->queue))) {
|
||||
if (logbuf_ctl_isset(lb, LBFLAG_REOPEN)) {
|
||||
if (logger->reopen() != 0)
|
||||
e = 1;
|
||||
} else if (logbuf_ctl_isset(lb, LBFLAG_OPEN)) {
|
||||
if (logger->open(lb->fh) != 0)
|
||||
e = 1;
|
||||
} else if (logbuf_ctl_isset(lb, LBFLAG_CLOSE)) {
|
||||
logger->close(lb->fh, lb->ctl);
|
||||
} else {
|
||||
if (logbuf_write_free(lb, logger->write) < 0)
|
||||
e = 1;
|
||||
}
|
||||
|
||||
if (e && logger->except) {
|
||||
logger->except();
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Start the logger's write thread.
|
||||
*/
|
||||
int
|
||||
logger_start(logger_t *logger) {
|
||||
int rv;
|
||||
|
||||
if (logger->queue) {
|
||||
thrqueue_free(logger->queue);
|
||||
}
|
||||
logger->queue = thrqueue_new(1024);
|
||||
|
||||
rv = pthread_create(&logger->thr, NULL, logger_thread, logger);
|
||||
if (rv)
|
||||
return -1;
|
||||
sched_yield();
|
||||
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);
|
||||
sched_yield();
|
||||
}
|
||||
|
||||
/*
|
||||
* 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, void *fh, unsigned long prepflags,
|
||||
const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
logbuf_t *lb;
|
||||
|
||||
if (!(lb = logbuf_new(0, NULL, 0, NULL)))
|
||||
return -1;
|
||||
lb->fh = fh;
|
||||
va_start(ap, fmt);
|
||||
lb->sz = vasprintf((char**)&lb->buf, fmt, ap);
|
||||
va_end(ap);
|
||||
if (lb->sz < 0) {
|
||||
logbuf_free(lb);
|
||||
return -1;
|
||||
}
|
||||
return logger_submit(logger, fh, prepflags, lb);
|
||||
}
|
||||
int
|
||||
logger_write(logger_t *logger, void *fh, unsigned long prepflags,
|
||||
const void *buf, size_t sz)
|
||||
{
|
||||
logbuf_t *lb;
|
||||
|
||||
if (!(lb = logbuf_new_copy(buf, sz, NULL)))
|
||||
return -1;
|
||||
lb->fh = fh;
|
||||
return logger_submit(logger, fh, prepflags, lb);
|
||||
}
|
||||
int
|
||||
logger_print(logger_t *logger, void *fh, unsigned long prepflags,
|
||||
const char *s)
|
||||
{
|
||||
logbuf_t *lb;
|
||||
|
||||
if (!(lb = logbuf_new_copy(s, strlen(s), NULL)))
|
||||
return -1;
|
||||
lb->fh = fh;
|
||||
return logger_submit(logger, fh, prepflags, lb);
|
||||
}
|
||||
int
|
||||
logger_write_freebuf(logger_t *logger, int level, void *fh, unsigned long prepflags,
|
||||
void *buf, size_t sz)
|
||||
{
|
||||
logbuf_t *lb;
|
||||
|
||||
if (!(lb = logbuf_new(level, buf, sz, NULL)))
|
||||
return -1;
|
||||
lb->fh = fh;
|
||||
return logger_submit(logger, fh, prepflags, lb);
|
||||
}
|
||||
int
|
||||
logger_print_freebuf(logger_t *logger, void *fh, unsigned long prepflags,
|
||||
char *s)
|
||||
{
|
||||
logbuf_t *lb;
|
||||
|
||||
if (!(lb = logbuf_new(0, s, strlen(s), NULL)))
|
||||
return -1;
|
||||
lb->fh = fh;
|
||||
return logger_submit(logger, fh, prepflags, lb);
|
||||
}
|
||||
|
||||
/* vim: set noet ft=c: */
|
@ -0,0 +1,74 @@
|
||||
/*-
|
||||
* SSLsplit - transparent SSL/TLS interception
|
||||
* https://www.roe.ch/SSLsplit
|
||||
*
|
||||
* Copyright (c) 2009-2018, Daniel Roethlisberger <daniel@roe.ch>.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef LOGGER_H
|
||||
#define LOGGER_H
|
||||
|
||||
#include "logbuf.h"
|
||||
#include "attrib.h"
|
||||
|
||||
#include <unistd.h>
|
||||
#include <pthread.h>
|
||||
|
||||
typedef int (*logger_reopen_func_t)(void);
|
||||
typedef int (*logger_open_func_t)(void *);
|
||||
typedef void (*logger_close_func_t)(void *, unsigned long);
|
||||
typedef ssize_t (*logger_write_func_t)(int, void *, unsigned long,
|
||||
const void *, size_t);
|
||||
typedef logbuf_t * (*logger_prep_func_t)(void *, unsigned long, logbuf_t *);
|
||||
typedef void (*logger_except_func_t)(void);
|
||||
typedef struct logger logger_t;
|
||||
|
||||
logger_t * logger_new(logger_reopen_func_t, logger_open_func_t,
|
||||
logger_close_func_t, logger_write_func_t,
|
||||
logger_prep_func_t, logger_except_func_t)
|
||||
NONNULL(4,6) MALLOC;
|
||||
void logger_free(logger_t *) NONNULL(1);
|
||||
int logger_start(logger_t *) NONNULL(1) WUNRES;
|
||||
void logger_leave(logger_t *) NONNULL(1);
|
||||
int logger_join(logger_t *) NONNULL(1);
|
||||
int logger_stop(logger_t *) NONNULL(1) WUNRES;
|
||||
int logger_reopen(logger_t *) NONNULL(1) WUNRES;
|
||||
int logger_open(logger_t *, void *) NONNULL(1,2) WUNRES;
|
||||
int logger_close(logger_t *, void *, unsigned long) NONNULL(1,2) WUNRES;
|
||||
int logger_submit(logger_t *, void *, unsigned long,
|
||||
logbuf_t *) NONNULL(1) WUNRES;
|
||||
int logger_printf(logger_t *, void *, unsigned long,
|
||||
const char *, ...) PRINTF(4,5) NONNULL(1,4) WUNRES;
|
||||
int logger_print(logger_t *, void *, unsigned long,
|
||||
const char *) NONNULL(1,4) WUNRES;
|
||||
int logger_write(logger_t *, void *, unsigned long,
|
||||
const void *, size_t) NONNULL(1,4) WUNRES;
|
||||
int logger_print_freebuf(logger_t *, void *, unsigned long,
|
||||
char *) NONNULL(1,4) WUNRES;
|
||||
int logger_write_freebuf(logger_t *, int, void *, unsigned long,
|
||||
void *, size_t) NONNULL(1,5) WUNRES;
|
||||
|
||||
#endif /* !LOGGER_H */
|
||||
|
||||
/* vim: set noet ft=c: */
|
Binary file not shown.
@ -0,0 +1,66 @@
|
||||
# Sample configuration for lp v0.6.0
|
||||
#
|
||||
# Use the -f command line option to start lp with a config file.
|
||||
# See lp.conf(5) and lp(1) for documentation.
|
||||
|
||||
# Drop privileges to user.
|
||||
# Equivalent to -u command line option.
|
||||
# (default: nobody, if run as root)
|
||||
User soner
|
||||
|
||||
# Drop privileges to group.
|
||||
# Equivalent to -m command line option.
|
||||
# (default: primary group of user)
|
||||
Group soner
|
||||
|
||||
# chroot() to jaildir (impacts sni proxyspecs, see lp(1)).
|
||||
# Equivalent to -j command line option.
|
||||
#Chroot /var/run/lp
|
||||
|
||||
# Write pid to file.
|
||||
# Equivalent to -p command line option.
|
||||
# (default: no pid file)
|
||||
PidFile /var/run/lp.pid
|
||||
|
||||
# Connect log: log one line summary per connection to logfile.
|
||||
# Equivalent to -l command line option.
|
||||
#ConnectLog /var/log/lp/connect.log
|
||||
|
||||
# Content log: full data to file or named pipe
|
||||
# (excludes ContentLogDir/ContentLogPathSpec).
|
||||
# Equivalent to -L command line option.
|
||||
#ContentLog /var/log/lp/content.log
|
||||
|
||||
# Content log: full data to separate files in dir
|
||||
# (excludes ContentLog/ContentLogPathSpec).
|
||||
# Equivalent to -S command line option.
|
||||
#ContentLogDir /var/log/lp/content
|
||||
|
||||
# Content log: full data to sep files with % subst
|
||||
# (excludes ContentLog/ContentLogDir).
|
||||
# Equivalent to -F command line option.
|
||||
#ContentLogPathSpec /var/log/lp/%X/%u-%s-%d-%T.log
|
||||
|
||||
# Daemon mode: run in background, log error messages to syslog.
|
||||
# Equivalent to -d command line option.
|
||||
Daemon yes
|
||||
|
||||
# Debug mode: run in foreground, log debug messages on stderr.
|
||||
# Equivalent to -D command line option.
|
||||
#Debug yes
|
||||
|
||||
# Verbose debug level
|
||||
#DebugLevel 4
|
||||
|
||||
# Log statistics to syslog
|
||||
# Equivalent to -J command line option.
|
||||
LogStats yes
|
||||
|
||||
# Log statistics every this many ExpiredConnCheckPeriod periods
|
||||
StatsPeriod 1
|
||||
|
||||
# Set open files limit, use 50-10000
|
||||
#OpenFilesLimit 1024
|
||||
|
||||
# Proxy specifications: listenaddr+port
|
||||
ProxySpec 127.0.0.1 8080
|
@ -0,0 +1,432 @@
|
||||
/*-
|
||||
* SSLsplit - transparent SSL/TLS interception
|
||||
* https://www.roe.ch/SSLsplit
|
||||
*
|
||||
* Copyright (c) 2009-2018, Daniel Roethlisberger <daniel@roe.ch>.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/* silence daemon(3) deprecation warning on Mac OS X */
|
||||
#if __APPLE__
|
||||
#define daemon xdaemon
|
||||
#endif /* __APPLE__ */
|
||||
|
||||
#include "opts.h"
|
||||
#include "proxy.h"
|
||||
#include "privsep.h"
|
||||
#include "sys.h"
|
||||
#include "log.h"
|
||||
#include "build.h"
|
||||
#include "defaults.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>
|
||||
|
||||
#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",
|
||||
PKGLABEL, build_version, build_date);
|
||||
if (strlen(build_version) < 5) {
|
||||
/*
|
||||
* Note to package maintainers: If you break the version
|
||||
* string in your build, it will be impossible to provide
|
||||
* proper upstream support to the users of the package,
|
||||
* because it will be difficult or impossible to identify
|
||||
* the exact codebase that is being used by the user
|
||||
* reporting a bug. The version string is provided through
|
||||
* different means depending on whether the code is a git
|
||||
* checkout, a tarball downloaded from GitHub or a release.
|
||||
* See GNUmakefile for the gory details.
|
||||
*/
|
||||
fprintf(stderr, "---------------------------------------"
|
||||
"---------------------------------------\n");
|
||||
fprintf(stderr, "WARNING: Something is wrong with the "
|
||||
"version compiled into lp!\n");
|
||||
fprintf(stderr, "The version should contain a release "
|
||||
"number and/or a git commit reference.\n");
|
||||
fprintf(stderr, "If using a package, please report a bug "
|
||||
"to the distro package maintainer.\n");
|
||||
fprintf(stderr, "---------------------------------------"
|
||||
"---------------------------------------\n");
|
||||
}
|
||||
fprintf(stderr, "Copyright (c) 2017-2019, Soner Tari <sonertari@gmail.com>\n");
|
||||
fprintf(stderr, "https://github.com/sonertari/SSLproxy\n");
|
||||
fprintf(stderr, "Copyright (c) 2009-2018, "
|
||||
"Daniel Roethlisberger <daniel@roe.ch>\n");
|
||||
fprintf(stderr, "https://www.roe.ch/SSLsplit\n");
|
||||
if (build_info[0]) {
|
||||
fprintf(stderr, "Build info: %s\n", build_info);
|
||||
}
|
||||
if (build_features[0]) {
|
||||
fprintf(stderr, "Features: %s\n", build_features);
|
||||
}
|
||||
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 *usagefmt =
|
||||
"Usage: %s [-D] [-f conffile] [-o opt=val] [options...] [proxyspecs...]\n"
|
||||
" -f conffile use conffile to load configuration from\n"
|
||||
" -o opt=val override conffile option opt with value val\n"
|
||||
" -u user drop privileges to user (default if run as root: " DFLT_DROPUSER ")\n"
|
||||
" -m group when using -u, override group (default: primary group of user)\n"
|
||||
" -j jaildir chroot() to jaildir (impacts sni proxyspecs, see manual page)\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"
|
||||
" -J enable connection statistics logging\n"
|
||||
" -L logfile content log: full data to file or named pipe (excludes -S/-F)\n"
|
||||
" -S logdir content log: full data to separate files in dir (excludes -L/-F)\n"
|
||||
" -F pathspec content log: full data to sep files with %% subst (excl. -L/-S):\n"
|
||||
" %%T - initial connection time as an ISO 8601 UTC timestamp\n"
|
||||
" %%d - destination host and port\n"
|
||||
" %%D - destination host\n"
|
||||
" %%p - destination port\n"
|
||||
" %%s - source host and port\n"
|
||||
" %%S - source host\n"
|
||||
" %%q - source port\n"
|
||||
" %%%% - literal '%%'\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 = listenaddr+port\n"
|
||||
" e.g. 127.0.0.1 8080 # tcp/4; static\n"
|
||||
" # et al\n"
|
||||
"Example:\n"
|
||||
" %s 127.0.0.1 8080\n";
|
||||
|
||||
fprintf(stderr, usagefmt, build_pkgname, build_pkgname);
|
||||
}
|
||||
|
||||
/*
|
||||
* Main entry point.
|
||||
*/
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
const char *argv0;
|
||||
int ch;
|
||||
opts_t *opts;
|
||||
int pidfd = -1;
|
||||
int rv = EXIT_FAILURE;
|
||||
|
||||
argv0 = argv[0];
|
||||
opts = opts_new();
|
||||
|
||||
while ((ch = getopt(argc, argv,
|
||||
"u:m:j:p:l:L:S:F:dD::Vhf:o:J")) != -1) {
|
||||
switch (ch) {
|
||||
case 'f':
|
||||
if (opts->conffile)
|
||||
free(opts->conffile);
|
||||
opts->conffile = strdup(optarg);
|
||||
if (!opts->conffile)
|
||||
oom_die(argv0);
|
||||
if (opts_load_conffile(opts, argv0) == -1) {
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
#ifdef DEBUG_OPTS
|
||||
log_dbg_printf("Conf file: %s\n", opts->conffile);
|
||||
#endif /* DEBUG_OPTS */
|
||||
break;
|
||||
case 'o':
|
||||
if (opts_set_option(opts, argv0, optarg) == -1) {
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
break;
|
||||
case 'u':
|
||||
opts_set_user(opts, argv0, optarg);
|
||||
break;
|
||||
case 'm':
|
||||
opts_set_group(opts, argv0, optarg);
|
||||
break;
|
||||
case 'p':
|
||||
opts_set_pidfile(opts, argv0, optarg);
|
||||
break;
|
||||
case 'j':
|
||||
opts_set_jaildir(opts, argv0, optarg);
|
||||
break;
|
||||
case 'l':
|
||||
opts_set_connectlog(opts, argv0, optarg);
|
||||
break;
|
||||
case 'J':
|
||||
opts_set_statslog(opts);
|
||||
break;
|
||||
case 'L':
|
||||
opts_set_contentlog(opts, argv0, optarg);
|
||||
break;
|
||||
case 'S':
|
||||
opts_set_contentlogdir(opts, argv0, optarg);
|
||||
break;
|
||||
case 'F':
|
||||
opts_set_contentlogpathspec(opts, argv0, optarg);
|
||||
break;
|
||||
case 'd':
|
||||
opts_set_daemon(opts);
|
||||
break;
|
||||
case 'D':
|
||||
opts_set_debug(opts);
|
||||
if (optarg) {
|
||||
opts_set_debug_level(optarg);
|
||||
}
|
||||
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;
|
||||
proxyspec_parse(&argc, &argv, &opts->spec);
|
||||
|
||||
/* usage checks before defaults */
|
||||
if (opts->detach && OPTS_DEBUG(opts)) {
|
||||
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)
|
||||
continue;
|
||||
}
|
||||
#ifdef __APPLE__
|
||||
if (opts->dropuser && !!strcmp(opts->dropuser, "root") &&
|
||||
nat_used("pf")) {
|
||||
fprintf(stderr, "%s: cannot use 'pf' proxyspec with -u due "
|
||||
"to Apple bug\n", argv0);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
#endif /* __APPLE__ */
|
||||
|
||||
/* 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);
|
||||
}
|
||||
}
|
||||
|
||||
if (!opts->dropuser && !geteuid() && !getuid() &&
|
||||
sys_isuser(DFLT_DROPUSER)) {
|
||||
#ifdef __APPLE__
|
||||
/* Apple broke ioctl(/dev/pf) for EUID != 0 so we do not
|
||||
* want to automatically drop privileges to nobody there
|
||||
* if pf has been used in any proxyspec */
|
||||
if (!nat_used("pf")) {
|
||||
#endif /* __APPLE__ */
|
||||
opts->dropuser = strdup(DFLT_DROPUSER);
|
||||
if (!opts->dropuser)
|
||||
oom_die(argv0);
|
||||
#ifdef __APPLE__
|
||||
}
|
||||
#endif /* __APPLE__ */
|
||||
}
|
||||
if (opts->dropuser && sys_isgeteuid(opts->dropuser)) {
|
||||
if (opts->dropgroup) {
|
||||
fprintf(stderr, "%s: cannot use -m when -u is "
|
||||
"current user\n", argv0);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
free(opts->dropuser);
|
||||
opts->dropuser = NULL;
|
||||
}
|
||||
|
||||
/* usage checks after defaults */
|
||||
if (opts->dropgroup && !opts->dropuser) {
|
||||
fprintf(stderr, "%s: -m depends on -u\n", argv0);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/* Warn about options that require per-connection privileged operations
|
||||
* to be executed through privsep, but only if dropuser is set and is
|
||||
* not root, because privsep will fastpath in that situation, skipping
|
||||
* the latency-incurring overhead. */
|
||||
int privsep_warn = 0;
|
||||
if (opts->dropuser) {
|
||||
if (opts->contentlog_isdir) {
|
||||
log_dbg_printf("| Warning: -F requires a privileged "
|
||||
"operation for each connection!\n");
|
||||
privsep_warn = 1;
|
||||
}
|
||||
if (opts->contentlog_isspec) {
|
||||
log_dbg_printf("| Warning: -S requires a privileged "
|
||||
"operation for each connection!\n");
|
||||
privsep_warn = 1;
|
||||
}
|
||||
}
|
||||
if (privsep_warn) {
|
||||
log_dbg_printf("| Privileged operations require communication "
|
||||
"between parent and child process\n"
|
||||
"| and will negatively impact latency and "
|
||||
"performance on each connection.\n");
|
||||
}
|
||||
|
||||
/* debug log, part 1 */
|
||||
if (OPTS_DEBUG(opts)) {
|
||||
main_version();
|
||||
}
|
||||
|
||||
/* debug log, part 2 */
|
||||
if (OPTS_DEBUG(opts)) {
|
||||
log_dbg_printf("proxyspecs:\n");
|
||||
for (proxyspec_t *spec = opts->spec; spec; spec = spec->next) {
|
||||
char *specstr = proxyspec_str(spec);
|
||||
if (!specstr) {
|
||||
fprintf(stderr, "%s: out of memory\n", argv0);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
log_dbg_printf("- %s\n", specstr);
|
||||
free(specstr);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize as much as possible before daemon() in order to be
|
||||
* able to provide direct feedback to the user when failing.
|
||||
*/
|
||||
if (log_preinit(opts) == -1) {
|
||||
fprintf(stderr, "%s: failed to preinit logging.\n", argv0);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/* Detach from tty; from this point on, only canonicalized absolute
|
||||
* paths should be used (-j, -F, -S). */
|
||||
if (opts->detach) {
|
||||
if (OPTS_DEBUG(opts)) {
|
||||
log_dbg_printf("Detaching from TTY, see syslog for "
|
||||
"errors after this point\n");
|
||||
}
|
||||
if (daemon(0, 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) == -1)) {
|
||||
log_err_level_printf(LOG_CRIT, "Failed to write PID to PID file '%s': %s (%i)"
|
||||
"\n", opts->pidfile, strerror(errno), errno);
|
||||
return -1;
|
||||
}
|
||||
|
||||
descriptor_table_size = getdtablesize();
|
||||
|
||||
/* Fork into parent monitor process and (potentially unprivileged)
|
||||
* child process doing the actual work. We request 6 privsep client
|
||||
* sockets: five logger threads, and the child process main thread,
|
||||
* which will become the main proxy thread. First slot is main thread,
|
||||
* remaining slots are passed down to log subsystem. */
|
||||
int clisock[6];
|
||||
if (privsep_fork(opts, clisock,
|
||||
sizeof(clisock)/sizeof(clisock[0])) != 0) {
|
||||
/* parent has exited the monitor loop after waiting for child,
|
||||
* or an error occurred */
|
||||
if (opts->pidfile) {
|
||||
sys_pidf_close(pidfd, opts->pidfile);
|
||||
}
|
||||
goto out_parent;
|
||||
}
|
||||
/* child */
|
||||
|
||||
/* close pidfile in child */
|
||||
if (opts->pidfile)
|
||||
close(pidfd);
|
||||
|
||||
/* Initialize proxy before dropping privs */
|
||||
proxy_ctx_t *proxy = proxy_new(opts, clisock[0]);
|
||||
if (!proxy) {
|
||||
log_err_level_printf(LOG_CRIT, "Failed to initialize proxy.\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/* Drop privs, chroot */
|
||||
if (sys_privdrop(opts->dropuser, opts->dropgroup,
|
||||
opts->jaildir) == -1) {
|
||||
log_err_level_printf(LOG_CRIT, "Failed to drop privileges: %s (%i)\n",
|
||||
strerror(errno), errno);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
log_dbg_printf("Dropped privs to user %s group %s chroot %s\n",
|
||||
opts->dropuser ? opts->dropuser : "-",
|
||||
opts->dropgroup ? opts->dropgroup : "-",
|
||||
opts->jaildir ? opts->jaildir : "-");
|
||||
/* Post-privdrop/chroot/detach initialization, thread spawning */
|
||||
if (log_init(opts, proxy, &clisock[1]) == -1) {
|
||||
fprintf(stderr, "%s: failed to init log facility: %s\n",
|
||||
argv0, strerror(errno));
|
||||
goto out_log_failed;
|
||||
}
|
||||
rv = EXIT_SUCCESS;
|
||||
|
||||
proxy_run(proxy);
|
||||
|
||||
proxy_free(proxy);
|
||||
log_fini();
|
||||
out_log_failed:
|
||||
out_parent:
|
||||
opts_free(opts);
|
||||
return rv;
|
||||
}
|
||||
|
||||
/* vim: set noet ft=c: */
|
@ -0,0 +1,743 @@
|
||||
/*-
|
||||
* SSLsplit - transparent SSL/TLS interception
|
||||
* https://www.roe.ch/SSLsplit
|
||||
*
|
||||
* Copyright (c) 2009-2018, Daniel Roethlisberger <daniel@roe.ch>.
|
||||
* Copyright (c) 2017-2019, Soner Tari <sonertari@gmail.com>.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "opts.h"
|
||||
|
||||
#include "sys.h"
|
||||
#include "log.h"
|
||||
#include "defaults.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/resource.h>
|
||||
|
||||
/*
|
||||
* Handle out of memory conditions in early stages of main().
|
||||
* Print error message and exit with failure status code.
|
||||
* Does not return.
|
||||
*/
|
||||
void NORET
|
||||
oom_die(const char *argv0)
|
||||
{
|
||||
fprintf(stderr, "%s: out of memory\n", argv0);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
opts_t *
|
||||
opts_new(void)
|
||||
{
|
||||
opts_t *opts;
|
||||
|
||||
opts = malloc(sizeof(opts_t));
|
||||
memset(opts, 0, sizeof(opts_t));
|
||||
|
||||
opts->stats_period = 1;
|
||||
return opts;
|
||||
}
|
||||
|
||||
void
|
||||
opts_free(opts_t *opts)
|
||||
{
|
||||
if (opts->spec) {
|
||||
proxyspec_free(opts->spec);
|
||||
}
|
||||
if (opts->dropuser) {
|
||||
free(opts->dropuser);
|
||||
}
|
||||
if (opts->dropgroup) {
|
||||
free(opts->dropgroup);
|
||||
}
|
||||
if (opts->jaildir) {
|
||||
free(opts->jaildir);
|
||||
}
|
||||
if (opts->pidfile) {
|
||||
free(opts->pidfile);
|
||||
}
|
||||
if (opts->connectlog) {
|
||||
free(opts->connectlog);
|
||||
}
|
||||
if (opts->contentlog) {
|
||||
free(opts->contentlog);
|
||||
}
|
||||
if (opts->contentlog_basedir) {
|
||||
free(opts->contentlog_basedir);
|
||||
}
|
||||
memset(opts, 0, sizeof(opts_t));
|
||||
free(opts);
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse proxyspecs using a simple state machine.
|
||||
*/
|
||||
void
|
||||
proxyspec_parse(int *argc, char **argv[], proxyspec_t **opts_spec)
|
||||
{
|
||||
proxyspec_t *spec = NULL;
|
||||
int af = AF_UNSPEC;
|
||||
int state = 0;
|
||||
char *la, *lp;
|
||||
|
||||
while ((*argc)--) {
|
||||
switch (state) {
|
||||
default:
|
||||
case 0:
|
||||
spec = malloc(sizeof(proxyspec_t));
|
||||
memset(spec, 0, sizeof(proxyspec_t));
|
||||
spec->next = *opts_spec;
|
||||
*opts_spec = spec;
|
||||
|
||||
// @todo IPv6?
|
||||
la = **argv;
|
||||
state++;
|
||||
break;
|
||||
case 1:
|
||||
lp = **argv;
|
||||
af = sys_sockaddr_parse(&spec->listen_addr,
|
||||
&spec->listen_addrlen,
|
||||
la, lp,
|
||||
sys_get_af(la),
|
||||
EVUTIL_AI_PASSIVE);
|
||||
if (af == -1) {
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
state = 0;
|
||||
break;
|
||||
}
|
||||
(*argv)++;
|
||||
}
|
||||
if (state != 0 && state != 4) {
|
||||
fprintf(stderr, "Incomplete proxyspec!\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Clear and free a proxy spec.
|
||||
*/
|
||||
void
|
||||
proxyspec_free(proxyspec_t *spec)
|
||||
{
|
||||
do {
|
||||
proxyspec_t *next = spec->next;
|
||||
memset(spec, 0, sizeof(proxyspec_t));
|
||||
free(spec);
|
||||
spec = next;
|
||||
} while (spec);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return text representation of proxy spec for display to the user.
|
||||
* Returned string must be freed by caller.
|
||||
*/
|
||||
char *
|
||||
proxyspec_str(proxyspec_t *spec)
|
||||
{
|
||||
char *s;
|
||||
char *lhbuf, *lpbuf;
|
||||
char *cbuf = NULL;
|
||||
if (sys_sockaddr_str((struct sockaddr *)&spec->listen_addr,
|
||||
spec->listen_addrlen, &lhbuf, &lpbuf) != 0) {
|
||||
return NULL;
|
||||
}
|
||||
if (spec->connect_addrlen) {
|
||||
char *chbuf, *cpbuf;
|
||||
if (sys_sockaddr_str((struct sockaddr *)&spec->connect_addr,
|
||||
spec->connect_addrlen,
|
||||
&chbuf, &cpbuf) != 0) {
|
||||
return NULL;
|
||||
}
|
||||
if (asprintf(&cbuf, "\nconnect= [%s]:%s", chbuf, cpbuf) < 0) {
|
||||
return NULL;
|
||||
}
|
||||
free(chbuf);
|
||||
free(cpbuf);
|
||||
}
|
||||
if (asprintf(&s, "listen=[%s]:%s %s", lhbuf, lpbuf, "tcp") < 0) {
|
||||
s = NULL;
|
||||
}
|
||||
free(lhbuf);
|
||||
free(lpbuf);
|
||||
if (cbuf)
|
||||
free(cbuf);
|
||||
return s;
|
||||
}
|
||||
|
||||
void
|
||||
opts_set_user(opts_t *opts, const char *argv0, const char *optarg)
|
||||
{
|
||||
if (!sys_isuser(optarg)) {
|
||||
fprintf(stderr, "%s: '%s' is not an existing user\n",
|
||||
argv0, optarg);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
if (opts->dropuser)
|
||||
free(opts->dropuser);
|
||||
opts->dropuser = strdup(optarg);
|
||||
if (!opts->dropuser)
|
||||
oom_die(argv0);
|
||||
#ifdef DEBUG_OPTS
|
||||
log_dbg_printf("User: %s\n", opts->dropuser);
|
||||
#endif /* DEBUG_OPTS */
|
||||
}
|
||||
|
||||
void
|
||||
opts_set_group(opts_t *opts, const char *argv0, const char *optarg)
|
||||
{
|
||||
|
||||
if (!sys_isgroup(optarg)) {
|
||||
fprintf(stderr, "%s: '%s' is not an existing group\n",
|
||||
argv0, optarg);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
if (opts->dropgroup)
|
||||
free(opts->dropgroup);
|
||||
opts->dropgroup = strdup(optarg);
|
||||
if (!opts->dropgroup)
|
||||
oom_die(argv0);
|
||||
#ifdef DEBUG_OPTS
|
||||
log_dbg_printf("Group: %s\n", opts->dropgroup);
|
||||
#endif /* DEBUG_OPTS */
|
||||
}
|
||||
|
||||
void
|
||||
opts_set_jaildir(opts_t *opts, const char *argv0, const char *optarg)
|
||||
{
|
||||
if (!sys_isdir(optarg)) {
|
||||
fprintf(stderr, "%s: '%s' is not a directory\n", argv0, optarg);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
if (opts->jaildir)
|
||||
free(opts->jaildir);
|
||||
opts->jaildir = realpath(optarg, NULL);
|
||||
if (!opts->jaildir) {
|
||||
fprintf(stderr, "%s: Failed to realpath '%s': %s (%i)\n",
|
||||
argv0, optarg, strerror(errno), errno);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
#ifdef DEBUG_OPTS
|
||||
log_dbg_printf("Chroot: %s\n", opts->jaildir);
|
||||
#endif /* DEBUG_OPTS */
|
||||
}
|
||||
|
||||
void
|
||||
opts_set_pidfile(opts_t *opts, const char *argv0, const char *optarg)
|
||||
{
|
||||
if (opts->pidfile)
|
||||
free(opts->pidfile);
|
||||
opts->pidfile = strdup(optarg);
|
||||
if (!opts->pidfile)
|
||||
oom_die(argv0);
|
||||
#ifdef DEBUG_OPTS
|
||||
log_dbg_printf("PidFile: %s\n", opts->pidfile);
|
||||
#endif /* DEBUG_OPTS */
|
||||
}
|
||||
|
||||
void
|
||||
opts_set_connectlog(opts_t *opts, const char *argv0, const char *optarg)
|
||||
{
|
||||
if (opts->connectlog)
|
||||
free(opts->connectlog);
|
||||
if (!(opts->connectlog = sys_realdir(optarg))) {
|
||||
if (errno == ENOENT) {
|
||||
fprintf(stderr, "Directory part of '%s' does not "
|
||||
"exist\n", optarg);
|
||||
exit(EXIT_FAILURE);
|
||||
} else {
|
||||
fprintf(stderr, "Failed to realpath '%s': %s (%i)\n",
|
||||
optarg, strerror(errno), errno);
|
||||
oom_die(argv0);
|
||||
}
|
||||
}
|
||||
#ifdef DEBUG_OPTS
|
||||
log_dbg_printf("ConnectLog: %s\n", opts->connectlog);
|
||||
#endif /* DEBUG_OPTS */
|
||||
}
|
||||
|
||||
void
|
||||
opts_set_contentlog(opts_t *opts, const char *argv0, const char *optarg)
|
||||
{
|
||||
if (opts->contentlog)
|
||||
free(opts->contentlog);
|
||||
if (!(opts->contentlog = sys_realdir(optarg))) {
|
||||
if (errno == ENOENT) {
|
||||
fprintf(stderr, "Directory part of '%s' does not "
|
||||
"exist\n", optarg);
|
||||
exit(EXIT_FAILURE);
|
||||
} else {
|
||||
fprintf(stderr, "Failed to realpath '%s': %s (%i)\n",
|
||||
optarg, strerror(errno), errno);
|
||||
oom_die(argv0);
|
||||
}
|
||||
}
|
||||
opts->contentlog_isdir = 0;
|
||||
opts->contentlog_isspec = 0;
|
||||
#ifdef DEBUG_OPTS
|
||||
log_dbg_printf("ContentLog: %s\n", opts->contentlog);
|
||||
#endif /* DEBUG_OPTS */
|
||||
}
|
||||
|
||||
void
|
||||
opts_set_contentlogdir(opts_t *opts, const char *argv0, const char *optarg)
|
||||
{
|
||||
if (!sys_isdir(optarg)) {
|
||||
fprintf(stderr, "%s: '%s' is not a directory\n", argv0, optarg);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
if (opts->contentlog)
|
||||
free(opts->contentlog);
|
||||
opts->contentlog = realpath(optarg, NULL);
|
||||
if (!opts->contentlog) {
|
||||
fprintf(stderr, "%s: Failed to realpath '%s': %s (%i)\n",
|
||||
argv0, optarg, strerror(errno), errno);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
opts->contentlog_isdir = 1;
|
||||
opts->contentlog_isspec = 0;
|
||||
#ifdef DEBUG_OPTS
|
||||
log_dbg_printf("ContentLogDir: %s\n", opts->contentlog);
|
||||
#endif /* DEBUG_OPTS */
|
||||
}
|
||||
|
||||
static void
|
||||
opts_set_logbasedir(const char *argv0, const char *optarg,
|
||||
char **basedir, char **log)
|
||||
{
|
||||
char *lhs, *rhs, *p, *q;
|
||||
size_t n;
|
||||
if (*basedir)
|
||||
free(*basedir);
|
||||
if (*log)
|
||||
free(*log);
|
||||
if (log_content_split_pathspec(optarg, &lhs, &rhs) == -1) {
|
||||
fprintf(stderr, "%s: Failed to split '%s' in lhs/rhs:"
|
||||
" %s (%i)\n", argv0, optarg,
|
||||
strerror(errno), errno);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
/* eliminate %% from lhs */
|
||||
for (p = q = lhs; *p; p++, q++) {
|
||||
if (q < p)
|
||||
*q = *p;
|
||||
if (*p == '%' && *(p+1) == '%')
|
||||
p++;
|
||||
}
|
||||
*q = '\0';
|
||||
/* all %% in lhs resolved to % */
|
||||
if (sys_mkpath(lhs, 0777) == -1) {
|
||||
fprintf(stderr, "%s: Failed to create '%s': %s (%i)\n",
|
||||
argv0, lhs, strerror(errno), errno);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
*basedir = realpath(lhs, NULL);
|
||||
if (!*basedir) {
|
||||
fprintf(stderr, "%s: Failed to realpath '%s': %s (%i)\n",
|
||||
argv0, lhs, strerror(errno), errno);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
/* count '%' in basedir */
|
||||
for (n = 0, p = *basedir;
|
||||
*p;
|
||||
p++) {
|
||||
if (*p == '%')
|
||||
n++;
|
||||
}
|
||||
free(lhs);
|
||||
n += strlen(*basedir);
|
||||
if (!(lhs = malloc(n + 1)))
|
||||
oom_die(argv0);
|
||||
/* re-encoding % to %%, copying basedir to lhs */
|
||||
for (p = *basedir, q = lhs;
|
||||
*p;
|
||||
p++, q++) {
|
||||
*q = *p;
|
||||
if (*q == '%')
|
||||
*(++q) = '%';
|
||||
}
|
||||
*q = '\0';
|
||||
/* lhs contains encoded realpathed basedir */
|
||||
if (asprintf(log, "%s/%s", lhs, rhs) < 0)
|
||||
oom_die(argv0);
|
||||
free(lhs);
|
||||
free(rhs);
|
||||
}
|
||||
|
||||
void
|
||||
opts_set_contentlogpathspec(opts_t *opts, const char *argv0, const char *optarg)
|
||||
{
|
||||
opts_set_logbasedir(argv0, optarg, &opts->contentlog_basedir,
|
||||
&opts->contentlog);
|
||||
opts->contentlog_isdir = 0;
|
||||
opts->contentlog_isspec = 1;
|
||||
#ifdef DEBUG_OPTS
|
||||
log_dbg_printf("ContentLogPathSpec: basedir=%s, %s\n",
|
||||
opts->contentlog_basedir, opts->contentlog);
|
||||
#endif /* DEBUG_OPTS */
|
||||
}
|
||||
|
||||
void
|
||||
opts_set_daemon(opts_t *opts)
|
||||
{
|
||||
opts->detach = 1;
|
||||
}
|
||||
|
||||
void
|
||||
opts_unset_daemon(opts_t *opts)
|
||||
{
|
||||
opts->detach = 0;
|
||||
}
|
||||
|
||||
void
|
||||
opts_set_debug(opts_t *opts)
|
||||
{
|
||||
log_dbg_mode(LOG_DBG_MODE_ERRLOG);
|
||||
opts->debug = 1;
|
||||
}
|
||||
|
||||
void
|
||||
opts_unset_debug(opts_t *opts)
|
||||
{
|
||||
log_dbg_mode(LOG_DBG_MODE_NONE);
|
||||
opts->debug = 0;
|
||||
}
|
||||
|
||||
void
|
||||
opts_set_debug_level(const char *optarg)
|
||||
{
|
||||
// Compare strlen(s2)+1 chars to match exactly
|
||||
if (strncmp(optarg, "2", 2) == 0) {
|
||||
log_dbg_mode(LOG_DBG_MODE_FINE);
|
||||
} else if (strncmp(optarg, "3", 2) == 0) {
|
||||
log_dbg_mode(LOG_DBG_MODE_FINER);
|
||||
} else if (strncmp(optarg, "4", 2) == 0) {
|
||||
log_dbg_mode(LOG_DBG_MODE_FINEST);
|
||||
} else {
|
||||
fprintf(stderr, "Invalid DebugLevel '%s', use 2-4\n", optarg);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
#ifdef DEBUG_OPTS
|
||||
log_dbg_printf("DebugLevel: %s\n", optarg);
|
||||
#endif /* DEBUG_OPTS */
|
||||
}
|
||||
|
||||
void
|
||||
opts_set_statslog(opts_t *opts)
|
||||
{
|
||||
opts->statslog = 1;
|
||||
}
|
||||
|
||||
void
|
||||
opts_unset_statslog(opts_t *opts)
|
||||
{
|
||||
opts->statslog = 0;
|
||||
}
|
||||
|
||||
static void
|
||||
opts_set_open_files_limit(const char *value, int line_num)
|
||||
{
|
||||
unsigned int i = atoi(value);
|
||||
if (i >= 50 && i <= 10000) {
|
||||
struct rlimit rl;
|
||||
rl.rlim_cur = i;
|
||||
rl.rlim_max = i;
|
||||
if (setrlimit(RLIMIT_NOFILE, &rl) == -1) {
|
||||
fprintf(stderr, "Failed setting OpenFilesLimit\n");
|
||||
if (errno) {
|
||||
fprintf(stderr, "%s\n", strerror(errno));
|
||||
}
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
} else {
|
||||
fprintf(stderr, "Invalid OpenFilesLimit %s at line %d, use 50-10000\n", value, line_num);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
#ifdef DEBUG_OPTS
|
||||
log_dbg_printf("OpenFilesLimit: %u\n", i);
|
||||
#endif /* DEBUG_OPTS */
|
||||
}
|
||||
|
||||
static int
|
||||
check_value_yesno(const char *value, const char *name, int line_num)
|
||||
{
|
||||
/* Compare strlen(s2)+1 chars to match exactly */
|
||||
if (!strncmp(value, "yes", 4)) {
|
||||
return 1;
|
||||
} else if (!strncmp(value, "no", 3)) {
|
||||
return 0;
|
||||
}
|
||||
fprintf(stderr, "Error in conf: Invalid '%s' value '%s' at line %d, use yes|no\n", name, value, line_num);
|
||||
return -1;
|
||||
}
|
||||
|
||||
#define MAX_TOKEN 10
|
||||
|
||||
static int
|
||||
set_option(opts_t *opts, const char *argv0,
|
||||
const char *name, char *value, int line_num)
|
||||
{
|
||||
int yes;
|
||||
int retval = -1;
|
||||
|
||||
/* Compare strlen(s2)+1 chars to match exactly */
|
||||
if (!strncmp(name, "User", 5)) {
|
||||
opts_set_user(opts, argv0, value);
|
||||
} else if (!strncmp(name, "Group", 6)) {
|
||||
opts_set_group(opts, argv0, value);
|
||||
} else if (!strncmp(name, "Chroot", 7)) {
|
||||
opts_set_jaildir(opts, argv0, value);
|
||||
} else if (!strncmp(name, "PidFile", 8)) {
|
||||
opts_set_pidfile(opts, argv0, value);
|
||||
} else if (!strncmp(name, "ConnectLog", 11)) {
|
||||
opts_set_connectlog(opts, argv0, value);
|
||||
} else if (!strncmp(name, "ContentLog", 11)) {
|
||||
opts_set_contentlog(opts, argv0, value);
|
||||
} else if (!strncmp(name, "ContentLogDir", 14)) {
|
||||
opts_set_contentlogdir(opts, argv0, value);
|
||||
} else if (!strncmp(name, "ContentLogPathSpec", 19)) {
|
||||
opts_set_contentlogpathspec(opts, argv0, value);
|
||||
} else if (!strncmp(name, "Daemon", 7)) {
|
||||
yes = check_value_yesno(value, "Daemon", line_num);
|
||||
if (yes == -1) {
|
||||
goto leave;
|
||||
}
|
||||
yes ? opts_set_daemon(opts) : opts_unset_daemon(opts);
|
||||
#ifdef DEBUG_OPTS
|
||||
log_dbg_printf("Daemon: %u\n", opts->detach);
|
||||
#endif /* DEBUG_OPTS */
|
||||
} else if (!strncmp(name, "Debug", 6)) {
|
||||
yes = check_value_yesno(value, "Debug", line_num);
|
||||
if (yes == -1) {
|
||||
goto leave;
|
||||
}
|
||||
yes ? opts_set_debug(opts) : opts_unset_debug(opts);
|
||||
#ifdef DEBUG_OPTS
|
||||
log_dbg_printf("Debug: %u\n", opts->debug);
|
||||
#endif /* DEBUG_OPTS */
|
||||
} else if (!strncmp(name, "DebugLevel", 11)) {
|
||||
opts_set_debug_level(value);
|
||||
} else if (!strncmp(name, "ProxySpec", 10)) {
|
||||
/* Use MAX_TOKEN instead of computing the actual number of tokens in value */
|
||||
char **argv = malloc(sizeof(char *) * MAX_TOKEN);
|
||||
char **save_argv = argv;
|
||||
int argc = 0;
|
||||
char *p, *last = NULL;
|
||||
|
||||
for ((p = strtok_r(value, " ", &last));
|
||||
p;
|
||||
(p = strtok_r(NULL, " ", &last))) {
|
||||
/* Limit max # token */
|
||||
if (argc < MAX_TOKEN) {
|
||||
argv[argc++] = p;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
proxyspec_parse(&argc, &argv, &opts->spec);
|
||||
free(save_argv);
|
||||
} else if (!strncasecmp(name, "LogStats", 9)) {
|
||||
yes = check_value_yesno(value, "LogStats", line_num);
|
||||
if (yes == -1) {
|
||||
goto leave;
|
||||
}
|
||||
yes ? opts_set_statslog(opts) : opts_unset_statslog(opts);
|
||||
#ifdef DEBUG_OPTS
|
||||
log_dbg_printf("LogStats: %u\n", opts->statslog);
|
||||
#endif /* DEBUG_OPTS */
|
||||
} else if (!strncasecmp(name, "StatsPeriod", 12)) {
|
||||
unsigned int i = atoi(value);
|
||||
if (i >= 1 && i <= 10) {
|
||||
opts->stats_period = i;
|
||||
} else {
|
||||
fprintf(stderr, "Invalid StatsPeriod %s at line %d, use 1-10\n", value, line_num);
|
||||
goto leave;
|
||||
}
|
||||
#ifdef DEBUG_OPTS
|
||||
log_dbg_printf("StatsPeriod: %u\n", opts->stats_period);
|
||||
#endif /* DEBUG_OPTS */
|
||||
} else if (!strncasecmp(name, "OpenFilesLimit", 15)) {
|
||||
opts_set_open_files_limit(value, line_num);
|
||||
} else {
|
||||
#ifdef DEBUG_OPTS
|
||||
log_dbg_printf("Skipping option '%s' at line %d\n", name, line_num);
|
||||
#endif /* DEBUG_OPTS */
|
||||
}
|
||||
|
||||
retval = 0;
|
||||
leave:
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*
|
||||
* Separator param is needed for command line options only.
|
||||
* Conf file option separator is ' '.
|
||||
*/
|
||||
static int
|
||||
get_name_value(char **name, char **value, const char sep)
|
||||
{
|
||||
char *n, *v, *value_end;
|
||||
int retval = -1;
|
||||
|
||||
/* Skip to the end of option name and terminate it with '\0' */
|
||||
for (n = *name;; n++) {
|
||||
/* White spaces possible around separator,
|
||||
* if the command line option is passed between the quotes */
|
||||
if (*n == ' ' || *n == '\t' || *n == sep) {
|
||||
*n = '\0';
|
||||
n++;
|
||||
break;
|
||||
}
|
||||
if (*n == '\0') {
|
||||
n = NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* No option name */
|
||||
if (n == NULL) {
|
||||
fprintf(stderr, "Error in option: No option name\n");
|
||||
goto leave;
|
||||
}
|
||||
|
||||
/* White spaces possible before value and around separator,
|
||||
* if the command line option is passed between the quotes */
|
||||
while (*n == ' ' || *n == '\t' || *n == sep) {
|
||||
n++;
|
||||
}
|
||||
|
||||
*value = n;
|
||||
|
||||
/* Find end of value and terminate it with '\0'
|
||||
* Find first occurrence of trailing white space */
|
||||
value_end = NULL;
|
||||
for (v = *value;; v++) {
|
||||
if (*v == '\0') {
|
||||
break;
|
||||
}
|
||||
if (*v == '\r' || *v == '\n') {
|
||||
*v = '\0';
|
||||
break;
|
||||
}
|
||||
if (*v == ' ' || *v == '\t') {
|
||||
if (!value_end) {
|
||||
value_end = v;
|
||||
}
|
||||
} else {
|
||||
value_end = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (value_end) {
|
||||
*value_end = '\0';
|
||||
}
|
||||
|
||||
retval = 0;
|
||||
leave:
|
||||
return retval;
|
||||
}
|
||||
|
||||
int
|
||||
opts_set_option(opts_t *opts, const char *argv0, const char *optarg)
|
||||
{
|
||||
char *name, *value;
|
||||
int retval = -1;
|
||||
char *line = strdup(optarg);
|
||||
|
||||
/* White spaces possible before option name,
|
||||
* if the command line option is passed between the quotes */
|
||||
for (name = line; *name == ' ' || *name == '\t'; name++);
|
||||
|
||||
/* Command line option separator is '=' */
|
||||
retval = get_name_value(&name, &value, '=');
|
||||
if (retval == 0) {
|
||||
/* Line number param is for conf file, pass 0 for command line options */
|
||||
retval = set_option(opts, argv0, name, value, 0);
|
||||
}
|
||||
|
||||
if (line) {
|
||||
free(line);
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
int
|
||||
opts_load_conffile(opts_t *opts, const char *argv0)
|
||||
{
|
||||
int retval, line_num;
|
||||
char *line, *name, *value;
|
||||
size_t line_len;
|
||||
FILE *f;
|
||||
|
||||
f = fopen(opts->conffile, "r");
|
||||
if (!f) {
|
||||
fprintf(stderr, "Error opening conf file '%s': %s\n", opts->conffile, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
line = NULL;
|
||||
line_num = 0;
|
||||
retval = -1;
|
||||
while (!feof(f)) {
|
||||
if (getline(&line, &line_len, f) == -1) {
|
||||
break;
|
||||
}
|
||||
if (line == NULL) {
|
||||
fprintf(stderr, "Error in conf file: getline() returns NULL line after line %d\n", line_num);
|
||||
goto leave;
|
||||
}
|
||||
line_num++;
|
||||
|
||||
/* Skip white space */
|
||||
for (name = line; *name == ' ' || *name == '\t'; name++);
|
||||
|
||||
/* Skip comments and empty lines */
|
||||
if ((name[0] == '\0') || (name[0] == '#') || (name[0] == ';') ||
|
||||
(name[0] == '\r') || (name[0] == '\n')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
retval = get_name_value(&name, &value, ' ');
|
||||
if (retval == 0) {
|
||||
retval = set_option(opts, argv0, name, value, line_num);
|
||||
}
|
||||
|
||||
if (retval == -1) {
|
||||
goto leave;
|
||||
}
|
||||
}
|
||||
|
||||
leave:
|
||||
fclose(f);
|
||||
if (line) {
|
||||
free(line);
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
/* vim: set noet ft=c: */
|
@ -0,0 +1,105 @@
|
||||
/*-
|
||||
* SSLsplit - transparent SSL/TLS interception
|
||||
* https://www.roe.ch/SSLsplit
|
||||
*
|
||||
* Copyright (c) 2009-2018, Daniel Roethlisberger <daniel@roe.ch>.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef OPTS_H
|
||||
#define OPTS_H
|
||||
|
||||
#include "attrib.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sqlite3.h>
|
||||
|
||||
/*
|
||||
* Print helper for logging code.
|
||||
*/
|
||||
#define STRORDASH(x) (((x)&&*(x))?(x):"-")
|
||||
#define STRORNONE(x) (((x)&&*(x))?(x):"")
|
||||
|
||||
typedef struct proxyspec {
|
||||
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;
|
||||
struct proxyspec *next;
|
||||
} proxyspec_t;
|
||||
|
||||
typedef struct opts {
|
||||
unsigned int debug : 1;
|
||||
unsigned int detach : 1;
|
||||
unsigned int contentlog_isdir : 1;
|
||||
unsigned int contentlog_isspec : 1;
|
||||
char *dropuser;
|
||||
char *dropgroup;
|
||||
char *jaildir;
|
||||
char *pidfile;
|
||||
char *conffile;
|
||||
char *connectlog;
|
||||
char *contentlog;
|
||||
char *contentlog_basedir; /* static part of logspec for privsep srv */
|
||||
proxyspec_t *spec;
|
||||
unsigned int stats_period;
|
||||
unsigned int statslog: 1;
|
||||
unsigned int log_stats: 1;
|
||||
} opts_t;
|
||||
|
||||
void NORET oom_die(const char *) NONNULL(1);
|
||||
|
||||
opts_t *opts_new(void) MALLOC;
|
||||
void opts_free(opts_t *) NONNULL(1);
|
||||
void opts_proto_dbg_dump(opts_t *) NONNULL(1);
|
||||
#define OPTS_DEBUG(opts) unlikely((opts)->debug)
|
||||
|
||||
void proxyspec_parse(int *, char **[], proxyspec_t **);
|
||||
void proxyspec_free(proxyspec_t *) NONNULL(1);
|
||||
char *proxyspec_str(proxyspec_t *) NONNULL(1) MALLOC;
|
||||
|
||||
void opts_set_user(opts_t *, const char *, const char *) NONNULL(1,2,3);
|
||||
void opts_set_group(opts_t *, const char *, const char *) NONNULL(1,2,3);
|
||||
void opts_set_jaildir(opts_t *, const char *, const char *) NONNULL(1,2,3);
|
||||
void opts_set_pidfile(opts_t *, const char *, const char *) NONNULL(1,2,3);
|
||||
void opts_set_connectlog(opts_t *, const char *, const char *) NONNULL(1,2,3);
|
||||
void opts_set_contentlog(opts_t *, const char *, const char *) NONNULL(1,2,3);
|
||||
void opts_set_contentlogdir(opts_t *, const char *, const char *)
|
||||
NONNULL(1,2,3);
|
||||
void opts_set_contentlogpathspec(opts_t *, const char *, const char *)
|
||||
NONNULL(1,2,3);
|
||||
void opts_set_daemon(opts_t *) NONNULL(1);
|
||||
void opts_set_debug(opts_t *) NONNULL(1);
|
||||
void opts_set_debug_level(const char *) NONNULL(1);
|
||||
void opts_set_statslog(opts_t *) NONNULL(1);
|
||||
int opts_set_option(opts_t *, const char *, const char *)
|
||||
NONNULL(1,2,3);
|
||||
|
||||
int opts_load_conffile(opts_t *, const char *) NONNULL(1,2);
|
||||
#endif /* !OPTS_H */
|
||||
|
||||
/* vim: set noet ft=c: */
|
@ -0,0 +1,896 @@
|
||||
/*-
|
||||
* SSLsplit - transparent SSL/TLS interception
|
||||
* https://www.roe.ch/SSLsplit
|
||||
*
|
||||
* Copyright (c) 2009-2018, Daniel Roethlisberger <daniel@roe.ch>.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "privsep.h"
|
||||
|
||||
#include "sys.h"
|
||||
#include "log.h"
|
||||
#include "attrib.h"
|
||||
#include "defaults.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/select.h>
|
||||
#include <sys/wait.h>
|
||||
#include <netinet/in.h>
|
||||
#include <signal.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <libgen.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
|
||||
/*
|
||||
* Privilege separation functionality.
|
||||
*
|
||||
* The server code has limitations on the internal functionality that can be
|
||||
* used, namely only those that are initialized before forking.
|
||||
*/
|
||||
|
||||
/* maximal message sizes */
|
||||
#define PRIVSEP_MAX_REQ_SIZE 512 /* arbitrary limit */
|
||||
#define PRIVSEP_MAX_ANS_SIZE (1+sizeof(int))
|
||||
/* command byte */
|
||||
#define PRIVSEP_REQ_CLOSE 0 /* closing command socket */
|
||||
#define PRIVSEP_REQ_OPENFILE 1 /* open content log file */
|
||||
#define PRIVSEP_REQ_OPENFILE_P 2 /* open content log file w/mkpath */
|
||||
#define PRIVSEP_REQ_OPENSOCK 3 /* open socket and pass fd */
|
||||
/* response byte */
|
||||
#define PRIVSEP_ANS_SUCCESS 0 /* success */
|
||||
#define PRIVSEP_ANS_UNK_CMD 1 /* unknown command */
|
||||
#define PRIVSEP_ANS_INVALID 2 /* invalid message */
|
||||
#define PRIVSEP_ANS_DENIED 3 /* request denied */
|
||||
#define PRIVSEP_ANS_SYS_ERR 4 /* system error; arg=errno */
|
||||
|
||||
/* Whether we short-circuit calls to privsep_client_* directly to
|
||||
* privsep_server_* within the client process, bypassing the privilege
|
||||
* separation mechanism; this is a performance optimization for use cases
|
||||
* where the user chooses performance over security, especially with options
|
||||
* that require privsep operations for each connection passing through.
|
||||
* In the current implementation, for consistency, we still fork normally, but
|
||||
* will not actually send any privsep requests to the parent process. */
|
||||
static int privsep_fastpath;
|
||||
|
||||
/* communication with signal handler */
|
||||
static volatile sig_atomic_t received_sighup;
|
||||
static volatile sig_atomic_t received_sigint;
|
||||
static volatile sig_atomic_t received_sigquit;
|
||||
static volatile sig_atomic_t received_sigterm;
|
||||
static volatile sig_atomic_t received_sigchld;
|
||||
static volatile sig_atomic_t received_sigusr1;
|
||||
/* write end of pipe used for unblocking select */
|
||||
static volatile sig_atomic_t selfpipe_wrfd;
|
||||
|
||||
static void
|
||||
privsep_server_signal_handler(int sig)
|
||||
{
|
||||
int saved_errno;
|
||||
|
||||
saved_errno = errno;
|
||||
|
||||
#ifdef DEBUG_PRIVSEP_SERVER
|
||||
log_dbg_printf("privsep_server_signal_handler\n");
|
||||
#endif /* DEBUG_PRIVSEP_SERVER */
|
||||
|
||||
switch (sig) {
|
||||
case SIGHUP:
|
||||
received_sighup = 1;
|
||||
break;
|
||||
case SIGINT:
|
||||
received_sigint = 1;
|
||||
break;
|
||||
case SIGQUIT:
|
||||
received_sigquit = 1;
|
||||
break;
|
||||
case SIGTERM:
|
||||
received_sigterm = 1;
|
||||
break;
|
||||
case SIGCHLD:
|
||||
received_sigchld = 1;
|
||||
break;
|
||||
case SIGUSR1:
|
||||
received_sigusr1 = 1;
|
||||
break;
|
||||
}
|
||||
if (selfpipe_wrfd != -1) {
|
||||
ssize_t n;
|
||||
|
||||
#ifdef DEBUG_PRIVSEP_SERVER
|
||||
log_dbg_printf("writing to selfpipe_wrfd %i\n", selfpipe_wrfd);
|
||||
#endif /* DEBUG_PRIVSEP_SERVER */
|
||||
do {
|
||||
n = write(selfpipe_wrfd, "!", 1);
|
||||
} while (n == -1 && errno == EINTR);
|
||||
if (n == -1) {
|
||||
log_err_level_printf(LOG_CRIT, "Failed to write from signal handler: "
|
||||
"%s (%i)\n", strerror(errno), errno);
|
||||
/* ignore error */
|
||||
}
|
||||
#ifdef DEBUG_PRIVSEP_SERVER
|
||||
} else {
|
||||
log_dbg_printf("selfpipe_wrfd is %i - not writing\n", selfpipe_wrfd);
|
||||
#endif /* DEBUG_PRIVSEP_SERVER */
|
||||
}
|
||||
errno = saved_errno;
|
||||
}
|
||||
|
||||
static int WUNRES
|
||||
privsep_server_openfile_verify(opts_t *opts, const char *fn, UNUSED int mkpath)
|
||||
{
|
||||
/* Prefix must match one of the active log files that use privsep. */
|
||||
do {
|
||||
if (opts->contentlog) {
|
||||
if (strstr(fn, opts->contentlog_isspec
|
||||
? opts->contentlog_basedir
|
||||
: opts->contentlog) == fn)
|
||||
break;
|
||||
}
|
||||
if (opts->connectlog) {
|
||||
if (strstr(fn, opts->connectlog) == fn)
|
||||
break;
|
||||
}
|
||||
return -1;
|
||||
} while (0);
|
||||
|
||||
/* Path must not contain dot-dot to prevent escaping the prefix. */
|
||||
if (strstr(fn, "/../"))
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int WUNRES
|
||||
privsep_server_openfile(const char *fn, int mkpath)
|
||||
{
|
||||
int fd, tmp;
|
||||
|
||||
if (mkpath) {
|
||||
char *filedir, *fn2;
|
||||
|
||||
fn2 = strdup(fn);
|
||||
if (!fn2) {
|
||||
tmp = errno;
|
||||
log_err_level_printf(LOG_CRIT, "Could not duplicate filname: %s (%i)\n",
|
||||
strerror(errno), errno);
|
||||
errno = tmp;
|
||||
return -1;
|
||||
}
|
||||
filedir = dirname(fn2);
|
||||
if (!filedir) {
|
||||
tmp = errno;
|
||||
log_err_level_printf(LOG_CRIT, "Could not get dirname: %s (%i)\n",
|
||||
strerror(errno), errno);
|
||||
free(fn2);
|
||||
errno = tmp;
|
||||
return -1;
|
||||
}
|
||||
if (sys_mkpath(filedir, DFLT_DIRMODE) == -1) {
|
||||
tmp = errno;
|
||||
log_err_level_printf(LOG_CRIT, "Could not create directory '%s': %s (%i)\n",
|
||||
filedir, strerror(errno), errno);
|
||||
free(fn2);
|
||||
errno = tmp;
|
||||
return -1;
|
||||
}
|
||||
free(fn2);
|
||||
}
|
||||
|
||||
fd = open(fn, O_RDWR|O_CREAT, DFLT_FILEMODE);
|
||||
if (fd == -1) {
|
||||
tmp = errno;
|
||||
log_err_level_printf(LOG_CRIT, "Failed to open '%s': %s (%i)\n",
|
||||
fn, strerror(errno), errno);
|
||||
errno = tmp;
|
||||
return -1;
|
||||
}
|
||||
if (lseek(fd, 0, SEEK_END) == -1) {
|
||||
tmp = errno;
|
||||
log_err_level_printf(LOG_CRIT, "Failed to seek on '%s': %s (%i)\n",
|
||||
fn, strerror(errno), errno);
|
||||
errno = tmp;
|
||||
return -1;
|
||||
}
|
||||
return fd;
|
||||
}
|
||||
|
||||
static int WUNRES
|
||||
privsep_server_opensock_verify(opts_t *opts, void *arg)
|
||||
{
|
||||
/* This check is safe, because modifications of the spec in the child
|
||||
* process do not affect the copy of the spec here in the parent. */
|
||||
for (proxyspec_t *spec = opts->spec; spec; spec = spec->next) {
|
||||
if (spec == arg)
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int WUNRES
|
||||
privsep_server_opensock(const proxyspec_t *spec)
|
||||
{
|
||||
evutil_socket_t fd;
|
||||
int on = 1;
|
||||
int rv;
|
||||
|
||||
fd = socket(spec->listen_addr.ss_family, SOCK_STREAM, IPPROTO_TCP);
|
||||
if (fd == -1) {
|
||||
log_err_level_printf(LOG_CRIT, "Error from socket(): %s (%i)\n",
|
||||
strerror(errno), errno);
|
||||
evutil_closesocket(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
rv = evutil_make_socket_nonblocking(fd);
|
||||
if (rv == -1) {
|
||||
log_err_level_printf(LOG_CRIT, "Error making socket nonblocking: %s (%i)\n",
|
||||
strerror(errno), errno);
|
||||
evutil_closesocket(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
rv = setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (void*)&on, sizeof(on));
|
||||
if (rv == -1) {
|
||||
log_err_level_printf(LOG_CRIT, "Error from setsockopt(SO_KEEPALIVE): %s (%i)\n",
|
||||
strerror(errno), errno);
|
||||
evutil_closesocket(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
rv = evutil_make_listen_socket_reuseable(fd);
|
||||
if (rv == -1) {
|
||||
log_err_level_printf(LOG_CRIT, "Error from setsockopt(SO_REUSABLE): %s\n",
|
||||
strerror(errno));
|
||||
evutil_closesocket(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
rv = bind(fd, (struct sockaddr *)&spec->listen_addr,
|
||||
spec->listen_addrlen);
|
||||
if (rv == -1) {
|
||||
log_err_level_printf(LOG_CRIT, "Error from bind(): %s\n", strerror(errno));
|
||||
evutil_closesocket(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle a single request on a readable server socket.
|
||||
* Returns 0 on success, 1 on EOF and -1 on error.
|
||||
*/
|
||||
static int WUNRES
|
||||
privsep_server_handle_req(opts_t *opts, int srvsock)
|
||||
{
|
||||
char req[PRIVSEP_MAX_REQ_SIZE];
|
||||
char ans[PRIVSEP_MAX_ANS_SIZE];
|
||||
ssize_t n;
|
||||
int mkpath = 0;
|
||||
|
||||
if ((n = sys_recvmsgfd(srvsock, req, sizeof(req),
|
||||
NULL)) == -1) {
|
||||
if (errno == EPIPE || errno == ECONNRESET) {
|
||||
/* unfriendly EOF, leave server */
|
||||
return 1;
|
||||
}
|
||||
log_err_level_printf(LOG_CRIT, "Failed to receive msg: %s (%i)\n",
|
||||
strerror(errno), errno);
|
||||
return -1;
|
||||
}
|
||||
if (n == 0) {
|
||||
/* EOF, leave server; will not happen for SOCK_DGRAM sockets */
|
||||
return 1;
|
||||
}
|
||||
log_dbg_printf("Received privsep req type %02x sz %zd on srvsock %i\n",
|
||||
req[0], n, srvsock);
|
||||
switch (req[0]) {
|
||||
case PRIVSEP_REQ_CLOSE: {
|
||||
/* client indicates EOF through close message */
|
||||
return 1;
|
||||
}
|
||||
case PRIVSEP_REQ_OPENFILE_P:
|
||||
mkpath = 1;
|
||||
/* fall through */
|
||||
case PRIVSEP_REQ_OPENFILE: {
|
||||
char *fn;
|
||||
int fd;
|
||||
|
||||
if (n < 2) {
|
||||
ans[0] = PRIVSEP_ANS_INVALID;
|
||||
if (sys_sendmsgfd(srvsock, ans, 1, -1) == -1) {
|
||||
log_err_level_printf(LOG_CRIT, "Sending message failed: %s (%i"
|
||||
")\n", strerror(errno), errno);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
if (!(fn = malloc(n))) {
|
||||
ans[0] = PRIVSEP_ANS_SYS_ERR;
|
||||
*((int*)&ans[1]) = errno;
|
||||
if (sys_sendmsgfd(srvsock, ans, 1 + sizeof(int),
|
||||
-1) == -1) {
|
||||
log_err_level_printf(LOG_CRIT, "Sending message failed: %s (%i"
|
||||
")\n", strerror(errno), errno);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
memcpy(fn, req + 1, n - 1);
|
||||
fn[n - 1] = '\0';
|
||||
if (privsep_server_openfile_verify(opts, fn, mkpath) == -1) {
|
||||
free(fn);
|
||||
ans[0] = PRIVSEP_ANS_DENIED;
|
||||
if (sys_sendmsgfd(srvsock, ans, 1, -1) == -1) {
|
||||
log_err_level_printf(LOG_CRIT, "Sending message failed: %s (%i"
|
||||
")\n", strerror(errno), errno);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
if ((fd = privsep_server_openfile(fn, mkpath)) == -1) {
|
||||
free(fn);
|
||||
ans[0] = PRIVSEP_ANS_SYS_ERR;
|
||||
*((int*)&ans[1]) = errno;
|
||||
if (sys_sendmsgfd(srvsock, ans, 1 + sizeof(int),
|
||||
-1) == -1) {
|
||||
log_err_level_printf(LOG_CRIT, "Sending message failed: %s (%i"
|
||||
")\n", strerror(errno), errno);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
} else {
|
||||
free(fn);
|
||||
ans[0] = PRIVSEP_ANS_SUCCESS;
|
||||
if (sys_sendmsgfd(srvsock, ans, 1, fd) == -1) {
|
||||
close(fd);
|
||||
log_err_level_printf(LOG_CRIT, "Sending message failed: %s (%i"
|
||||
")\n", strerror(errno), errno);
|
||||
return -1;
|
||||
}
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
||||
/* not reached */
|
||||
break;
|
||||
}
|
||||
case PRIVSEP_REQ_OPENSOCK: {
|
||||
proxyspec_t *arg;
|
||||
int s;
|
||||
|
||||
if (n != sizeof(char) + sizeof(arg)) {
|
||||
ans[0] = PRIVSEP_ANS_INVALID;
|
||||
if (sys_sendmsgfd(srvsock, ans, 1, -1) == -1) {
|
||||
log_err_level_printf(LOG_CRIT, "Sending message failed: %s (%i"
|
||||
")\n", strerror(errno), errno);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
arg = *(proxyspec_t**)(&req[1]);
|
||||
if (privsep_server_opensock_verify(opts, arg) == -1) {
|
||||
ans[0] = PRIVSEP_ANS_DENIED;
|
||||
if (sys_sendmsgfd(srvsock, ans, 1, -1) == -1) {
|
||||
log_err_level_printf(LOG_CRIT, "Sending message failed: %s (%i"
|
||||
")\n", strerror(errno), errno);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
if ((s = privsep_server_opensock(arg)) == -1) {
|
||||
ans[0] = PRIVSEP_ANS_SYS_ERR;
|
||||
*((int*)&ans[1]) = errno;
|
||||
if (sys_sendmsgfd(srvsock, ans, 1 + sizeof(int),
|
||||
-1) == -1) {
|
||||
log_err_level_printf(LOG_CRIT, "Sending message failed: %s (%i"
|
||||
")\n", strerror(errno), errno);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
} else {
|
||||
ans[0] = PRIVSEP_ANS_SUCCESS;
|
||||
if (sys_sendmsgfd(srvsock, ans, 1, s) == -1) {
|
||||
evutil_closesocket(s);
|
||||
log_err_level_printf(LOG_CRIT, "Sending message failed: %s (%i"
|
||||
")\n", strerror(errno), errno);
|
||||
return -1;
|
||||
}
|
||||
evutil_closesocket(s);
|
||||
return 0;
|
||||
}
|
||||
/* not reached */
|
||||
break;
|
||||
}
|
||||
default:
|
||||
ans[0] = PRIVSEP_ANS_UNK_CMD;
|
||||
if (sys_sendmsgfd(srvsock, ans, 1, -1) == -1) {
|
||||
log_err_level_printf(LOG_CRIT, "Sending message failed: %s (%i"
|
||||
")\n", strerror(errno), errno);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define util_max(a,b) ((a) > (b) ? (a) : (b))
|
||||
|
||||
/*
|
||||
* Privilege separation server (main privileged monitor loop)
|
||||
*
|
||||
* sigpipe is the self-pipe trick pipe used for communicating signals to
|
||||
* the main event loop and break out of select() without race conditions.
|
||||
* srvsock[] is a dynamic array of connected privsep server sockets to serve.
|
||||
* Caller is responsible for freeing memory after returning, if necessary.
|
||||
* childpid is the pid of the child process to forward signals to.
|
||||
*
|
||||
* Returns 0 on a successful clean exit and -1 on errors.
|
||||
*/
|
||||
static int
|
||||
privsep_server(opts_t *opts, int sigpipe, int srvsock[], size_t nsrvsock,
|
||||
pid_t childpid)
|
||||
{
|
||||
int srveof[nsrvsock];
|
||||
size_t i = 0;
|
||||
|
||||
for (i = 0; i < nsrvsock; i++) {
|
||||
srveof[i] = 0;
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
fd_set readfds;
|
||||
int maxfd, rv;
|
||||
|
||||
#ifdef DEBUG_PRIVSEP_SERVER
|
||||
log_dbg_printf("privsep_server select()\n");
|
||||
#endif /* DEBUG_PRIVSEP_SERVER */
|
||||
do {
|
||||
FD_ZERO(&readfds);
|
||||
FD_SET(sigpipe, &readfds);
|
||||
maxfd = sigpipe;
|
||||
for (i = 0; i < nsrvsock; i++) {
|
||||
if (!srveof[i]) {
|
||||
FD_SET(srvsock[i], &readfds);
|
||||
maxfd = util_max(maxfd, srvsock[i]);
|
||||
}
|
||||
}
|
||||
rv = select(maxfd + 1, &readfds, NULL, NULL, NULL);
|
||||
#ifdef DEBUG_PRIVSEP_SERVER
|
||||
log_dbg_printf("privsep_server woke up (1)\n");
|
||||
#endif /* DEBUG_PRIVSEP_SERVER */
|
||||
} while (rv == -1 && errno == EINTR);
|
||||
if (rv == -1) {
|
||||
log_err_level_printf(LOG_CRIT, "select() failed: %s (%i)\n",
|
||||
strerror(errno), errno);
|
||||
return -1;
|
||||
}
|
||||
#ifdef DEBUG_PRIVSEP_SERVER
|
||||
log_dbg_printf("privsep_server woke up (2)\n");
|
||||
#endif /* DEBUG_PRIVSEP_SERVER */
|
||||
|
||||
if (FD_ISSET(sigpipe, &readfds)) {
|
||||
char buf[16];
|
||||
ssize_t n;
|
||||
/* first drain the signal pipe, then deal with
|
||||
* all the individual signal flags */
|
||||
n = read(sigpipe, buf, sizeof(buf));
|
||||
if (n == -1) {
|
||||
log_err_level_printf(LOG_CRIT, "read(sigpipe) failed:"
|
||||
" %s (%i)\n",
|
||||
strerror(errno), errno);
|
||||
return -1;
|
||||
}
|
||||
if (received_sigquit) {
|
||||
if (kill(childpid, SIGQUIT) == -1) {
|
||||
log_err_level_printf(LOG_CRIT, "kill(%i,SIGQUIT) "
|
||||
"failed: %s (%i)\n",
|
||||
childpid,
|
||||
strerror(errno), errno);
|
||||
}
|
||||
received_sigquit = 0;
|
||||
}
|
||||
if (received_sigterm) {
|
||||
if (kill(childpid, SIGTERM) == -1) {
|
||||
log_err_level_printf(LOG_CRIT, "kill(%i,SIGTERM) "
|
||||
"failed: %s (%i)\n",
|
||||
childpid,
|
||||
strerror(errno), errno);
|
||||
}
|
||||
received_sigterm = 0;
|
||||
}
|
||||
if (received_sighup) {
|
||||
if (kill(childpid, SIGHUP) == -1) {
|
||||
log_err_level_printf(LOG_CRIT, "kill(%i,SIGHUP) "
|
||||
"failed: %s (%i)\n",
|
||||
childpid,
|
||||
strerror(errno), errno);
|
||||
}
|
||||
received_sighup = 0;
|
||||
}
|
||||
if (received_sigusr1) {
|
||||
if (kill(childpid, SIGUSR1) == -1) {
|
||||
log_err_level_printf(LOG_CRIT, "kill(%i,SIGUSR1) "
|
||||
"failed: %s (%i)\n",
|
||||
childpid,
|
||||
strerror(errno), errno);
|
||||
}
|
||||
received_sigusr1 = 0;
|
||||
}
|
||||
if (received_sigint) {
|
||||
/* if we don't detach from the TTY, the
|
||||
* child process receives SIGINT directly */
|
||||
if (opts->detach) {
|
||||
if (kill(childpid, SIGINT) == -1) {
|
||||
log_err_level_printf(LOG_CRIT, "kill(%i,SIGINT"
|
||||
") failed: "
|
||||
"%s (%i)\n",
|
||||
childpid,
|
||||
strerror(errno),
|
||||
errno);
|
||||
}
|
||||
}
|
||||
received_sigint = 0;
|
||||
}
|
||||
if (received_sigchld) {
|
||||
/* break the loop; because we are using
|
||||
* SOCKET_DGRAM we don't get EOF conditions
|
||||
* on the disconnected socket ends here
|
||||
* unless we attempt to write or read, so
|
||||
* we depend on SIGCHLD to notify us of
|
||||
* our child erroring out or crashing */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < nsrvsock; i++) {
|
||||
if (FD_ISSET(srvsock[i], &readfds)) {
|
||||
int rv = privsep_server_handle_req(opts,
|
||||
srvsock[i]);
|
||||
if (rv == -1) {
|
||||
log_err_level_printf(LOG_CRIT, "Failed to handle "
|
||||
"privsep req "
|
||||
"on srvsock %i\n",
|
||||
srvsock[i]);
|
||||
return -1;
|
||||
}
|
||||
if (rv == 1) {
|
||||
#ifdef DEBUG_PRIVSEP_SERVER
|
||||
log_dbg_printf("srveof[%zu]=1\n", i);
|
||||
#endif /* DEBUG_PRIVSEP_SERVER */
|
||||
srveof[i] = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* We cannot exit as long as we need the signal handling,
|
||||
* which is as long as the child process is running.
|
||||
* The only way out of here is receiving SIGCHLD.
|
||||
*/
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
privsep_client_openfile(int clisock, const char *fn, int mkpath)
|
||||
{
|
||||
char ans[PRIVSEP_MAX_ANS_SIZE];
|
||||
char req[1 + strlen(fn)];
|
||||
int fd = -1;
|
||||
ssize_t n;
|
||||
|
||||
if (privsep_fastpath)
|
||||
return privsep_server_openfile(fn, mkpath);
|
||||
|
||||
req[0] = mkpath ? PRIVSEP_REQ_OPENFILE_P : PRIVSEP_REQ_OPENFILE;
|
||||
memcpy(req + 1, fn, sizeof(req) - 1);
|
||||
|
||||
if (sys_sendmsgfd(clisock, req, sizeof(req), -1) == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((n = sys_recvmsgfd(clisock, ans, sizeof(ans), &fd)) == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (n < 1) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
switch (ans[0]) {
|
||||
case PRIVSEP_ANS_SUCCESS:
|
||||
break;
|
||||
case PRIVSEP_ANS_DENIED:
|
||||
errno = EACCES;
|
||||
return -1;
|
||||
case PRIVSEP_ANS_SYS_ERR:
|
||||
if (n < (ssize_t)(1 + sizeof(int))) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
errno = *((int*)&ans[1]);
|
||||
return -1;
|
||||
case PRIVSEP_ANS_UNK_CMD:
|
||||
case PRIVSEP_ANS_INVALID:
|
||||
default:
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
int
|
||||
privsep_client_opensock(int clisock, const proxyspec_t *spec)
|
||||
{
|
||||
char ans[PRIVSEP_MAX_ANS_SIZE];
|
||||
char req[1 + sizeof(spec)];
|
||||
int fd = -1;
|
||||
ssize_t n;
|
||||
|
||||
if (privsep_fastpath)
|
||||
return privsep_server_opensock(spec);
|
||||
|
||||
req[0] = PRIVSEP_REQ_OPENSOCK;
|
||||
*((const proxyspec_t **)&req[1]) = spec;
|
||||
|
||||
if (sys_sendmsgfd(clisock, req, sizeof(req), -1) == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((n = sys_recvmsgfd(clisock, ans, sizeof(ans), &fd)) == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (n < 1) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
switch (ans[0]) {
|
||||
case PRIVSEP_ANS_SUCCESS:
|
||||
break;
|
||||
case PRIVSEP_ANS_DENIED:
|
||||
errno = EACCES;
|
||||
return -1;
|
||||
case PRIVSEP_ANS_SYS_ERR:
|
||||
if (n < (ssize_t)(1 + sizeof(int))) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
errno = *((int*)&ans[1]);
|
||||
return -1;
|
||||
case PRIVSEP_ANS_UNK_CMD:
|
||||
case PRIVSEP_ANS_INVALID:
|
||||
default:
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
int
|
||||
privsep_client_close(int clisock)
|
||||
{
|
||||
char req[1];
|
||||
|
||||
req[0] = PRIVSEP_REQ_CLOSE;
|
||||
|
||||
if (sys_sendmsgfd(clisock, req, sizeof(req), -1) == -1) {
|
||||
close(clisock);
|
||||
return -1;
|
||||
}
|
||||
|
||||
close(clisock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Fork and set up privilege separated monitor process.
|
||||
* Returns -1 on error before forking, 1 as parent, or 0 as child.
|
||||
* The array of clisock's will get filled with nclisock privsep client
|
||||
* sockets only for the child; on error and in the parent process it
|
||||
* will not be touched.
|
||||
*/
|
||||
int
|
||||
privsep_fork(opts_t *opts, int clisock[], size_t nclisock)
|
||||
{
|
||||
int selfpipev[2]; /* self-pipe trick: signal handler -> select */
|
||||
int chldpipev[2]; /* el cheapo interprocess sync early after fork */
|
||||
int sockcliv[nclisock][2];
|
||||
pid_t pid;
|
||||
|
||||
if (!opts->dropuser) {
|
||||
log_dbg_printf("Privsep fastpath enabled\n");
|
||||
privsep_fastpath = 1;
|
||||
} else {
|
||||
log_dbg_printf("Privsep fastpath disabled\n");
|
||||
privsep_fastpath = 0;
|
||||
}
|
||||
|
||||
received_sigquit = 0;
|
||||
received_sighup = 0;
|
||||
received_sigint = 0;
|
||||
received_sigchld = 0;
|
||||
received_sigusr1 = 0;
|
||||
|
||||
if (pipe(selfpipev) == -1) {
|
||||
log_err_level_printf(LOG_CRIT, "Failed to create self-pipe: %s (%i)\n",
|
||||
strerror(errno), errno);
|
||||
return -1;
|
||||
}
|
||||
log_dbg_printf("Created self-pipe [r=%i,w=%i]\n",
|
||||
selfpipev[0], selfpipev[1]);
|
||||
|
||||
if (pipe(chldpipev) == -1) {
|
||||
log_err_level_printf(LOG_CRIT, "Failed to create chld-pipe: %s (%i)\n",
|
||||
strerror(errno), errno);
|
||||
return -1;
|
||||
}
|
||||
log_dbg_printf("Created chld-pipe [r=%i,w=%i]\n",
|
||||
chldpipev[0], chldpipev[1]);
|
||||
|
||||
for (size_t i = 0; i < nclisock; i++) {
|
||||
if (socketpair(AF_UNIX, SOCK_DGRAM, 0, sockcliv[i]) == -1) {
|
||||
log_err_level_printf(LOG_CRIT, "Failed to create socket pair %zu: "
|
||||
"%s (%i)\n", i, strerror(errno), errno);
|
||||
return -1;
|
||||
}
|
||||
log_dbg_printf("Created socketpair %zu [p=%i,c=%i]\n",
|
||||
i, sockcliv[i][0], sockcliv[i][1]);
|
||||
}
|
||||
|
||||
log_dbg_printf("Privsep parent pid %i\n", getpid());
|
||||
pid = fork();
|
||||
if (pid == -1) {
|
||||
log_err_level_printf(LOG_CRIT, "Failed to fork: %s (%i)\n",
|
||||
strerror(errno), errno);
|
||||
close(selfpipev[0]);
|
||||
close(selfpipev[1]);
|
||||
close(chldpipev[0]);
|
||||
close(chldpipev[1]);
|
||||
for (size_t i = 0; i < nclisock; i++) {
|
||||
close(sockcliv[i][0]);
|
||||
close(sockcliv[i][1]);
|
||||
}
|
||||
return -1;
|
||||
} else if (pid == 0) {
|
||||
/* child */
|
||||
close(selfpipev[0]);
|
||||
close(selfpipev[1]);
|
||||
for (size_t i = 0; i < nclisock; i++)
|
||||
close(sockcliv[i][0]);
|
||||
/* wait until parent has installed signal handlers,
|
||||
* intentionally ignoring errors */
|
||||
char buf[1];
|
||||
ssize_t n;
|
||||
close(chldpipev[1]);
|
||||
do {
|
||||
n = read(chldpipev[0], buf, sizeof(buf));
|
||||
} while (n == -1 && errno == EINTR);
|
||||
close(chldpipev[0]);
|
||||
log_dbg_printf("Privsep child pid %i\n", getpid());
|
||||
/* return the privsep client sockets */
|
||||
for (size_t i = 0; i < nclisock; i++)
|
||||
clisock[i] = sockcliv[i][1];
|
||||
return 0;
|
||||
}
|
||||
/* parent */
|
||||
for (size_t i = 0; i < nclisock; i++)
|
||||
close(sockcliv[i][1]);
|
||||
selfpipe_wrfd = selfpipev[1];
|
||||
|
||||
/* close file descriptors opened by preinit's only needed in client;
|
||||
* we still call the preinit's before forking in order to provide
|
||||
* better user feedback and less privsep complexity */
|
||||
log_preinit_undo();
|
||||
|
||||
/* If the child exits before the parent installs the signal handler
|
||||
* here, we have a race condition; this is solved by the client
|
||||
* blocking on the reading end of a pipe (chldpipev[0]). */
|
||||
if (signal(SIGHUP, privsep_server_signal_handler) == SIG_ERR) {
|
||||
log_err_level_printf(LOG_CRIT, "Failed to install SIGHUP handler: %s (%i)\n",
|
||||
strerror(errno), errno);
|
||||
return -1;
|
||||
}
|
||||
if (signal(SIGINT, privsep_server_signal_handler) == SIG_ERR) {
|
||||
log_err_level_printf(LOG_CRIT, "Failed to install SIGINT handler: %s (%i)\n",
|
||||
strerror(errno), errno);
|
||||
return -1;
|
||||
}
|
||||
if (signal(SIGTERM, privsep_server_signal_handler) == SIG_ERR) {
|
||||
log_err_level_printf(LOG_CRIT, "Failed to install SIGTERM handler: %s (%i)\n",
|
||||
strerror(errno), errno);
|
||||
return -1;
|
||||
}
|
||||
if (signal(SIGQUIT, privsep_server_signal_handler) == SIG_ERR) {
|
||||
log_err_level_printf(LOG_CRIT, "Failed to install SIGQUIT handler: %s (%i)\n",
|
||||
strerror(errno), errno);
|
||||
return -1;
|
||||
}
|
||||
if (signal(SIGUSR1, privsep_server_signal_handler) == SIG_ERR) {
|
||||
log_err_level_printf(LOG_CRIT, "Failed to install SIGUSR1 handler: %s (%i)\n",
|
||||
strerror(errno), errno);
|
||||
return -1;
|
||||
}
|
||||
if (signal(SIGCHLD, privsep_server_signal_handler) == SIG_ERR) {
|
||||
log_err_level_printf(LOG_CRIT, "Failed to install SIGCHLD handler: %s (%i)\n",
|
||||
strerror(errno), errno);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* unblock the child */
|
||||
close(chldpipev[0]);
|
||||
close(chldpipev[1]);
|
||||
|
||||
int socksrv[nclisock];
|
||||
for (size_t i = 0; i < nclisock; i++)
|
||||
socksrv[i] = sockcliv[i][0];
|
||||
if (privsep_server(opts, selfpipev[0], socksrv, nclisock, pid) == -1) {
|
||||
log_err_level_printf(LOG_CRIT, "Privsep server failed: %s (%i)\n",
|
||||
strerror(errno), errno);
|
||||
/* fall through */
|
||||
}
|
||||
#ifdef DEBUG_PRIVSEP_SERVER
|
||||
log_dbg_printf("privsep_server exited\n");
|
||||
#endif /* DEBUG_PRIVSEP_SERVER */
|
||||
|
||||
for (size_t i = 0; i < nclisock; i++)
|
||||
close(sockcliv[i][0]);
|
||||
selfpipe_wrfd = -1; /* tell signal handler not to write anymore */
|
||||
close(selfpipev[0]);
|
||||
close(selfpipev[1]);
|
||||
|
||||
int status;
|
||||
wait(&status);
|
||||
if (WIFEXITED(status)) {
|
||||
if (WEXITSTATUS(status) != 0) {
|
||||
log_err_level_printf(LOG_CRIT, "Child proc %lld exited with status %d\n",
|
||||
(long long)pid, WEXITSTATUS(status));
|
||||
} else {
|
||||
log_dbg_printf("Child proc %lld exited with status %d\n",
|
||||
(long long)pid, WEXITSTATUS(status));
|
||||
}
|
||||
} else if (WIFSIGNALED(status)) {
|
||||
log_err_level_printf(LOG_CRIT, "Child proc %lld killed by signal %d\n",
|
||||
(long long)pid, WTERMSIG(status));
|
||||
} else {
|
||||
log_err_level_printf(LOG_CRIT, "Child proc %lld neither exited nor killed\n",
|
||||
(long long)pid);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* vim: set noet ft=c: */
|
||||
|
||||
|
||||
|
@ -0,0 +1,42 @@
|
||||
/*-
|
||||
* SSLsplit - transparent SSL/TLS interception
|
||||
* https://www.roe.ch/SSLsplit
|
||||
*
|
||||
* Copyright (c) 2009-2018, Daniel Roethlisberger <daniel@roe.ch>.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef PRIVSEP_H
|
||||
#define PRIVSEP_H
|
||||
|
||||
#include "attrib.h"
|
||||
#include "opts.h"
|
||||
|
||||
int privsep_fork(opts_t *, int[], size_t);
|
||||
|
||||
int privsep_client_openfile(int, const char *, int);
|
||||
int privsep_client_opensock(int, const proxyspec_t *spec);
|
||||
int privsep_client_close(int);
|
||||
#endif /* !PRIVSEP_H */
|
||||
|
||||
/* vim: set noet ft=c: */
|
@ -0,0 +1,552 @@
|
||||
/*-
|
||||
* SSLsplit - transparent SSL/TLS interception
|
||||
* https://www.roe.ch/SSLsplit
|
||||
*
|
||||
* Copyright (c) 2009-2018, Daniel Roethlisberger <daniel@roe.ch>.
|
||||
* Copyright (c) 2017-2019, Soner Tari <sonertari@gmail.com>.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "prototcp.h"
|
||||
#include "sys.h"
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <string.h>
|
||||
|
||||
/*
|
||||
* Set up a bufferevent structure for either a dst or src connection,
|
||||
* optionally with or without SSL. Sets all callbacks, enables read
|
||||
* and write events, but does not call bufferevent_socket_connect().
|
||||
*
|
||||
* For dst connections, pass -1 as fd. Pass a pointer to an initialized
|
||||
* SSL struct as ssl if the connection should use SSL.
|
||||
*
|
||||
* Returns pointer to initialized bufferevent structure, as returned
|
||||
* by bufferevent_socket_new() or bufferevent_openssl_socket_new().
|
||||
*/
|
||||
static struct bufferevent * NONNULL(1)
|
||||
prototcp_bufferevent_setup(pxy_conn_ctx_t *ctx, evutil_socket_t fd)
|
||||
{
|
||||
#ifdef DEBUG_PROXY
|
||||
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "prototcp_bufferevent_setup: ENTER, fd=%d\n", fd);
|
||||
#endif /* DEBUG_PROXY */
|
||||
|
||||
// @todo Do we really need to defer callbacks? BEV_OPT_DEFER_CALLBACKS seems responsible for the issue with dst: We get writecb sometimes, no eventcb for CONNECTED event
|
||||
struct bufferevent *bev = bufferevent_socket_new(ctx->evbase, fd, BEV_OPT_DEFER_CALLBACKS|BEV_OPT_THREADSAFE);
|
||||
if (!bev) {
|
||||
log_err_level_printf(LOG_CRIT, "Error creating bufferevent socket\n");
|
||||
#ifdef DEBUG_PROXY
|
||||
log_dbg_level_printf(LOG_DBG_MODE_FINE, "prototcp_bufferevent_setup: bufferevent_socket_connect failed, fd=%d\n", fd);
|
||||
#endif /* DEBUG_PROXY */
|
||||
|
||||
return NULL;
|
||||
}
|
||||
return bev;
|
||||
}
|
||||
|
||||
/*
|
||||
* Free bufferenvent and close underlying socket properly.
|
||||
*/
|
||||
static void
|
||||
prototcp_bufferevent_free_and_close_fd(struct bufferevent *bev, UNUSED pxy_conn_ctx_t *ctx)
|
||||
{
|
||||
evutil_socket_t fd = bufferevent_getfd(bev);
|
||||
|
||||
#ifdef DEBUG_PROXY
|
||||
log_dbg_level_printf(LOG_DBG_MODE_FINER, "prototcp_bufferevent_free_and_close_fd: in=%zu, out=%zu, fd=%d\n",
|
||||
evbuffer_get_length(bufferevent_get_input(bev)), evbuffer_get_length(bufferevent_get_output(bev)), fd);
|
||||
#endif /* DEBUG_PROXY */
|
||||
|
||||
bufferevent_free(bev);
|
||||
evutil_closesocket(fd);
|
||||
}
|
||||
|
||||
static int NONNULL(1)
|
||||
prototcp_setup_src(pxy_conn_ctx_t *ctx)
|
||||
{
|
||||
ctx->src.bev = prototcp_bufferevent_setup(ctx, ctx->fd);
|
||||
if (!ctx->src.bev) {
|
||||
log_err_level_printf(LOG_CRIT, "Error creating src bufferevent\n");
|
||||
pxy_conn_term(ctx, 1);
|
||||
return -1;
|
||||
}
|
||||
ctx->src.free = prototcp_bufferevent_free_and_close_fd;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int NONNULL(1)
|
||||
prototcp_setup_dst(pxy_conn_ctx_t *ctx)
|
||||
{
|
||||
ctx->dst.bev = prototcp_bufferevent_setup(ctx, -1);
|
||||
if (!ctx->dst.bev) {
|
||||
log_err_level_printf(LOG_CRIT, "Error creating parent dst\n");
|
||||
pxy_conn_term(ctx, 1);
|
||||
return -1;
|
||||
}
|
||||
ctx->dst.free = prototcp_bufferevent_free_and_close_fd;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int NONNULL(1) WUNRES
|
||||
prototcp_conn_connect(pxy_conn_ctx_t *ctx)
|
||||
{
|
||||
#ifdef DEBUG_PROXY
|
||||
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "prototcp_conn_connect: ENTER, fd=%d\n", ctx->fd);
|
||||
#endif /* DEBUG_PROXY */
|
||||
|
||||
if (prototcp_setup_src(ctx) == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (prototcp_setup_dst(ctx) == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Conn setup is successful, so add the conn to the conn list of its thread now
|
||||
pxy_thrmgr_add_conn(ctx);
|
||||
|
||||
bufferevent_setcb(ctx->src.bev, pxy_bev_readcb, pxy_bev_writecb, pxy_bev_eventcb, ctx);
|
||||
bufferevent_setcb(ctx->dst.bev, pxy_bev_readcb, pxy_bev_writecb, pxy_bev_eventcb, ctx);
|
||||
|
||||
#ifdef DEBUG_PROXY
|
||||
log_dbg_level_printf(LOG_DBG_MODE_FINER, "prototcp_conn_connect: Enabling src, fd=%d\n", ctx->fd);
|
||||
#endif /* DEBUG_PROXY */
|
||||
|
||||
// Now open the gates
|
||||
bufferevent_enable(ctx->src.bev, EV_READ|EV_WRITE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
prototcp_fd_readcb(UNUSED evutil_socket_t fd, UNUSED short what, void *arg)
|
||||
{
|
||||
pxy_conn_ctx_t *ctx = arg;
|
||||
|
||||
#ifdef DEBUG_PROXY
|
||||
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "prototcp_fd_readcb: ENTER, fd=%d\n", ctx->fd);
|
||||
#endif /* DEBUG_PROXY */
|
||||
|
||||
pxy_conn_connect(ctx);
|
||||
}
|
||||
|
||||
static int
|
||||
prototcp_parse_sslproxy_line(char *line, pxy_conn_ctx_t *ctx)
|
||||
{
|
||||
#define MAX_IPADDR_LEN 45
|
||||
#define MAX_PORT_LEN 5
|
||||
|
||||
// SSLproxy: [127.0.0.1]:34649,[192.168.3.24]:47286,[74.125.206.108]:465,s,soner
|
||||
if (!strncasecmp(line, "SSLproxy:", 9)) {
|
||||
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "%s\n", line);
|
||||
|
||||
char *ip_start = strchr(line, '[') + 1;
|
||||
char *ip_end = strchr(ip_start, ']');
|
||||
char *port_start = strchr(ip_end, ':') + 1;
|
||||
char *port_end = strchr(port_start, ',');
|
||||
|
||||
if (!ip_start || !ip_end || !port_start || !port_end) {
|
||||
log_err_level_printf(LOG_ERR, "Unable to find sslproxy addr: %s", line);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int addr_len = ip_end - ip_start;
|
||||
if (addr_len > MAX_IPADDR_LEN) {
|
||||
log_err_level_printf(LOG_ERR, "sslproxy addr_len larger than MAX_IPADDR_LEN: %d\n", addr_len);
|
||||
return -1;
|
||||
}
|
||||
|
||||
char addr[MAX_IPADDR_LEN + 1];
|
||||
strncpy(addr, ip_start, addr_len);
|
||||
addr[addr_len] = '\0';
|
||||
|
||||
int port_len = port_end - port_start;
|
||||
if (port_len > MAX_PORT_LEN) {
|
||||
log_err_level_printf(LOG_ERR, "sslproxy port_len larger than MAX_PORT_LEN: %d\n", port_len);
|
||||
return -1;
|
||||
}
|
||||
|
||||
char port[MAX_PORT_LEN + 1];
|
||||
strncpy(port, port_start, port_len);
|
||||
port[port_len] = '\0';
|
||||
|
||||
if (sys_sockaddr_parse(&ctx->dstaddr,
|
||||
&ctx->dstaddrlen,
|
||||
addr, port,
|
||||
sys_get_af(addr),
|
||||
EVUTIL_AI_PASSIVE) == -1) {
|
||||
log_err_level_printf(LOG_ERR, "Cannot convert sslproxy addr to sockaddr: [%s]:%s\n", addr, port);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (OPTS_DEBUG(ctx->opts)) {
|
||||
log_dbg_printf("Connecting to [%s]:%s\n", addr, port);
|
||||
ctx->dsthost_str = strdup(addr);
|
||||
ctx->dstport_str = strdup(port);
|
||||
if (!ctx->dsthost_str || !ctx->dstport_str) {
|
||||
log_err_level_printf(LOG_ERR, "Cannot dup addr or port: [%s]:%s\n", addr, port);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
ctx->seen_sslproxy_line = 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void NONNULL(1,2)
|
||||
prototcp_bev_readcb_src(struct bufferevent *bev, pxy_conn_ctx_t *ctx)
|
||||
{
|
||||
#ifdef DEBUG_PROXY
|
||||
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "prototcp_bev_readcb_src: ENTER, size=%zu, fd=%d\n",
|
||||
evbuffer_get_length(bufferevent_get_input(bev)), ctx->fd);
|
||||
#endif /* DEBUG_PROXY */
|
||||
|
||||
struct evbuffer *inbuf = bufferevent_get_input(bev);
|
||||
struct evbuffer *outbuf = bufferevent_get_output(ctx->dst.bev);
|
||||
|
||||
if (!ctx->seen_sslproxy_line) {
|
||||
char *line;
|
||||
while (!ctx->seen_sslproxy_line && (line = evbuffer_readln(inbuf, NULL, EVBUFFER_EOL_CRLF))) {
|
||||
#ifdef DEBUG_PROXY
|
||||
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "prototcp_bev_readcb_src: %s, fd=%d\n", line, ctx->fd);
|
||||
#endif /* DEBUG_PROXY */
|
||||
|
||||
if (prototcp_parse_sslproxy_line(line, ctx) == -1) {
|
||||
free(line);
|
||||
pxy_conn_term(ctx, 1);
|
||||
return;
|
||||
}
|
||||
|
||||
if (ctx->seen_sslproxy_line) {
|
||||
/* initiate connection */
|
||||
if (bufferevent_socket_connect(ctx->dst.bev, (struct sockaddr *)&ctx->dstaddr, ctx->dstaddrlen) == -1) {
|
||||
log_err_level_printf(LOG_CRIT, "prototcp_bev_readcb_src: bufferevent_socket_connect for dst failed\n");
|
||||
#ifdef DEBUG_PROXY
|
||||
log_dbg_level_printf(LOG_DBG_MODE_FINE, "prototcp_bev_readcb_src: bufferevent_socket_connect for dst failed, fd=%d\n", ctx->fd);
|
||||
#endif /* DEBUG_PROXY */
|
||||
}
|
||||
}
|
||||
|
||||
evbuffer_add_printf(outbuf, "%s\r\n", line);
|
||||
free(line);
|
||||
}
|
||||
|
||||
if (evbuffer_get_length(inbuf) == 0) {
|
||||
goto out;
|
||||
}
|
||||
} else {
|
||||
if (ctx->dst.closed) {
|
||||
pxy_discard_inbuf(bev);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
evbuffer_add_buffer(outbuf, inbuf);
|
||||
out:
|
||||
pxy_try_set_watermark(bev, ctx, ctx->dst.bev);
|
||||
}
|
||||
|
||||
static void NONNULL(1)
|
||||
prototcp_bev_readcb_dst(struct bufferevent *bev, pxy_conn_ctx_t *ctx)
|
||||
{
|
||||
#ifdef DEBUG_PROXY
|
||||
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "prototcp_bev_readcb_dst: ENTER, size=%zu, fd=%d\n",
|
||||
evbuffer_get_length(bufferevent_get_input(bev)), ctx->fd);
|
||||
#endif /* DEBUG_PROXY */
|
||||
|
||||
if (!ctx->dst_connected) {
|
||||
log_err_level_printf(LOG_CRIT, "prototcp_bev_readcb_dst: readcb called when not connected - aborting.\n");
|
||||
log_exceptcb();
|
||||
return;
|
||||
}
|
||||
|
||||
if (ctx->src.closed) {
|
||||
pxy_discard_inbuf(bev);
|
||||
return;
|
||||
}
|
||||
|
||||
struct evbuffer *inbuf = bufferevent_get_input(bev);
|
||||
struct evbuffer *outbuf = bufferevent_get_output(ctx->src.bev);
|
||||
evbuffer_add_buffer(outbuf, inbuf);
|
||||
pxy_try_set_watermark(bev, ctx, ctx->src.bev);
|
||||
}
|
||||
|
||||
static int NONNULL(1,2)
|
||||
prototcp_connect_conn_end(struct bufferevent *bev, pxy_conn_ctx_t *ctx)
|
||||
{
|
||||
#ifdef DEBUG_PROXY
|
||||
log_dbg_level_printf(LOG_DBG_MODE_FINE, "prototcp_connect_conn_end: writecb before connected, fd=%d\n", ctx->fd);
|
||||
#endif /* DEBUG_PROXY */
|
||||
|
||||
// @attention Sometimes write cb fires but not event cb, especially if the listener cb is not finished yet, so the conn stalls.
|
||||
// This is a workaround for this error condition, nothing else seems to work.
|
||||
// @attention Do not try to free the conn here, since the listener cb may not be finished yet, which causes multithreading issues
|
||||
// XXX: Workaround, should find the real cause: BEV_OPT_DEFER_CALLBACKS?
|
||||
ctx->protoctx->bev_eventcb(bev, BEV_EVENT_CONNECTED, ctx);
|
||||
|
||||
return pxy_bev_eventcb_postexec_logging_and_stats(bev, BEV_EVENT_CONNECTED, ctx);
|
||||
}
|
||||
|
||||
static void NONNULL(1)
|
||||
prototcp_bev_writecb_src(struct bufferevent *bev, pxy_conn_ctx_t *ctx)
|
||||
{
|
||||
#ifdef DEBUG_PROXY
|
||||
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "prototcp_bev_writecb_src: ENTER, fd=%d\n", ctx->fd);
|
||||
#endif /* DEBUG_PROXY */
|
||||
|
||||
if (!ctx->src_connected) {
|
||||
if (prototcp_connect_conn_end(bev, ctx) == -1) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (ctx->dst.closed) {
|
||||
if (pxy_try_close_conn_end(&ctx->src, ctx) == 1) {
|
||||
#ifdef DEBUG_PROXY
|
||||
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "prototcp_bev_writecb_src: other->closed, terminate conn, fd=%d\n", ctx->fd);
|
||||
#endif /* DEBUG_PROXY */
|
||||
|
||||
pxy_conn_term(ctx, 1);
|
||||
}
|
||||
return;
|
||||
}
|
||||
pxy_try_unset_watermark(bev, ctx, &ctx->dst);
|
||||
}
|
||||
|
||||
static void NONNULL(1)
|
||||
prototcp_bev_writecb_dst(struct bufferevent *bev, pxy_conn_ctx_t *ctx)
|
||||
{
|
||||
#ifdef DEBUG_PROXY
|
||||
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "prototcp_bev_writecb_dst: ENTER, fd=%d\n", ctx->fd);
|
||||
#endif /* DEBUG_PROXY */
|
||||
|
||||
if (!ctx->dst_connected) {
|
||||
if (prototcp_connect_conn_end(bev, ctx) == -1) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (ctx->src.closed) {
|
||||
if (pxy_try_close_conn_end(&ctx->dst, ctx) == 1) {
|
||||
#ifdef DEBUG_PROXY
|
||||
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "prototcp_bev_writecb_dst: other->closed, terminate conn, fd=%d\n", ctx->fd);
|
||||
#endif /* DEBUG_PROXY */
|
||||
|
||||
pxy_conn_term(ctx, 0);
|
||||
}
|
||||
return;
|
||||
}
|
||||
pxy_try_unset_watermark(bev, ctx, &ctx->src);
|
||||
}
|
||||
|
||||
static void NONNULL(1,2)
|
||||
prototcp_bev_eventcb_connected_src(UNUSED struct bufferevent *bev, UNUSED pxy_conn_ctx_t *ctx)
|
||||
{
|
||||
#ifdef DEBUG_PROXY
|
||||
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "prototcp_bev_eventcb_connected_src: ENTER, fd=%d\n", ctx->fd);
|
||||
#endif /* DEBUG_PROXY */
|
||||
|
||||
ctx->src_connected = 1;
|
||||
}
|
||||
|
||||
static void NONNULL(1,2)
|
||||
prototcp_bev_eventcb_connected_dst(UNUSED struct bufferevent *bev, pxy_conn_ctx_t *ctx)
|
||||
{
|
||||
#ifdef DEBUG_PROXY
|
||||
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "prototcp_bev_eventcb_connected_dst: ENTER, fd=%d\n", ctx->fd);
|
||||
#endif /* DEBUG_PROXY */
|
||||
|
||||
ctx->dst_connected = 1;
|
||||
}
|
||||
|
||||
static void NONNULL(1,2)
|
||||
prototcp_bev_eventcb_eof_src(struct bufferevent *bev, pxy_conn_ctx_t *ctx)
|
||||
{
|
||||
#ifdef DEBUG_PROXY
|
||||
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "prototcp_bev_eventcb_eof_src: ENTER, fd=%d\n", ctx->fd);
|
||||
pxy_log_dbg_evbuf_info(ctx, &ctx->src, &ctx->dst);
|
||||
#endif /* DEBUG_PROXY */
|
||||
|
||||
if (!ctx->src_connected) {
|
||||
log_err_level_printf(LOG_WARNING, "EOF on connection before connection establishment\n");
|
||||
#ifdef DEBUG_PROXY
|
||||
log_dbg_level_printf(LOG_DBG_MODE_FINE, "prototcp_bev_eventcb_eof_src: EOF on connection before connection establishment, fd=%d\n", ctx->fd);
|
||||
#endif /* DEBUG_PROXY */
|
||||
|
||||
ctx->dst.closed = 1;
|
||||
} else if (!ctx->dst.closed) {
|
||||
#ifdef DEBUG_PROXY
|
||||
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "prototcp_bev_eventcb_eof_src: !other->closed, terminate conn, fd=%d\n", ctx->fd);
|
||||
#endif /* DEBUG_PROXY */
|
||||
|
||||
if (pxy_try_consume_last_input(bev, ctx) == -1) {
|
||||
return;
|
||||
}
|
||||
pxy_try_close_conn_end(&ctx->dst, ctx);
|
||||
}
|
||||
|
||||
pxy_try_disconnect(ctx, &ctx->src, &ctx->dst, 1);
|
||||
}
|
||||
|
||||
static void NONNULL(1,2)
|
||||
prototcp_bev_eventcb_eof_dst(struct bufferevent *bev, pxy_conn_ctx_t *ctx)
|
||||
{
|
||||
#ifdef DEBUG_PROXY
|
||||
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "prototcp_bev_eventcb_eof_dst: ENTER, fd=%d\n", ctx->fd);
|
||||
pxy_log_dbg_evbuf_info(ctx, &ctx->dst, &ctx->src);
|
||||
#endif /* DEBUG_PROXY */
|
||||
|
||||
if (!ctx->dst_connected) {
|
||||
log_err_level_printf(LOG_WARNING, "EOF on connection before connection establishment\n");
|
||||
#ifdef DEBUG_PROXY
|
||||
log_dbg_level_printf(LOG_DBG_MODE_FINE, "prototcp_bev_eventcb_eof_dst: EOF on connection before connection establishment, fd=%d\n", ctx->fd);
|
||||
#endif /* DEBUG_PROXY */
|
||||
|
||||
ctx->src.closed = 1;
|
||||
} else if (!ctx->src.closed) {
|
||||
#ifdef DEBUG_PROXY
|
||||
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "prototcp_bev_eventcb_eof_dst: !other->closed, terminate conn, fd=%d\n", ctx->fd);
|
||||
#endif /* DEBUG_PROXY */
|
||||
|
||||
if (pxy_try_consume_last_input(bev, ctx) == -1) {
|
||||
return;
|
||||
}
|
||||
pxy_try_close_conn_end(&ctx->src, ctx);
|
||||
}
|
||||
|
||||
pxy_try_disconnect(ctx, &ctx->dst, &ctx->src, 0);
|
||||
}
|
||||
|
||||
static void NONNULL(1,2)
|
||||
prototcp_bev_eventcb_error_src(UNUSED struct bufferevent *bev, pxy_conn_ctx_t *ctx)
|
||||
{
|
||||
#ifdef DEBUG_PROXY
|
||||
log_dbg_level_printf(LOG_DBG_MODE_FINE, "prototcp_bev_eventcb_error_src: BEV_EVENT_ERROR, fd=%d\n", ctx->fd);
|
||||
#endif /* DEBUG_PROXY */
|
||||
|
||||
if (!ctx->src_connected) {
|
||||
ctx->dst.closed = 1;
|
||||
} else if (!ctx->dst.closed) {
|
||||
pxy_try_close_conn_end(&ctx->dst, ctx);
|
||||
}
|
||||
|
||||
pxy_try_disconnect(ctx, &ctx->src, &ctx->dst, 1);
|
||||
}
|
||||
|
||||
static void NONNULL(1,2)
|
||||
prototcp_bev_eventcb_error_dst(UNUSED struct bufferevent *bev, pxy_conn_ctx_t *ctx)
|
||||
{
|
||||
#ifdef DEBUG_PROXY
|
||||
log_dbg_level_printf(LOG_DBG_MODE_FINE, "prototcp_bev_eventcb_error_dst: BEV_EVENT_ERROR, fd=%d\n", ctx->fd);
|
||||
#endif /* DEBUG_PROXY */
|
||||
|
||||
if (!ctx->dst_connected) {
|
||||
ctx->src.closed = 1;
|
||||
} else if (!ctx->src.closed) {
|
||||
pxy_try_close_conn_end(&ctx->src, ctx);
|
||||
}
|
||||
|
||||
pxy_try_disconnect(ctx, &ctx->dst, &ctx->src, 0);
|
||||
}
|
||||
|
||||
static void NONNULL(1,3)
|
||||
prototcp_bev_eventcb_src(struct bufferevent *bev, short events, pxy_conn_ctx_t *ctx)
|
||||
{
|
||||
if (events & BEV_EVENT_CONNECTED) {
|
||||
prototcp_bev_eventcb_connected_src(bev, ctx);
|
||||
} else if (events & BEV_EVENT_EOF) {
|
||||
prototcp_bev_eventcb_eof_src(bev, ctx);
|
||||
} else if (events & BEV_EVENT_ERROR) {
|
||||
prototcp_bev_eventcb_error_src(bev, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
static void NONNULL(1)
|
||||
prototcp_bev_eventcb_dst(struct bufferevent *bev, short events, pxy_conn_ctx_t *ctx)
|
||||
{
|
||||
if (events & BEV_EVENT_CONNECTED) {
|
||||
prototcp_bev_eventcb_connected_dst(bev, ctx);
|
||||
} else if (events & BEV_EVENT_EOF) {
|
||||
prototcp_bev_eventcb_eof_dst(bev, ctx);
|
||||
} else if (events & BEV_EVENT_ERROR) {
|
||||
prototcp_bev_eventcb_error_dst(bev, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
static void NONNULL(1)
|
||||
prototcp_bev_readcb(struct bufferevent *bev, void *arg)
|
||||
{
|
||||
pxy_conn_ctx_t *ctx = arg;
|
||||
|
||||
if (bev == ctx->src.bev) {
|
||||
prototcp_bev_readcb_src(bev, ctx);
|
||||
} else if (bev == ctx->dst.bev) {
|
||||
prototcp_bev_readcb_dst(bev, ctx);
|
||||
} else {
|
||||
log_err_printf("prototcp_bev_readcb: UNKWN conn end\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void NONNULL(1)
|
||||
prototcp_bev_writecb(struct bufferevent *bev, void *arg)
|
||||
{
|
||||
pxy_conn_ctx_t *ctx = arg;
|
||||
|
||||
if (bev == ctx->src.bev) {
|
||||
prototcp_bev_writecb_src(bev, ctx);
|
||||
} else if (bev == ctx->dst.bev) {
|
||||
prototcp_bev_writecb_dst(bev, ctx);
|
||||
} else {
|
||||
log_err_printf("prototcp_bev_writecb: UNKWN conn end\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void NONNULL(1)
|
||||
prototcp_bev_eventcb(struct bufferevent *bev, short events, void *arg)
|
||||
{
|
||||
pxy_conn_ctx_t *ctx = arg;
|
||||
|
||||
if (bev == ctx->src.bev) {
|
||||
prototcp_bev_eventcb_src(bev, events, ctx);
|
||||
} else if (bev == ctx->dst.bev) {
|
||||
prototcp_bev_eventcb_dst(bev, events, ctx);
|
||||
} else {
|
||||
log_err_printf("prototcp_bev_eventcb: UNKWN conn end\n");
|
||||
}
|
||||
}
|
||||
|
||||
protocol_t
|
||||
prototcp_setup(pxy_conn_ctx_t *ctx)
|
||||
{
|
||||
ctx->protoctx->proto = PROTO_TCP;
|
||||
ctx->protoctx->connectcb = prototcp_conn_connect;
|
||||
ctx->protoctx->fd_readcb = prototcp_fd_readcb;
|
||||
|
||||
ctx->protoctx->bev_readcb = prototcp_bev_readcb;
|
||||
ctx->protoctx->bev_writecb = prototcp_bev_writecb;
|
||||
ctx->protoctx->bev_eventcb = prototcp_bev_eventcb;
|
||||
|
||||
return PROTO_TCP;
|
||||
}
|
||||
|
||||
/* vim: set noet ft=c: */
|
@ -0,0 +1,38 @@
|
||||
/*-
|
||||
* SSLsplit - transparent SSL/TLS interception
|
||||
* https://www.roe.ch/SSLsplit
|
||||
*
|
||||
* Copyright (c) 2009-2018, Daniel Roethlisberger <daniel@roe.ch>.
|
||||
* Copyright (c) 2017-2019, Soner Tari <sonertari@gmail.com>.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef PROTOTCP_H
|
||||
#define PROTOTCP_H
|
||||
|
||||
#include "pxyconn.h"
|
||||
|
||||
protocol_t prototcp_setup(pxy_conn_ctx_t *) NONNULL(1);
|
||||
|
||||
#endif /* PROTOTCP_H */
|
||||
|
@ -0,0 +1,383 @@
|
||||
/*-
|
||||
* SSLsplit - transparent SSL/TLS interception
|
||||
* https://www.roe.ch/SSLsplit
|
||||
*
|
||||
* Copyright (c) 2009-2018, Daniel Roethlisberger <daniel@roe.ch>.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "proxy.h"
|
||||
|
||||
#include "privsep.h"
|
||||
#include "pxythrmgr.h"
|
||||
#include "pxyconn.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[] = { SIGTERM, SIGQUIT, SIGHUP, SIGINT, SIGPIPE, SIGUSR1 };
|
||||
|
||||
struct proxy_ctx {
|
||||
pxy_thrmgr_ctx_t *thrmgr;
|
||||
struct event_base *evbase;
|
||||
struct event *sev[sizeof(signals)/sizeof(int)];
|
||||
struct proxy_listener_ctx *lctx;
|
||||
opts_t *opts;
|
||||
};
|
||||
|
||||
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(1);
|
||||
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 *lctx = arg;
|
||||
|
||||
#ifdef DEBUG_PROXY
|
||||
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "proxy_listener_acceptcb: ENTER, fd=%d\n", fd);
|
||||
#endif /* DEBUG_PROXY */
|
||||
pxy_conn_setup(fd, peeraddr, peeraddrlen, lctx->thrmgr, lctx->opts);
|
||||
}
|
||||
|
||||
/*
|
||||
* Callback for error events on the socket listener bufferevent.
|
||||
*/
|
||||
void
|
||||
proxy_listener_errorcb(struct evconnlistener *listener, UNUSED void *arg)
|
||||
{
|
||||
struct event_base *evbase = evconnlistener_get_base(listener);
|
||||
int err = EVUTIL_SOCKET_ERROR();
|
||||
log_err_level_printf(LOG_CRIT, "Error %d on listener: %s\n", err,
|
||||
evutil_socket_error_to_string(err));
|
||||
/* Do not break the event loop if out of fds:
|
||||
* Too many open files (24) */
|
||||
if (err == 24) {
|
||||
return;
|
||||
}
|
||||
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, evutil_socket_t clisock)
|
||||
{
|
||||
#ifdef DEBUG_PROXY
|
||||
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "proxy_listener_setup: ENTER\n");
|
||||
#endif /* DEBUG_PROXY */
|
||||
|
||||
proxy_listener_ctx_t *lctx;
|
||||
int fd;
|
||||
|
||||
if ((fd = privsep_client_opensock(clisock, spec)) == -1) {
|
||||
log_err_level_printf(LOG_CRIT, "Error opening socket: %s (%i)\n",
|
||||
strerror(errno), errno);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
lctx = proxy_listener_ctx_new(thrmgr, spec, opts);
|
||||
if (!lctx) {
|
||||
log_err_level_printf(LOG_CRIT, "Error creating listener context\n");
|
||||
evutil_closesocket(fd);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// @todo Should we enable threadsafe event structs?
|
||||
// @attention Do not pass NULL as user-supplied pointer
|
||||
lctx->evcl = evconnlistener_new(evbase, proxy_listener_acceptcb,
|
||||
lctx, LEV_OPT_CLOSE_ON_FREE, 1024, fd);
|
||||
|
||||
if (!lctx->evcl) {
|
||||
log_err_level_printf(LOG_CRIT, "Error creating evconnlistener: %s\n",
|
||||
strerror(errno));
|
||||
proxy_listener_ctx_free(lctx);
|
||||
evutil_closesocket(fd);
|
||||
return NULL;
|
||||
}
|
||||
evconnlistener_set_error_cb(lctx->evcl, proxy_listener_errorcb);
|
||||
return lctx;
|
||||
}
|
||||
|
||||
/*
|
||||
* Signal handler for SIGTERM, SIGQUIT, SIGINT, SIGHUP, SIGPIPE and SIGUSR1.
|
||||
*/
|
||||
static void
|
||||
proxy_signal_cb(evutil_socket_t fd, UNUSED short what, void *arg)
|
||||
{
|
||||
proxy_ctx_t *ctx = arg;
|
||||
|
||||
if (OPTS_DEBUG(ctx->opts)) {
|
||||
log_dbg_printf("Received signal %i\n", fd);
|
||||
}
|
||||
|
||||
switch(fd) {
|
||||
case SIGTERM:
|
||||
case SIGQUIT:
|
||||
case SIGINT:
|
||||
proxy_loopbreak(ctx);
|
||||
break;
|
||||
case SIGHUP:
|
||||
case SIGUSR1:
|
||||
if (log_reopen() == -1) {
|
||||
log_err_level_printf(LOG_WARNING, "Failed to reopen logs\n");
|
||||
} else {
|
||||
log_dbg_printf("Reopened log files\n");
|
||||
}
|
||||
break;
|
||||
case SIGPIPE:
|
||||
log_err_level_printf(LOG_WARNING, "Received SIGPIPE; ignoring.\n");
|
||||
break;
|
||||
default:
|
||||
log_err_level_printf(LOG_WARNING, "Received unexpected signal %i\n", fd);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Set up the core event loop.
|
||||
* Socket clisock is the privsep client socket used for binding to ports.
|
||||
* Returns ctx on success, or NULL on error.
|
||||
*/
|
||||
proxy_ctx_t *
|
||||
proxy_new(opts_t *opts, int clisock)
|
||||
{
|
||||
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(opts)) {
|
||||
event_enable_debug_mode();
|
||||
}
|
||||
#endif /* PURIFY */
|
||||
|
||||
ctx = malloc(sizeof(proxy_ctx_t));
|
||||
if (!ctx) {
|
||||
log_err_level_printf(LOG_CRIT, "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_level_printf(LOG_CRIT, "Error getting event base\n");
|
||||
goto leave1;
|
||||
}
|
||||
|
||||
if (OPTS_DEBUG(opts)) {
|
||||
proxy_debug_base(ctx->evbase);
|
||||
}
|
||||
|
||||
ctx->thrmgr = pxy_thrmgr_new(opts);
|
||||
if (!ctx->thrmgr) {
|
||||
log_err_level_printf(LOG_CRIT, "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, clisock);
|
||||
if (!head)
|
||||
goto leave2;
|
||||
head->next = ctx->lctx;
|
||||
ctx->lctx = head;
|
||||
|
||||
char *specstr = proxyspec_str(spec);
|
||||
if (!specstr) {
|
||||
fprintf(stderr, "out of memory\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
log_dbg_printf("proxy_listener_setup: %s\n", specstr);
|
||||
free(specstr);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
privsep_client_close(clisock);
|
||||
return ctx;
|
||||
|
||||
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 canceled by a signal
|
||||
* or on failure.
|
||||
*/
|
||||
void
|
||||
proxy_run(proxy_ctx_t *ctx)
|
||||
{
|
||||
if (ctx->opts->detach) {
|
||||
event_reinit(ctx->evbase);
|
||||
}
|
||||
#ifndef PURIFY
|
||||
if (OPTS_DEBUG(ctx->opts)) {
|
||||
event_base_dump_events(ctx->evbase, stderr);
|
||||
}
|
||||
#endif /* PURIFY */
|
||||
if (pxy_thrmgr_run(ctx->thrmgr) == -1) {
|
||||
log_err_level_printf(LOG_CRIT, "Failed to start thread manager\n");
|
||||
return;
|
||||
}
|
||||
if (OPTS_DEBUG(ctx->opts)) {
|
||||
log_dbg_printf("Starting main event loop.\n");
|
||||
}
|
||||
event_base_dispatch(ctx->evbase);
|
||||
if (OPTS_DEBUG(ctx->opts)) {
|
||||
log_dbg_printf("Main event loop stopped.\n");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Break the loop of the proxy, causing the proxy_run to return.
|
||||
*/
|
||||
void
|
||||
proxy_loopbreak(proxy_ctx_t *ctx)
|
||||
{
|
||||
event_base_loopbreak(ctx->evbase);
|
||||
}
|
||||
|
||||
/*
|
||||
* Free the proxy data structures.
|
||||
*/
|
||||
void
|
||||
proxy_free(proxy_ctx_t *ctx)
|
||||
{
|
||||
if (ctx->lctx) {
|
||||
proxy_listener_ctx_free(ctx->lctx);
|
||||
}
|
||||
for (size_t i = 0; i < (sizeof(ctx->sev) / sizeof(ctx->sev[0])); i++) {
|
||||
if (ctx->sev[i]) {
|
||||
event_free(ctx->sev[i]);
|
||||
}
|
||||
}
|
||||
if (ctx->thrmgr) {
|
||||
pxy_thrmgr_free(ctx->thrmgr);
|
||||
}
|
||||
if (ctx->evbase) {
|
||||
event_base_free(ctx->evbase);
|
||||
}
|
||||
free(ctx);
|
||||
}
|
||||
|
||||
/* vim: set noet ft=c: */
|
@ -0,0 +1,59 @@
|
||||
/*-
|
||||
* SSLsplit - transparent SSL/TLS interception
|
||||
* https://www.roe.ch/SSLsplit
|
||||
*
|
||||
* Copyright (c) 2009-2018, Daniel Roethlisberger <daniel@roe.ch>.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef PROXY_H
|
||||
#define PROXY_H
|
||||
|
||||
#include "opts.h"
|
||||
#include "attrib.h"
|
||||
#include "pxythrmgr.h"
|
||||
|
||||
#include <sys/syslog.h>
|
||||
|
||||
typedef struct proxy_ctx proxy_ctx_t;
|
||||
|
||||
/*
|
||||
* 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;
|
||||
|
||||
proxy_ctx_t * proxy_new(opts_t *, int) NONNULL(1) MALLOC;
|
||||
void proxy_run(proxy_ctx_t *) NONNULL(1);
|
||||
void proxy_loopbreak(proxy_ctx_t *) NONNULL(1);
|
||||
void proxy_free(proxy_ctx_t *) NONNULL(1);
|
||||
void proxy_listener_errorcb(struct evconnlistener *, UNUSED void *);
|
||||
|
||||
#endif /* !PROXY_H */
|
||||
|
||||
/* vim: set noet ft=c: */
|
@ -0,0 +1,796 @@
|
||||
/*-
|
||||
* SSLsplit - transparent SSL/TLS interception
|
||||
* https://www.roe.ch/SSLsplit
|
||||
*
|
||||
* Copyright (c) 2009-2018, Daniel Roethlisberger <daniel@roe.ch>.
|
||||
* Copyright (c) 2017-2019, Soner Tari <sonertari@gmail.com>.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "pxyconn.h"
|
||||
|
||||
#include "prototcp.h"
|
||||
|
||||
#include "privsep.h"
|
||||
#include "sys.h"
|
||||
#include "log.h"
|
||||
#include "attrib.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/param.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <event2/listener.h>
|
||||
|
||||
#ifdef __linux__
|
||||
#include <glob.h>
|
||||
#endif /* __linux__ */
|
||||
|
||||
#include <net/if_arp.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <net/route.h>
|
||||
#include <netinet/if_ether.h>
|
||||
#ifdef __OpenBSD__
|
||||
#include <net/if_dl.h>
|
||||
#endif /* __OpenBSD__ */
|
||||
|
||||
/*
|
||||
* Maximum size of data to buffer per connection direction before
|
||||
* temporarily stopping to read data from the other end.
|
||||
*/
|
||||
#define OUTBUF_LIMIT (128*1024)
|
||||
|
||||
int descriptor_table_size = 0;
|
||||
|
||||
// @attention The order of names should match the order in protocol enum
|
||||
char *protocol_names[] = {
|
||||
// ERROR = -1
|
||||
"TCP", // = 0
|
||||
};
|
||||
|
||||
static protocol_t NONNULL(1)
|
||||
pxy_setup_proto(pxy_conn_ctx_t *ctx)
|
||||
{
|
||||
ctx->protoctx = malloc(sizeof(proto_ctx_t));
|
||||
if (!ctx->protoctx) {
|
||||
return PROTO_ERROR;
|
||||
}
|
||||
memset(ctx->protoctx, 0, sizeof(proto_ctx_t));
|
||||
|
||||
// Default to tcp
|
||||
protocol_t proto = prototcp_setup(ctx);
|
||||
|
||||
if (proto == PROTO_ERROR) {
|
||||
free(ctx->protoctx);
|
||||
}
|
||||
return proto;
|
||||
}
|
||||
|
||||
static pxy_conn_ctx_t * MALLOC NONNULL(2,3)
|
||||
pxy_conn_ctx_new(evutil_socket_t fd,
|
||||
pxy_thrmgr_ctx_t *thrmgr, opts_t *opts)
|
||||
{
|
||||
#ifdef DEBUG_PROXY
|
||||
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "pxy_conn_ctx_new: ENTER, fd=%d\n", fd);
|
||||
#endif /* DEBUG_PROXY */
|
||||
|
||||
pxy_conn_ctx_t *ctx = malloc(sizeof(pxy_conn_ctx_t));
|
||||
if (!ctx) {
|
||||
return NULL;
|
||||
}
|
||||
memset(ctx, 0, sizeof(pxy_conn_ctx_t));
|
||||
|
||||
ctx->id = thrmgr->conn_count++;
|
||||
|
||||
#ifdef DEBUG_PROXY
|
||||
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "pxy_conn_ctx_new: id=%llu, fd=%d\n", ctx->id, fd);
|
||||
#endif /* DEBUG_PROXY */
|
||||
|
||||
ctx->fd = fd;
|
||||
ctx->thrmgr = thrmgr;
|
||||
|
||||
ctx->proto = pxy_setup_proto(ctx);
|
||||
if (ctx->proto == PROTO_ERROR) {
|
||||
free(ctx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ctx->opts = opts;
|
||||
|
||||
ctx->ctime = time(NULL);
|
||||
ctx->atime = ctx->ctime;
|
||||
|
||||
ctx->next = NULL;
|
||||
|
||||
pxy_thrmgr_attach(ctx);
|
||||
return ctx;
|
||||
}
|
||||
|
||||
void
|
||||
pxy_conn_ctx_free(pxy_conn_ctx_t *ctx, int by_requestor)
|
||||
{
|
||||
#ifdef DEBUG_PROXY
|
||||
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "pxy_conn_ctx_free: ENTER, fd=%d\n", ctx->fd);
|
||||
#endif /* DEBUG_PROXY */
|
||||
|
||||
if (WANT_CONTENT_LOG(ctx)) {
|
||||
if (log_content_close(&ctx->logctx, by_requestor) == -1) {
|
||||
log_err_level_printf(LOG_WARNING, "Content log close failed\n");
|
||||
}
|
||||
}
|
||||
|
||||
if (ctx->thr_locked) {
|
||||
pxy_thrmgr_detach_unlocked(ctx);
|
||||
} else {
|
||||
pxy_thrmgr_detach(ctx);
|
||||
}
|
||||
|
||||
if (ctx->srchost_str) {
|
||||
free(ctx->srchost_str);
|
||||
}
|
||||
if (ctx->srcport_str) {
|
||||
free(ctx->srcport_str);
|
||||
}
|
||||
if (ctx->dsthost_str) {
|
||||
free(ctx->dsthost_str);
|
||||
}
|
||||
if (ctx->dstport_str) {
|
||||
free(ctx->dstport_str);
|
||||
}
|
||||
// If the proto doesn't have special args, proto_free() callback is NULL
|
||||
if (ctx->protoctx->proto_free) {
|
||||
ctx->protoctx->proto_free(ctx);
|
||||
}
|
||||
free(ctx->protoctx);
|
||||
free(ctx);
|
||||
}
|
||||
|
||||
void
|
||||
pxy_conn_free(pxy_conn_ctx_t *ctx, int by_requestor)
|
||||
{
|
||||
#ifdef DEBUG_PROXY
|
||||
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "pxy_conn_free: ENTER, fd=%d\n", ctx->fd);
|
||||
#endif /* DEBUG_PROXY */
|
||||
|
||||
// We always assign NULL to bevs after freeing them
|
||||
if (ctx->src.bev) {
|
||||
ctx->src.free(ctx->src.bev, ctx);
|
||||
ctx->src.bev = NULL;
|
||||
} else if (!ctx->src.closed) {
|
||||
#ifdef DEBUG_PROXY
|
||||
log_dbg_level_printf(LOG_DBG_MODE_FINE, "pxy_conn_free: evutil_closesocket on NULL src->bev, fd=%d\n", ctx->fd);
|
||||
#endif /* DEBUG_PROXY */
|
||||
|
||||
// @attention early in the conn setup, src fd may be open, although src.bev is NULL
|
||||
evutil_closesocket(ctx->fd);
|
||||
}
|
||||
|
||||
if (ctx->dst.bev) {
|
||||
ctx->dst.free(ctx->dst.bev, ctx);
|
||||
ctx->dst.bev = NULL;
|
||||
}
|
||||
|
||||
pxy_conn_ctx_free(ctx, by_requestor);
|
||||
}
|
||||
|
||||
void
|
||||
pxy_conn_term(pxy_conn_ctx_t *ctx, int by_requestor)
|
||||
{
|
||||
#ifdef DEBUG_PROXY
|
||||
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "pxy_conn_term: ENTER, fd=%d\n", ctx->fd);
|
||||
#endif /* DEBUG_PROXY */
|
||||
|
||||
ctx->term = 1;
|
||||
ctx->term_requestor = by_requestor;
|
||||
}
|
||||
|
||||
void
|
||||
pxy_log_connect_nonhttp(pxy_conn_ctx_t *ctx)
|
||||
{
|
||||
char *msg;
|
||||
int rv;
|
||||
|
||||
/*
|
||||
* The following ifdef's within asprintf arguments list generates
|
||||
* warnings with -Wembedded-directive on some compilers.
|
||||
* Not fixing the code in order to avoid more code duplication.
|
||||
*/
|
||||
|
||||
rv = asprintf(&msg, "CONN: tcp %s %s %s %s\n",
|
||||
STRORDASH(ctx->srchost_str),
|
||||
STRORDASH(ctx->srcport_str),
|
||||
STRORDASH(ctx->dsthost_str),
|
||||
STRORDASH(ctx->dstport_str));
|
||||
|
||||
if ((rv < 0) || !msg) {
|
||||
ctx->enomem = 1;
|
||||
goto out;
|
||||
}
|
||||
if (!ctx->opts->detach) {
|
||||
log_err_printf("%s", msg);
|
||||
} else if (ctx->opts->statslog) {
|
||||
if (log_conn(msg) == -1) {
|
||||
log_err_level_printf(LOG_WARNING, "Conn logging failed\n");
|
||||
}
|
||||
}
|
||||
if (ctx->opts->connectlog) {
|
||||
if (log_connect_print_free(msg) == -1) {
|
||||
free(msg);
|
||||
log_err_level_printf(LOG_WARNING, "Connection logging failed\n");
|
||||
}
|
||||
} else {
|
||||
free(msg);
|
||||
}
|
||||
out:
|
||||
return;
|
||||
}
|
||||
|
||||
int
|
||||
pxy_log_content_inbuf(pxy_conn_ctx_t *ctx, struct evbuffer *inbuf, int req)
|
||||
{
|
||||
size_t sz = evbuffer_get_length(inbuf);
|
||||
unsigned char *buf = malloc(sz);
|
||||
if (!buf) {
|
||||
ctx->enomem = 1;
|
||||
return -1;
|
||||
}
|
||||
if (evbuffer_copyout(inbuf, buf, sz) == -1) {
|
||||
free(buf);
|
||||
return -1;
|
||||
}
|
||||
logbuf_t *lb = logbuf_new_alloc(sz, NULL);
|
||||
if (!lb) {
|
||||
free(buf);
|
||||
ctx->enomem = 1;
|
||||
return -1;
|
||||
}
|
||||
memcpy(lb->buf, buf, lb->sz);
|
||||
free(buf);
|
||||
if (log_content_submit(&ctx->logctx, lb, req) == -1) {
|
||||
logbuf_free(lb);
|
||||
log_err_level_printf(LOG_WARNING, "Content log submission failed\n");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
pxy_prepare_logging(pxy_conn_ctx_t *ctx)
|
||||
{
|
||||
/* prepare logging, part 2 */
|
||||
if (WANT_CONTENT_LOG(ctx)) {
|
||||
if (log_content_open(&ctx->logctx, ctx->opts,
|
||||
STRORDASH(ctx->srchost_str), STRORDASH(ctx->srcport_str),
|
||||
STRORDASH(ctx->dsthost_str), STRORDASH(ctx->dstport_str),
|
||||
NULL, NULL, NULL
|
||||
) == -1) {
|
||||
if (errno == ENOMEM)
|
||||
ctx->enomem = 1;
|
||||
pxy_conn_term(ctx, 1);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void NONNULL(1)
|
||||
pxy_log_dbg_connect_type(pxy_conn_ctx_t *ctx)
|
||||
{
|
||||
if (OPTS_DEBUG(ctx->opts)) {
|
||||
/* for TCP, we get only a dst connect event,
|
||||
* since src was already connected from the
|
||||
* beginning; mirror SSL debug output anyway
|
||||
* in order not to confuse anyone who might be
|
||||
* looking closely at the output */
|
||||
log_dbg_printf("%s connected to [%s]:%s\n",
|
||||
protocol_names[ctx->proto],
|
||||
STRORDASH(ctx->dsthost_str), STRORDASH(ctx->dstport_str));
|
||||
log_dbg_printf("%s connected from [%s]:%s\n",
|
||||
protocol_names[ctx->proto],
|
||||
STRORDASH(ctx->srchost_str), STRORDASH(ctx->srcport_str));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
pxy_log_connect(pxy_conn_ctx_t *ctx)
|
||||
{
|
||||
/* log connection if we don't analyze any headers */
|
||||
if (WANT_CONNECT_LOG(ctx)) {
|
||||
pxy_log_connect_nonhttp(ctx);
|
||||
}
|
||||
|
||||
pxy_log_dbg_connect_type(ctx);
|
||||
}
|
||||
|
||||
static void
|
||||
pxy_log_dbg_disconnect(pxy_conn_ctx_t *ctx)
|
||||
{
|
||||
/* we only get a single disconnect event here for both connections */
|
||||
if (OPTS_DEBUG(ctx->opts)) {
|
||||
log_dbg_printf("%s disconnected to [%s]:%s\n",
|
||||
protocol_names[ctx->proto],
|
||||
STRORDASH(ctx->dsthost_str), STRORDASH(ctx->dstport_str));
|
||||
log_dbg_printf("%s disconnected from [%s]:%s\n",
|
||||
protocol_names[ctx->proto],
|
||||
STRORDASH(ctx->srchost_str), STRORDASH(ctx->srcport_str));
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DEBUG_PROXY
|
||||
void
|
||||
pxy_log_dbg_evbuf_info(pxy_conn_ctx_t *ctx, pxy_conn_desc_t *this, pxy_conn_desc_t *other)
|
||||
{
|
||||
// Use ctx->conn, because this function is used by child conns too
|
||||
if (OPTS_DEBUG(ctx->opts)) {
|
||||
log_dbg_printf("evbuffer size at EOF: i:%zu o:%zu i:%zu o:%zu\n",
|
||||
evbuffer_get_length(bufferevent_get_input(this->bev)),
|
||||
evbuffer_get_length(bufferevent_get_output(this->bev)),
|
||||
other->closed ? 0 : evbuffer_get_length(bufferevent_get_input(other->bev)),
|
||||
other->closed ? 0 : evbuffer_get_length(bufferevent_get_output(other->bev)));
|
||||
}
|
||||
}
|
||||
#endif /* DEBUG_PROXY */
|
||||
|
||||
#ifdef DEBUG_PROXY
|
||||
char *bev_names[] = {
|
||||
"src",
|
||||
"dst",
|
||||
"NULL",
|
||||
"UNKWN"
|
||||
};
|
||||
|
||||
static char *
|
||||
pxy_get_event_name(struct bufferevent *bev, pxy_conn_ctx_t *ctx)
|
||||
{
|
||||
// XXX: Used by watermark functions only, remove
|
||||
if (bev == ctx->src.bev) {
|
||||
return bev_names[0];
|
||||
} else if (bev == ctx->dst.bev) {
|
||||
return bev_names[1];
|
||||
} else if (bev == NULL) {
|
||||
log_dbg_level_printf(LOG_DBG_MODE_FINE, "pxy_get_event_name: event_name=NULL\n");
|
||||
return bev_names[2];
|
||||
} else {
|
||||
log_dbg_level_printf(LOG_DBG_MODE_FINE, "pxy_get_event_name: event_name=UNKWN\n");
|
||||
return bev_names[3];
|
||||
}
|
||||
}
|
||||
#endif /* DEBUG_PROXY */
|
||||
|
||||
void
|
||||
pxy_try_set_watermark(struct bufferevent *bev, pxy_conn_ctx_t *ctx, struct bufferevent *other)
|
||||
{
|
||||
if (evbuffer_get_length(bufferevent_get_output(other)) >= OUTBUF_LIMIT) {
|
||||
#ifdef DEBUG_PROXY
|
||||
log_dbg_level_printf(LOG_DBG_MODE_FINE, "pxy_try_set_watermark: %s, fd=%d\n", pxy_get_event_name(bev, ctx), ctx->fd);
|
||||
#endif /* DEBUG_PROXY */
|
||||
|
||||
/* temporarily disable data source;
|
||||
* set an appropriate watermark. */
|
||||
bufferevent_setwatermark(other, EV_WRITE, OUTBUF_LIMIT/2, OUTBUF_LIMIT);
|
||||
bufferevent_disable(bev, EV_READ);
|
||||
ctx->thr->set_watermarks++;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
pxy_try_unset_watermark(struct bufferevent *bev, pxy_conn_ctx_t *ctx, pxy_conn_desc_t *other)
|
||||
{
|
||||
if (other->bev && !(bufferevent_get_enabled(other->bev) & EV_READ)) {
|
||||
#ifdef DEBUG_PROXY
|
||||
log_dbg_level_printf(LOG_DBG_MODE_FINE, "pxy_try_unset_watermark: %s, fd=%d\n", pxy_get_event_name(bev, ctx), ctx->fd);
|
||||
#endif /* DEBUG_PROXY */
|
||||
|
||||
/* data source temporarily disabled;
|
||||
* re-enable and reset watermark to 0. */
|
||||
bufferevent_setwatermark(bev, EV_WRITE, 0, 0);
|
||||
bufferevent_enable(other->bev, EV_READ);
|
||||
ctx->thr->unset_watermarks++;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
pxy_discard_inbuf(struct bufferevent *bev)
|
||||
{
|
||||
struct evbuffer *inbuf = bufferevent_get_input(bev);
|
||||
size_t inbuf_size = evbuffer_get_length(inbuf);
|
||||
|
||||
log_dbg_printf("Warning: Drained %zu bytes (conn closed)\n", inbuf_size);
|
||||
evbuffer_drain(inbuf, inbuf_size);
|
||||
}
|
||||
|
||||
#if defined(__APPLE__) || defined(__FreeBSD__)
|
||||
#define getdtablecount() 0
|
||||
|
||||
/*
|
||||
* Copied from:
|
||||
* opensmtpd-201801101641p1/openbsd-compat/imsg.c
|
||||
*
|
||||
* Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
static int
|
||||
available_fds(unsigned int n)
|
||||
{
|
||||
unsigned int i;
|
||||
int ret, fds[256];
|
||||
|
||||
if (n > (sizeof(fds)/sizeof(fds[0])))
|
||||
return -1;
|
||||
|
||||
ret = 0;
|
||||
for (i = 0; i < n; i++) {
|
||||
fds[i] = -1;
|
||||
if ((fds[i] = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
|
||||
ret = -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < n && fds[i] >= 0; i++)
|
||||
close(fds[i]);
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif /* __APPLE__ || __FreeBSD__ */
|
||||
|
||||
#ifdef __linux__
|
||||
/*
|
||||
* Copied from:
|
||||
* https://github.com/tmux/tmux/blob/master/compat/getdtablecount.c
|
||||
*
|
||||
* Copyright (c) 2017 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
|
||||
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
int
|
||||
getdtablecount()
|
||||
{
|
||||
char path[PATH_MAX];
|
||||
glob_t g;
|
||||
int n = 0;
|
||||
|
||||
if (snprintf(path, sizeof path, "/proc/%ld/fd/*", (long)getpid()) < 0) {
|
||||
log_err_level_printf(LOG_CRIT, "snprintf overflow\n");
|
||||
return 0;
|
||||
}
|
||||
if (glob(path, 0, NULL, &g) == 0)
|
||||
n = g.gl_pathc;
|
||||
globfree(&g);
|
||||
return n;
|
||||
}
|
||||
#endif /* __linux__ */
|
||||
|
||||
/*
|
||||
* Check if we are out of file descriptors to close the conn, or else libevent will crash us
|
||||
* @attention We cannot guess the number of children in a connection at conn setup time. So, FD_RESERVE is just a ball park figure.
|
||||
* But what if a connection passes the check below, but eventually tries to create more children than FD_RESERVE allows for? This will crash us the same.
|
||||
* Beware, this applies to all current conns, not just the last connection setup.
|
||||
* For example, 20x conns pass the check below before creating any children, at which point we reach the last FD_RESERVE fds,
|
||||
* then they all start creating children, which crashes us again.
|
||||
* So, no matter how large an FD_RESERVE we choose, there will always be a risk of running out of fds, if we check the number of fds during parent conn setup only.
|
||||
* If we are left with less than FD_RESERVE fds, we should not create more children than FD_RESERVE allows for either.
|
||||
* Therefore, we check if we are out of fds in pxy_listener_acceptcb_child() and close the conn there too.
|
||||
* @attention These checks are expected to slow us further down, but it is critical to avoid a crash in case we run out of fds.
|
||||
*/
|
||||
static int
|
||||
check_fd_usage(
|
||||
#ifdef DEBUG_PROXY
|
||||
evutil_socket_t fd
|
||||
#endif /* DEBUG_PROXY */
|
||||
)
|
||||
{
|
||||
int dtable_count = getdtablecount();
|
||||
|
||||
#ifdef DEBUG_PROXY
|
||||
log_dbg_level_printf(LOG_DBG_MODE_FINER, "check_fd_usage: descriptor_table_size=%d, dtablecount=%d, reserve=%d, fd=%d\n",
|
||||
descriptor_table_size, dtable_count, FD_RESERVE, fd);
|
||||
#endif /* DEBUG_PROXY */
|
||||
|
||||
if (dtable_count + FD_RESERVE >= descriptor_table_size) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
#if defined(__APPLE__) || defined(__FreeBSD__)
|
||||
if (available_fds(FD_RESERVE) == -1) {
|
||||
goto out;
|
||||
}
|
||||
#endif /* __APPLE__ || __FreeBSD__ */
|
||||
|
||||
return 0;
|
||||
out:
|
||||
errno = EMFILE;
|
||||
log_err_level_printf(LOG_CRIT, "Out of file descriptors\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int
|
||||
pxy_try_close_conn_end(pxy_conn_desc_t *conn_end, pxy_conn_ctx_t *ctx)
|
||||
{
|
||||
/* if the other end is still open and doesn't have data
|
||||
* to send, close it, otherwise its writecb will close
|
||||
* it after writing what's left in the output buffer */
|
||||
if (evbuffer_get_length(bufferevent_get_output(conn_end->bev)) == 0) {
|
||||
#ifdef DEBUG_PROXY
|
||||
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "pxy_try_close_conn_end: evbuffer_get_length(outbuf) == 0, terminate conn, fd=%d\n", ctx->fd);
|
||||
#endif /* DEBUG_PROXY */
|
||||
|
||||
conn_end->free(conn_end->bev, ctx);
|
||||
conn_end->bev = NULL;
|
||||
conn_end->closed = 1;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
pxy_try_disconnect(pxy_conn_ctx_t *ctx, pxy_conn_desc_t *this, pxy_conn_desc_t *other, int is_requestor)
|
||||
{
|
||||
// @attention srvdst should never reach here unless in passthrough mode, its bev may be NULL
|
||||
this->closed = 1;
|
||||
this->free(this->bev, ctx);
|
||||
this->bev = NULL;
|
||||
if (other->closed) {
|
||||
#ifdef DEBUG_PROXY
|
||||
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "pxy_try_disconnect: other->closed, terminate conn, fd=%d\n", ctx->fd);
|
||||
#endif /* DEBUG_PROXY */
|
||||
|
||||
// Uses only ctx to log disconnect, never any of the bevs
|
||||
pxy_log_dbg_disconnect(ctx);
|
||||
pxy_conn_term(ctx, is_requestor);
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
pxy_try_consume_last_input(struct bufferevent *bev, pxy_conn_ctx_t *ctx)
|
||||
{
|
||||
/* if there is data pending in the closed connection,
|
||||
* handle it here, otherwise it will be lost. */
|
||||
if (evbuffer_get_length(bufferevent_get_input(bev))) {
|
||||
#ifdef DEBUG_PROXY
|
||||
log_dbg_level_printf(LOG_DBG_MODE_FINE, "pxy_try_consume_last_input: evbuffer_get_length(inbuf) > 0, terminate conn, fd=%d\n", ctx->fd);
|
||||
#endif /* DEBUG_PROXY */
|
||||
|
||||
if (pxy_bev_readcb_preexec_logging_and_stats(bev, ctx) == -1) {
|
||||
return -1;
|
||||
}
|
||||
ctx->protoctx->bev_readcb(bev, ctx);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
pxy_bev_readcb_preexec_logging_and_stats(struct bufferevent *bev, pxy_conn_ctx_t *ctx)
|
||||
{
|
||||
if (bev == ctx->src.bev || bev == ctx->dst.bev) {
|
||||
struct evbuffer *inbuf = bufferevent_get_input(bev);
|
||||
size_t inbuf_size = evbuffer_get_length(inbuf);
|
||||
|
||||
if (bev == ctx->src.bev) {
|
||||
ctx->thr->intif_in_bytes += inbuf_size;
|
||||
} else {
|
||||
ctx->thr->intif_out_bytes += inbuf_size;
|
||||
}
|
||||
|
||||
if (WANT_CONTENT_LOG(ctx)) {
|
||||
// HTTP content logging at this point may record certain header lines twice, if we have not seen all headers yet
|
||||
return pxy_log_content_inbuf(ctx, inbuf, (bev == ctx->src.bev));
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Callback for read events on the up- and downstream connection bufferevents.
|
||||
* Called when there is data ready in the input evbuffer.
|
||||
*/
|
||||
void
|
||||
pxy_bev_readcb(struct bufferevent *bev, void *arg)
|
||||
{
|
||||
pxy_conn_ctx_t *ctx = arg;
|
||||
|
||||
if (pxy_bev_readcb_preexec_logging_and_stats(bev, ctx) == -1) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
ctx->atime = time(NULL);
|
||||
ctx->protoctx->bev_readcb(bev, ctx);
|
||||
|
||||
out:
|
||||
if (ctx->term || ctx->enomem) {
|
||||
pxy_conn_free(ctx, ctx->term ? ctx->term_requestor : (bev == ctx->src.bev));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Callback for write events on the up- and downstream connection bufferevents.
|
||||
* Called when either all data from the output evbuffer has been written,
|
||||
* or if the outbuf is only half full again after having been full.
|
||||
*/
|
||||
void
|
||||
pxy_bev_writecb(struct bufferevent *bev, void *arg)
|
||||
{
|
||||
pxy_conn_ctx_t *ctx = arg;
|
||||
|
||||
ctx->atime = time(NULL);
|
||||
ctx->protoctx->bev_writecb(bev, ctx);
|
||||
|
||||
if (ctx->term || ctx->enomem) {
|
||||
pxy_conn_free(ctx, ctx->term ? ctx->term_requestor : (bev == ctx->src.bev));
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
pxy_bev_eventcb_postexec_logging_and_stats(struct bufferevent *bev, short events, pxy_conn_ctx_t *ctx)
|
||||
{
|
||||
if (ctx->term || ctx->enomem) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (events & BEV_EVENT_CONNECTED) {
|
||||
if (bev == ctx->dst.bev) {
|
||||
pxy_log_connect(ctx);
|
||||
ctx->dst_fd = bufferevent_getfd(ctx->dst.bev);
|
||||
ctx->thr->max_fd = MAX(ctx->thr->max_fd, ctx->dst_fd);
|
||||
}
|
||||
if (ctx->src_connected && ctx->dst_connected) {
|
||||
if (pxy_prepare_logging(ctx) == -1) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Callback for meta events on the up- and downstream connection bufferevents.
|
||||
* Called when EOF has been reached, a connection has been made, and on errors.
|
||||
*/
|
||||
void
|
||||
pxy_bev_eventcb(struct bufferevent *bev, short events, void *arg)
|
||||
{
|
||||
pxy_conn_ctx_t *ctx = arg;
|
||||
|
||||
ctx->atime = time(NULL);
|
||||
|
||||
if (events & BEV_EVENT_ERROR) {
|
||||
log_err_printf("BEV_EVENT_ERROR\n");
|
||||
ctx->thr->errors++;
|
||||
}
|
||||
|
||||
ctx->protoctx->bev_eventcb(bev, events, arg);
|
||||
|
||||
pxy_bev_eventcb_postexec_logging_and_stats(bev, events, ctx);
|
||||
|
||||
// Logging functions may set term or enomem too
|
||||
// EOF eventcb may call readcb possibly causing enomem
|
||||
if (ctx->term || ctx->enomem) {
|
||||
pxy_conn_free(ctx, ctx->term ? ctx->term_requestor : (bev == ctx->src.bev));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Complete the connection.
|
||||
*/
|
||||
void
|
||||
pxy_conn_connect(pxy_conn_ctx_t *ctx)
|
||||
{
|
||||
#ifdef DEBUG_PROXY
|
||||
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "pxy_conn_connect: ENTER, fd=%d\n", ctx->fd);
|
||||
#endif /* DEBUG_PROXY */
|
||||
|
||||
if (ctx->protoctx->connectcb(ctx) == -1) {
|
||||
// @attention Do not try to close conns or do anything else with conn ctx on the thrmgr thread after setting event callbacks and/or socket connect.
|
||||
// The return value of -1 from connectcb indicates that there was a fatal error before event callbacks were set, so we can terminate the connection.
|
||||
// Otherwise, it is up to the event callbacks to terminate the connection. This is necessary to avoid multithreading issues.
|
||||
if (ctx->term || ctx->enomem) {
|
||||
pxy_conn_free(ctx, ctx->term ? ctx->term_requestor : 1);
|
||||
}
|
||||
}
|
||||
// @attention Do not do anything with the conn ctx after this point on the thrmgr thread
|
||||
}
|
||||
|
||||
/*
|
||||
* Callback for accept events on the socket listener bufferevent.
|
||||
* Called when a new incoming connection has been accepted.
|
||||
* Initiates the connection to the server. The incoming connection
|
||||
* from the client is not being activated until we have a successful
|
||||
* connection to the server, because we need the server's certificate
|
||||
* in order to set up the SSL session to the client.
|
||||
* For consistency, plain TCP works the same way, even if we could
|
||||
* start reading from the client while waiting on the connection to
|
||||
* the server to connect.
|
||||
*/
|
||||
void
|
||||
pxy_conn_setup(evutil_socket_t fd,
|
||||
struct sockaddr *peeraddr, int peeraddrlen,
|
||||
pxy_thrmgr_ctx_t *thrmgr, opts_t *opts)
|
||||
{
|
||||
#ifdef DEBUG_PROXY
|
||||
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "pxy_conn_setup: ENTER, fd=%d\n", fd);
|
||||
|
||||
char *host, *port;
|
||||
if (sys_sockaddr_str(peeraddr, peeraddrlen, &host, &port) == 0) {
|
||||
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "pxy_conn_setup: peer addr=[%s]:%s, fd=%d\n", host, port, fd);
|
||||
free(host);
|
||||
free(port);
|
||||
}
|
||||
#endif /* DEBUG_PROXY */
|
||||
|
||||
if (check_fd_usage(
|
||||
#ifdef DEBUG_PROXY
|
||||
fd
|
||||
#endif /* DEBUG_PROXY */
|
||||
) == -1) {
|
||||
evutil_closesocket(fd);
|
||||
return;
|
||||
}
|
||||
|
||||
/* create per connection state and attach to thread */
|
||||
pxy_conn_ctx_t *ctx = pxy_conn_ctx_new(fd, thrmgr, opts);
|
||||
if (!ctx) {
|
||||
log_err_level_printf(LOG_CRIT, "Error allocating memory\n");
|
||||
evutil_closesocket(fd);
|
||||
return;
|
||||
}
|
||||
|
||||
ctx->af = peeraddr->sa_family;
|
||||
|
||||
if (sys_sockaddr_str(peeraddr, peeraddrlen, &ctx->srchost_str, &ctx->srcport_str) != 0) {
|
||||
log_err_level_printf(LOG_CRIT, "Aborting connection setup (out of memory)!\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
ctx->protoctx->fd_readcb(ctx->fd, 0, ctx);
|
||||
return;
|
||||
|
||||
out:
|
||||
evutil_closesocket(fd);
|
||||
pxy_conn_ctx_free(ctx, 1);
|
||||
}
|
||||
|
||||
/* vim: set noet ft=c: */
|
@ -0,0 +1,198 @@
|
||||
/*-
|
||||
* SSLsplit - transparent SSL/TLS interception
|
||||
* https://www.roe.ch/SSLsplit
|
||||
*
|
||||
* Copyright (c) 2009-2018, Daniel Roethlisberger <daniel@roe.ch>.
|
||||
* Copyright (c) 2017-2019, Soner Tari <sonertari@gmail.com>.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef PXYCONN_H
|
||||
#define PXYCONN_H
|
||||
|
||||
#if defined(__FreeBSD__) || defined(__DragonFly__)
|
||||
#include <netinet/in.h>
|
||||
#endif
|
||||
|
||||
#include "proxy.h"
|
||||
#include "opts.h"
|
||||
#include "attrib.h"
|
||||
#include "pxythrmgr.h"
|
||||
#include "log.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include <event2/buffer.h>
|
||||
#include <event2/bufferevent.h>
|
||||
|
||||
#define WANT_CONNECT_LOG(ctx) ((ctx)->opts->connectlog||!(ctx)->opts->detach||(ctx)->opts->statslog)
|
||||
#define WANT_CONTENT_LOG(ctx) ((ctx)->opts->contentlog)
|
||||
|
||||
typedef void (*fd_readcb_func_t)(evutil_socket_t, short, void *);
|
||||
typedef int (*connect_func_t)(pxy_conn_ctx_t *);
|
||||
|
||||
typedef void (*callback_func_t)(struct bufferevent *, void *);
|
||||
typedef void (*eventcb_func_t)(struct bufferevent *, short, void *);
|
||||
|
||||
typedef void (*bev_free_func_t)(struct bufferevent *, pxy_conn_ctx_t *);
|
||||
|
||||
typedef void (*proto_free_func_t)(pxy_conn_ctx_t *);
|
||||
|
||||
/*
|
||||
* Proxy connection context state, describes a proxy connection
|
||||
* with source and destination socket bufferevents, SSL context and
|
||||
* other session state. One of these exists per handled proxy
|
||||
* connection.
|
||||
*/
|
||||
|
||||
/* single socket bufferevent descriptor */
|
||||
typedef struct pxy_conn_desc {
|
||||
struct bufferevent *bev;
|
||||
unsigned int closed : 1;
|
||||
bev_free_func_t free;
|
||||
} pxy_conn_desc_t;
|
||||
|
||||
typedef enum protocol {
|
||||
PROTO_ERROR = -1,
|
||||
PROTO_TCP = 0,
|
||||
} protocol_t;
|
||||
|
||||
typedef struct proto_ctx proto_ctx_t;
|
||||
|
||||
struct proto_ctx {
|
||||
protocol_t proto;
|
||||
|
||||
connect_func_t connectcb;
|
||||
fd_readcb_func_t fd_readcb;
|
||||
|
||||
callback_func_t bev_readcb;
|
||||
callback_func_t bev_writecb;
|
||||
eventcb_func_t bev_eventcb;
|
||||
|
||||
proto_free_func_t proto_free;
|
||||
|
||||
// For protocol specific fields, if any
|
||||
void *arg;
|
||||
};
|
||||
|
||||
/* connection state consisting of two connection descriptors,
|
||||
* connection-wide state and the specs and options */
|
||||
struct pxy_conn_ctx {
|
||||
protocol_t proto;
|
||||
|
||||
/* per-connection state */
|
||||
struct pxy_conn_desc src;
|
||||
struct pxy_conn_desc dst;
|
||||
|
||||
/* store fd and fd event while connected is 0 */
|
||||
evutil_socket_t fd;
|
||||
|
||||
// For protocol specific fields, never NULL
|
||||
proto_ctx_t *protoctx;
|
||||
|
||||
/* log strings from socket */
|
||||
char *srchost_str;
|
||||
char *srcport_str;
|
||||
char *dsthost_str;
|
||||
char *dstport_str;
|
||||
|
||||
/* content log context */
|
||||
log_content_ctx_t logctx;
|
||||
|
||||
/* status flags */
|
||||
unsigned int src_connected : 1; /* 0 until src connected */
|
||||
unsigned int dst_connected : 1; /* 0 until dst connected */
|
||||
unsigned int enomem : 1; /* 1 if out of memory */
|
||||
unsigned int term : 1; /* 0 until term requested */
|
||||
unsigned int term_requestor : 1; /* 1 client, 0 server side */
|
||||
unsigned int seen_sslproxy_line : 1; /* 1 if seen sslproxy line */
|
||||
|
||||
/* original source and destination address, and family */
|
||||
struct sockaddr_storage srcaddr;
|
||||
socklen_t srcaddrlen;
|
||||
struct sockaddr_storage dstaddr;
|
||||
socklen_t dstaddrlen;
|
||||
int af;
|
||||
|
||||
// Thread that the conn is attached to
|
||||
pxy_thr_ctx_t *thr;
|
||||
unsigned int thr_locked : 1; /* 1 to prevent double locking */
|
||||
unsigned int in_thr_conns : 1; /* 1 to prevent adding twice */
|
||||
|
||||
// Unique id of the conn
|
||||
long long unsigned int id;
|
||||
|
||||
pxy_thrmgr_ctx_t *thrmgr;
|
||||
opts_t *opts;
|
||||
|
||||
struct event_base *evbase;
|
||||
|
||||
evutil_socket_t dst_fd;
|
||||
|
||||
// Conn create time
|
||||
time_t ctime;
|
||||
|
||||
// Conn last access time, used to determine expired conns
|
||||
// Updated on entry to callback functions, parent or child
|
||||
time_t atime;
|
||||
|
||||
// Per-thread conn list, used to determine idle and expired conns, and to close them
|
||||
pxy_conn_ctx_t *next;
|
||||
};
|
||||
|
||||
void pxy_log_connect(pxy_conn_ctx_t *) NONNULL(1);
|
||||
|
||||
int pxy_log_content_inbuf(pxy_conn_ctx_t *, struct evbuffer *, int) NONNULL(1);
|
||||
void pxy_log_connect_nonhttp(pxy_conn_ctx_t *) NONNULL(1);
|
||||
void pxy_log_dbg_evbuf_info(pxy_conn_ctx_t *, pxy_conn_desc_t *, pxy_conn_desc_t *) NONNULL(1,2,3);
|
||||
|
||||
void pxy_try_set_watermark(struct bufferevent *, pxy_conn_ctx_t *, struct bufferevent *) NONNULL(1,2,3);
|
||||
void pxy_try_unset_watermark(struct bufferevent *, pxy_conn_ctx_t *, pxy_conn_desc_t *) NONNULL(1,2,3);
|
||||
|
||||
int pxy_try_close_conn_end(pxy_conn_desc_t *, pxy_conn_ctx_t *) NONNULL(1,2);
|
||||
|
||||
void pxy_try_disconnect(pxy_conn_ctx_t *, pxy_conn_desc_t *, pxy_conn_desc_t *, int) NONNULL(1,2,3);
|
||||
|
||||
int pxy_try_consume_last_input(struct bufferevent *, pxy_conn_ctx_t *) NONNULL(1,2);
|
||||
void pxy_discard_inbuf(struct bufferevent *) NONNULL(1);
|
||||
|
||||
void pxy_conn_ctx_free(pxy_conn_ctx_t *, int) NONNULL(1);
|
||||
void pxy_conn_free(pxy_conn_ctx_t *, int) NONNULL(1);
|
||||
void pxy_conn_term(pxy_conn_ctx_t *, int) NONNULL(1);
|
||||
|
||||
int pxy_bev_readcb_preexec_logging_and_stats(struct bufferevent *, pxy_conn_ctx_t *) NONNULL(1,2);
|
||||
int pxy_bev_eventcb_postexec_logging_and_stats(struct bufferevent *, short , pxy_conn_ctx_t *) NONNULL(1,3);
|
||||
|
||||
void pxy_bev_readcb(struct bufferevent *, void *);
|
||||
void pxy_bev_writecb(struct bufferevent *, void *);
|
||||
void pxy_bev_eventcb(struct bufferevent *, short, void *);
|
||||
|
||||
void pxy_conn_connect(pxy_conn_ctx_t *) NONNULL(1);
|
||||
void pxy_conn_setup(evutil_socket_t, struct sockaddr *, int,
|
||||
pxy_thrmgr_ctx_t *, opts_t *)
|
||||
NONNULL(2,4,5);
|
||||
|
||||
#endif /* !PXYCONN_H */
|
||||
|
||||
/* vim: set noet ft=c: */
|
@ -0,0 +1,438 @@
|
||||
/*-
|
||||
* SSLsplit - transparent SSL/TLS interception
|
||||
* https://www.roe.ch/SSLsplit
|
||||
*
|
||||
* Copyright (c) 2009-2018, Daniel Roethlisberger <daniel@roe.ch>.
|
||||
* Copyright (c) 2017-2019, Soner Tari <sonertari@gmail.com>.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "pxythrmgr.h"
|
||||
|
||||
#include "sys.h"
|
||||
#include "log.h"
|
||||
#include "pxyconn.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <event2/bufferevent.h>
|
||||
#include <pthread.h>
|
||||
#include <assert.h>
|
||||
#include <sys/param.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.
|
||||
*/
|
||||
|
||||
static void
|
||||
pxy_thrmgr_print_thr_info(pxy_thr_ctx_t *tctx)
|
||||
{
|
||||
#ifdef DEBUG_PROXY
|
||||
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "pxy_thrmgr_print_thr_info: thr=%d, load=%lu\n", tctx->thridx, tctx->load);
|
||||
#endif /* DEBUG_PROXY */
|
||||
|
||||
unsigned int idx = 1;
|
||||
evutil_socket_t max_fd = 0;
|
||||
time_t max_atime = 0;
|
||||
time_t max_ctime = 0;
|
||||
|
||||
char *smsg = NULL;
|
||||
|
||||
if (tctx->conns) {
|
||||
time_t now = time(NULL);
|
||||
|
||||
pxy_conn_ctx_t *ctx = tctx->conns;
|
||||
while (ctx) {
|
||||
time_t atime = now - ctx->atime;
|
||||
time_t ctime = now - ctx->ctime;
|
||||
|
||||
#ifdef DEBUG_PROXY
|
||||
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "pxy_thrmgr_print_thr_info: CONN: thr=%d, id=%u, fd=%d, dst=%d, p=%d-%d, at=%lld ct=%lld, src_addr=%s:%s, dst_addr=%s:%s\n",
|
||||
tctx->thridx, idx, ctx->fd, ctx->dst_fd, ctx->src.closed, ctx->dst.closed, (long long)atime, (long long)ctime,
|
||||
STRORDASH(ctx->srchost_str), STRORDASH(ctx->srcport_str), STRORDASH(ctx->dsthost_str), STRORDASH(ctx->dstport_str));
|
||||
#endif /* DEBUG_PROXY */
|
||||
|
||||
max_fd = MAX(max_fd, MAX(ctx->fd, ctx->dst_fd));
|
||||
max_atime = MAX(max_atime, atime);
|
||||
max_ctime = MAX(max_ctime, ctime);
|
||||
|
||||
idx++;
|
||||
ctx = ctx->next;
|
||||
}
|
||||
}
|
||||
|
||||
if (asprintf(&smsg, "STATS: thr=%d, mld=%zu, mfd=%d, mat=%lld, mct=%lld, iib=%llu, iob=%llu, eib=%llu, eob=%llu, swm=%zu, uwm=%zu, err=%zu, si=%u\n",
|
||||
tctx->thridx, tctx->max_load, tctx->max_fd, (long long)max_atime, (long long)max_ctime, tctx->intif_in_bytes, tctx->intif_out_bytes, tctx->extif_in_bytes, tctx->extif_out_bytes,
|
||||
tctx->set_watermarks, tctx->unset_watermarks, tctx->errors, tctx->stats_id) < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_PROXY
|
||||
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "pxy_thrmgr_print_thr_info: %s", smsg);
|
||||
#endif /* DEBUG_PROXY */
|
||||
|
||||
if (log_stats(smsg) == -1) {
|
||||
log_err_level_printf(LOG_WARNING, "Stats logging failed\n");
|
||||
}
|
||||
free(smsg);
|
||||
smsg = NULL;
|
||||
|
||||
tctx->stats_id++;
|
||||
|
||||
tctx->errors = 0;
|
||||
tctx->set_watermarks = 0;
|
||||
tctx->unset_watermarks = 0;
|
||||
|
||||
tctx->intif_in_bytes = 0;
|
||||
tctx->intif_out_bytes = 0;
|
||||
tctx->extif_in_bytes = 0;
|
||||
tctx->extif_out_bytes = 0;
|
||||
|
||||
// Reset these stats with the current values (do not reset to 0 directly, there may be active conns)
|
||||
tctx->max_fd = max_fd;
|
||||
tctx->max_load = tctx->load;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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)
|
||||
{
|
||||
pxy_thr_ctx_t *ctx = arg;
|
||||
|
||||
pthread_mutex_lock(&ctx->mutex);
|
||||
#ifdef DEBUG_PROXY
|
||||
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "pxy_thrmgr_timer_cb: thr=%d, load=%lu, to=%u\n", ctx->thridx, ctx->load, ctx->timeout_count);
|
||||
#endif /* DEBUG_PROXY */
|
||||
|
||||
// @attention Print thread info only if stats logging is enabled, if disabled debug logs are not printed either
|
||||
if (ctx->thrmgr->opts->statslog) {
|
||||
ctx->timeout_count++;
|
||||
if (ctx->timeout_count >= ctx->thrmgr->opts->stats_period) {
|
||||
ctx->timeout_count = 0;
|
||||
pxy_thrmgr_print_thr_info(ctx);
|
||||
}
|
||||
}
|
||||
pthread_mutex_unlock(&ctx->mutex);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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 = {10, 0};
|
||||
struct event *ev;
|
||||
|
||||
ev = event_new(ctx->evbase, -1, EV_PERSIST, pxy_thrmgr_timer_cb, ctx);
|
||||
if (!ev)
|
||||
return NULL;
|
||||
evtimer_add(ev, &timer_delay);
|
||||
ctx->running = 1;
|
||||
event_base_dispatch(ctx->evbase);
|
||||
event_free(ev);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Create new thread manager but do not start any threads yet.
|
||||
* This gets called before forking to background.
|
||||
*/
|
||||
pxy_thrmgr_ctx_t *
|
||||
pxy_thrmgr_new(opts_t *opts)
|
||||
{
|
||||
pxy_thrmgr_ctx_t *ctx;
|
||||
|
||||
if (!(ctx = malloc(sizeof(pxy_thrmgr_ctx_t))))
|
||||
return NULL;
|
||||
memset(ctx, 0, sizeof(pxy_thrmgr_ctx_t));
|
||||
|
||||
ctx->opts = opts;
|
||||
ctx->num_thr = 2 * sys_get_cpu_cores();
|
||||
return ctx;
|
||||
}
|
||||
|
||||
/*
|
||||
* Start the thread manager and associated threads.
|
||||
* This must be called after forking.
|
||||
*
|
||||
* Returns -1 on failure, 0 on success.
|
||||
*/
|
||||
int
|
||||
pxy_thrmgr_run(pxy_thrmgr_ctx_t *ctx)
|
||||
{
|
||||
int idx = -1;
|
||||
|
||||
if (!(ctx->thr = malloc(ctx->num_thr * sizeof(pxy_thr_ctx_t*)))) {
|
||||
log_dbg_printf("Failed to allocate memory\n");
|
||||
goto leave;
|
||||
}
|
||||
memset(ctx->thr, 0, ctx->num_thr * sizeof(pxy_thr_ctx_t*));
|
||||
|
||||
for (idx = 0; idx < ctx->num_thr; idx++) {
|
||||
if (!(ctx->thr[idx] = malloc(sizeof(pxy_thr_ctx_t)))) {
|
||||
log_dbg_printf("Failed to allocate memory\n");
|
||||
goto leave;
|
||||
}
|
||||
memset(ctx->thr[idx], 0, sizeof(pxy_thr_ctx_t));
|
||||
ctx->thr[idx]->evbase = event_base_new();
|
||||
if (!ctx->thr[idx]->evbase) {
|
||||
log_dbg_printf("Failed to create evbase %d\n", idx);
|
||||
goto leave;
|
||||
}
|
||||
ctx->thr[idx]->load = 0;
|
||||
ctx->thr[idx]->running = 0;
|
||||
ctx->thr[idx]->conns = NULL;
|
||||
ctx->thr[idx]->thridx = idx;
|
||||
ctx->thr[idx]->timeout_count = 0;
|
||||
ctx->thr[idx]->thrmgr = ctx;
|
||||
|
||||
if (pthread_mutex_init(&ctx->thr[idx]->mutex, NULL)) {
|
||||
log_dbg_printf("Failed to initialize thr mutex\n");
|
||||
goto leave;
|
||||
}
|
||||
}
|
||||
|
||||
log_dbg_printf("Initialized %d connection handling threads\n",
|
||||
ctx->num_thr);
|
||||
|
||||
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;
|
||||
while (!ctx->thr[idx]->running) {
|
||||
sched_yield();
|
||||
}
|
||||
}
|
||||
|
||||
log_dbg_printf("Started %d connection handling threads\n",
|
||||
ctx->num_thr);
|
||||
|
||||
return 0;
|
||||
|
||||
leave_thr:
|
||||
idx--;
|
||||
while (idx >= 0) {
|
||||
pthread_cancel(ctx->thr[idx]->thr);
|
||||
pthread_join(ctx->thr[idx]->thr, NULL);
|
||||
idx--;
|
||||
}
|
||||
idx = ctx->num_thr - 1;
|
||||
|
||||
leave:
|
||||
while (idx >= 0) {
|
||||
if (ctx->thr[idx]) {
|
||||
if (ctx->thr[idx]->evbase) {
|
||||
event_base_free(ctx->thr[idx]->evbase);
|
||||
}
|
||||
pthread_mutex_destroy(&ctx->thr[idx]->mutex);
|
||||
free(ctx->thr[idx]);
|
||||
}
|
||||
idx--;
|
||||
}
|
||||
if (ctx->thr) {
|
||||
free(ctx->thr);
|
||||
ctx->thr = NULL;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Destroy the event manager and stop all threads.
|
||||
*/
|
||||
void
|
||||
pxy_thrmgr_free(pxy_thrmgr_ctx_t *ctx)
|
||||
{
|
||||
if (ctx->thr) {
|
||||
for (int idx = 0; idx < ctx->num_thr; idx++) {
|
||||
event_base_loopbreak(ctx->thr[idx]->evbase);
|
||||
sched_yield();
|
||||
}
|
||||
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++) {
|
||||
if (ctx->thr[idx]->evbase) {
|
||||
event_base_free(ctx->thr[idx]->evbase);
|
||||
}
|
||||
pthread_mutex_destroy(&ctx->thr[idx]->mutex);
|
||||
free(ctx->thr[idx]);
|
||||
}
|
||||
free(ctx->thr);
|
||||
}
|
||||
free(ctx);
|
||||
}
|
||||
|
||||
void
|
||||
pxy_thrmgr_add_conn(pxy_conn_ctx_t *ctx)
|
||||
{
|
||||
pthread_mutex_lock(&ctx->thr->mutex);
|
||||
if (!ctx->in_thr_conns) {
|
||||
#ifdef DEBUG_PROXY
|
||||
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "pxy_thrmgr_add_conn: Adding conn, id=%llu, fd=%d\n", ctx->id, ctx->fd);
|
||||
#endif /* DEBUG_PROXY */
|
||||
|
||||
ctx->in_thr_conns = 1;
|
||||
// Always keep thr load and conns list in sync
|
||||
ctx->thr->load++;
|
||||
ctx->next = ctx->thr->conns;
|
||||
ctx->thr->conns = ctx;
|
||||
} else {
|
||||
// Do not add conns twice
|
||||
#ifdef DEBUG_PROXY
|
||||
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "pxy_thrmgr_add_conn: Will not add conn twice, id=%llu, fd=%d\n", ctx->id, ctx->fd);
|
||||
#endif /* DEBUG_PROXY */
|
||||
}
|
||||
pthread_mutex_unlock(&ctx->thr->mutex);
|
||||
}
|
||||
|
||||
static void NONNULL(1)
|
||||
pxy_thrmgr_remove_conn_unlocked(pxy_conn_ctx_t *ctx)
|
||||
{
|
||||
assert(ctx != NULL);
|
||||
|
||||
if (ctx->in_thr_conns) {
|
||||
#ifdef DEBUG_PROXY
|
||||
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "pxy_thrmgr_remove_conn_unlocked: Removing conn, id=%llu, fd=%d\n", ctx->id, ctx->fd);
|
||||
#endif /* DEBUG_PROXY */
|
||||
|
||||
// Thr conns list cannot be empty, if the in_thr_conns flag of a conn is set
|
||||
assert(ctx->thr->conns != NULL);
|
||||
|
||||
// Shouldn't need to reset the in_thr_conns flag, because the conn ctx will be freed next, but just in case
|
||||
ctx->in_thr_conns = 0;
|
||||
// We increment thr load in pxy_thrmgr_add_conn() only
|
||||
ctx->thr->load--;
|
||||
|
||||
// @attention We may get multiple conns with the same fd combinations, so fds cannot uniquely define a conn; hence the need for unique ids.
|
||||
if (ctx->id == ctx->thr->conns->id) {
|
||||
ctx->thr->conns = ctx->thr->conns->next;
|
||||
return;
|
||||
} else {
|
||||
pxy_conn_ctx_t *current = ctx->thr->conns->next;
|
||||
pxy_conn_ctx_t *previous = ctx->thr->conns;
|
||||
while (current != NULL && previous != NULL) {
|
||||
if (ctx->id == current->id) {
|
||||
previous->next = current->next;
|
||||
return;
|
||||
}
|
||||
previous = current;
|
||||
current = current->next;
|
||||
}
|
||||
// This should never happen
|
||||
log_err_level_printf(LOG_CRIT, "Cannot find conn in thr conns\n");
|
||||
#ifdef DEBUG_PROXY
|
||||
log_dbg_level_printf(LOG_DBG_MODE_FINE, "pxy_thrmgr_remove_conn_unlocked: Cannot find conn in thr conns, id=%llu, fd=%d\n", ctx->id, ctx->fd);
|
||||
#endif /* DEBUG_PROXY */
|
||||
assert(0);
|
||||
}
|
||||
} else {
|
||||
// This can happen if we are closing the conn after a fatal error before setting its event callback
|
||||
#ifdef DEBUG_PROXY
|
||||
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "pxy_thrmgr_remove_conn_unlocked: Conn not in thr conns, id=%llu, fd=%d\n", ctx->id, ctx->fd);
|
||||
#endif /* DEBUG_PROXY */
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Attach a new connection to a thread. Chooses the thread with the fewest
|
||||
* currently active connections, returns the appropriate event bases.
|
||||
* No need to be so accurate about balancing thread loads, so uses
|
||||
* thread-level mutexes, instead of a thrmgr level mutex.
|
||||
* Returns the index of the chosen thread (for passing to _detach later).
|
||||
* This function cannot fail.
|
||||
*/
|
||||
void
|
||||
pxy_thrmgr_attach(pxy_conn_ctx_t *ctx)
|
||||
{
|
||||
#ifdef DEBUG_PROXY
|
||||
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "pxy_thrmgr_attach: ENTER, fd=%d\n", ctx->fd);
|
||||
#endif /* DEBUG_PROXY */
|
||||
|
||||
int thridx = 0;
|
||||
size_t minload;
|
||||
|
||||
pxy_thrmgr_ctx_t *tmctx = ctx->thrmgr;
|
||||
pthread_mutex_lock(&tmctx->thr[0]->mutex);
|
||||
minload = tmctx->thr[0]->load;
|
||||
pthread_mutex_unlock(&tmctx->thr[0]->mutex);
|
||||
|
||||
#ifdef DEBUG_THREAD
|
||||
log_dbg_printf("===> Proxy connection handler thread status:\n"
|
||||
"thr[0]: %zu\n", minload);
|
||||
#endif /* DEBUG_THREAD */
|
||||
for (int idx = 1; idx < tmctx->num_thr; idx++) {
|
||||
pthread_mutex_lock(&tmctx->thr[idx]->mutex);
|
||||
#ifdef DEBUG_THREAD
|
||||
log_dbg_printf("thr[%d]: %zu\n", idx, tmctx->thr[idx]->load);
|
||||
#endif /* DEBUG_THREAD */
|
||||
if (minload > tmctx->thr[idx]->load) {
|
||||
minload = tmctx->thr[idx]->load;
|
||||
thridx = idx;
|
||||
}
|
||||
pthread_mutex_unlock(&tmctx->thr[idx]->mutex);
|
||||
}
|
||||
|
||||
// Defer adding the conn to the conn list of its thread until after a successful conn setup while returning from pxy_conn_connect()
|
||||
// otherwise pxy_thrmgr_timer_cb() may try to access the conn ctx while it is being freed on failure (signal 6 crash)
|
||||
ctx->thr = tmctx->thr[thridx];
|
||||
ctx->evbase = ctx->thr->evbase;
|
||||
|
||||
#ifdef DEBUG_THREAD
|
||||
log_dbg_printf("thridx: %d\n", thridx);
|
||||
#endif /* DEBUG_THREAD */
|
||||
}
|
||||
|
||||
/*
|
||||
* Detach a connection from a thread by index.
|
||||
* This function cannot fail.
|
||||
*/
|
||||
void
|
||||
pxy_thrmgr_detach_unlocked(pxy_conn_ctx_t *ctx)
|
||||
{
|
||||
#ifdef DEBUG_PROXY
|
||||
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "pxy_thrmgr_detach_unlocked: ENTER, fd=%d\n", ctx->fd);
|
||||
#endif /* DEBUG_PROXY */
|
||||
|
||||
pxy_thrmgr_remove_conn_unlocked(ctx);
|
||||
}
|
||||
|
||||
void
|
||||
pxy_thrmgr_detach(pxy_conn_ctx_t *ctx)
|
||||
{
|
||||
pthread_mutex_lock(&ctx->thr->mutex);
|
||||
pxy_thrmgr_detach_unlocked(ctx);
|
||||
pthread_mutex_unlock(&ctx->thr->mutex);
|
||||
}
|
||||
|
||||
/* vim: set noet ft=c: */
|
@ -0,0 +1,100 @@
|
||||
/*-
|
||||
* SSLsplit - transparent SSL/TLS interception
|
||||
* https://www.roe.ch/SSLsplit
|
||||
*
|
||||
* Copyright (c) 2009-2018, Daniel Roethlisberger <daniel@roe.ch>.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef 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>
|
||||
#include <pthread.h>
|
||||
|
||||
extern int descriptor_table_size;
|
||||
#define FD_RESERVE 10
|
||||
|
||||
typedef struct pxy_conn_ctx pxy_conn_ctx_t;
|
||||
typedef struct pxy_thrmgr_ctx pxy_thrmgr_ctx_t;
|
||||
|
||||
typedef struct pxy_thr_ctx {
|
||||
pthread_t thr;
|
||||
int thridx;
|
||||
pxy_thrmgr_ctx_t *thrmgr;
|
||||
size_t load;
|
||||
struct event_base *evbase;
|
||||
int running;
|
||||
|
||||
// Per-thread locking is necessary during connection setup and termination
|
||||
// to prevent multithreading issues between thrmgr thread and conn handling threads
|
||||
pthread_mutex_t mutex;
|
||||
|
||||
// Statistics
|
||||
evutil_socket_t max_fd;
|
||||
size_t max_load;
|
||||
size_t errors;
|
||||
size_t set_watermarks;
|
||||
size_t unset_watermarks;
|
||||
long long unsigned int intif_in_bytes;
|
||||
long long unsigned int intif_out_bytes;
|
||||
long long unsigned int extif_in_bytes;
|
||||
long long unsigned int extif_out_bytes;
|
||||
// Each stats has an id, incremented on each stats print
|
||||
unsigned short stats_id;
|
||||
// Used to print statistics, compared against stats_period
|
||||
unsigned int timeout_count;
|
||||
|
||||
// List of active connections on the thread
|
||||
pxy_conn_ctx_t *conns;
|
||||
} pxy_thr_ctx_t;
|
||||
|
||||
struct pxy_thrmgr_ctx {
|
||||
int num_thr;
|
||||
opts_t *opts;
|
||||
pxy_thr_ctx_t **thr;
|
||||
// Provides unique conn id, always goes up, never down
|
||||
// There is no risk of collision if/when it rolls back to 0
|
||||
long long unsigned int conn_count;
|
||||
};
|
||||
|
||||
pxy_thrmgr_ctx_t * pxy_thrmgr_new(opts_t *) MALLOC;
|
||||
int pxy_thrmgr_run(pxy_thrmgr_ctx_t *) NONNULL(1) WUNRES;
|
||||
void pxy_thrmgr_free(pxy_thrmgr_ctx_t *) NONNULL(1);
|
||||
|
||||
void pxy_thrmgr_add_conn(pxy_conn_ctx_t *) NONNULL(1);
|
||||
|
||||
void pxy_thrmgr_attach(pxy_conn_ctx_t *) NONNULL(1);
|
||||
void pxy_thrmgr_detach_unlocked(pxy_conn_ctx_t *) NONNULL(1);
|
||||
void pxy_thrmgr_detach(pxy_conn_ctx_t *) NONNULL(1);
|
||||
|
||||
#endif /* !PXYTHRMGR_H */
|
||||
|
||||
/* vim: set noet ft=c: */
|
@ -0,0 +1,676 @@
|
||||
/*-
|
||||
* SSLsplit - transparent SSL/TLS interception
|
||||
* https://www.roe.ch/SSLsplit
|
||||
*
|
||||
* Copyright (c) 2009-2018, Daniel Roethlisberger <daniel@roe.ch>.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "sys.h"
|
||||
|
||||
#include "log.h"
|
||||
#include "defaults.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/uio.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/file.h>
|
||||
#include <sys/un.h>
|
||||
#include <sys/time.h>
|
||||
#include <net/if.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 */
|
||||
|
||||
#if HAVE_DARWIN_LIBPROC
|
||||
#include <libproc.h>
|
||||
#endif
|
||||
|
||||
#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 groupname is set, it will be used instead of the user's default primary
|
||||
* group.
|
||||
* 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 *groupname, const char *jaildir)
|
||||
{
|
||||
struct passwd *pw = NULL;
|
||||
struct group *gr = NULL;
|
||||
int ret = -1;
|
||||
|
||||
if (groupname) {
|
||||
errno = 0;
|
||||
if (!(gr = getgrnam(groupname))) {
|
||||
log_err_level_printf(LOG_CRIT, "Failed to getgrnam group '%s': %s\n",
|
||||
groupname, strerror(errno));
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
if (username) {
|
||||
errno = 0;
|
||||
if (!(pw = getpwnam(username))) {
|
||||
log_err_level_printf(LOG_CRIT, "Failed to getpwnam user '%s': %s\n",
|
||||
username, strerror(errno));
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (gr != NULL) {
|
||||
pw->pw_gid = gr->gr_gid;
|
||||
}
|
||||
|
||||
if (initgroups(username, pw->pw_gid) == -1) {
|
||||
log_err_level_printf(LOG_CRIT, "Failed to initgroups user '%s': %s\n",
|
||||
username, strerror(errno));
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
if (jaildir) {
|
||||
if (chroot(jaildir) == -1) {
|
||||
log_err_level_printf(LOG_CRIT, "Failed to chroot to '%s': %s\n",
|
||||
jaildir, strerror(errno));
|
||||
goto error;
|
||||
}
|
||||
if (chdir("/") == -1) {
|
||||
log_err_level_printf(LOG_CRIT, "Failed to chdir to '/': %s\n",
|
||||
strerror(errno));
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
if (username) {
|
||||
if (setgid(pw->pw_gid) == -1) {
|
||||
log_err_level_printf(LOG_CRIT, "Failed to setgid to %i: %s\n",
|
||||
pw->pw_gid, strerror(errno));
|
||||
goto error;
|
||||
}
|
||||
if (setuid(pw->pw_uid) == -1) {
|
||||
log_err_level_printf(LOG_CRIT, "Failed to setuid to %i: %s\n",
|
||||
pw->pw_uid, strerror(errno));
|
||||
goto error;
|
||||
}
|
||||
} else if (getuid() != geteuid()) {
|
||||
if (setuid(getuid()) == -1) {
|
||||
log_err_level_printf(LOG_CRIT, "Failed to setuid(getuid()): %s\n",
|
||||
strerror(errno));
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
error:
|
||||
if (pw) {
|
||||
endpwent();
|
||||
}
|
||||
if (gr) {
|
||||
endgrent();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* If the user exists and on successful lookup, return 0 and if uid != NULL,
|
||||
* write the uid of *username* to the value pointed to by uid.
|
||||
* Return -1 on failure or if the user does not exist.
|
||||
*/
|
||||
int
|
||||
sys_uid(const char *username, uid_t *uid)
|
||||
{
|
||||
struct passwd *pw;
|
||||
int rv;
|
||||
|
||||
errno = 0;
|
||||
if (!(pw = getpwnam(username))) {
|
||||
if (errno != 0 && errno != ENOENT) {
|
||||
log_err_level_printf(LOG_CRIT, "Failed to load user '%s': %s (%i)\n",
|
||||
username, strerror(errno), errno);
|
||||
}
|
||||
rv = -1;
|
||||
} else {
|
||||
if (uid)
|
||||
*uid = pw->pw_uid;
|
||||
rv = 0;
|
||||
}
|
||||
endpwent();
|
||||
return rv;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns 1 if username can be loaded from user database, 0 otherwise.
|
||||
*/
|
||||
int
|
||||
sys_isuser(const char *username)
|
||||
{
|
||||
return sys_uid(username, NULL) == 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* If the group exists and on successful lookup, return 0 and if gid != NULL,
|
||||
* write the gid of *groupname* to the value pointed to by gid.
|
||||
* Return -1 on failure or if the group does not exist.
|
||||
*/
|
||||
int
|
||||
sys_gid(const char *groupname, gid_t *gid)
|
||||
{
|
||||
struct group *gr;
|
||||
int rv;
|
||||
|
||||
errno = 0;
|
||||
if (!(gr = getgrnam(groupname))) {
|
||||
if (errno != 0 && errno != ENOENT) {
|
||||
log_err_level_printf(LOG_CRIT, "Failed to load group '%s': %s (%i)\n",
|
||||
groupname, strerror(errno), errno);
|
||||
}
|
||||
rv = -1;
|
||||
} else {
|
||||
if (gid)
|
||||
*gid = gr->gr_gid;
|
||||
rv = 0;
|
||||
}
|
||||
endgrent();
|
||||
return rv;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns 1 if groupname can be loaded from group database, 0 otherwise.
|
||||
*/
|
||||
int
|
||||
sys_isgroup(const char *groupname)
|
||||
{
|
||||
return sys_gid(groupname, NULL) == 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns 1 if username is equivalent to the current effective UID.
|
||||
* Returns 0 otherwise.
|
||||
*/
|
||||
int
|
||||
sys_isgeteuid(const char *username)
|
||||
{
|
||||
uid_t uid;
|
||||
|
||||
if (sys_uid(username, &uid) == -1)
|
||||
return 0;
|
||||
if (uid == geteuid())
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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, DFLT_PIDFMODE)) == -1) {
|
||||
log_err_level_printf(LOG_CRIT, "Failed to open '%s': %s\n", fn,
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
if (flock(fd, LOCK_EX|LOCK_NB) == -1) {
|
||||
log_err_level_printf(LOG_CRIT, "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;
|
||||
ssize_t n;
|
||||
|
||||
rv = snprintf(pidbuf, sizeof(pidbuf), "%d\n", getpid());
|
||||
if (rv == -1 || rv >= (int)sizeof(pidbuf))
|
||||
return -1;
|
||||
|
||||
n = write(fd, pidbuf, strlen(pidbuf));
|
||||
if (n < (ssize_t)strlen(pidbuf))
|
||||
return -1;
|
||||
|
||||
rv = fsync(fd);
|
||||
if (rv == -1)
|
||||
return -1;
|
||||
|
||||
rv = fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC);
|
||||
if (rv == -1)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Close and remove open process ID file before quitting.
|
||||
*/
|
||||
void
|
||||
sys_pidf_close(int fd, const char *fn)
|
||||
{
|
||||
unlink(fn);
|
||||
close(fd);
|
||||
}
|
||||
|
||||
/*
|
||||
* Determine address family of addr
|
||||
*/
|
||||
int
|
||||
sys_get_af(const char *addr)
|
||||
{
|
||||
if (strstr(addr, ":"))
|
||||
return AF_INET6;
|
||||
else if (!strpbrk(addr, "abcdefghijklmnopqrstu"
|
||||
"vwxyzABCDEFGHIJKLMNOP"
|
||||
"QRSTUVWXYZ-"))
|
||||
return AF_INET;
|
||||
else
|
||||
return AF_UNSPEC;
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse an ascii host/IP and port tuple into a sockaddr_storage.
|
||||
* On success, returns address family and fills in addr, addrlen.
|
||||
* Returns -1 on error.
|
||||
*/
|
||||
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_level_printf(LOG_CRIT, "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 printable string representations of the
|
||||
* host and the service (port) part. Writes allocated buffers to *host and
|
||||
* *serv which must both be freed by the caller. Neither *host nor *port are
|
||||
* freed by this function before newly allocating.
|
||||
* Returns 0 on success, -1 otherwise. When -1 is returned, pointers in *host
|
||||
* and *serv are invalid and must not be used nor freed by the caller.
|
||||
*/
|
||||
int
|
||||
sys_sockaddr_str(struct sockaddr *addr, socklen_t addrlen,
|
||||
char **host, char **serv)
|
||||
{
|
||||
char tmphost[INET6_ADDRSTRLEN];
|
||||
int rv;
|
||||
size_t hostsz;
|
||||
|
||||
*serv = malloc(6); /* max decimal digits of short plus terminator */
|
||||
if (!*serv) {
|
||||
log_err_level_printf(LOG_CRIT, "Cannot allocate memory\n");
|
||||
return -1;
|
||||
}
|
||||
rv = getnameinfo(addr, addrlen,
|
||||
tmphost, sizeof(tmphost),
|
||||
*serv, 6,
|
||||
NI_NUMERICHOST | NI_NUMERICSERV);
|
||||
if (rv != 0) {
|
||||
log_err_level_printf(LOG_CRIT, "Cannot get nameinfo for socket address: %s\n",
|
||||
gai_strerror(rv));
|
||||
free(*serv);
|
||||
return -1;
|
||||
}
|
||||
hostsz = strlen(tmphost) + 1; /* including terminator */
|
||||
*host = malloc(hostsz);
|
||||
if (!*host) {
|
||||
log_err_level_printf(LOG_CRIT, "Cannot allocate memory\n");
|
||||
free(*serv);
|
||||
return -1;
|
||||
}
|
||||
memcpy(*host, tmphost, hostsz);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Sanitizes a valid IPv4 or IPv6 address for use in a filename, i.e. removes
|
||||
* characters that are invalid on NTFS and replaces them with more innocent
|
||||
* characters. The function assumes that the input is a valid IPv4 or IPv6
|
||||
* address; it is not a generic filename sanitizer.
|
||||
*
|
||||
* Returns a copy of string s that must be freed by the caller.
|
||||
*
|
||||
* Invalid NTFS characters are < > : " / \ | ? * according to
|
||||
* https://msdn.microsoft.com/en-gb/library/windows/desktop/aa365247.aspx
|
||||
*/
|
||||
char *
|
||||
sys_ip46str_sanitize(const char *s)
|
||||
{
|
||||
char *copy, *p;
|
||||
|
||||
copy = strdup(s);
|
||||
if (!copy)
|
||||
return NULL;
|
||||
p = copy;
|
||||
while (*p) {
|
||||
switch (*p) {
|
||||
case ':':
|
||||
case '%':
|
||||
*p = '_';
|
||||
break;
|
||||
}
|
||||
p++;
|
||||
}
|
||||
|
||||
return copy;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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) {
|
||||
if (errno != ENOENT) {
|
||||
log_err_level_printf(LOG_CRIT, "Error stating file: %s (%i)\n",
|
||||
strerror(errno), errno);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
if (s.st_mode & S_IFDIR)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Create directory including parent directories with mode_t.
|
||||
* Mode of existing parent directories is not changed.
|
||||
* Returns 0 on success, -1 and sets errno on error.
|
||||
*/
|
||||
int
|
||||
sys_mkpath(const char *path, mode_t mode)
|
||||
{
|
||||
char parent[strlen(path)+1];
|
||||
char *p;
|
||||
|
||||
memcpy(parent, path, sizeof(parent));
|
||||
|
||||
p = parent;
|
||||
do {
|
||||
/* skip leading '/' characters */
|
||||
while (*p == '/') p++;
|
||||
p = strchr(p, '/');
|
||||
if (p) {
|
||||
/* overwrite '/' to terminate the string at the next
|
||||
* parent directory */
|
||||
*p = '\0';
|
||||
}
|
||||
|
||||
struct stat sbuf;
|
||||
if (stat(parent, &sbuf) == -1) {
|
||||
if (errno == ENOENT) {
|
||||
if (mkdir(parent, mode) != 0)
|
||||
return -1;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
} else if (!S_ISDIR(sbuf.st_mode)) {
|
||||
errno = ENOTDIR;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (p) {
|
||||
/* replace the overwritten slash */
|
||||
*p = '/';
|
||||
p++;
|
||||
}
|
||||
} while (p);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return realpath(dirname(path)) + / + basename(path) in a newly allocated
|
||||
* string. Returns NULL on failure and sets errno to ENOENT if the directory
|
||||
* part does not exist.
|
||||
*/
|
||||
char *
|
||||
sys_realdir(const char *path)
|
||||
{
|
||||
char *sep, *udir, *rdir, *p;
|
||||
int rerrno, rv;
|
||||
|
||||
if (path[0] == '\0') {
|
||||
errno = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
udir = strdup(path);
|
||||
if (!udir)
|
||||
return NULL;
|
||||
|
||||
sep = strrchr(udir, '/');
|
||||
if (!sep) {
|
||||
free(udir);
|
||||
rv = asprintf(&udir, "./%s", path);
|
||||
if (rv == -1)
|
||||
return NULL;
|
||||
sep = udir + 1;
|
||||
} else if (sep == udir) {
|
||||
return udir;
|
||||
}
|
||||
*sep = '\0';
|
||||
rdir = realpath(udir, NULL);
|
||||
if (!rdir) {
|
||||
rerrno = errno;
|
||||
free(udir);
|
||||
errno = rerrno;
|
||||
return NULL;
|
||||
}
|
||||
rv = asprintf(&p, "%s/%s", rdir, sep + 1);
|
||||
rerrno = errno;
|
||||
free(rdir);
|
||||
free(udir);
|
||||
errno = rerrno;
|
||||
if (rv == -1)
|
||||
return NULL;
|
||||
return p;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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 */
|
||||
}
|
||||
|
||||
/*
|
||||
* Send a message and optional file descriptor on a connected AF_UNIX
|
||||
* SOCKET_DGRAM socket s. Returns the return value of sendmsg().
|
||||
* If fd is -1, no file descriptor is passed.
|
||||
*/
|
||||
ssize_t
|
||||
sys_sendmsgfd(int sock, void *buf, size_t bufsz, int fd)
|
||||
{
|
||||
struct iovec iov;
|
||||
struct msghdr msg;
|
||||
struct cmsghdr *cmsg;
|
||||
char cmsgbuf[CMSG_SPACE(sizeof(int))];
|
||||
ssize_t n;
|
||||
|
||||
iov.iov_base = buf;
|
||||
iov.iov_len = bufsz;
|
||||
|
||||
msg.msg_name = NULL;
|
||||
msg.msg_namelen = 0;
|
||||
msg.msg_iov = &iov;
|
||||
msg.msg_iovlen = 1;
|
||||
|
||||
if (fd != -1) {
|
||||
msg.msg_control = cmsgbuf;
|
||||
msg.msg_controllen = sizeof(cmsgbuf);
|
||||
|
||||
cmsg = CMSG_FIRSTHDR(&msg);
|
||||
if (!cmsg)
|
||||
return -1;
|
||||
cmsg->cmsg_len = CMSG_LEN(sizeof(int));
|
||||
cmsg->cmsg_level = SOL_SOCKET;
|
||||
cmsg->cmsg_type = SCM_RIGHTS;
|
||||
|
||||
*((int *) CMSG_DATA(cmsg)) = fd;
|
||||
} else {
|
||||
msg.msg_control = NULL;
|
||||
msg.msg_controllen = 0;
|
||||
}
|
||||
do {
|
||||
#ifdef MSG_NOSIGNAL
|
||||
n = sendmsg(sock, &msg, MSG_NOSIGNAL);
|
||||
#else /* !MSG_NOSIGNAL */
|
||||
n = sendmsg(sock, &msg, 0);
|
||||
#endif /* !MSG_NOSIGNAL */
|
||||
} while (n == -1 && errno == EINTR);
|
||||
return n;
|
||||
}
|
||||
|
||||
/*
|
||||
* Receive a message and optional file descriptor on a connected AF_UNIX
|
||||
* SOCKET_DGRAM socket s. Returns the return value of recvmsg()/recv()
|
||||
* and sets errno to EINVAL if the received message is malformed.
|
||||
* If pfd is NULL, no file descriptor is received; if a file descriptor was
|
||||
* part of the received message and pfd is NULL, then the kernel will close it.
|
||||
*/
|
||||
ssize_t
|
||||
sys_recvmsgfd(int sock, void *buf, size_t bufsz, int *pfd)
|
||||
{
|
||||
ssize_t n;
|
||||
|
||||
if (pfd) {
|
||||
struct iovec iov;
|
||||
struct msghdr msg;
|
||||
struct cmsghdr *cmsg;
|
||||
unsigned char cmsgbuf[CMSG_SPACE(sizeof(int))];
|
||||
|
||||
iov.iov_base = buf;
|
||||
iov.iov_len = bufsz;
|
||||
|
||||
msg.msg_name = NULL;
|
||||
msg.msg_namelen = 0;
|
||||
msg.msg_iov = &iov;
|
||||
msg.msg_iovlen = 1;
|
||||
msg.msg_control = cmsgbuf;
|
||||
msg.msg_controllen = sizeof(cmsgbuf);
|
||||
do {
|
||||
n = recvmsg(sock, &msg, 0);
|
||||
} while (n == -1 && errno == EINTR);
|
||||
if (n <= 0)
|
||||
return n;
|
||||
cmsg = CMSG_FIRSTHDR(&msg);
|
||||
if (cmsg && cmsg->cmsg_len == CMSG_LEN(sizeof(int))) {
|
||||
if (cmsg->cmsg_level != SOL_SOCKET) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
if (cmsg->cmsg_type != SCM_RIGHTS) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
*pfd = *((int *) CMSG_DATA(cmsg));
|
||||
} else {
|
||||
*pfd = -1;
|
||||
}
|
||||
} else {
|
||||
do {
|
||||
n = recv(sock, buf, bufsz, 0);
|
||||
} while (n == -1 && errno == EINTR);
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
/* vim: set noet ft=c: */
|
||||
|
@ -0,0 +1,68 @@
|
||||
/*-
|
||||
* SSLsplit - transparent SSL/TLS interception
|
||||
* https://www.roe.ch/SSLsplit
|
||||
*
|
||||
* Copyright (c) 2009-2018, Daniel Roethlisberger <daniel@roe.ch>.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef 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 *, const char *) WUNRES;
|
||||
|
||||
int sys_pidf_open(const char *) NONNULL(1) WUNRES;
|
||||
int sys_pidf_write(int) WUNRES;
|
||||
void sys_pidf_close(int, const char *) NONNULL(2);
|
||||
|
||||
int sys_uid(const char *, uid_t *) NONNULL(1) WUNRES;
|
||||
int sys_gid(const char *, gid_t *) NONNULL(1) WUNRES;
|
||||
int sys_isuser(const char *) NONNULL(1) WUNRES;
|
||||
int sys_isgroup(const char *) NONNULL(1) WUNRES;
|
||||
int sys_isgeteuid(const char *) NONNULL(1) WUNRES;
|
||||
|
||||
int sys_get_af(const char *);
|
||||
int sys_sockaddr_parse(struct sockaddr_storage *, socklen_t *,
|
||||
char *, char *, int, int) NONNULL(1,2,3,4) WUNRES;
|
||||
int sys_sockaddr_str(struct sockaddr *, socklen_t,
|
||||
char **, char **) NONNULL(1,3,4);
|
||||
char * sys_ip46str_sanitize(const char *) NONNULL(1) MALLOC;
|
||||
|
||||
int sys_isdir(const char *) NONNULL(1) WUNRES;
|
||||
int sys_mkpath(const char *, mode_t) NONNULL(1) WUNRES;
|
||||
char * sys_realdir(const char *) NONNULL(1) MALLOC;
|
||||
|
||||
uint32_t sys_get_cpu_cores(void) WUNRES;
|
||||
|
||||
ssize_t sys_sendmsgfd(int, void *, size_t, int) NONNULL(2) WUNRES;
|
||||
ssize_t sys_recvmsgfd(int, void *, size_t, int *) NONNULL(2) WUNRES;
|
||||
|
||||
#endif /* !SYS_H */
|
||||
|
||||
/* vim: set noet ft=c: */
|
@ -0,0 +1,229 @@
|
||||
/*-
|
||||
* SSLsplit - transparent SSL/TLS interception
|
||||
* https://www.roe.ch/SSLsplit
|
||||
*
|
||||
* Copyright (c) 2009-2018, Daniel Roethlisberger <daniel@roe.ch>.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "thrqueue.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <pthread.h>
|
||||
|
||||
/*
|
||||
* Thread-safe, 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))))
|
||||
goto out0;
|
||||
if (!(queue->data = malloc(sz * sizeof(void*))))
|
||||
goto out1;
|
||||
if (pthread_mutex_init(&queue->mutex, NULL))
|
||||
goto out2;
|
||||
if (pthread_cond_init(&queue->notempty, NULL))
|
||||
goto out3;
|
||||
if (pthread_cond_init(&queue->notfull, NULL))
|
||||
goto out4;
|
||||
queue->sz = sz;
|
||||
queue->n = 0;
|
||||
queue->in = 0;
|
||||
queue->out = 0;
|
||||
queue->block_enqueue = 1;
|
||||
queue->block_dequeue = 1;
|
||||
return queue;
|
||||
|
||||
out4:
|
||||
pthread_cond_destroy(&queue->notempty);
|
||||
out3:
|
||||
pthread_mutex_destroy(&queue->mutex);
|
||||
out2:
|
||||
free(queue->data);
|
||||
out1:
|
||||
free(queue);
|
||||
out0:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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);
|
||||
sched_yield();
|
||||
}
|
||||
|
||||
/*
|
||||
* 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);
|
||||
sched_yield();
|
||||
}
|
||||
|
||||
/* vim: set noet ft=c: */
|
@ -0,0 +1,50 @@
|
||||
/*-
|
||||
* SSLsplit - transparent SSL/TLS interception
|
||||
* https://www.roe.ch/SSLsplit
|
||||
*
|
||||
* Copyright (c) 2009-2018, Daniel Roethlisberger <daniel@roe.ch>.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef 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(1);
|
||||
|
||||
void * thrqueue_enqueue(thrqueue_t *, void *) NONNULL(1) WUNRES;
|
||||
void * thrqueue_enqueue_nb(thrqueue_t *, void *) NONNULL(1) WUNRES;
|
||||
void * thrqueue_dequeue(thrqueue_t *) NONNULL(1) WUNRES;
|
||||
void * thrqueue_dequeue_nb(thrqueue_t *) NONNULL(1) WUNRES;
|
||||
void thrqueue_unblock_enqueue(thrqueue_t *) NONNULL(1);
|
||||
void thrqueue_unblock_dequeue(thrqueue_t *) NONNULL(1);
|
||||
|
||||
#endif /* !THRQUEUE_H */
|
||||
|
||||
/* vim: set noet ft=c: */
|
@ -0,0 +1,27 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIElDCCA3ygAwIBAgIBATANBgkqhkiG9w0BAQsFADCBkjELMAkGA1UEBhMCVFIx
|
||||
EDAOBgNVBAgMB0FudGFseWExDjAMBgNVBAcMBVNlcmlrMRIwEAYDVQQKDAlDb21p
|
||||
eFdhbGwxETAPBgNVBAsMCFNTTHByb3h5MRYwFAYDVQQDDA1jb21peHdhbGwub3Jn
|
||||
MSIwIAYJKoZIhvcNAQkBFhNzb25lcnRhcmlAZ21haWwuY29tMB4XDTE5MDQyNzEy
|
||||
MTY0MloXDTIwMDQyNjEyMTY0MlowgZIxCzAJBgNVBAYTAlRSMRAwDgYDVQQIDAdB
|
||||
bnRhbHlhMQ4wDAYDVQQHDAVTZXJpazESMBAGA1UECgwJQ29taXhXYWxsMREwDwYD
|
||||
VQQLDAhTU0xwcm94eTEWMBQGA1UEAwwNY29taXh3YWxsLm9yZzEiMCAGCSqGSIb3
|
||||
DQEJARYTc29uZXJ0YXJpQGdtYWlsLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEP
|
||||
ADCCAQoCggEBANU+3/V8KIBopview+Kg3q4c2FRyR9SIe87sXhxGrHhAdQqmfhGv
|
||||
7BcdLeROxjGHNmJkXJTD9yH0RY9C3cYdySwPx6sRrrQlHvKHLLPSs6xnPMDMnjwf
|
||||
BLxZRI5njZ+UI2FiqAnAxqy5DQn21N803gBlAG1YbWguDF7m8h0bkGmFKjXXBllU
|
||||
h3qZf/mYPV9TdIj7daUkz/4ZxkKfrwRYkCdet+b5jBFTaWYkakMaE62XjRS2TpYB
|
||||
3UDrrnuwbR79NU54C4GlNV2i0sARRpK31baiuap40Nhz5wSfHwvMP+x+I+IsBhEW
|
||||
KePs/HWD2eOetsCy1rDEhi1Tpc+AKvGmqpECAwEAAaOB8jCB7zAMBgNVHRMEBTAD
|
||||
AQH/MB0GA1UdDgQWBBTtGNULL6e88kyjlAnOpyX6jX4M0TCBvwYDVR0jBIG3MIG0
|
||||
gBTtGNULL6e88kyjlAnOpyX6jX4M0aGBmKSBlTCBkjELMAkGA1UEBhMCVFIxEDAO
|
||||
BgNVBAgMB0FudGFseWExDjAMBgNVBAcMBVNlcmlrMRIwEAYDVQQKDAlDb21peFdh
|
||||
bGwxETAPBgNVBAsMCFNTTHByb3h5MRYwFAYDVQQDDA1jb21peHdhbGwub3JnMSIw
|
||||
IAYJKoZIhvcNAQkBFhNzb25lcnRhcmlAZ21haWwuY29tggEBMA0GCSqGSIb3DQEB
|
||||
CwUAA4IBAQAe3v0HBHuJbLbDOUDXcBxg8LXRfwatDcrAwktn1mYctMjJAI7zRhDy
|
||||
Y+Sm9Ke4VhBUWwdqgYEv7mQDkoUsaZ0+do+kgbIiCOwNwifPh8UuqTK1Kq7/BNYq
|
||||
UghtJFZKreB3CJDAyCzpK+fiXUnPpo8hdEnAUQDfGhDilmmb0VizdiINxRZqUkhZ
|
||||
LAmaHC9G56uO9zQKF7j1ngXeQ17pxn2XHkv53DRpcBFxNq4mX75O6dagKAmhllUw
|
||||
E2UCw5dOfHgoMJy2Cesvu+vr87MRfRH3MKw4dvOYFPEyMaBB6nZnScRiKZArSdlh
|
||||
frqk1iL+PKJAKeaY9AqR6+bAPXbsr8hb
|
||||
-----END CERTIFICATE-----
|
@ -0,0 +1,27 @@
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEpAIBAAKCAQEA1T7f9XwogGim+J7D4qDerhzYVHJH1Ih7zuxeHEaseEB1CqZ+
|
||||
Ea/sFx0t5E7GMYc2YmRclMP3IfRFj0Ldxh3JLA/HqxGutCUe8ocss9KzrGc8wMye
|
||||
PB8EvFlEjmeNn5QjYWKoCcDGrLkNCfbU3zTeAGUAbVhtaC4MXubyHRuQaYUqNdcG
|
||||
WVSHepl/+Zg9X1N0iPt1pSTP/hnGQp+vBFiQJ1635vmMEVNpZiRqQxoTrZeNFLZO
|
||||
lgHdQOuue7BtHv01TngLgaU1XaLSwBFGkrfVtqK5qnjQ2HPnBJ8fC8w/7H4j4iwG
|
||||
ERYp4+z8dYPZ4562wLLWsMSGLVOlz4Aq8aaqkQIDAQABAoIBAQDK9qohM8g01+a8
|
||||
UhhtVuI7ZBb+4Tq+7hyxCUZVsWqsleUQewEZApE6OrqYR+XZ9DDuG0oHCjHpLl8a
|
||||
iPEehPEZr/dCOnVG3uICwmfoy83ZdPVdKCQfYwKV5n0a8TLZxRybr1Py3Hn8obDo
|
||||
X/eibuYyB/ANIxIXC2UY2sVssPSViL2exoqi5IV8fwoHR4fMPME1tAxk5lKUf8qD
|
||||
GFzMomREREl678tPU1RuqvU/pd98Cup8mEwK3yGEbKBHBhjsWPiqlHNCPy0nPt87
|
||||
fe+4nWMUrWvwhKofHtzFDL/Ojjgm8fejISTXdcTiut3QH94fDi5ksanaRMUJRa/9
|
||||
h/ReMooBAoGBAO1IWZryMdGnV8+dexSXrr8P8MpcKag6chPw4eTwb34MIWFCqQbE
|
||||
fK66fh7vw5cSMZaMz679ejFFQvX3lGKFEkrauAbkGausv0R40QtxaOgewUq8sWI2
|
||||
Ryhh0jnVUTGj9VZOefa6Jm+sabyyCxNeME9iLtoUgtnNdr9JnTaXhoOxAoGBAOYR
|
||||
H/0RLNAcnFd5/UILplg1u9d4Q+ssI6TL/XGr/59/Us3GKfD9mlSaJOORYPB1Dm3Y
|
||||
ujodFjq7nUaVjvwIt0DvYok5Nf60ZdRbqOeSrL6To5cujig6m8TDgO3y+xeFXERu
|
||||
PAO0xxqVJv7TJYTn0jeaNH0ReYECt8g5zi4ZkqzhAoGBAN/9zmiQfSr2l9QrS0bT
|
||||
MWi3eYztl31xPsNIP1ZJClaoyNHxhXIYBcND1U8K804nJJyD7IG6UqE0StO6lV6z
|
||||
U+NJuTL4HKuM9TmD+4Kb2nZUog8VNTYLGv0p6rQhhPYhWrXQex3H+ASvc1lnkUEF
|
||||
RwGJI8VGW4eTh6dnrHseayLhAoGAM4NOGEnKMObXErcbv7gADOwrHPmuq298o5+R
|
||||
JeyPFdVaYvyl03HVJnBFJjcc73omOu419GEi4w+zDWTbWQ9SiNWQyIzozBj0W/IS
|
||||
BZfP2fQQwv8HkXZd/laP/bdUBVY07JWKFoJZulf/HTuFwRZu+UgrzH0nKX3ETK60
|
||||
vZm4P8ECgYAU1tgyXZPbKh3a7VZQbiVOW2AHPrpw+c/Jo8/ROqnV+/PzbtAEn9gW
|
||||
cKtEWRcYt0CxLqetW4ZpuMVHRPmjeQ5erEk9Wi+SMRGb0OAgjj5T+lgzzHXwCpYJ
|
||||
MOGenMH+/M4Y6/QeQ42vUaCtqMojpxiO8InMTYL0xHCvy76MgELb8Q==
|
||||
-----END RSA PRIVATE KEY-----
|
@ -0,0 +1,28 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIErzCCA5egAwIBAgIBADANBgkqhkiG9w0BAQsFADCBmzELMAkGA1UEBhMCTlox
|
||||
EzARBgNVBAgMCldlbGxpbmd0b24xEzARBgNVBAcMCkxvd2VyIEh1dHQxFDASBgNV
|
||||
BAoMC0NvbWl4V2FsbE5aMQ4wDAYDVQQLDAVVVE1GVzEYMBYGA1UEAwwPY29taXh3
|
||||
YWxsbnoub3JnMSIwIAYJKoZIhvcNAQkBFhNzb25lcnRhcmlAZ21haWwuY29tMB4X
|
||||
DTE5MDcxNjIwMDk1NloXDTIwMDcxNTIwMDk1NlowgZsxCzAJBgNVBAYTAk5aMRMw
|
||||
EQYDVQQIDApXZWxsaW5ndG9uMRMwEQYDVQQHDApMb3dlciBIdXR0MRQwEgYDVQQK
|
||||
DAtDb21peFdhbGxOWjEOMAwGA1UECwwFVVRNRlcxGDAWBgNVBAMMD2NvbWl4d2Fs
|
||||
bG56Lm9yZzEiMCAGCSqGSIb3DQEJARYTc29uZXJ0YXJpQGdtYWlsLmNvbTCCASIw
|
||||
DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALvy41PXpMn8O3FC0EcU5gbEizo0
|
||||
l7JN/aSfxHCBjn3r+rhZurbM25d50F2P5dlGY3oQiMfkYz6CIEfU+qmYaVG42VJi
|
||||
QY1Bu7oMvsGwUIOfGlZRpAtHX7yd0V9OXF0WaliS2T2DBeXdsSGYl/BRmBs0sBCH
|
||||
5kQzq67npwCxOBjJ55dwp51In56JA1gLV3hFZf9Nf0YjuKBr8p6HXPjug8LRPjMl
|
||||
naR2NyE4Nu0Ud6L2RtSrmfzEKym3IcwHE6Ml9PiT2DFLwBsvRYjXgq5dL1Cl2jcV
|
||||
vRE4LtloeRuDjjdqNnAHRCYKowkVIt81wYlFMn1SSGONk3E7RClvM1T+wy8CAwEA
|
||||
AaOB+zCB+DAMBgNVHRMEBTADAQH/MB0GA1UdDgQWBBSmCS1uoDAm9AKxVw/E7wL3
|
||||
sAwBYzCByAYDVR0jBIHAMIG9gBSmCS1uoDAm9AKxVw/E7wL3sAwBY6GBoaSBnjCB
|
||||
mzELMAkGA1UEBhMCTloxEzARBgNVBAgMCldlbGxpbmd0b24xEzARBgNVBAcMCkxv
|
||||
d2VyIEh1dHQxFDASBgNVBAoMC0NvbWl4V2FsbE5aMQ4wDAYDVQQLDAVVVE1GVzEY
|
||||
MBYGA1UEAwwPY29taXh3YWxsbnoub3JnMSIwIAYJKoZIhvcNAQkBFhNzb25lcnRh
|
||||
cmlAZ21haWwuY29tggEAMA0GCSqGSIb3DQEBCwUAA4IBAQA709412mgZaAI1k4Em
|
||||
N2ZwCaMJAFimQKWVNJtFUV+doa6oa0zwXEBHq8VnRC1mFX5BLmZ/XkXOA6Df/onQ
|
||||
qg9nNjFo0SnxSbh/Z/6WBmh2RmJOpXjY6QM7t/aEsmy+/e1LEcnZhXxSH+8PWYOl
|
||||
UqVzZxLfTXfIpxaH3F65CW//ziXzWng1AeQRqF9+YhJnsuDXxP4WWfAoaVk+95Uz
|
||||
1Z5lPTjkGpJoDwwTJK5rMzfl/QPDJKzhUtnDkwP4PfsRIzzIJ5PEvJ8utBkoaz70
|
||||
/vuOaXX+oVgNg+1rLZv65UfJdbsBLiWam3dBpwxv/7jfqBcR3BpiSmGX+y6kEKBR
|
||||
OkNT
|
||||
-----END CERTIFICATE-----
|
@ -0,0 +1,27 @@
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEpAIBAAKCAQEAu/LjU9ekyfw7cULQRxTmBsSLOjSXsk39pJ/EcIGOfev6uFm6
|
||||
tszbl3nQXY/l2UZjehCIx+RjPoIgR9T6qZhpUbjZUmJBjUG7ugy+wbBQg58aVlGk
|
||||
C0dfvJ3RX05cXRZqWJLZPYMF5d2xIZiX8FGYGzSwEIfmRDOrruenALE4GMnnl3Cn
|
||||
nUifnokDWAtXeEVl/01/RiO4oGvynodc+O6DwtE+MyWdpHY3ITg27RR3ovZG1KuZ
|
||||
/MQrKbchzAcToyX0+JPYMUvAGy9FiNeCrl0vUKXaNxW9ETgu2Wh5G4OON2o2cAdE
|
||||
JgqjCRUi3zXBiUUyfVJIY42TcTtEKW8zVP7DLwIDAQABAoIBAQCbIdo9hAsCpW0z
|
||||
CWD8quVKxMPeoTZs+C7ZyP3Nl0JT8YmazIVvCRxfpS41a+beNlk5kZg01fCM0b9v
|
||||
Sv4zKhgUWi0W8P8GZzFaWdbB7JQocBS/Ftf+b0U4XFNLBVT7iNryjRM+0Hz3xMrF
|
||||
3jvbIp4YJp8EpgeMV9HnplGUIrMY0XOx5zlOGPEIiHLwqox61NXf5AKJKebXlDXy
|
||||
s+IB+fyh4OaJM+xXKZXQNMSO8AkXsBJyxzMUi1AZnxOvOYrcT/9sEdG8OVHdqi4e
|
||||
WxTzbKVVi5VbiZfH7i2+fjx3j+Q+oWri5+NdrDM5FW/h3yc904uMzMBpiRYsoBbI
|
||||
XNnDc6aBAoGBAOo17oTaSZNw+w6m71oELVnpD0nIwdeU41pg9if45RRqyxFVEDVE
|
||||
Y+5TrRNnjjt0nuGBV0hXcwXLPAPHVmhI8mJqSX9Ucl7oagXEiBnGzFQ9bBQbgoAL
|
||||
JrrEHVmK7knye+3c+hWuZVj6e3MbHrG+iByAYr/nK6aR8GxkyXJsgnuhAoGBAM1v
|
||||
KXirV5GDky7ZWPVMLulMr67Q7YkDdHcBKY/WIqMqVrhyjK2sQ9uC8AXLG7c8ozNM
|
||||
hpQqwyFGmFuiUu/exhK2MGKyVjkBWBI/QUNMNYsTctCvLWrFNGZYpYJyVzcLTzeG
|
||||
wyKvfcJZ5pqnH0zpjY461du6rv2gbK7btyZOKUzPAoGAGTg/e9zJiW/kCNre8TF7
|
||||
9K+M7uQIQ3+Lz8KbHwjFGiK5xR8ExMedfx9RhsJi5XIUXbIAxRBtmVUeHEYNvuMD
|
||||
/qb1TRm5yxB5wi88fJIUlImcprmjnsgUno3Znze/mwTaZW2jHEyQKzmlq2pjLr6W
|
||||
h4MnnwR0hdY3LmTX8FNQ5aECgYBhlYtSmbmIEsMofvAl3WFvSxEs7mvXKL/7A59V
|
||||
hvd/IE5/YVJ38Rtuu1z+s4Nf4Dr51EEdQs7cEKew54OUE/Ns0gRb7bDNdVj7mfaL
|
||||
XDkW7k8c2Amv7Ss97p/4Pg41xHaFvssUv93yIzhKgFZ693pZdJM/xAb5zWmzCgWO
|
||||
+ZPeuwKBgQDRRyGy3qQH6SxiKMNQ8FxjjagxKe/NR37EJTBmJoDaHfkcHU7fmov5
|
||||
qP6zAEdtpY2smZEsfkhKYSvLeiqPwdAYmppLWAnP2WJ+USbEFiENT4vNzX/3QmHK
|
||||
dBB/CetyXlqzdnSS21CsPA+CSChNJme8z+GLI8uq5RPVY09FrH0q5A==
|
||||
-----END RSA PRIVATE KEY-----
|
@ -0,0 +1,49 @@
|
||||
{
|
||||
"comment": "Tests for CACert/CAKey",
|
||||
"configs": {
|
||||
"1": {
|
||||
"proto": {
|
||||
"proto": "ssl"
|
||||
},
|
||||
"client": {
|
||||
"ip": "127.0.0.1",
|
||||
"port": "8457"
|
||||
},
|
||||
"server": {
|
||||
"ip": "127.0.0.1",
|
||||
"port": "9457",
|
||||
"crt": "server.crt",
|
||||
"key": "server.key"
|
||||
}
|
||||
}
|
||||
},
|
||||
"tests": {
|
||||
"1": {
|
||||
"comment": "Forges certs using the global CA cert/key pair",
|
||||
"states": {
|
||||
"1": {
|
||||
"testend": "client",
|
||||
"cmd": "send",
|
||||
"payload": "GET / HTTP/1.1\r\nHost: comixwall.org\r\n\r\n",
|
||||
"assert": {
|
||||
"peer_certificate": {
|
||||
"==": [
|
||||
"TR, Antalya, Serik, ComixWall, SSLproxy, comixwall.org, sonertari@gmail.com"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"2": {
|
||||
"testend": "server",
|
||||
"cmd": "recv",
|
||||
"payload": ""
|
||||
},
|
||||
"3": {
|
||||
"testend": "server",
|
||||
"cmd": "recv",
|
||||
"payload": "GET / HTTP/1.1\r\nHost: comixwall.org\r\nConnection: close\r\n\r\n"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
{
|
||||
"comment": "Tests for CACert/CAKey",
|
||||
"configs": {
|
||||
"1": {
|
||||
"proto": {
|
||||
"proto": "ssl"
|
||||
},
|
||||
"client": {
|
||||
"ip": "127.0.0.1",
|
||||
"port": "8458"
|
||||
},
|
||||
"server": {
|
||||
"ip": "127.0.0.1",
|
||||
"port": "9458",
|
||||
"crt": "server2.crt",
|
||||
"key": "server2.key",
|
||||
"comment": "We need a crt/key pair different from the other tests to avoid cache HIT"
|
||||
}
|
||||
}
|
||||
},
|
||||
"tests": {
|
||||
"1": {
|
||||
"comment": "Forges certs using the proxyspec CA cert/key pair",
|
||||
"states": {
|
||||
"1": {
|
||||
"testend": "client",
|
||||
"cmd": "send",
|
||||
"payload": "GET / HTTP/1.1\r\nHost: comixwall.org\r\n\r\n",
|
||||
"assert": {
|
||||
"peer_certificate": {
|
||||
"==": [
|
||||
"NZ, Wellington, Lower Hutt, ComixWallNZ, UTMFW, comixwallnz.org, sonertari@gmail.com"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"2": {
|
||||
"testend": "server",
|
||||
"cmd": "recv",
|
||||
"payload": ""
|
||||
},
|
||||
"3": {
|
||||
"testend": "server",
|
||||
"cmd": "recv",
|
||||
"payload": "GET / HTTP/1.1\r\nHost: comixwall.org\r\nConnection: close\r\n\r\n"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,340 @@
|
||||
{
|
||||
"comment": "Tests for HTTP request headers: SSLproxy, Connection, Upgrade, Keep-Alive, Accept-Encoding, Via, X-Forwarded-For, and Referer",
|
||||
"configs": {
|
||||
"1": {
|
||||
"proto": {
|
||||
"proto": "tcp"
|
||||
},
|
||||
"client": {
|
||||
"ip": "127.0.0.1",
|
||||
"port": "8180"
|
||||
},
|
||||
"server": {
|
||||
"ip": "127.0.0.1",
|
||||
"port": "9180"
|
||||
}
|
||||
},
|
||||
"2": {
|
||||
"proto": {
|
||||
"proto": "ssl",
|
||||
"tcp_nodelay": "yes",
|
||||
"ip_ttl": "15",
|
||||
"connect_timeout": "1000",
|
||||
"read_timeout": "50",
|
||||
"write_timeout": "50",
|
||||
"verify_peer": "no",
|
||||
"ciphers": "MEDIUM:HIGH",
|
||||
"no_ssl2": "yes",
|
||||
"no_ssl3": "yes",
|
||||
"no_tls10": "yes",
|
||||
"no_tls11": "yes",
|
||||
"no_tls12": "yes",
|
||||
"no_tls13": "yes",
|
||||
"min_proto_version": "ssl3",
|
||||
"max_proto_version": "tls13",
|
||||
"ecdhcurve": "prime256v1",
|
||||
"use_sni": "no",
|
||||
"verify_hostname": "no",
|
||||
"compression": "no"
|
||||
},
|
||||
"client": {
|
||||
"ip": "127.0.0.1",
|
||||
"port": "8446",
|
||||
"ciphers": "MEDIUM",
|
||||
"use_sni": "yes",
|
||||
"verify_hostname": "yes",
|
||||
"no_tls10": "no",
|
||||
"max_proto_version": "tls11"
|
||||
},
|
||||
"server": {
|
||||
"ip": "127.0.0.1",
|
||||
"port": "9446",
|
||||
"crt": "server.crt",
|
||||
"key": "server.key",
|
||||
"ciphers": "HIGH",
|
||||
"no_tls12": "no",
|
||||
"min_proto_version": "tls12",
|
||||
"compression": "yes"
|
||||
}
|
||||
}
|
||||
},
|
||||
"tests": {
|
||||
"1": {
|
||||
"comment": "Removes any extra SSLproxy line",
|
||||
"states": {
|
||||
"1": {
|
||||
"testend": "client",
|
||||
"cmd": "send",
|
||||
"payload": "GET / HTTP/1.1\r\nHost: comixwall.org\r\nSSLproxy: sslproxy\r\n\r\n"
|
||||
},
|
||||
"2": {
|
||||
"testend": "server",
|
||||
"cmd": "recv",
|
||||
"payload": "",
|
||||
"comment": "To obtain server crt, SSLproxy srvdst connects/disconnects to the server without sending any data, so we should have this as the second state in all tests"
|
||||
},
|
||||
"3": {
|
||||
"testend": "server",
|
||||
"cmd": "recv",
|
||||
"payload": "GET / HTTP/1.1\r\nHost: comixwall.org\r\nConnection: close\r\n\r\n"
|
||||
},
|
||||
"4": {
|
||||
"testend": "server",
|
||||
"cmd": "send",
|
||||
"payload": "HTTP/1.1 302 Found\r\nLocation: sslproxy\r\n\r\n"
|
||||
},
|
||||
"5": {
|
||||
"testend": "client",
|
||||
"cmd": "recv",
|
||||
"payload": "HTTP/1.1 302 Found\r\nLocation: sslproxy\r\n\r\n"
|
||||
},
|
||||
"6": {
|
||||
"testend": "server",
|
||||
"cmd": "timeout",
|
||||
"payload": "",
|
||||
"comment": "Just a sample timeout command"
|
||||
}
|
||||
}
|
||||
},
|
||||
"2": {
|
||||
"comment": "Removes all extra SSLproxy lines",
|
||||
"states": {
|
||||
"1": {
|
||||
"testend": "client",
|
||||
"cmd": "send",
|
||||
"payload": "GET / HTTP/1.1\r\nHost: comixwall.org\r\nSSLproxy: sslproxy\r\nSSLproxy: sslproxy\r\n\r\n"
|
||||
},
|
||||
"2": {
|
||||
"testend": "server",
|
||||
"cmd": "recv",
|
||||
"payload": ""
|
||||
},
|
||||
"3": {
|
||||
"testend": "server",
|
||||
"cmd": "recv",
|
||||
"payload": "GET / HTTP/1.1\r\nHost: comixwall.org\r\nConnection: close\r\n\r\n"
|
||||
},
|
||||
"4": {
|
||||
"testend": "server",
|
||||
"cmd": "send",
|
||||
"payload": "HTTP/1.1 302 Found\r\nLocation: sslproxy\r\n\r\n"
|
||||
},
|
||||
"5": {
|
||||
"testend": "client",
|
||||
"cmd": "recv",
|
||||
"payload": "HTTP/1.1 302 Found\r\nLocation: sslproxy\r\n\r\n"
|
||||
}
|
||||
}
|
||||
},
|
||||
"3": {
|
||||
"comment": "Changes Connection header to close",
|
||||
"states": {
|
||||
"1": {
|
||||
"testend": "client",
|
||||
"cmd": "send",
|
||||
"payload": "GET / HTTP/1.1\r\nHost: comixwall.org\r\nConnection: Keep-Alive\r\n\r\n"
|
||||
},
|
||||
"2": {
|
||||
"testend": "server",
|
||||
"cmd": "recv",
|
||||
"payload": ""
|
||||
},
|
||||
"3": {
|
||||
"testend": "server",
|
||||
"cmd": "recv",
|
||||
"payload": "GET / HTTP/1.1\r\nHost: comixwall.org\r\nConnection: close\r\n\r\n"
|
||||
},
|
||||
"4": {
|
||||
"testend": "server",
|
||||
"cmd": "send",
|
||||
"payload": "HTTP/1.1 302 Found\r\nLocation: sslproxy\r\n\r\n"
|
||||
},
|
||||
"5": {
|
||||
"testend": "client",
|
||||
"cmd": "recv",
|
||||
"payload": "HTTP/1.1 302 Found\r\nLocation: sslproxy\r\n\r\n"
|
||||
}
|
||||
}
|
||||
},
|
||||
"4": {
|
||||
"comment": "Suppresses upgrading to SSL/TLS, WebSockets or HTTP/2",
|
||||
"states": {
|
||||
"1": {
|
||||
"testend": "client",
|
||||
"cmd": "send",
|
||||
"payload": "GET / HTTP/1.1\r\nHost: comixwall.org\r\nUpgrade: websocket\r\n\r\n"
|
||||
},
|
||||
"2": {
|
||||
"testend": "server",
|
||||
"cmd": "recv",
|
||||
"payload": ""
|
||||
},
|
||||
"3": {
|
||||
"testend": "server",
|
||||
"cmd": "recv",
|
||||
"payload": "GET / HTTP/1.1\r\nHost: comixwall.org\r\nConnection: close\r\n\r\n"
|
||||
},
|
||||
"4": {
|
||||
"testend": "server",
|
||||
"cmd": "send",
|
||||
"payload": "HTTP/1.1 302 Found\r\nLocation: sslproxy\r\n\r\n"
|
||||
},
|
||||
"5": {
|
||||
"testend": "client",
|
||||
"cmd": "recv",
|
||||
"payload": "HTTP/1.1 302 Found\r\nLocation: sslproxy\r\n\r\n"
|
||||
}
|
||||
}
|
||||
},
|
||||
"5": {
|
||||
"comment": "Removes Keep-Alive",
|
||||
"states": {
|
||||
"1": {
|
||||
"testend": "client",
|
||||
"cmd": "send",
|
||||
"payload": "GET / HTTP/1.1\r\nHost: comixwall.org\r\nKeep-Alive: keep-alive\r\n\r\n"
|
||||
},
|
||||
"2": {
|
||||
"testend": "server",
|
||||
"cmd": "recv",
|
||||
"payload": ""
|
||||
},
|
||||
"3": {
|
||||
"testend": "server",
|
||||
"cmd": "recv",
|
||||
"payload": "GET / HTTP/1.1\r\nHost: comixwall.org\r\nConnection: close\r\n\r\n"
|
||||
},
|
||||
"4": {
|
||||
"testend": "server",
|
||||
"cmd": "send",
|
||||
"payload": "HTTP/1.1 302 Found\r\nLocation: sslproxy\r\n\r\n"
|
||||
},
|
||||
"5": {
|
||||
"testend": "client",
|
||||
"cmd": "recv",
|
||||
"payload": "HTTP/1.1 302 Found\r\nLocation: sslproxy\r\n\r\n"
|
||||
}
|
||||
}
|
||||
},
|
||||
"6": {
|
||||
"comment": "Does not remove Accept-Encoding by default (it's a config option)",
|
||||
"states": {
|
||||
"1": {
|
||||
"testend": "client",
|
||||
"cmd": "send",
|
||||
"payload": "GET / HTTP/1.1\r\nHost: comixwall.org\r\nAccept-Encoding: encoding\r\n\r\n"
|
||||
},
|
||||
"2": {
|
||||
"testend": "server",
|
||||
"cmd": "recv",
|
||||
"payload": ""
|
||||
},
|
||||
"3": {
|
||||
"testend": "server",
|
||||
"cmd": "recv",
|
||||
"payload": "GET / HTTP/1.1\r\nHost: comixwall.org\r\nAccept-Encoding: encoding\r\nConnection: close\r\n\r\n"
|
||||
},
|
||||
"4": {
|
||||
"testend": "server",
|
||||
"cmd": "send",
|
||||
"payload": "HTTP/1.1 302 Found\r\nLocation: sslproxy\r\n\r\n"
|
||||
},
|
||||
"5": {
|
||||
"testend": "client",
|
||||
"cmd": "recv",
|
||||
"payload": "HTTP/1.1 302 Found\r\nLocation: sslproxy\r\n\r\n"
|
||||
}
|
||||
}
|
||||
},
|
||||
"7": {
|
||||
"comment": "Removes Via",
|
||||
"states": {
|
||||
"1": {
|
||||
"testend": "client",
|
||||
"cmd": "send",
|
||||
"payload": "GET / HTTP/1.1\r\nHost: comixwall.org\r\nVia: via\r\n\r\n"
|
||||
},
|
||||
"2": {
|
||||
"testend": "server",
|
||||
"cmd": "recv",
|
||||
"payload": ""
|
||||
},
|
||||
"3": {
|
||||
"testend": "server",
|
||||
"cmd": "recv",
|
||||
"payload": "GET / HTTP/1.1\r\nHost: comixwall.org\r\nConnection: close\r\n\r\n"
|
||||
},
|
||||
"4": {
|
||||
"testend": "server",
|
||||
"cmd": "send",
|
||||
"payload": "HTTP/1.1 302 Found\r\nLocation: sslproxy\r\n\r\n"
|
||||
},
|
||||
"5": {
|
||||
"testend": "client",
|
||||
"cmd": "recv",
|
||||
"payload": "HTTP/1.1 302 Found\r\nLocation: sslproxy\r\n\r\n"
|
||||
}
|
||||
}
|
||||
},
|
||||
"8": {
|
||||
"comment": "Removes X-Forwarded-For",
|
||||
"states": {
|
||||
"1": {
|
||||
"testend": "client",
|
||||
"cmd": "send",
|
||||
"payload": "GET / HTTP/1.1\r\nHost: comixwall.org\r\nX-Forwarded-For: x-forwarded-for\r\n\r\n"
|
||||
},
|
||||
"2": {
|
||||
"testend": "server",
|
||||
"cmd": "recv",
|
||||
"payload": ""
|
||||
},
|
||||
"3": {
|
||||
"testend": "server",
|
||||
"cmd": "recv",
|
||||
"payload": "GET / HTTP/1.1\r\nHost: comixwall.org\r\nConnection: close\r\n\r\n"
|
||||
},
|
||||
"4": {
|
||||
"testend": "server",
|
||||
"cmd": "send",
|
||||
"payload": "HTTP/1.1 302 Found\r\nLocation: sslproxy\r\n\r\n"
|
||||
},
|
||||
"5": {
|
||||
"testend": "client",
|
||||
"cmd": "recv",
|
||||
"payload": "HTTP/1.1 302 Found\r\nLocation: sslproxy\r\n\r\n"
|
||||
}
|
||||
}
|
||||
},
|
||||
"9": {
|
||||
"comment": "Removes Referer",
|
||||
"states": {
|
||||
"1": {
|
||||
"testend": "client",
|
||||
"cmd": "send",
|
||||
"payload": "GET / HTTP/1.1\r\nHost: comixwall.org\r\nReferer: referer\r\n\r\n"
|
||||
},
|
||||
"2": {
|
||||
"testend": "server",
|
||||
"cmd": "recv",
|
||||
"payload": ""
|
||||
},
|
||||
"3": {
|
||||
"testend": "server",
|
||||
"cmd": "recv",
|
||||
"payload": "GET / HTTP/1.1\r\nHost: comixwall.org\r\nConnection: close\r\n\r\n"
|
||||
},
|
||||
"4": {
|
||||
"testend": "server",
|
||||
"cmd": "send",
|
||||
"payload": "HTTP/1.1 302 Found\r\nLocation: sslproxy\r\n\r\n"
|
||||
},
|
||||
"5": {
|
||||
"testend": "client",
|
||||
"cmd": "recv",
|
||||
"payload": "HTTP/1.1 302 Found\r\nLocation: sslproxy\r\n\r\n"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,236 @@
|
||||
{
|
||||
"comment": "Tests for HTTP response headers: Public-Key-Pins, Public-Key-Pins-Report-Only, Strict-Transport-Security, Expect-CT, Alternate-Protocol, Upgrade, OCSP request",
|
||||
"configs": {
|
||||
"1": {
|
||||
"proto": {
|
||||
"proto": "tcp"
|
||||
},
|
||||
"client": {
|
||||
"ip": "127.0.0.1",
|
||||
"port": "8181"
|
||||
},
|
||||
"server": {
|
||||
"ip": "127.0.0.1",
|
||||
"port": "9181"
|
||||
}
|
||||
},
|
||||
"2": {
|
||||
"proto": {
|
||||
"proto": "ssl",
|
||||
"crt": "server.crt",
|
||||
"key": "server.key"
|
||||
},
|
||||
"client": {
|
||||
"ip": "127.0.0.1",
|
||||
"port": "8447"
|
||||
},
|
||||
"server": {
|
||||
"ip": "127.0.0.1",
|
||||
"port": "9447"
|
||||
}
|
||||
}
|
||||
},
|
||||
"tests": {
|
||||
"1": {
|
||||
"comment": "Removes Public-Key-Pins",
|
||||
"states": {
|
||||
"1": {
|
||||
"testend": "client",
|
||||
"cmd": "send",
|
||||
"payload": "GET / HTTP/1.1\r\nHost: comixwall.org\r\n\r\n"
|
||||
},
|
||||
"2": {
|
||||
"testend": "server",
|
||||
"cmd": "recv",
|
||||
"payload": ""
|
||||
},
|
||||
"3": {
|
||||
"testend": "server",
|
||||
"cmd": "recv",
|
||||
"payload": "GET / HTTP/1.1\r\nHost: comixwall.org\r\nConnection: close\r\n\r\n"
|
||||
},
|
||||
"4": {
|
||||
"testend": "server",
|
||||
"cmd": "send",
|
||||
"payload": "HTTP/1.1 302 Found\r\nPublic-Key-Pins: public-key-pins\r\nLocation: sslproxy\r\n\r\n"
|
||||
},
|
||||
"5": {
|
||||
"testend": "client",
|
||||
"cmd": "recv",
|
||||
"payload": "HTTP/1.1 302 Found\r\nLocation: sslproxy\r\n\r\n"
|
||||
}
|
||||
}
|
||||
},
|
||||
"2": {
|
||||
"comment": "Removes Public-Key-Pins-Report-Only",
|
||||
"states": {
|
||||
"1": {
|
||||
"testend": "client",
|
||||
"cmd": "send",
|
||||
"payload": "GET / HTTP/1.1\r\nHost: comixwall.org\r\n\r\n"
|
||||
},
|
||||
"2": {
|
||||
"testend": "server",
|
||||
"cmd": "recv",
|
||||
"payload": ""
|
||||
},
|
||||
"3": {
|
||||
"testend": "server",
|
||||
"cmd": "recv",
|
||||
"payload": "GET / HTTP/1.1\r\nHost: comixwall.org\r\nConnection: close\r\n\r\n"
|
||||
},
|
||||
"4": {
|
||||
"testend": "server",
|
||||
"cmd": "send",
|
||||
"payload": "HTTP/1.1 302 Found\r\nPublic-Key-Pins-Report-Only: public-key-pins-report-only\r\nLocation: sslproxy\r\n\r\n"
|
||||
},
|
||||
"5": {
|
||||
"testend": "client",
|
||||
"cmd": "recv",
|
||||
"payload": "HTTP/1.1 302 Found\r\nLocation: sslproxy\r\n\r\n"
|
||||
}
|
||||
}
|
||||
},
|
||||
"3": {
|
||||
"comment": "Removes Strict-Transport-Security",
|
||||
"states": {
|
||||
"1": {
|
||||
"testend": "client",
|
||||
"cmd": "send",
|
||||
"payload": "GET / HTTP/1.1\r\nHost: comixwall.org\r\n\r\n"
|
||||
},
|
||||
"2": {
|
||||
"testend": "server",
|
||||
"cmd": "recv",
|
||||
"payload": ""
|
||||
},
|
||||
"3": {
|
||||
"testend": "server",
|
||||
"cmd": "recv",
|
||||
"payload": "GET / HTTP/1.1\r\nHost: comixwall.org\r\nConnection: close\r\n\r\n"
|
||||
},
|
||||
"4": {
|
||||
"testend": "server",
|
||||
"cmd": "send",
|
||||
"payload": "HTTP/1.1 302 Found\r\nStrict-Transport-Security: strict-transport-security\r\nLocation: sslproxy\r\n\r\n"
|
||||
},
|
||||
"5": {
|
||||
"testend": "client",
|
||||
"cmd": "recv",
|
||||
"payload": "HTTP/1.1 302 Found\r\nLocation: sslproxy\r\n\r\n"
|
||||
}
|
||||
}
|
||||
},
|
||||
"4": {
|
||||
"comment": "Removes Expect-CT",
|
||||
"states": {
|
||||
"1": {
|
||||
"testend": "client",
|
||||
"cmd": "send",
|
||||
"payload": "GET / HTTP/1.1\r\nHost: comixwall.org\r\n\r\n"
|
||||
},
|
||||
"2": {
|
||||
"testend": "server",
|
||||
"cmd": "recv",
|
||||
"payload": ""
|
||||
},
|
||||
"3": {
|
||||
"testend": "server",
|
||||
"cmd": "recv",
|
||||
"payload": "GET / HTTP/1.1\r\nHost: comixwall.org\r\nConnection: close\r\n\r\n"
|
||||
},
|
||||
"4": {
|
||||
"testend": "server",
|
||||
"cmd": "send",
|
||||
"payload": "HTTP/1.1 302 Found\r\nExpect-CT: expect-ct\r\nLocation: sslproxy\r\n\r\n"
|
||||
},
|
||||
"5": {
|
||||
"testend": "client",
|
||||
"cmd": "recv",
|
||||
"payload": "HTTP/1.1 302 Found\r\nLocation: sslproxy\r\n\r\n"
|
||||
}
|
||||
}
|
||||
},
|
||||
"5": {
|
||||
"comment": "Removes Alternate-Protocol",
|
||||
"states": {
|
||||
"1": {
|
||||
"testend": "client",
|
||||
"cmd": "send",
|
||||
"payload": "GET / HTTP/1.1\r\nHost: comixwall.org\r\n\r\n"
|
||||
},
|
||||
"2": {
|
||||
"testend": "server",
|
||||
"cmd": "recv",
|
||||
"payload": ""
|
||||
},
|
||||
"3": {
|
||||
"testend": "server",
|
||||
"cmd": "recv",
|
||||
"payload": "GET / HTTP/1.1\r\nHost: comixwall.org\r\nConnection: close\r\n\r\n"
|
||||
},
|
||||
"4": {
|
||||
"testend": "server",
|
||||
"cmd": "send",
|
||||
"payload": "HTTP/1.1 302 Found\r\nAlternate-Protocol: alternate-protocol\r\nLocation: sslproxy\r\n\r\n"
|
||||
},
|
||||
"5": {
|
||||
"testend": "client",
|
||||
"cmd": "recv",
|
||||
"payload": "HTTP/1.1 302 Found\r\nLocation: sslproxy\r\n\r\n"
|
||||
}
|
||||
}
|
||||
},
|
||||
"6": {
|
||||
"comment": "Removes Upgrade",
|
||||
"states": {
|
||||
"1": {
|
||||
"testend": "client",
|
||||
"cmd": "send",
|
||||
"payload": "GET / HTTP/1.1\r\nHost: comixwall.org\r\n\r\n"
|
||||
},
|
||||
"2": {
|
||||
"testend": "server",
|
||||
"cmd": "recv",
|
||||
"payload": ""
|
||||
},
|
||||
"3": {
|
||||
"testend": "server",
|
||||
"cmd": "recv",
|
||||
"payload": "GET / HTTP/1.1\r\nHost: comixwall.org\r\nConnection: close\r\n\r\n"
|
||||
},
|
||||
"4": {
|
||||
"testend": "server",
|
||||
"cmd": "send",
|
||||
"payload": "HTTP/1.1 302 Found\r\nUpgrade: upgrade\r\nLocation: sslproxy\r\n\r\n"
|
||||
},
|
||||
"5": {
|
||||
"testend": "client",
|
||||
"cmd": "recv",
|
||||
"payload": "HTTP/1.1 302 Found\r\nLocation: sslproxy\r\n\r\n"
|
||||
}
|
||||
}
|
||||
},
|
||||
"7": {
|
||||
"comment": "Does not deny OCSP request",
|
||||
"states": {
|
||||
"1": {
|
||||
"testend": "client",
|
||||
"cmd": "send",
|
||||
"payload": "POST / HTTP/1.1\r\nHost: comixwall.org\r\nContent-Type: application/ocsp-request\r\n\r\n",
|
||||
"comment": "It is easier to send a dummy POST ocsp request than a valid GET one"
|
||||
},
|
||||
"2": {
|
||||
"testend": "server",
|
||||
"cmd": "recv",
|
||||
"payload": ""
|
||||
},
|
||||
"3": {
|
||||
"testend": "server",
|
||||
"cmd": "recv",
|
||||
"payload": "POST / HTTP/1.1\r\nHost: comixwall.org\r\nContent-Type: application/ocsp-request\r\nConnection: close\r\n\r\n"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,101 @@
|
||||
{
|
||||
"comment": "Tests for HTTP response headers: Deny OCSP request, Remove Accept-Encoding, and Do not remove Referer",
|
||||
"configs": {
|
||||
"1": {
|
||||
"proto": {
|
||||
"proto": "tcp"
|
||||
},
|
||||
"client": {
|
||||
"ip": "127.0.0.1",
|
||||
"port": "8186"
|
||||
},
|
||||
"server": {
|
||||
"ip": "127.0.0.1",
|
||||
"port": "9186"
|
||||
}
|
||||
},
|
||||
"2": {
|
||||
"proto": {
|
||||
"proto": "ssl",
|
||||
"crt": "server.crt",
|
||||
"key": "server.key"
|
||||
},
|
||||
"client": {
|
||||
"ip": "127.0.0.1",
|
||||
"port": "8448"
|
||||
},
|
||||
"server": {
|
||||
"ip": "127.0.0.1",
|
||||
"port": "9448"
|
||||
}
|
||||
}
|
||||
},
|
||||
"tests": {
|
||||
"1": {
|
||||
"comment": "Denies OCSP request",
|
||||
"states": {
|
||||
"1": {
|
||||
"testend": "client",
|
||||
"cmd": "send",
|
||||
"payload": "POST / HTTP/1.1\r\nHost: comixwall.org\r\nContent-Type: application/ocsp-request\r\n\r\n",
|
||||
"comment": "It is easier to send a dummy POST ocsp request than a valid GET one"
|
||||
},
|
||||
"2": {
|
||||
"testend": "server",
|
||||
"cmd": "recv",
|
||||
"payload": ""
|
||||
},
|
||||
"3": {
|
||||
"testend": "client",
|
||||
"cmd": "recv",
|
||||
"payload_file": "payload_ocsp_denied_response.bin"
|
||||
},
|
||||
"4": {
|
||||
"testend": "server",
|
||||
"cmd": "timeout",
|
||||
"payload": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"2": {
|
||||
"comment": "Remove Accept-Encoding",
|
||||
"states": {
|
||||
"1": {
|
||||
"testend": "client",
|
||||
"cmd": "send",
|
||||
"payload": "GET / HTTP/1.1\r\nHost: comixwall.org\r\nAccept-Encoding: encoding\r\n\r\n"
|
||||
},
|
||||
"2": {
|
||||
"testend": "server",
|
||||
"cmd": "recv",
|
||||
"payload": ""
|
||||
},
|
||||
"3": {
|
||||
"testend": "server",
|
||||
"cmd": "recv",
|
||||
"payload": "GET / HTTP/1.1\r\nHost: comixwall.org\r\nConnection: close\r\n\r\n"
|
||||
}
|
||||
}
|
||||
},
|
||||
"3": {
|
||||
"comment": "Does not remove Referer",
|
||||
"states": {
|
||||
"1": {
|
||||
"testend": "client",
|
||||
"cmd": "send",
|
||||
"payload": "GET / HTTP/1.1\r\nHost: comixwall.org\r\nReferer: referer\r\n\r\n"
|
||||
},
|
||||
"2": {
|
||||
"testend": "server",
|
||||
"cmd": "recv",
|
||||
"payload": ""
|
||||
},
|
||||
"3": {
|
||||
"testend": "server",
|
||||
"cmd": "recv",
|
||||
"payload": "GET / HTTP/1.1\r\nHost: comixwall.org\r\nReferer: referer\r\nConnection: close\r\n\r\n"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
{
|
||||
"comment": "Tests for Passthrough",
|
||||
"configs": {
|
||||
"1": {
|
||||
"comment": "Passthrough should behave similar to direct connection, so test direct connection first",
|
||||
"proto": {
|
||||
"proto": "ssl",
|
||||
"verify_peer": "no"
|
||||
},
|
||||
"client": {
|
||||
"ip": "127.0.0.1",
|
||||
"port": "9454"
|
||||
},
|
||||
"server": {
|
||||
"ip": "127.0.0.1",
|
||||
"port": "9454",
|
||||
"crt": "server.crt",
|
||||
"key": "server.key"
|
||||
}
|
||||
},
|
||||
"2": {
|
||||
"proto": {
|
||||
"proto": "ssl",
|
||||
"verify_peer": "no"
|
||||
},
|
||||
"client": {
|
||||
"ip": "127.0.0.1",
|
||||
"port": "8454"
|
||||
},
|
||||
"server": {
|
||||
"ip": "127.0.0.1",
|
||||
"port": "9454",
|
||||
"crt": "server.crt",
|
||||
"key": "server.key"
|
||||
}
|
||||
}
|
||||
},
|
||||
"tests": {
|
||||
"1": {
|
||||
"comment": "Passes conn through if ssl handshake fails",
|
||||
"states": {
|
||||
"1": {
|
||||
"testend": "client",
|
||||
"cmd": "send",
|
||||
"payload": "GET / HTTP/1.1\r\nHost: comixwall.org\r\n\r\n"
|
||||
},
|
||||
"2": {
|
||||
"testend": "server",
|
||||
"cmd": "recv",
|
||||
"payload": "GET / HTTP/1.1\r\nHost: comixwall.org\r\n\r\n"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,117 @@
|
||||
{
|
||||
"comment": "Tests for HTTP GET method validation",
|
||||
"configs": {
|
||||
"1": {
|
||||
"proto": {
|
||||
"proto": "tcp"
|
||||
},
|
||||
"client": {
|
||||
"ip": "127.0.0.1",
|
||||
"port": "8184"
|
||||
},
|
||||
"server": {
|
||||
"ip": "127.0.0.1",
|
||||
"port": "9184"
|
||||
}
|
||||
},
|
||||
"2": {
|
||||
"proto": {
|
||||
"proto": "ssl"
|
||||
},
|
||||
"client": {
|
||||
"ip": "127.0.0.1",
|
||||
"port": "8444"
|
||||
},
|
||||
"server": {
|
||||
"ip": "127.0.0.1",
|
||||
"port": "9444",
|
||||
"crt": "server.crt",
|
||||
"key": "server.key"
|
||||
}
|
||||
}
|
||||
},
|
||||
"tests": {
|
||||
"1": {
|
||||
"comment": "Validates GET",
|
||||
"states": {
|
||||
"1": {
|
||||
"testend": "client",
|
||||
"cmd": "send",
|
||||
"payload": "GET / HTTP/1.1\r\nHost: comixwall.org\r\n\r\n"
|
||||
},
|
||||
"2": {
|
||||
"testend": "server",
|
||||
"cmd": "recv",
|
||||
"payload": "",
|
||||
"comment": "To obtain server crt, SSLproxy srvdst connects/disconnects to the server without sending any data, so we should have this as the second state in all tests"
|
||||
},
|
||||
"3": {
|
||||
"testend": "server",
|
||||
"cmd": "recv",
|
||||
"payload": "GET / HTTP/1.1\r\nHost: comixwall.org\r\nConnection: close\r\n\r\n"
|
||||
}
|
||||
}
|
||||
},
|
||||
"2": {
|
||||
"comment": "Does not validate GE as a method",
|
||||
"states": {
|
||||
"1": {
|
||||
"testend": "client",
|
||||
"cmd": "send",
|
||||
"payload": "GE / HTTP/1.1\r\nHost: comixwall.org\r\n\r\n"
|
||||
},
|
||||
"2": {
|
||||
"testend": "server",
|
||||
"cmd": "recv",
|
||||
"payload": ""
|
||||
},
|
||||
"3": {
|
||||
"testend": "server",
|
||||
"cmd": "timeout",
|
||||
"payload": "",
|
||||
"comment": "SSLproxy should not validate method GE, so not connect to server"
|
||||
}
|
||||
}
|
||||
},
|
||||
"3": {
|
||||
"comment": "Does not validate GE1 as a method",
|
||||
"states": {
|
||||
"1": {
|
||||
"testend": "client",
|
||||
"cmd": "send",
|
||||
"payload": "GE1 / HTTP/1.1\r\nHost: comixwall.org\r\n\r\n"
|
||||
},
|
||||
"2": {
|
||||
"testend": "server",
|
||||
"cmd": "recv",
|
||||
"payload": ""
|
||||
},
|
||||
"3": {
|
||||
"testend": "server",
|
||||
"cmd": "timeout",
|
||||
"payload": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"4": {
|
||||
"comment": "Does not validate GET1 as a method",
|
||||
"states": {
|
||||
"1": {
|
||||
"testend": "client",
|
||||
"cmd": "send",
|
||||
"payload": "GET1 / HTTP/1.1\r\nHost: comixwall.org\r\n\r\n"
|
||||
},
|
||||
"2": {
|
||||
"testend": "server",
|
||||
"cmd": "recv",
|
||||
"payload": ""
|
||||
},
|
||||
"3": {
|
||||
"testend": "server",
|
||||
"cmd": "timeout",
|
||||
"payload": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,116 @@
|
||||
{
|
||||
"comment": "Tests for HTTP POST method validation",
|
||||
"configs": {
|
||||
"1": {
|
||||
"proto": {
|
||||
"proto": "tcp"
|
||||
},
|
||||
"client": {
|
||||
"ip": "127.0.0.1",
|
||||
"port": "8185"
|
||||
},
|
||||
"server": {
|
||||
"ip": "127.0.0.1",
|
||||
"port": "9185"
|
||||
}
|
||||
},
|
||||
"2": {
|
||||
"proto": {
|
||||
"proto": "ssl"
|
||||
},
|
||||
"client": {
|
||||
"ip": "127.0.0.1",
|
||||
"port": "8445"
|
||||
},
|
||||
"server": {
|
||||
"ip": "127.0.0.1",
|
||||
"port": "9445",
|
||||
"crt": "server.crt",
|
||||
"key": "server.key"
|
||||
}
|
||||
}
|
||||
},
|
||||
"tests": {
|
||||
"1": {
|
||||
"comment": "Validates POST",
|
||||
"states": {
|
||||
"1": {
|
||||
"testend": "client",
|
||||
"cmd": "send",
|
||||
"payload": "POST / HTTP/1.1\r\nHost: comixwall.org\r\n\r\n"
|
||||
},
|
||||
"2": {
|
||||
"testend": "server",
|
||||
"cmd": "recv",
|
||||
"payload": ""
|
||||
},
|
||||
"3": {
|
||||
"testend": "server",
|
||||
"cmd": "recv",
|
||||
"payload": "POST / HTTP/1.1\r\nHost: comixwall.org\r\nConnection: close\r\n\r\n"
|
||||
}
|
||||
}
|
||||
},
|
||||
"2": {
|
||||
"comment": "Does not validate POS as a method",
|
||||
"states": {
|
||||
"1": {
|
||||
"testend": "client",
|
||||
"cmd": "send",
|
||||
"payload": "POS / HTTP/1.1\r\nHost: comixwall.org\r\n\r\n"
|
||||
},
|
||||
"2": {
|
||||
"testend": "server",
|
||||
"cmd": "recv",
|
||||
"payload": ""
|
||||
},
|
||||
"3": {
|
||||
"testend": "server",
|
||||
"cmd": "timeout",
|
||||
"payload": "",
|
||||
"comment": "SSLproxy should not validate method POS, so not connect to server"
|
||||
}
|
||||
}
|
||||
},
|
||||
"3": {
|
||||
"comment": "Does not validate POS1 as a method",
|
||||
"states": {
|
||||
"1": {
|
||||
"testend": "client",
|
||||
"cmd": "send",
|
||||
"payload": "POS1 / HTTP/1.1\r\nHost: comixwall.org\r\n\r\n"
|
||||
},
|
||||
"2": {
|
||||
"testend": "server",
|
||||
"cmd": "recv",
|
||||
"payload": ""
|
||||
},
|
||||
"3": {
|
||||
"testend": "server",
|
||||
"cmd": "timeout",
|
||||
"payload": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"4": {
|
||||
"comment": "Does not validate POST1 as a method",
|
||||
"states": {
|
||||
"1": {
|
||||
"testend": "client",
|
||||
"cmd": "send",
|
||||
"payload": "POST1 / HTTP/1.1\r\nHost: comixwall.org\r\n\r\n"
|
||||
},
|
||||
"2": {
|
||||
"testend": "server",
|
||||
"cmd": "recv",
|
||||
"payload": ""
|
||||
},
|
||||
"3": {
|
||||
"testend": "server",
|
||||
"cmd": "timeout",
|
||||
"payload": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDlDCCAnwCAQEwDQYJKoZIhvcNAQELBQAwgY8xCzAJBgNVBAYTAlRSMRAwDgYD
|
||||
VQQIDAdBbnRhbHlhMQ4wDAYDVQQHDAVTZXJpazESMBAGA1UECgwJQ29taXhXYWxs
|
||||
MQ4wDAYDVQQLDAVVVE1GVzEWMBQGA1UEAwwNY29taXh3YWxsLm9yZzEiMCAGCSqG
|
||||
SIb3DQEJARYTc29uZXJ0YXJpQGdtYWlsLmNvbTAeFw0xOTA0MjcxMjIyNDlaFw0y
|
||||
MDA0MjYxMjIyNDlaMIGPMQswCQYDVQQGEwJUUjEQMA4GA1UECAwHQW50YWx5YTEO
|
||||
MAwGA1UEBwwFU2VyaWsxEjAQBgNVBAoMCUNvbWl4V2FsbDEOMAwGA1UECwwFVVRN
|
||||
RlcxFjAUBgNVBAMMDWNvbWl4d2FsbC5vcmcxIjAgBgkqhkiG9w0BCQEWE3NvbmVy
|
||||
dGFyaUBnbWFpbC5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDl
|
||||
KmwGjqh9BCtLRPM2VZBI0N7WM0ihygo21bp3f/Hb4/WH1+yxy5evqM96rlQz1z+l
|
||||
CP72AU5+qPf3niXXvYBqyXl0kd/ZlQ50qkUdvO85ttFcwZqCaZeEhJNeh+R0cUfj
|
||||
A0JBfJJf20sTTpRj0+GNHOtfoGoT9AI60TPZDygh62qRGWwxhfESc9g2UIu4Zbzc
|
||||
llBa+mi+sqkI+HRoJyT0f/QBK1yGRNYbZ3uChuKW4fBSfXQMzftK80kMpzRtLSUP
|
||||
TfKoUWcl/PMedyeRH4xOwmj79UrQkN8Nw/fX9N7EheUesxmUMri5KbaHtmWsCgd7
|
||||
vdAlZWR9Mkp6AmJihe69AgMBAAEwDQYJKoZIhvcNAQELBQADggEBAELYz1kevbF2
|
||||
8evTEUKf2MzWx4NXnpVeYvHKYQaZhs9Q8fbjRpPUfOv43QQoBQf8GObxXTikjne/
|
||||
Egz+abXY99jvckc2SuU72EHq1wtnjcy6pkCiZ1X1NUlcjKvNWjFpzbqnZtMG+8/b
|
||||
qcTtz72HhEfu1yMk0v9yja0n7MIhTHDCXT25DhKE/kDEyUGyIIUEqOrmvloQcrpO
|
||||
LmXlLfh92tMh+3hcE/NdfwzUB+KcKFffYxbGcKDq3QjqxALzZQNjWKIcLIemjAbS
|
||||
EXrmlT7pO2aSXGY5W5cq/k4M9teZd19lRxd7NlIXxQ0nqfLLKAfModpto9buvVnY
|
||||
NLTngehTe2k=
|
||||
-----END CERTIFICATE-----
|
@ -0,0 +1,28 @@
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDlKmwGjqh9BCtL
|
||||
RPM2VZBI0N7WM0ihygo21bp3f/Hb4/WH1+yxy5evqM96rlQz1z+lCP72AU5+qPf3
|
||||
niXXvYBqyXl0kd/ZlQ50qkUdvO85ttFcwZqCaZeEhJNeh+R0cUfjA0JBfJJf20sT
|
||||
TpRj0+GNHOtfoGoT9AI60TPZDygh62qRGWwxhfESc9g2UIu4ZbzcllBa+mi+sqkI
|
||||
+HRoJyT0f/QBK1yGRNYbZ3uChuKW4fBSfXQMzftK80kMpzRtLSUPTfKoUWcl/PMe
|
||||
dyeRH4xOwmj79UrQkN8Nw/fX9N7EheUesxmUMri5KbaHtmWsCgd7vdAlZWR9Mkp6
|
||||
AmJihe69AgMBAAECggEBAKo0JsV8ARHRHeKqlET/qckbMNZAgqp/Z7SwauzEXzrl
|
||||
BYBeU9L9lmyF4YuJnMpgDUoi81J9R/J0xq7ni82/Vnh5qTZ1/Z8ZwQHPUMQ0hi0E
|
||||
Zl0RAhPoHvvyh43HOltto2mtCvGedwyw0ALH8xhtzvDC0OJWP1MQgPN0W6DUTAdb
|
||||
7JvWSUsVtYGvAfodF4O+OpeExUhK5UmJLs4shx49QGwx2wiIGiBeppXUSXbeXFTS
|
||||
7fH+/Jn1u1djJtUolmFqSVnQ+TlzdzAzJv3oI2Dh6Z/Zmmt253KmMwOIFSPb37Oj
|
||||
jIaNRjaUk8+/o218eAIxvP2PukqnleBhiKEajiC6YgECgYEA/5OjKcftaSPpwOJT
|
||||
l+KT3zfR87dRX64hK/QQl4tzk4jAfC5GGS3mZz+DLp7Tb9q2MJ05JMDY9nCw9dgX
|
||||
Y10qhmwx9QllV7eZAYj1PNyaHSJBwZW+nOHW3/8Wb5fm/08M2Ix1nqrFaWak+9MH
|
||||
ZJJJxmx1Yg8vrPk0gbs1HhEpXj0CgYEA5YuWKCaF5NeBg86poVO5q8EpFc5D+o/2
|
||||
sWO3NkXO3bCiPDpSB1lagoFk+YGsJFEavcMzA4fL56QyBRIII0Q7eC9xiMFaAmZ2
|
||||
df+6+WkG0p0y/3IysCIffPvit3wl0NyGELBV2RjyT+TWTnj1bhJB+cG9fnJYHED5
|
||||
aIbo1Hx8WoECgYEA7U10+09SRgR9f4XmHinYKTOYMtXsrY1t6XTFjyCBYORh7Su+
|
||||
FJUE0KstL7VzFvGZpsvlChYxnakG9k39cVpJKOT/AckGmwqy+7TiF++w/QoYyhkz
|
||||
ElGzhOpCN71wU3BfuhW5BGkFwZb364aG37/g1mdQGouFNfNx6F8ds9w1b/UCgYB5
|
||||
RmtZ4SiChD2AJvLGMlb5YH9VbvnJasA6bmWAvhcSV10Plvx5t4KzSqvPiuwo79TC
|
||||
B1xvdBKN2tk2hpVYFJea4u4IA6eLdxkFNMxTM2MQaU9lWK9hEVYzkVzx9hyAodIP
|
||||
BSsGHUEAZvW21f9NXUYQL4TvRng4zTc4O3bNtMt6gQKBgCREU9pT8BFyMtd2Hye3
|
||||
xW9LnsUA+eIM85CrXaIgZiqqnhqogTy0UqMZzABs4B+CZLeUv5nutjBvicNzJmir
|
||||
TzFLGcIN35J2g3sZvA/wuL0U09nKywkOoYhq5lMEptmaPCLeh7XiuU9gM1wpvbNM
|
||||
EfnCgF6NRIF84AnhWU0i1iVx
|
||||
-----END PRIVATE KEY-----
|
@ -0,0 +1,22 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDrDCCApQCAQAwDQYJKoZIhvcNAQELBQAwgZsxCzAJBgNVBAYTAk5aMRMwEQYD
|
||||
VQQIDApXZWxsaW5ndG9uMRMwEQYDVQQHDApMb3dlciBIdXR0MRQwEgYDVQQKDAtD
|
||||
b21peFdhbGxOWjEOMAwGA1UECwwFVVRNRlcxGDAWBgNVBAMMD2NvbWl4d2FsbG56
|
||||
Lm9yZzEiMCAGCSqGSIb3DQEJARYTc29uZXJ0YXJpQGdtYWlsLmNvbTAeFw0xOTA3
|
||||
MTYyMDE2MzFaFw0yMDA3MTUyMDE2MzFaMIGbMQswCQYDVQQGEwJOWjETMBEGA1UE
|
||||
CAwKV2VsbGluZ3RvbjETMBEGA1UEBwwKTG93ZXIgSHV0dDEUMBIGA1UECgwLQ29t
|
||||
aXhXYWxsTloxDjAMBgNVBAsMBVVUTUZXMRgwFgYDVQQDDA9jb21peHdhbGxuei5v
|
||||
cmcxIjAgBgkqhkiG9w0BCQEWE3NvbmVydGFyaUBnbWFpbC5jb20wggEiMA0GCSqG
|
||||
SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCrsqnVqGCBiBCpU0isyxlLiN9OGs/Re3ZZ
|
||||
ZEr9wVdEyn7x3UVmdYcYBrLwhCTVW1NRsJ1fVh4dQwDSC0fMENdsyNmhMt9yDO7P
|
||||
QGMvVu/ILzsBJJLUfyzzqffTsZfujQ7ftQu87zEgoy+91dehpjpH3/7NhwkjHkc4
|
||||
/a08EhhBbbYIgrjwaXbZLAvJOjQoPM5UHqK34i1uzCleFnI17hDzkZjNfbnk6TYv
|
||||
5PLWLaGpRhQ39upg1hrvbTIHtF9eoBryApNyR5emYrQUGih26HszfefCg8vswI+e
|
||||
Cm2E5saSrrJWN7PF44yTsCgGkTV/Co1rNap0ATwyDsP4jGIGTaLrAgMBAAEwDQYJ
|
||||
KoZIhvcNAQELBQADggEBAJ9j+EQj/+JvOG890TtxS7RZG/FjKJjJ9I/WzKadpzaG
|
||||
fHlqDwz6xK3Z7LusxawXf+/NN3SmXRydRloHEYIGBk0x9DfIpzD/bXoD81+37vCD
|
||||
2W1D7WrUAjnvdHr4P6bGIJLf8chZEKBV1UKV187iCvdXunV1TxZYV1Yp7ZayLlJD
|
||||
/cyEP5CY/ZFVXN17i/XfqiUt8tiX+P8gzI0snbfKXADcbR3ki4iZ7jtM4vP1FLsJ
|
||||
NvJJEvtc3+Ujifiyl8w0g4LF/tFStcQmXOfU1j6egj0a7RBItyfovEbRZWCAa5fV
|
||||
EKLkrXZROnLubRVZ4gPuwvSPZf3gvFsHaiYYsCrG5Jw=
|
||||
-----END CERTIFICATE-----
|
@ -0,0 +1,28 @@
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCrsqnVqGCBiBCp
|
||||
U0isyxlLiN9OGs/Re3ZZZEr9wVdEyn7x3UVmdYcYBrLwhCTVW1NRsJ1fVh4dQwDS
|
||||
C0fMENdsyNmhMt9yDO7PQGMvVu/ILzsBJJLUfyzzqffTsZfujQ7ftQu87zEgoy+9
|
||||
1dehpjpH3/7NhwkjHkc4/a08EhhBbbYIgrjwaXbZLAvJOjQoPM5UHqK34i1uzCle
|
||||
FnI17hDzkZjNfbnk6TYv5PLWLaGpRhQ39upg1hrvbTIHtF9eoBryApNyR5emYrQU
|
||||
Gih26HszfefCg8vswI+eCm2E5saSrrJWN7PF44yTsCgGkTV/Co1rNap0ATwyDsP4
|
||||
jGIGTaLrAgMBAAECggEAbflU8OYVUkJNsbcHkK3R+OClb0ibHek/h8hGhDxnYkd1
|
||||
ggOhZRwYOJpp2MY7NXF2b5d5ZUwqo8F78A2LojiTUzles/J6znlcKOFr5jGACi++
|
||||
UmATsAn5R+TxOOXCk+hDw6QZ7Z0jUW1UsfobUI3BPLJLaWd5wcWuBuHtKNHKOXU3
|
||||
wh1dNYALEFxe3YSsspV5uSrVJPXP0FvFfODRWDldh2s2VNq9p8hBSYl8HSULA2Df
|
||||
yPkGy0SpRL6PhdnG36YgtrjasWAq605aCGhai2jMgguRzeEtJMadcDvh6yUX94xW
|
||||
/DfZ/NJqdv08128Rn7QFydXe+jgIPgxi7ErCaJqx0QKBgQDivkQU/kn0pAbYu3/h
|
||||
ciSf3hkGUJRxKsKbjDIu3ix2RrBNJY7tLQj0RmKDr4lljk2RE6Xl/EZHAYisNRFI
|
||||
tHytZFcxM6pnBoJpmdGepN0V3PlroMbUveBtr0FbkRDFcaeqRXwEzpZIYIO/X1iD
|
||||
9A46NpMZpqDkAIYjCZBsSeIGMwKBgQDB2ibu6pusFxUwdNCcsQs1O40soUuYn+io
|
||||
wNtBDKqPJnsutjP6lR1aQ9lgrNiMI/kShTY6P2BDAyvxChVgp/LxKV/46iZKTw/u
|
||||
UtK9D+soxiCK1S09gAARym4KtQuAHaQRDq9c8r/0Wtcm1uBUtKWt9Zu5Y0LTF28e
|
||||
VA7Ri3qIaQKBgEXDTKc45gBDR2f/qITw3ZvidcifmkyHX4EETZxl90Ac17mkyKjo
|
||||
pkyiK0VhCOEaO3tblDuCtwy5yBdT7JF2FrYynTEiOFeihRWAoiIxj2RerM8UlJh4
|
||||
I0kcFvvZUlOteGzHHDVOaqayK1cCOvW+bXIzwGawAeik0KCPHMR0pvpJAoGAKE3t
|
||||
LluFBmC+PRbskMeWpvi8v3zBtPf6bau7amjxxVWg4vNrFzyNn8jfl2QYmfqYvKsJ
|
||||
vU6T+xrbtf/8td31ewK5O42jbGvHyitaOYjnwdB/z53HDDRiz1AhVQSTYY3IIOvG
|
||||
tjKainmgpiii97mfgO3B9OeYaz9CETI06ohvb5ECgYBgNq1Qy1MjRP7takJnRB0p
|
||||
3cRPs9eGvom0N0yLmsPZNbPsTQVgA6X2pQ8pIOHEXG8WE3AQ9BcRgI0GivERlr3+
|
||||
7nzcgMsF/6UsDOYQyAlqduBaIE+fmar1Mbbzkgm7PZ/FnhRQ967qH22+fvJaSDoY
|
||||
vwMYU2YZfUECNzIkk6FRiQ==
|
||||
-----END PRIVATE KEY-----
|
@ -0,0 +1,41 @@
|
||||
{
|
||||
"comment": "Tests for ssl connection on tcp proxyspec",
|
||||
"configs": {
|
||||
"1": {
|
||||
"proto": {
|
||||
},
|
||||
"client": {
|
||||
"proto": "ssl",
|
||||
"ip": "127.0.0.1",
|
||||
"port": "8183"
|
||||
},
|
||||
"server": {
|
||||
"proto": "tcp",
|
||||
"ip": "127.0.0.1",
|
||||
"port": "9183"
|
||||
}
|
||||
}
|
||||
},
|
||||
"tests": {
|
||||
"1": {
|
||||
"comment": "Does not accept ssl connection on tcp proxyspec if validating HTTP only",
|
||||
"states": {
|
||||
"1": {
|
||||
"testend": "client",
|
||||
"cmd": "sslconnectfail",
|
||||
"payload": "GET / HTTP/1.1\r\nHost: comixwall.org\r\n\r\n"
|
||||
},
|
||||
"2": {
|
||||
"testend": "server",
|
||||
"cmd": "recv",
|
||||
"payload": ""
|
||||
},
|
||||
"3": {
|
||||
"testend": "server",
|
||||
"cmd": "timeout",
|
||||
"payload": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,213 @@
|
||||
{
|
||||
"comment": "Tests for SSL configuration",
|
||||
"configs": {
|
||||
"1": {
|
||||
"proto": {
|
||||
"proto": "ssl",
|
||||
"tcp_nodelay": "yes",
|
||||
"ip_ttl": "15",
|
||||
"connect_timeout": "1000",
|
||||
"read_timeout": "50",
|
||||
"write_timeout": "50",
|
||||
"verify_peer": "no",
|
||||
"ciphers": "MEDIUM:HIGH",
|
||||
"no_ssl2": "yes",
|
||||
"no_ssl3": "yes",
|
||||
"no_tls10": "yes",
|
||||
"no_tls11": "yes",
|
||||
"no_tls12": "yes",
|
||||
"no_tls13": "yes",
|
||||
"min_proto_version": "ssl3",
|
||||
"max_proto_version": "tls13",
|
||||
"ecdhcurve": "prime256v1",
|
||||
"use_sni": "no",
|
||||
"verify_hostname": "no",
|
||||
"compression": "no"
|
||||
},
|
||||
"client": {
|
||||
"ip": "127.0.0.1",
|
||||
"port": "8443",
|
||||
"crt": "server.crt",
|
||||
"key": "server.key",
|
||||
"ciphers": "MEDIUM",
|
||||
"use_sni": "yes",
|
||||
"sni_servername": "comixwall.org",
|
||||
"verify_hostname": "yes",
|
||||
"no_tls10": "no",
|
||||
"max_proto_version": "tls11"
|
||||
},
|
||||
"server": {
|
||||
"ip": "127.0.0.1",
|
||||
"port": "9443",
|
||||
"crt": "server.crt",
|
||||
"key": "server.key",
|
||||
"ciphers": "HIGH",
|
||||
"no_tls12": "no",
|
||||
"min_proto_version": "tls12",
|
||||
"compression": "yes"
|
||||
}
|
||||
}
|
||||
},
|
||||
"tests": {
|
||||
"1": {
|
||||
"comment": "Configures ssl cert, proto, ciphers correctly",
|
||||
"states": {
|
||||
"1": {
|
||||
"testend": "client",
|
||||
"cmd": "send",
|
||||
"payload": "GET / HTTP/1.1\r\nHost: comixwall.org\r\n\r\n",
|
||||
"assert": {
|
||||
"current_cipher_name": {
|
||||
"match": [
|
||||
"^DHE-\\w+-\\w+-\\w+",
|
||||
"\\w+-\\w+-SEED-\\w+",
|
||||
"\\w+-\\w+-\\w+-SHA$"
|
||||
],
|
||||
"!match": [
|
||||
"ECDHE-[A-Z0-9]+-[A-Z0-9]+-[A-Z0-9]+-[A-Z0-9]+",
|
||||
"[A-Z0-9]+-[A-Z0-9]+-AES256-[A-Z0-9]+-[A-Z0-9]+",
|
||||
"[A-Z0-9]+-[A-Z0-9]+-[A-Z0-9]+-[A-Z0-9]+-SHA384"
|
||||
]
|
||||
},
|
||||
"current_cipher_version": {
|
||||
"==": [
|
||||
"SSLv3",
|
||||
"TLSv1"
|
||||
],
|
||||
"!match": [
|
||||
"^TLSv1\\.[1-3]?$"
|
||||
]
|
||||
},
|
||||
"ssl_proto_version": {
|
||||
"==": [
|
||||
"TLSv1"
|
||||
],
|
||||
"!=": [
|
||||
"SSLv3"
|
||||
],
|
||||
"!match": [
|
||||
"^TLSv1\\.[1-3]?$"
|
||||
]
|
||||
},
|
||||
"ssl_state": {
|
||||
"==": [
|
||||
"SSLOK "
|
||||
]
|
||||
},
|
||||
"peer_certificate": {
|
||||
"==": [
|
||||
"TR, Antalya, Serik, ComixWall, SSLproxy, comixwall.org, sonertari@gmail.com"
|
||||
]
|
||||
},
|
||||
"peer_certificate_not_before": {
|
||||
">=": [
|
||||
"-2"
|
||||
],
|
||||
"<=": [
|
||||
"0"
|
||||
]
|
||||
},
|
||||
"peer_certificate_not_after": {
|
||||
">=": [
|
||||
"363"
|
||||
],
|
||||
"<=": [
|
||||
"365"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"2": {
|
||||
"testend": "server",
|
||||
"cmd": "recv",
|
||||
"payload": "",
|
||||
"assert": {
|
||||
"current_cipher_name": {
|
||||
"match": [
|
||||
"ECDHE-[A-Z0-9]+-[A-Z0-9]+-[A-Z0-9]+-[A-Z0-9]+",
|
||||
"[A-Z0-9]+-[A-Z0-9]+-AES256-[A-Z0-9]+-[A-Z0-9]+",
|
||||
"[A-Z0-9]+-[A-Z0-9]+-[A-Z0-9]+-[A-Z0-9]+-SHA384"
|
||||
],
|
||||
"!match": [
|
||||
"^DHE-\\w+-\\w+-\\w+",
|
||||
"\\w+-\\w+-SEED-\\w+",
|
||||
"\\w+-\\w+-\\w+-SHA$"
|
||||
]
|
||||
},
|
||||
"current_cipher_version": {
|
||||
"==": [
|
||||
"TLSv1.2"
|
||||
],
|
||||
"!match": [
|
||||
"^(SSLv3|TLSv1|TLSv1\\.[13]?)$"
|
||||
]
|
||||
},
|
||||
"ssl_proto_version": {
|
||||
"==": [
|
||||
"TLSv1.2"
|
||||
],
|
||||
"!match": [
|
||||
"^(SSLv3|TLSv1|TLSv1\\.[13]?)$"
|
||||
]
|
||||
},
|
||||
"ssl_state": {
|
||||
"==": [
|
||||
"SSLOK "
|
||||
]
|
||||
},
|
||||
"sni_servername": {
|
||||
"==": [
|
||||
"comixwall.org"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"3": {
|
||||
"testend": "server",
|
||||
"cmd": "recv",
|
||||
"payload": "GET / HTTP/1.1\r\nHost: comixwall.org\r\nConnection: close\r\n\r\n",
|
||||
"assert": {
|
||||
"current_cipher_name": {
|
||||
"match": [
|
||||
"ECDHE-[A-Z0-9]+-[A-Z0-9]+-[A-Z0-9]+-[A-Z0-9]+",
|
||||
"[A-Z0-9]+-[A-Z0-9]+-AES256-[A-Z0-9]+-[A-Z0-9]+",
|
||||
"[A-Z0-9]+-[A-Z0-9]+-[A-Z0-9]+-[A-Z0-9]+-SHA384"
|
||||
],
|
||||
"!match": [
|
||||
"^DHE-\\w+-\\w+-\\w+",
|
||||
"\\w+-\\w+-SEED-\\w+",
|
||||
"\\w+-\\w+-\\w+-SHA$"
|
||||
]
|
||||
},
|
||||
"current_cipher_version": {
|
||||
"==": [
|
||||
"TLSv1.2"
|
||||
],
|
||||
"!match": [
|
||||
"^(SSLv3|TLSv1|TLSv1\\.[13]?)$"
|
||||
]
|
||||
},
|
||||
"ssl_proto_version": {
|
||||
"==": [
|
||||
"TLSv1.2"
|
||||
],
|
||||
"!match": [
|
||||
"^(SSLv3|TLSv1|TLSv1\\.[13]?)$"
|
||||
]
|
||||
},
|
||||
"ssl_state": {
|
||||
"==": [
|
||||
"SSLOK "
|
||||
]
|
||||
},
|
||||
"sni_servername": {
|
||||
"==": [
|
||||
"comixwall.org"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,144 @@
|
||||
{
|
||||
"comment": "Tests for SSL configuration: tls10 only",
|
||||
"configs": {
|
||||
"1": {
|
||||
"proto": {
|
||||
"proto": "ssl",
|
||||
"no_ssl2": "yes",
|
||||
"no_ssl3": "yes",
|
||||
"no_tls10": "no",
|
||||
"no_tls11": "yes",
|
||||
"no_tls12": "yes",
|
||||
"no_tls13": "yes"
|
||||
},
|
||||
"client": {
|
||||
"ip": "127.0.0.1",
|
||||
"port": "8449"
|
||||
},
|
||||
"server": {
|
||||
"ip": "127.0.0.1",
|
||||
"port": "9449",
|
||||
"crt": "server.crt",
|
||||
"key": "server.key"
|
||||
}
|
||||
},
|
||||
"2": {
|
||||
"proto": {
|
||||
"proto": "ssl",
|
||||
"no_ssl2": "no",
|
||||
"no_ssl3": "no",
|
||||
"no_tls10": "no",
|
||||
"no_tls11": "no",
|
||||
"no_tls12": "no",
|
||||
"no_tls13": "no"
|
||||
},
|
||||
"client": {
|
||||
"ip": "127.0.0.1",
|
||||
"port": "8449"
|
||||
},
|
||||
"server": {
|
||||
"ip": "127.0.0.1",
|
||||
"port": "9449",
|
||||
"crt": "server.crt",
|
||||
"key": "server.key"
|
||||
}
|
||||
}
|
||||
},
|
||||
"tests": {
|
||||
"1": {
|
||||
"comment": "Configures tls10 only",
|
||||
"states": {
|
||||
"1": {
|
||||
"testend": "client",
|
||||
"cmd": "send",
|
||||
"payload": "GET / HTTP/1.1\r\nHost: comixwall.org\r\n\r\n",
|
||||
"assert": {
|
||||
"current_cipher_version": {
|
||||
"==": [
|
||||
"TLSv1.0"
|
||||
],
|
||||
"!match": [
|
||||
"SSLv3",
|
||||
"^TLSv1\\.[1-3]?$"
|
||||
]
|
||||
},
|
||||
"ssl_proto_version": {
|
||||
"==": [
|
||||
"TLSv1"
|
||||
],
|
||||
"!match": [
|
||||
"SSLv3",
|
||||
"^TLSv1\\.[1-3]?$"
|
||||
]
|
||||
},
|
||||
"ssl_state": {
|
||||
"==": [
|
||||
"SSLOK "
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"2": {
|
||||
"testend": "server",
|
||||
"cmd": "recv",
|
||||
"payload": "",
|
||||
"assert": {
|
||||
"current_cipher_version": {
|
||||
"==": [
|
||||
"TLSv1.0"
|
||||
],
|
||||
"!match": [
|
||||
"SSLv3",
|
||||
"^TLSv1\\.[1-3]?$"
|
||||
]
|
||||
},
|
||||
"ssl_proto_version": {
|
||||
"==": [
|
||||
"TLSv1"
|
||||
],
|
||||
"!match": [
|
||||
"SSLv3",
|
||||
"^TLSv1\\.[1-3]?$"
|
||||
]
|
||||
},
|
||||
"ssl_state": {
|
||||
"==": [
|
||||
"SSLOK "
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"3": {
|
||||
"testend": "server",
|
||||
"cmd": "recv",
|
||||
"payload": "GET / HTTP/1.1\r\nHost: comixwall.org\r\nConnection: close\r\n\r\n",
|
||||
"assert": {
|
||||
"current_cipher_version": {
|
||||
"==": [
|
||||
"TLSv1.0"
|
||||
],
|
||||
"!match": [
|
||||
"SSLv3",
|
||||
"^TLSv1\\.[1-3]?$"
|
||||
]
|
||||
},
|
||||
"ssl_proto_version": {
|
||||
"==": [
|
||||
"TLSv1"
|
||||
],
|
||||
"!match": [
|
||||
"SSLv3",
|
||||
"^TLSv1\\.[1-3]?$"
|
||||
]
|
||||
},
|
||||
"ssl_state": {
|
||||
"==": [
|
||||
"SSLOK "
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,150 @@
|
||||
{
|
||||
"comment": "Tests for SSL configuration: tls10/tls11 only",
|
||||
"configs": {
|
||||
"1": {
|
||||
"proto": {
|
||||
"proto": "ssl",
|
||||
"no_ssl2": "yes",
|
||||
"no_ssl3": "yes",
|
||||
"no_tls10": "yes",
|
||||
"no_tls11": "no",
|
||||
"no_tls12": "yes",
|
||||
"no_tls13": "yes"
|
||||
},
|
||||
"client": {
|
||||
"ip": "127.0.0.1",
|
||||
"port": "8450"
|
||||
},
|
||||
"server": {
|
||||
"ip": "127.0.0.1",
|
||||
"port": "9450",
|
||||
"crt": "server.crt",
|
||||
"key": "server.key"
|
||||
}
|
||||
},
|
||||
"2": {
|
||||
"proto": {
|
||||
"proto": "ssl",
|
||||
"no_ssl2": "no",
|
||||
"no_ssl3": "no",
|
||||
"no_tls10": "no",
|
||||
"no_tls11": "no",
|
||||
"no_tls12": "no",
|
||||
"no_tls13": "no"
|
||||
},
|
||||
"client": {
|
||||
"ip": "127.0.0.1",
|
||||
"port": "8450"
|
||||
},
|
||||
"server": {
|
||||
"ip": "127.0.0.1",
|
||||
"port": "9450",
|
||||
"crt": "server.crt",
|
||||
"key": "server.key"
|
||||
}
|
||||
}
|
||||
},
|
||||
"tests": {
|
||||
"1": {
|
||||
"comment": "Configures tls10/tls11 only",
|
||||
"states": {
|
||||
"1": {
|
||||
"testend": "client",
|
||||
"cmd": "send",
|
||||
"payload": "GET / HTTP/1.1\r\nHost: comixwall.org\r\n\r\n",
|
||||
"assert": {
|
||||
"current_cipher_version": {
|
||||
"==": [
|
||||
"TLSv1.0",
|
||||
"TLSv1.1"
|
||||
],
|
||||
"!match": [
|
||||
"SSLv3",
|
||||
"^TLSv1\\.[23]?$"
|
||||
]
|
||||
},
|
||||
"ssl_proto_version": {
|
||||
"==": [
|
||||
"TLSv1.0",
|
||||
"TLSv1.1"
|
||||
],
|
||||
"!match": [
|
||||
"SSLv3",
|
||||
"^TLSv1\\.[23]?$"
|
||||
]
|
||||
},
|
||||
"ssl_state": {
|
||||
"==": [
|
||||
"SSLOK "
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"2": {
|
||||
"testend": "server",
|
||||
"cmd": "recv",
|
||||
"payload": "",
|
||||
"assert": {
|
||||
"current_cipher_version": {
|
||||
"==": [
|
||||
"TLSv1.0",
|
||||
"TLSv1.1"
|
||||
],
|
||||
"!match": [
|
||||
"SSLv3",
|
||||
"^TLSv1\\.[23]?$"
|
||||
]
|
||||
},
|
||||
"ssl_proto_version": {
|
||||
"==": [
|
||||
"TLSv1.0",
|
||||
"TLSv1.1"
|
||||
],
|
||||
"!match": [
|
||||
"SSLv3",
|
||||
"^TLSv1\\.[23]?$"
|
||||
]
|
||||
},
|
||||
"ssl_state": {
|
||||
"==": [
|
||||
"SSLOK "
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"3": {
|
||||
"testend": "server",
|
||||
"cmd": "recv",
|
||||
"payload": "GET / HTTP/1.1\r\nHost: comixwall.org\r\nConnection: close\r\n\r\n",
|
||||
"assert": {
|
||||
"current_cipher_version": {
|
||||
"==": [
|
||||
"TLSv1.0",
|
||||
"TLSv1.1"
|
||||
],
|
||||
"!match": [
|
||||
"SSLv3",
|
||||
"^TLSv1\\.[23]?$"
|
||||
]
|
||||
},
|
||||
"ssl_proto_version": {
|
||||
"==": [
|
||||
"TLSv1.0",
|
||||
"TLSv1.1"
|
||||
],
|
||||
"!match": [
|
||||
"SSLv3",
|
||||
"^TLSv1\\.[23]?$"
|
||||
]
|
||||
},
|
||||
"ssl_state": {
|
||||
"==": [
|
||||
"SSLOK "
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,150 @@
|
||||
{
|
||||
"comment": "Tests for SSL configuration: tls12 only",
|
||||
"configs": {
|
||||
"1": {
|
||||
"proto": {
|
||||
"proto": "ssl",
|
||||
"no_ssl2": "yes",
|
||||
"no_ssl3": "yes",
|
||||
"no_tls10": "yes",
|
||||
"no_tls11": "yes",
|
||||
"no_tls12": "no",
|
||||
"no_tls13": "yes"
|
||||
},
|
||||
"client": {
|
||||
"ip": "127.0.0.1",
|
||||
"port": "8451"
|
||||
},
|
||||
"server": {
|
||||
"ip": "127.0.0.1",
|
||||
"port": "9451",
|
||||
"crt": "server.crt",
|
||||
"key": "server.key"
|
||||
}
|
||||
},
|
||||
"2": {
|
||||
"proto": {
|
||||
"proto": "ssl",
|
||||
"no_ssl2": "no",
|
||||
"no_ssl3": "no",
|
||||
"no_tls10": "no",
|
||||
"no_tls11": "no",
|
||||
"no_tls12": "no",
|
||||
"no_tls13": "no"
|
||||
},
|
||||
"client": {
|
||||
"ip": "127.0.0.1",
|
||||
"port": "8451"
|
||||
},
|
||||
"server": {
|
||||
"ip": "127.0.0.1",
|
||||
"port": "9451",
|
||||
"crt": "server.crt",
|
||||
"key": "server.key"
|
||||
}
|
||||
}
|
||||
},
|
||||
"tests": {
|
||||
"1": {
|
||||
"comment": "Configures tls12 only",
|
||||
"states": {
|
||||
"1": {
|
||||
"testend": "client",
|
||||
"cmd": "send",
|
||||
"payload": "GET / HTTP/1.1\r\nHost: comixwall.org\r\n\r\n",
|
||||
"assert": {
|
||||
"current_cipher_version": {
|
||||
"==": [
|
||||
"TLSv1.2"
|
||||
],
|
||||
"!match": [
|
||||
"SSLv3",
|
||||
"^TLSv1\\.[013]?$"
|
||||
]
|
||||
},
|
||||
"ssl_proto_version": {
|
||||
"==": [
|
||||
"TLSv1.2"
|
||||
],
|
||||
"!=": [
|
||||
"SSLv3"
|
||||
],
|
||||
"!match": [
|
||||
"^TLSv1\\.[013]?$"
|
||||
]
|
||||
},
|
||||
"ssl_state": {
|
||||
"==": [
|
||||
"SSLOK "
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"2": {
|
||||
"testend": "server",
|
||||
"cmd": "recv",
|
||||
"payload": "",
|
||||
"assert": {
|
||||
"current_cipher_version": {
|
||||
"==": [
|
||||
"TLSv1.2"
|
||||
],
|
||||
"!match": [
|
||||
"SSLv3",
|
||||
"^TLSv1\\.[013]?$"
|
||||
]
|
||||
},
|
||||
"ssl_proto_version": {
|
||||
"==": [
|
||||
"TLSv1.2"
|
||||
],
|
||||
"!=": [
|
||||
"SSLv3"
|
||||
],
|
||||
"!match": [
|
||||
"^TLSv1\\.[013]?$"
|
||||
]
|
||||
},
|
||||
"ssl_state": {
|
||||
"==": [
|
||||
"SSLOK "
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"3": {
|
||||
"testend": "server",
|
||||
"cmd": "recv",
|
||||
"payload": "GET / HTTP/1.1\r\nHost: comixwall.org\r\nConnection: close\r\n\r\n",
|
||||
"assert": {
|
||||
"current_cipher_version": {
|
||||
"==": [
|
||||
"TLSv1.2"
|
||||
],
|
||||
"!match": [
|
||||
"SSLv3",
|
||||
"^TLSv1\\.[013]?$"
|
||||
]
|
||||
},
|
||||
"ssl_proto_version": {
|
||||
"==": [
|
||||
"TLSv1.2"
|
||||
],
|
||||
"!=": [
|
||||
"SSLv3"
|
||||
],
|
||||
"!match": [
|
||||
"^TLSv1\\.[013]?$"
|
||||
]
|
||||
},
|
||||
"ssl_state": {
|
||||
"==": [
|
||||
"SSLOK "
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
{
|
||||
"comment": "Tests for SSL configuration: Rejects unsupported SSL/TLS proto",
|
||||
"configs": {
|
||||
"1": {
|
||||
"proto": {
|
||||
"proto": "ssl",
|
||||
"no_ssl2": "yes",
|
||||
"no_ssl3": "yes",
|
||||
"no_tls10": "yes",
|
||||
"no_tls11": "yes",
|
||||
"no_tls12": "no",
|
||||
"no_tls13": "yes"
|
||||
},
|
||||
"client": {
|
||||
"ip": "127.0.0.1",
|
||||
"port": "8452"
|
||||
},
|
||||
"server": {
|
||||
"ip": "127.0.0.1",
|
||||
"port": "9452",
|
||||
"crt": "server.crt",
|
||||
"key": "server.key"
|
||||
}
|
||||
},
|
||||
"2": {
|
||||
"proto": {
|
||||
"proto": "ssl",
|
||||
"no_ssl2": "yes",
|
||||
"no_ssl3": "yes",
|
||||
"no_tls10": "no",
|
||||
"no_tls11": "yes",
|
||||
"no_tls12": "yes",
|
||||
"no_tls13": "yes"
|
||||
},
|
||||
"client": {
|
||||
"ip": "127.0.0.1",
|
||||
"port": "8453"
|
||||
},
|
||||
"server": {
|
||||
"ip": "127.0.0.1",
|
||||
"port": "9453",
|
||||
"crt": "server.crt",
|
||||
"key": "server.key"
|
||||
}
|
||||
}
|
||||
},
|
||||
"tests": {
|
||||
"1": {
|
||||
"comment": "Rejects tls10 over tls12 and tls12 over tls10 proxyspecs",
|
||||
"states": {
|
||||
"1": {
|
||||
"testend": "client",
|
||||
"cmd": "sslconnectfail",
|
||||
"payload": "GET / HTTP/1.1\r\nHost: comixwall.org\r\n\r\n"
|
||||
},
|
||||
"2": {
|
||||
"testend": "server",
|
||||
"cmd": "timeout",
|
||||
"payload": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,248 @@
|
||||
# TestProxy test configuration for sslproxy v0.6.0
|
||||
|
||||
# Global options
|
||||
#User _sslproxy
|
||||
#Group _sslproxy
|
||||
#Chroot /var/run/sslproxy
|
||||
PidFile /var/run/sslproxy.pid
|
||||
#Daemon yes
|
||||
Debug yes
|
||||
DebugLevel 4
|
||||
#OpenFilesLimit 1024
|
||||
#LeafCerts /etc/sslproxy/leaf.key
|
||||
#LeafKeyRSABits 1024
|
||||
#OpenSSLEngine cloudhsm
|
||||
#TargetCertDir /etc/sslproxy/target
|
||||
#WriteGenCertsDir /var/log/sslproxy
|
||||
#WriteAllCertsDir /var/log/sslproxy
|
||||
#ConnectLog /var/log/sslproxy/connect.log
|
||||
#ContentLog /var/log/sslproxy/content.log
|
||||
#ContentLogDir /var/log/sslproxy/content
|
||||
#ContentLogPathSpec /var/log/sslproxy/%X/%u-%s-%d-%T.log
|
||||
#LogProcInfo yes
|
||||
#PcapLog /var/log/sslproxy/content.pcap
|
||||
#PcapLogDir /var/log/sslproxy/pcap
|
||||
#PcapLogPathSpec /var/log/sslproxy/%X/%u-%s-%d-%T.pcap
|
||||
#MirrorIf lo
|
||||
#MirrorTarget 192.0.2.1
|
||||
#MasterKeyLog /var/log/sslproxy/masterkeys.log
|
||||
LogStats yes
|
||||
StatsPeriod 1
|
||||
ConnIdleTimeout 120
|
||||
ExpiredConnCheckPeriod 10
|
||||
SSLShutdownRetryDelay 100
|
||||
#UserDBPath /var/db/users.db
|
||||
|
||||
# Default ProxySpec options (cloned to each proxyspec)
|
||||
CACert ca.crt
|
||||
CAKey ca.key
|
||||
#ClientCert /etc/sslproxy/client.crt
|
||||
#ClientKey /etc/sslproxy/client.key
|
||||
#CAChain /etc/sslproxy/chain.crt
|
||||
#CRL http://example.com/example.crl
|
||||
#DenyOCSP yes
|
||||
#Passthrough yes
|
||||
#DHGroupParams /etc/sslproxy/dh.pem
|
||||
#ECDHCurve prime256v1
|
||||
#SSLCompression no
|
||||
#ForceSSLProto tls12
|
||||
#DisableSSLProto tls10
|
||||
#Ciphers MEDIUM:HIGH
|
||||
#NATEngine netfilter
|
||||
#RemoveHTTPAcceptEncoding no
|
||||
#RemoveHTTPReferer yes
|
||||
VerifyPeer no
|
||||
#AllowWrongHost no
|
||||
#UserAuth no
|
||||
#UserTimeout 300
|
||||
#UserAuthURL https://192.168.0.1/userdblogin.php
|
||||
#ValidateProto no
|
||||
#MaxHTTPHeaderSize 8192
|
||||
#PassSite example.com
|
||||
#PassSite example.com 192.168.0.1
|
||||
#PassSite example.com soner
|
||||
#PassSite *.google.com * android
|
||||
|
||||
# Tests for tcp connection over ssl proxyspec
|
||||
ProxySpec https 127.0.0.1 8441 up:8080 127.0.0.1 9441
|
||||
ProxySpec {
|
||||
Proto https
|
||||
Addr 127.0.0.1
|
||||
Port 8442
|
||||
DivertPort 8080
|
||||
TargetAddr 127.0.0.1
|
||||
TargetPort 9442
|
||||
ValidateProto yes
|
||||
}
|
||||
|
||||
# Tests for ssl connection on tcp proxyspec
|
||||
ProxySpec {
|
||||
Proto http
|
||||
Addr 127.0.0.1
|
||||
Port 8183
|
||||
DivertPort 8080
|
||||
TargetAddr 127.0.0.1
|
||||
TargetPort 9183
|
||||
ValidateProto yes
|
||||
}
|
||||
|
||||
# Tests for HTTP GET method validation
|
||||
ProxySpec {
|
||||
Proto http
|
||||
Addr 127.0.0.1
|
||||
Port 8184
|
||||
DivertPort 8080
|
||||
TargetAddr 127.0.0.1
|
||||
TargetPort 9184
|
||||
ValidateProto yes
|
||||
}
|
||||
ProxySpec {
|
||||
Proto https
|
||||
Addr 127.0.0.1
|
||||
Port 8444
|
||||
DivertPort 8080
|
||||
TargetAddr 127.0.0.1
|
||||
TargetPort 9444
|
||||
ValidateProto yes
|
||||
}
|
||||
|
||||
# Tests for HTTP POST method validation
|
||||
ProxySpec {
|
||||
Proto http
|
||||
Addr 127.0.0.1
|
||||
Port 8185
|
||||
DivertPort 8080
|
||||
TargetAddr 127.0.0.1
|
||||
TargetPort 9185
|
||||
ValidateProto yes
|
||||
}
|
||||
ProxySpec {
|
||||
Proto https
|
||||
Addr 127.0.0.1
|
||||
Port 8445
|
||||
DivertPort 8080
|
||||
TargetAddr 127.0.0.1
|
||||
TargetPort 9445
|
||||
ValidateProto yes
|
||||
}
|
||||
|
||||
# Tests for SSL configuration
|
||||
ProxySpec https 127.0.0.1 8443 up:8080 127.0.0.1 9443
|
||||
# Tests for SSL configuration: tls10 only
|
||||
ProxySpec {
|
||||
Proto https
|
||||
Addr 127.0.0.1
|
||||
Port 8449
|
||||
DivertPort 8080
|
||||
TargetAddr 127.0.0.1
|
||||
TargetPort 9449
|
||||
ForceSSLProto tls10
|
||||
}
|
||||
# Tests for SSL configuration: tls11 only
|
||||
ProxySpec {
|
||||
Proto https
|
||||
Addr 127.0.0.1
|
||||
Port 8450
|
||||
DivertPort 8080
|
||||
TargetAddr 127.0.0.1
|
||||
TargetPort 9450
|
||||
ForceSSLProto tls11
|
||||
}
|
||||
# Tests for SSL configuration: tls12 only
|
||||
ProxySpec {
|
||||
Proto https
|
||||
Addr 127.0.0.1
|
||||
Port 8451
|
||||
DivertPort 8080
|
||||
TargetAddr 127.0.0.1
|
||||
TargetPort 9451
|
||||
ForceSSLProto tls12
|
||||
}
|
||||
# Tests for SSL configuration: Rejects unsupported SSL/TLS proto
|
||||
ProxySpec {
|
||||
Proto https
|
||||
Addr 127.0.0.1
|
||||
Port 8452
|
||||
DivertPort 8080
|
||||
TargetAddr 127.0.0.1
|
||||
TargetPort 9452
|
||||
ForceSSLProto tls10
|
||||
}
|
||||
ProxySpec {
|
||||
Proto https
|
||||
Addr 127.0.0.1
|
||||
Port 8453
|
||||
DivertPort 8080
|
||||
TargetAddr 127.0.0.1
|
||||
TargetPort 9453
|
||||
ForceSSLProto tls12
|
||||
}
|
||||
|
||||
# Tests for HTTP request headers: SSLproxy, Connection, Upgrade, Keep-Alive, Accept-Encoding, Via, X-Forwarded-For, and Referer
|
||||
ProxySpec http 127.0.0.1 8180 up:8080 127.0.0.1 9180
|
||||
ProxySpec https 127.0.0.1 8446 up:8080 127.0.0.1 9446
|
||||
|
||||
# Tests for HTTP response headers: Public-Key-Pins, Public-Key-Pins-Report-Only, Strict-Transport-Security, Expect-CT, Alternate-Protocol, Upgrade, OCSP request
|
||||
ProxySpec http 127.0.0.1 8181 up:8080 127.0.0.1 9181
|
||||
ProxySpec https 127.0.0.1 8447 up:8080 127.0.0.1 9447
|
||||
|
||||
# Tests for HTTP response headers: Deny OCSP request, remove Accept-Encoding, and do not remove Referer
|
||||
ProxySpec {
|
||||
Proto http
|
||||
Addr 127.0.0.1
|
||||
Port 8186
|
||||
DivertPort 8080
|
||||
TargetAddr 127.0.0.1
|
||||
TargetPort 9186
|
||||
DenyOCSP yes
|
||||
RemoveHTTPAcceptEncoding yes
|
||||
RemoveHTTPReferer no
|
||||
}
|
||||
ProxySpec {
|
||||
Proto https
|
||||
Addr 127.0.0.1
|
||||
Port 8448
|
||||
DivertPort 8080
|
||||
TargetAddr 127.0.0.1
|
||||
TargetPort 9448
|
||||
DenyOCSP yes
|
||||
RemoveHTTPAcceptEncoding yes
|
||||
RemoveHTTPReferer no
|
||||
}
|
||||
|
||||
# Tests for Passthrough
|
||||
ProxySpec {
|
||||
Proto https
|
||||
Addr 127.0.0.1
|
||||
Port 8454
|
||||
DivertPort 8080
|
||||
TargetAddr 127.0.0.1
|
||||
TargetPort 9454
|
||||
Passthrough yes
|
||||
VerifyPeer yes
|
||||
}
|
||||
|
||||
# Tests for VerifyPeer
|
||||
ProxySpec https 127.0.0.1 8455 up:8080 127.0.0.1 9455
|
||||
ProxySpec {
|
||||
Proto https
|
||||
Addr 127.0.0.1
|
||||
Port 8456
|
||||
DivertPort 8080
|
||||
TargetAddr 127.0.0.1
|
||||
TargetPort 9456
|
||||
VerifyPeer yes
|
||||
}
|
||||
|
||||
# Tests for CACert/CAKey
|
||||
ProxySpec https 127.0.0.1 8457 up:8080 127.0.0.1 9457
|
||||
ProxySpec {
|
||||
Proto https
|
||||
Addr 127.0.0.1
|
||||
Port 8458
|
||||
DivertPort 8080
|
||||
TargetAddr 127.0.0.1
|
||||
TargetPort 9458
|
||||
CACert ca2.crt
|
||||
CAKey ca2.key
|
||||
}
|
@ -0,0 +1,57 @@
|
||||
{
|
||||
"comment": "Tests for tcp connection over ssl proxyspec",
|
||||
"configs": {
|
||||
"1": {
|
||||
"comment": "Test should pass without proto validation enabled",
|
||||
"proto": {
|
||||
"comment": "Test config should always have the proto key"
|
||||
},
|
||||
"client": {
|
||||
"proto": "tcp",
|
||||
"ip": "127.0.0.1",
|
||||
"port": "8441"
|
||||
},
|
||||
"server": {
|
||||
"proto": "ssl",
|
||||
"ip": "127.0.0.1",
|
||||
"port": "9441",
|
||||
"crt": "server.crt",
|
||||
"key": "server.key"
|
||||
}
|
||||
},
|
||||
"2": {
|
||||
"comment": "Test should pass with proto validation enabled",
|
||||
"proto": {
|
||||
},
|
||||
"client": {
|
||||
"proto": "tcp",
|
||||
"ip": "127.0.0.1",
|
||||
"port": "8442"
|
||||
},
|
||||
"server": {
|
||||
"proto": "ssl",
|
||||
"ip": "127.0.0.1",
|
||||
"port": "9442",
|
||||
"crt": "server.crt",
|
||||
"key": "server.key"
|
||||
}
|
||||
}
|
||||
},
|
||||
"tests": {
|
||||
"1": {
|
||||
"comment": "Does not pass tcp connection over ssl proxyspec",
|
||||
"states": {
|
||||
"1": {
|
||||
"testend": "client",
|
||||
"cmd": "send",
|
||||
"payload": "GET / HTTP/1.1\r\nHost: comixwall.org\r\n\r\n"
|
||||
},
|
||||
"2": {
|
||||
"testend": "server",
|
||||
"cmd": "timeout",
|
||||
"payload": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
{
|
||||
"comment": "SSLproxy tests",
|
||||
"testharnesses": {
|
||||
"1": {
|
||||
"comment": "HTTP tests",
|
||||
"testsets": {
|
||||
"1": "http_testset_1.json",
|
||||
"2": "http_testset_2.json",
|
||||
"3": "http_testset_3.json"
|
||||
}
|
||||
},
|
||||
"2": {
|
||||
"comment": "SSL config tests",
|
||||
"testsets": {
|
||||
"1": "ssl_testset_1.json",
|
||||
"2": "ssl_testset_2.json",
|
||||
"3": "ssl_testset_3.json",
|
||||
"4": "ssl_testset_4.json",
|
||||
"5": "ssl_testset_5.json"
|
||||
}
|
||||
},
|
||||
"3": {
|
||||
"comment": "Protocol validation tests",
|
||||
"testsets": {
|
||||
"1": "tcp_ssl_testends_testset_1.json",
|
||||
"2": "ssl_tcp_testends_testset_1.json",
|
||||
"3": "proto_validate_testset_1.json",
|
||||
"4": "proto_validate_testset_2.json"
|
||||
}
|
||||
},
|
||||
"4": {
|
||||
"comment": "Various option tests",
|
||||
"testsets": {
|
||||
"1": "passthrough_testset_1.json",
|
||||
"2": "verifypeer_testset_1.json",
|
||||
"3": "verifypeer_testset_2.json",
|
||||
"4": "ca_testset_1.json",
|
||||
"5": "ca_testset_2.json"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
{
|
||||
"comment": "Tests for UserAuth",
|
||||
"configs": {
|
||||
"1": {
|
||||
"proto": {
|
||||
"proto": "ssl"
|
||||
},
|
||||
"client": {
|
||||
"ip": "127.0.0.1",
|
||||
"port": "8458"
|
||||
},
|
||||
"server": {
|
||||
"ip": "127.0.0.1",
|
||||
"port": "9458",
|
||||
"crt": "server.crt",
|
||||
"key": "server.key"
|
||||
}
|
||||
}
|
||||
},
|
||||
"tests": {
|
||||
"1": {
|
||||
"comment": "Rejects IP with user auth enabled",
|
||||
"states": {
|
||||
"1": {
|
||||
"testend": "client",
|
||||
"cmd": "send",
|
||||
"payload": "GET / HTTP/1.1\r\nHost: comixwall.org\r\n\r\n"
|
||||
},
|
||||
"2": {
|
||||
"testend": "server",
|
||||
"cmd": "timeout",
|
||||
"payload": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
{
|
||||
"comment": "Tests for VerifyPeer",
|
||||
"configs": {
|
||||
"1": {
|
||||
"proto": {
|
||||
"proto": "ssl"
|
||||
},
|
||||
"client": {
|
||||
"ip": "127.0.0.1",
|
||||
"port": "8455"
|
||||
},
|
||||
"server": {
|
||||
"ip": "127.0.0.1",
|
||||
"port": "9455",
|
||||
"crt": "server.crt",
|
||||
"key": "server.key"
|
||||
}
|
||||
}
|
||||
},
|
||||
"tests": {
|
||||
"1": {
|
||||
"comment": "Accepts peer without verification",
|
||||
"states": {
|
||||
"1": {
|
||||
"testend": "client",
|
||||
"cmd": "send",
|
||||
"payload": "GET / HTTP/1.1\r\nHost: comixwall.org\r\n\r\n"
|
||||
},
|
||||
"2": {
|
||||
"testend": "server",
|
||||
"cmd": "recv",
|
||||
"payload": ""
|
||||
},
|
||||
"3": {
|
||||
"testend": "server",
|
||||
"cmd": "recv",
|
||||
"payload": "GET / HTTP/1.1\r\nHost: comixwall.org\r\nConnection: close\r\n\r\n"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
{
|
||||
"comment": "Tests for VerifyPeer",
|
||||
"configs": {
|
||||
"1": {
|
||||
"proto": {
|
||||
"proto": "ssl"
|
||||
},
|
||||
"client": {
|
||||
"ip": "127.0.0.1",
|
||||
"port": "8456"
|
||||
},
|
||||
"server": {
|
||||
"ip": "127.0.0.1",
|
||||
"port": "9456",
|
||||
"crt": "server.crt",
|
||||
"key": "server.key"
|
||||
}
|
||||
}
|
||||
},
|
||||
"tests": {
|
||||
"1": {
|
||||
"comment": "Rejects peer with verification",
|
||||
"states": {
|
||||
"1": {
|
||||
"testend": "client",
|
||||
"cmd": "sslconnectfail",
|
||||
"payload": "GET / HTTP/1.1\r\nHost: comixwall.org\r\n\r\n"
|
||||
},
|
||||
"2": {
|
||||
"testend": "server",
|
||||
"cmd": "timeout",
|
||||
"payload": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue