Compare commits

...

3 Commits

Author SHA1 Message Date
Vladimir Dubrovin
44c47ee7d9 replace recursion with loop for local redirects, fix wolfSSL build
Some checks are pending
C/C++ CI Linux / ${{ matrix.target }} (ubuntu-24.04-arm) (push) Waiting to run
C/C++ CI Linux / ${{ matrix.target }} (ubuntu-latest) (push) Waiting to run
C/C++ CI MacOS / ${{ matrix.target }} (macos-15) (push) Waiting to run
C/C++ CI Windows / ${{ matrix.target }} (windows-2022) (push) Waiting to run
C/C++ CI cmake / ${{ matrix.target }} (macos-15) (push) Waiting to run
C/C++ CI cmake / ${{ matrix.target }} (ubuntu-24.04-arm) (push) Waiting to run
C/C++ CI cmake / ${{ matrix.target }} (ubuntu-latest) (push) Waiting to run
C/C++ CI cmake / ${{ matrix.target }} (windows-2022) (push) Waiting to run
C/C++ CI cmake / ubuntu-latest (wolfSSL) (push) Waiting to run
2026-07-31 15:44:22 +03:00
Vladimir Dubrovin
5f1ed7363b Fix: race conditions / stack overflow on FreeBSD / minor bugs 2026-07-31 13:27:33 +03:00
Vladimir Dubrovin
c370635fc4 fix wolfSSL, switch minimal/busybox to musl+wolfssl 2026-07-31 11:27:10 +03:00
34 changed files with 496 additions and 122 deletions

73
.dockerignore Normal file
View File

@ -0,0 +1,73 @@
# Exclude top-level dirs not needed for build
/bin/
/bin64/
/build/
/cfg/
/cmake/
/CMakeFiles/
/debian/
/doc/
/man/
# Build artifacts
**/*.o
**/*.obj
**/*.so
**/*.dll
**/*.exe
**/*.exp
**/*.lib
**/*.pdb
**/*.ilk
**/*.idb
**/*.err
**/*.ld.so
**/*.dSYM
**/*.var
build*/
buildlinux.sh
tmp/
# macOS
.DS_Store
# Editor / IDE
**/*.swp
**/*.swo
**/*~
**/*.bak
**/*.tmp
**/*.log
.project
.cproject
.classpath
.settings/
.vscode/
.idea/
# Git
.git
.gitignore
# Claude / docs
CLAUDE.md
.claude/
# Generated version files
version
version.c
version.sh
verfile.sh
# Local config / keys
**/*.key
**/*.pem
**/*.pfx
3proxy.cfg
3proxy-ssl.cfg
3proxy-pcre.cfg
# Misc
res
3proxy.res
copytgz.sh

35
.github/workflows/docker-test.yml vendored Normal file
View File

@ -0,0 +1,35 @@
name: Build Docker images (test, no publish)
on:
workflow_dispatch:
permissions:
contents: read
jobs:
build:
name: Build ${{ matrix.file }} (${{ matrix.platform }})
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
file: [Dockerfile.minimal, Dockerfile.busybox, Dockerfile.full]
platform: [linux/amd64, linux/arm64]
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up QEMU
uses: docker/setup-qemu-action@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4
- name: Build image
uses: docker/build-push-action@v6
with:
context: .
file: ${{ matrix.file }}
platforms: ${{ matrix.platform }}
push: false
load: ${{ matrix.platform == 'linux/amd64' }}

1
.gitignore vendored
View File

@ -271,3 +271,4 @@ bin/3proxy_tcppm
bin/3proxy_tlspr bin/3proxy_tlspr
bin/3proxy_udppm bin/3proxy_udppm
build*/* build*/*
testcerts/

View File

@ -1,4 +1,5 @@
# 3proxy.full is fully functional 3proxy build based on busybox:glibc # 3proxy.busybox is fully functional 3proxy build based on busybox:musl
# built against wolfSSL and PCRE2.
# #
# Examples are for podman, for docker change 'podman' to 'docker' # Examples are for podman, for docker change 'podman' to 'docker'
# #
@ -19,11 +20,27 @@
# configuration is supported for compatibility only. # configuration is supported for compatibility only.
FROM docker.io/gcc AS buildenv FROM docker.io/alpine:latest AS wolfssl
RUN apk add --no-cache build-base curl autoconf automake libtool
RUN TAG=$(curl -s https://api.github.com/repos/wolfSSL/wolfssl/releases/latest \
| sed -n 's/.*"tag_name": *"\([^"]*\)".*/\1/p') && \
echo "wolfssl tag: $TAG" && \
curl -sL https://api.github.com/repos/wolfSSL/wolfssl/tarball/$TAG -o /tmp/w.tar.gz && \
mkdir /src && tar -xzf /tmp/w.tar.gz -C /src --strip-components=1 && \
cd /src && \
autoreconf -i && \
./configure --enable-opensslextra --enable-opensslall --enable-certgen \
--enable-tls13 --enable-sni --enable-session-ticket \
--enable-static --disable-shared --prefix=/usr/local && \
make -j$(nproc) && make install
FROM docker.io/alpine:latest AS buildenv
RUN apk add --no-cache gcc make musl-dev pcre2-dev
COPY --from=wolfssl /usr/local /usr/local
COPY . 3proxy COPY . 3proxy
RUN cd 3proxy &&\ RUN cd 3proxy && mkdir -p bin &&\
apt --assume-yes update && apt --assume-yes install libssl-dev libpcre2-dev &&\ make -f Makefile.Linux LIBSTATIC=true PAM_CHECK=false WOLFSSL_CHECK=true \
make -f Makefile.Linux LIBSTATIC=true PAM_CHECK=false &&\ EXTRA_LDFLAGS="-L/usr/local/lib" &&\
strip bin/3proxy &&\ strip bin/3proxy &&\
strip bin/*so &&\ strip bin/*so &&\
mkdir /dist &&\ mkdir /dist &&\
@ -42,11 +59,9 @@ RUN cd /dist &&\
ln -s /lib lib64 &&\ ln -s /lib lib64 &&\
ln -s /lib usr/lib &&\ ln -s /lib usr/lib &&\
ln -s /lib usr/lib64 ln -s /lib usr/lib64
RUN cp /lib/ld-*.so.* /dist/usr/local/3proxy/libexec || true RUN cp /lib/ld-musl-*.so.* /dist/usr/local/3proxy/libexec || true
RUN cp /lib64/ld-*.so.* /dist/usr/local/3proxy/libexec || true
RUN cp "/lib/`gcc -dumpmachine`"/libdl.so.* /dist/usr/local/3proxy/libexec
FROM docker.io/busybox:glibc FROM docker.io/busybox:musl
COPY --from=buildenv /dist / COPY --from=buildenv /dist /
RUN ln -sf /usr/local/3proxy/libexec/* /lib/ && cd /usr/local/3proxy/ && ln -s libexec lib && ln -s libexec lib64 && mkdir usr && ln -s libexec usr/lib && ln -s libexec usr//lib64 RUN mkdir -p /lib /lib64 && ln -sf /usr/local/3proxy/libexec/* /lib/ && cd /usr/local/3proxy/ && ln -s libexec lib && ln -s libexec lib64 && mkdir usr && ln -s libexec usr/lib && ln -s libexec usr/lib64
CMD ["/bin/3proxy", "/etc/3proxy/3proxy.cfg"] CMD ["/bin/3proxy", "/etc/3proxy/3proxy.cfg"]

View File

@ -23,6 +23,7 @@ FROM docker.io/gcc AS buildenv
COPY . 3proxy COPY . 3proxy
RUN cd 3proxy &&\ RUN cd 3proxy &&\
apt --assume-yes update && apt --assume-yes install libssl-dev libpcre2-dev &&\ apt --assume-yes update && apt --assume-yes install libssl-dev libpcre2-dev &&\
mkdir -p bin &&\
make -f Makefile.Linux LIBSTATIC=true PAM_CHECK=false &&\ make -f Makefile.Linux LIBSTATIC=true PAM_CHECK=false &&\
strip bin/3proxy &&\ strip bin/3proxy &&\
mkdir /dist &&\ mkdir /dist &&\

View File

@ -3,7 +3,8 @@
# #
# Examples are for podman. For docker change 'podman' to 'docker'. # Examples are for podman. For docker change 'podman' to 'docker'.
# #
# This is busybox based docker with only 3proxy static executable. # This is a scratch-based docker with statically linked 3proxy executable
# built against musl and wolfSSL.
# #
# Limitations for minimal version: # Limitations for minimal version:
# no support for plugins and system resolver. # no support for plugins and system resolver.
@ -25,10 +26,27 @@
#<end #<end
# #
FROM docker.io/gcc AS buildenv FROM docker.io/alpine:latest AS wolfssl
RUN apk add --no-cache build-base curl autoconf automake libtool
RUN TAG=$(curl -s https://api.github.com/repos/wolfSSL/wolfssl/releases/latest \
| sed -n 's/.*"tag_name": *"\([^"]*\)".*/\1/p') && \
echo "wolfssl tag: $TAG" && \
curl -sL https://api.github.com/repos/wolfSSL/wolfssl/tarball/$TAG -o /tmp/w.tar.gz && \
mkdir /src && tar -xzf /tmp/w.tar.gz -C /src --strip-components=1 && \
cd /src && \
autoreconf -i && \
./configure --enable-opensslextra --enable-opensslall --enable-certgen \
--enable-tls13 --enable-sni --enable-session-ticket \
--enable-static --disable-shared --prefix=/usr/local && \
make -j$(nproc) && make install
FROM docker.io/alpine:latest AS buildenv
RUN apk add --no-cache gcc make musl-dev
COPY --from=wolfssl /usr/local /usr/local
COPY . 3proxy COPY . 3proxy
RUN cd 3proxy &&\ RUN cd 3proxy && mkdir -p bin &&\
make -f Makefile.Linux STATIC=true &&\ make -f Makefile.Linux STATIC=true WOLFSSL_CHECK=true \
EXTRA_LDFLAGS="-L/usr/local/lib" &&\
strip bin/3proxy strip bin/3proxy
FROM scratch FROM scratch

View File

@ -16,6 +16,11 @@ COUT = -o
LN ?= ${CC} LN ?= ${CC}
LDFLAGS ?= -O3 -flto LDFLAGS ?= -O3 -flto
LDFLAGS += -pthread -fno-strict-aliasing LDFLAGS += -pthread -fno-strict-aliasing
# Use EXTRA_CFLAGS/EXTRA_LDFLAGS to add flags from the make command line.
# Setting CFLAGS or LDFLAGS there instead overrides every assignment in this
# makefile, including the += above and the STATIC/LIBSTATIC handling below.
CFLAGS += $(EXTRA_CFLAGS)
LDFLAGS += $(EXTRA_LDFLAGS)
# -lpthreads may be reuiured on some platforms instead of -pthreads # -lpthreads may be reuiured on some platforms instead of -pthreads
# -ldl or -lld may be required for some platforms # -ldl or -lld may be required for some platforms
DCFLAGS ?= -fPIC DCFLAGS ?= -fPIC

View File

@ -16,6 +16,11 @@ LN ?= ${CC}
DCFLAGS ?= -fPIC DCFLAGS ?= -fPIC
LDFLAGS ?= -O3 -flto LDFLAGS ?= -O3 -flto
LDFLAGS += -fno-strict-aliasing -pthread LDFLAGS += -fno-strict-aliasing -pthread
# Use EXTRA_CFLAGS/EXTRA_LDFLAGS to add flags from the make command line.
# Setting CFLAGS or LDFLAGS there instead overrides every assignment in this
# makefile, including the += above and the STATIC/LIBSTATIC handling below.
CFLAGS += $(EXTRA_CFLAGS)
LDFLAGS += $(EXTRA_LDFLAGS)
DLFLAGS ?= -shared DLFLAGS ?= -shared
DLSUFFICS = .ld.so DLSUFFICS = .ld.so
# -lpthreads may be reuqired on some platforms instead of -pthreads # -lpthreads may be reuqired on some platforms instead of -pthreads

View File

@ -18,6 +18,11 @@ COUT = -o
LN ?= $(CC) LN ?= $(CC)
LDFLAGS ?= -O3 -flto LDFLAGS ?= -O3 -flto
LDFLAGS += -pthread -fno-strict-aliasing LDFLAGS += -pthread -fno-strict-aliasing
# Use EXTRA_CFLAGS/EXTRA_LDFLAGS to add flags from the make command line.
# Setting CFLAGS or LDFLAGS there instead overrides every assignment in this
# makefile, including the += above and the STATIC/LIBSTATIC handling below.
CFLAGS += $(EXTRA_CFLAGS)
LDFLAGS += $(EXTRA_LDFLAGS)
# -lpthreads may be reuqired on some platforms instead of -pthreads # -lpthreads may be reuqired on some platforms instead of -pthreads
# -ldl or -lld may be required for some platforms # -ldl or -lld may be required for some platforms
DCFLAGS ?= -fPIC DCFLAGS ?= -fPIC

View File

@ -15,6 +15,11 @@ COUT = -o
LN ?= $(CC) LN ?= $(CC)
LDFLAGS ?= -O3 -flto LDFLAGS ?= -O3 -flto
LDFLAGS += -fno-strict-aliasing -mthreads LDFLAGS += -fno-strict-aliasing -mthreads
# Use EXTRA_CFLAGS/EXTRA_LDFLAGS to add flags from the make command line.
# Setting CFLAGS or LDFLAGS there instead overrides every assignment in this
# makefile, including the += above and the STATIC/LIBSTATIC handling below.
CFLAGS += $(EXTRA_CFLAGS)
LDFLAGS += $(EXTRA_LDFLAGS)
DLFLAGS ?= -shared DLFLAGS ?= -shared
DLSUFFICS = .dll DLSUFFICS = .dll
LIBS += -lws2_32 -lodbc32 -ladvapi32 -luser32 -lbcrypt LIBS += -lws2_32 -lodbc32 -ladvapi32 -luser32 -lbcrypt

View File

@ -1178,6 +1178,19 @@ crash on request processing, try to set some positive value. You may start with
stacksize 65536 stacksize 65536
and then find the minimal value for the service to work. If you experience and then find the minimal value for the service to work. If you experience
memory shortage, you can try to experiment with negative values. memory shortage, you can try to experiment with negative values.
.br
With SQL logging (log &ODBC_string) the value is automatically raised to
32768 if it is smaller, because ODBC drivers require more stack. A
.BR stacksize
command placed after the
.BR log
command overrides this.
.br
The base stack size the value is added to is 49152. On FreeBSD, NetBSD,
OpenBSD and DragonFly it is 65536, because libc functions such as vfprintf()
called by syslog() use significantly more stack there. The result is never
lowered below PTHREAD_STACK_MIN, so a large negative value can not disable
the thread stack.
.SH PLUGINS .SH PLUGINS

View File

@ -519,15 +519,10 @@ int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int
#ifndef NORADIUS #ifndef NORADIUS
_3proxy_mutex_init(&rad_mutex); _3proxy_mutex_init(&rad_mutex);
#endif #endif
#ifdef _WIN32 if(_3proxy_sem_init(conf.threadinit, 1, 1)){
conf.threadinit = CreateSemaphore(NULL, 1, 1, NULL);
if(!conf.threadinit){
fprintf(stderr, "semaphore init failed\n"); fprintf(stderr, "semaphore init failed\n");
return 1; return 1;
} }
#else
_3proxy_mutex_init(&conf.threadinit);
#endif
#ifdef WITH_SSL #ifdef WITH_SSL
ssl_install(); ssl_install();

View File

@ -24,13 +24,13 @@ void * autochild(struct clientparam* param) {
} }
if(*param->clibuf == 4 || *param->clibuf == 5) { if(*param->clibuf == 4 || *param->clibuf == 5) {
param->service = S_SOCKS; param->service = S_SOCKS;
return sockschild(param); return (void *)sockschild;
} }
if(*param->clibuf == 22) { if(*param->clibuf == 22) {
param->service = S_TLSPR; param->service = S_TLSPR;
return tlsprchild(param); return (void *)tlsprchild;
} }
param->service = S_PROXY; param->service = S_PROXY;
return proxychild(param); return (void *)proxychild;
} }

View File

@ -134,12 +134,47 @@ int timeouts[12] = {
0 0
}; };
struct extparam conf = { #ifndef _WIN32
#ifdef _WIN32 /* PTHREAD_STACK_MIN is 128K on glibc/aarch64 and glibc/powerpc and may be
.threadinit = NULL, a sysconf() call with _GNU_SOURCE. pthread_attr_setstacksize() fails with
#else EINVAL below it and the thread silently gets the 8M system default stack.
.threadinit = 0, */
size_t threadstacksize(int extra){
long size = BASESTACKSIZE + extra;
if(size < (long)PTHREAD_STACK_MIN) size = (long)PTHREAD_STACK_MIN;
return (size_t)size;
}
int _3proxy_sem_init_f(_3proxy_sem_t *sem, unsigned count, unsigned maxcount){
sem->count = count;
sem->maxcount = maxcount;
if(pthread_mutex_init(&sem->mutex, NULL)) return 1;
if(pthread_cond_init(&sem->cond, NULL)){
pthread_mutex_destroy(&sem->mutex);
return 1;
}
return 0;
}
void _3proxy_sem_lock_f(_3proxy_sem_t *sem){
pthread_mutex_lock(&sem->mutex);
while(!sem->count) pthread_cond_wait(&sem->cond, &sem->mutex);
sem->count--;
pthread_mutex_unlock(&sem->mutex);
}
void _3proxy_sem_unlock_f(_3proxy_sem_t *sem){
pthread_mutex_lock(&sem->mutex);
if(sem->count < sem->maxcount){
sem->count++;
pthread_cond_signal(&sem->cond);
}
pthread_mutex_unlock(&sem->mutex);
}
#endif #endif
struct extparam conf = {
.timeouts = timeouts, .timeouts = timeouts,
.acl = NULL, .acl = NULL,
.conffile = NULL, .conffile = NULL,

View File

@ -156,7 +156,7 @@ int start_proxy_thread(struct child * chp){
if(h)CloseHandle(h); if(h)CloseHandle(h);
#else #else
pthread_attr_init(&pa); pthread_attr_init(&pa);
pthread_attr_setstacksize(&pa,PTHREAD_STACK_MIN + (32768+conf.stacksize)); pthread_attr_setstacksize(&pa,threadstacksize(conf.stacksize));
pthread_attr_setdetachstate(&pa,PTHREAD_CREATE_DETACHED); pthread_attr_setdetachstate(&pa,PTHREAD_CREATE_DETACHED);
pthread_create(&thread, &pa, startsrv, (void *)chp); pthread_create(&thread, &pa, startsrv, (void *)chp);
pthread_attr_destroy(&pa); pthread_attr_destroy(&pa);
@ -301,6 +301,12 @@ static int h_external(int argc, unsigned char ** argv){
} }
/* ODBC drivers require noticeably more stack than the file logger, raise
the client thread stack size unless a larger one is configured
explicitly. An explicit stacksize placed after the log command still wins.
*/
#define LOGSTACKSIZE 32768
static int h_log(int argc, unsigned char ** argv){ static int h_log(int argc, unsigned char ** argv){
unsigned char tmpbuf[8192]; unsigned char tmpbuf[8192];
int notchanged = 0; int notchanged = 0;
@ -330,6 +336,7 @@ static int h_log(int argc, unsigned char ** argv){
#ifdef WITH_ODBC #ifdef WITH_ODBC
else if(*argv[1]=='&'){ else if(*argv[1]=='&'){
conf.logfunc = logsql; conf.logfunc = logsql;
if(conf.stacksize < LOGSTACKSIZE) conf.stacksize = LOGSTACKSIZE;
if(notchanged) return 0; if(notchanged) return 0;
_3proxy_mutex_lock(&log_mutex); _3proxy_mutex_lock(&log_mutex);
close_sql(); close_sql();

View File

@ -222,7 +222,6 @@ CLEANRET:
#ifndef _WIN32 #ifndef _WIN32
param->clisock = INVALID_SOCKET; param->clisock = INVALID_SOCKET;
#endif #endif
freeparam(param);
return (NULL); return (NULL);
} }

View File

@ -325,7 +325,6 @@ CLEANRET:
} }
if(req) free(req); if(req) free(req);
if(buf) free(buf); if(buf) free(buf);
freeparam(param);
return (NULL); return (NULL);
} }

View File

@ -64,7 +64,7 @@ void * imappchild(struct clientparam* param) {
if(socksend(param, param->clisock, buf, (int)strlen((char *)buf), conf.timeouts[STRING_S]) <= 0) {RETURN(698);} if(socksend(param, param->clisock, buf, (int)strlen((char *)buf), conf.timeouts[STRING_S]) <= 0) {RETURN(698);}
param->clientstarttls = S_IMAPP; param->clientstarttls = S_IMAPP;
if(!param->srv->targetport) param->srv->targetport = htons(143); if(!param->srv->targetport) param->srv->targetport = htons(143);
return tlsprchild(param); return (void *)tlsprchild;
} }
#endif #endif
if(!strncasecmp((char *)cmd, "LOGIN ", 6)){ if(!strncasecmp((char *)cmd, "LOGIN ", 6)){
@ -238,7 +238,6 @@ CLEANRET:
socksend(param, param->clisock, buf, (int)strlen((char *)buf),conf.timeouts[STRING_S]); socksend(param, param->clisock, buf, (int)strlen((char *)buf),conf.timeouts[STRING_S]);
} }
} }
freeparam(param);
return (NULL); return (NULL);
} }

View File

@ -48,6 +48,13 @@ typedef struct {
#endif #endif
} MD4_CTX; } MD4_CTX;
/* Remap public symbols to a 3proxy-private namespace so the bundled
implementation does not collide with OpenSSL/libcrypto's MD4 symbols
when statically linked. Callers keep using the MD4_* names. */
#define MD4_Init _3proxy_MD4_Init
#define MD4_Update _3proxy_MD4_Update
#define MD4_Final _3proxy_MD4_Final
extern void MD4_Init(MD4_CTX *ctx); extern void MD4_Init(MD4_CTX *ctx);
extern void MD4_Update(MD4_CTX *ctx, const void *data, size_t size); extern void MD4_Update(MD4_CTX *ctx, const void *data, size_t size);
extern void MD4_Final(unsigned char *result, MD4_CTX *ctx); extern void MD4_Final(unsigned char *result, MD4_CTX *ctx);

View File

@ -44,6 +44,13 @@ typedef struct {
MD5_u32plus block[16]; MD5_u32plus block[16];
} MD5_CTX; } MD5_CTX;
/* Remap public symbols to a 3proxy-private namespace so the bundled
implementation does not collide with OpenSSL/libcrypto's MD5 symbols
when statically linked. Callers keep using the MD5_* names. */
#define MD5_Init _3proxy_MD5_Init
#define MD5_Update _3proxy_MD5_Update
#define MD5_Final _3proxy_MD5_Final
extern void MD5_Init(MD5_CTX *ctx); extern void MD5_Init(MD5_CTX *ctx);
extern void MD5_Update(MD5_CTX *ctx, const void *data, unsigned long size); extern void MD5_Update(MD5_CTX *ctx, const void *data, unsigned long size);
extern void MD5_Final(unsigned char *result, MD5_CTX *ctx); extern void MD5_Final(unsigned char *result, MD5_CTX *ctx);

View File

@ -338,7 +338,7 @@ void lognone(struct clientparam * param, const unsigned char *s) {
clearstat(param); clearstat(param);
} }
void logstdout(struct clientparam * param, const unsigned char *s) { NOINLINE void logstdout(struct clientparam * param, const unsigned char *s) {
FILE *log; FILE *log;
unsigned char tmpbuf[8192]; unsigned char tmpbuf[8192];

View File

@ -38,7 +38,7 @@ void * pop3pchild(struct clientparam* param) {
if(socksend(param, param->clisock, (unsigned char *)"+OK Begin TLS negotiation\r\n", 27, conf.timeouts[STRING_S])!=27) {RETURN(623);} if(socksend(param, param->clisock, (unsigned char *)"+OK Begin TLS negotiation\r\n", 27, conf.timeouts[STRING_S])!=27) {RETURN(623);}
param->clientstarttls = S_POP3P; param->clientstarttls = S_POP3P;
if(!param->srv->targetport) param->srv->targetport = htons(110); if(!param->srv->targetport) param->srv->targetport = htons(110);
return tlsprchild(param); return (void *)tlsprchild;
} }
#endif #endif
socksend(param, param->clisock, (unsigned char *)"-ERR need USER first\r\n", 22, conf.timeouts[STRING_S]); socksend(param, param->clisock, (unsigned char *)"-ERR need USER first\r\n", 22, conf.timeouts[STRING_S]);
@ -74,7 +74,6 @@ CLEANRET:
if(param->clisock != INVALID_SOCKET) { if(param->clisock != INVALID_SOCKET) {
if ((param->res > 0 && param->res < 100) || (param->res > 611 && param->res <700)) socksend(param, param->clisock, (unsigned char *)"-ERR\r\n", 6,conf.timeouts[STRING_S]); if ((param->res > 0 && param->res < 100) || (param->res > 611 && param->res <700)) socksend(param, param->clisock, (unsigned char *)"-ERR\r\n", 6,conf.timeouts[STRING_S]);
} }
freeparam(param);
return (NULL); return (NULL);
} }

View File

@ -864,7 +864,7 @@ for(;;){
if(isconnect && param->redirtype != R_HTTP) { if(isconnect && param->redirtype != R_HTTP) {
if(param->redirectfunc) { if(param->redirectfunc) {
freeptr(&req); freeptr(&buf); freeptr(&ftpbase); freeptr(&req); freeptr(&buf); freeptr(&ftpbase);
return (*param->redirectfunc)(param); return (void *)param->redirectfunc;
} }
param->res = mapsocket(param, conf.timeouts[CONNECTION_L]); param->res = mapsocket(param, conf.timeouts[CONNECTION_L]);
RETURN(param->res); RETURN(param->res);
@ -1226,7 +1226,6 @@ CLEANRET:
} }
logurl(param, (char *)buf, (char *)req, ftp); logurl(param, (char *)buf, (char *)req, ftp);
freeptr(&req); freeptr(&buf); freeptr(&ftpbase); freeptr(&req); freeptr(&buf); freeptr(&ftpbase);
freeparam(param);
return (NULL); return (NULL);
} }

View File

@ -112,6 +112,33 @@ void daemonize(void);
#endif #endif
#endif #endif
/* Keeps a callee with a large frame out of the caller's frame, e.g. the
8K log buffer of logstdout() out of dolog(), which calls it in a branch
taken only when there is no service.
*/
#if defined(__GNUC__)
#define NOINLINE __attribute__((noinline))
#elif defined(_MSC_VER)
#define NOINLINE __declspec(noinline)
#else
#define NOINLINE
#endif
/* Thread stack size, stacksize command value is added to it. BSD libc uses
significantly more stack, e.g. in vfprintf() called by syslog().
*/
#ifndef BASESTACKSIZE
#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__)
#define BASESTACKSIZE 65536
#else
#define BASESTACKSIZE 49152
#endif
#endif
#ifndef _WIN32
size_t threadstacksize(int extra);
#endif
#ifdef WITH_ODBC #ifdef WITH_ODBC
#ifndef _WIN32 #ifndef _WIN32
#include <sqltypes.h> #include <sqltypes.h>
@ -198,7 +225,7 @@ void dolog(struct clientparam * param, const unsigned char *s);
int dobuf(struct clientparam * param, unsigned char * buf, const unsigned char *s, const unsigned char * doublec); int dobuf(struct clientparam * param, unsigned char * buf, const unsigned char *s, const unsigned char * doublec);
int dobuf2(struct clientparam * param, unsigned char * buf, const unsigned char *s, const unsigned char * doublec, struct tm* tm, char * format); int dobuf2(struct clientparam * param, unsigned char * buf, const unsigned char *s, const unsigned char * doublec, struct tm* tm, char * format);
extern FILE * stdlog; extern FILE * stdlog;
void logstdout(struct clientparam * param, const unsigned char *s); NOINLINE void logstdout(struct clientparam * param, const unsigned char *s);
void logsyslog(struct clientparam * param, const unsigned char *s); void logsyslog(struct clientparam * param, const unsigned char *s);
void lognone(struct clientparam * param, const unsigned char *s); void lognone(struct clientparam * param, const unsigned char *s);
void logradius(struct clientparam * param, const unsigned char *s); void logradius(struct clientparam * param, const unsigned char *s);
@ -248,14 +275,18 @@ unsigned char* en64 (const unsigned char *in, unsigned char *out, int inlen);
void tohex(unsigned char *in, unsigned char *out, int len); void tohex(unsigned char *in, unsigned char *out, int len);
void fromhex(unsigned char *in, unsigned char *out, int len); void fromhex(unsigned char *in, unsigned char *out, int len);
extern _3proxy_sem_t udpinit;
#ifdef _WIN32 #ifdef _WIN32
extern HANDLE udpinit; #define _3proxy_sem_init(x, count, maxcount) (((x) = CreateSemaphore(NULL, (count), (maxcount), NULL))? 0 : 1)
#define _3proxy_sem_lock(x) WaitForSingleObject(x, INFINITE) #define _3proxy_sem_lock(x) WaitForSingleObject(x, INFINITE)
#define _3proxy_sem_unlock(x) ReleaseSemaphore(x, 1, NULL) #define _3proxy_sem_unlock(x) ReleaseSemaphore(x, 1, NULL)
#else #else
extern _3proxy_mutex_t udpinit; int _3proxy_sem_init_f(_3proxy_sem_t *sem, unsigned count, unsigned maxcount);
#define _3proxy_sem_lock(x) pthread_mutex_lock(&x) void _3proxy_sem_lock_f(_3proxy_sem_t *sem);
#define _3proxy_sem_unlock(x) pthread_mutex_unlock(&x) void _3proxy_sem_unlock_f(_3proxy_sem_t *sem);
#define _3proxy_sem_init(x, count, maxcount) _3proxy_sem_init_f(&x, (count), (maxcount))
#define _3proxy_sem_lock(x) _3proxy_sem_lock_f(&x)
#define _3proxy_sem_unlock(x) _3proxy_sem_unlock_f(&x)
#endif #endif
@ -338,6 +369,13 @@ void * udppmchild(struct clientparam * param);
void * adminchild(struct clientparam * param); void * adminchild(struct clientparam * param);
void * ftpprchild(struct clientparam * param); void * ftpprchild(struct clientparam * param);
void * tlsprchild(struct clientparam * param); void * tlsprchild(struct clientparam * param);
/* Child functions return the child to redirect the request to, or NULL if
the request is complete. childfunc() calls them and releases param.
Recursive redirection, e.g. a socks service redirected to socks5, used to
be limited by the stack size only, MAXCHILDREDIRECTS limits it now.
*/
#define MAXCHILDREDIRECTS 16
void * childfunc(struct clientparam * param);
struct datatype; struct datatype;

View File

@ -11,6 +11,24 @@
#include <sched.h> #include <sched.h>
#endif #endif
/* Child functions do not call each other, a child requesting redirection to
another child returns it instead of calling it, to keep the stack flat.
The child which completed the request returns NULL. param is logged by
the child and released here.
*/
void * childfunc(struct clientparam * param){
PROXYFUNC pf = param->srv->pf;
int i;
for(i = 0; pf && i < MAXCHILDREDIRECTS; i++) pf = (PROXYFUNC)(*pf)(param);
if(pf){
param->res = 101;
dolog(param, (unsigned char *)"Redirection loop");
}
freeparam(param);
return NULL;
}
#define param ((struct clientparam *) p) #define param ((struct clientparam *) p)
#ifdef _WIN32 #ifdef _WIN32
DWORD WINAPI threadfunc(LPVOID p) { DWORD WINAPI threadfunc(LPVOID p) {
@ -121,7 +139,7 @@ void * threadfunc (void *p) {
} }
} }
} }
((struct clientparam *) p)->srv->pf((struct clientparam *)p); childfunc((struct clientparam *)p);
} }
#ifdef _WIN32 #ifdef _WIN32
return 0; return 0;
@ -367,6 +385,7 @@ int MODULEMAINFUNC (int argc, char** argv){
if(!srv.udpbuf || !srv.udpbuf2) { if(!srv.udpbuf || !srv.udpbuf2) {
#ifndef STDMAIN #ifndef STDMAIN
haveerror = 2; haveerror = 2;
_3proxy_sem_unlock(conf.threadinit);
#endif #endif
return 11; return 11;
} }
@ -608,7 +627,6 @@ int MODULEMAINFUNC (int argc, char** argv){
if (error || i!=argc) { if (error || i!=argc) {
#ifndef STDMAIN #ifndef STDMAIN
haveerror = 1; haveerror = 1;
_3proxy_sem_unlock(conf.threadinit);
#endif #endif
fprintf(stderr, "%s of %s\n" fprintf(stderr, "%s of %s\n"
"Usage: %s options\n" "Usage: %s options\n"
@ -628,6 +646,9 @@ int MODULEMAINFUNC (int argc, char** argv){
"" ""
#endif #endif
); );
#ifndef STDMAIN
_3proxy_sem_unlock(conf.threadinit);
#endif
return (1); return (1);
} }
@ -640,7 +661,6 @@ int MODULEMAINFUNC (int argc, char** argv){
if (error || argc != i+3 || *argv[i]=='-'|| (*SAPORT(&srv.intsa) = htons((uint16_t)atoi(argv[i])))==0 || (srv.targetport = htons((uint16_t)atoi(argv[i+2])))==0) { if (error || argc != i+3 || *argv[i]=='-'|| (*SAPORT(&srv.intsa) = htons((uint16_t)atoi(argv[i])))==0 || (srv.targetport = htons((uint16_t)atoi(argv[i+2])))==0) {
#ifndef STDMAIN #ifndef STDMAIN
haveerror = 1; haveerror = 1;
_3proxy_sem_unlock(conf.threadinit);
#endif #endif
fprintf(stderr, "%s of %s\n" fprintf(stderr, "%s of %s\n"
"Usage: %s options" "Usage: %s options"
@ -661,6 +681,9 @@ int MODULEMAINFUNC (int argc, char** argv){
"" ""
#endif #endif
); );
#ifndef STDMAIN
_3proxy_sem_unlock(conf.threadinit);
#endif
return (1); return (1);
} }
srv.target = (unsigned char *)strdup(argv[i+1]); srv.target = (unsigned char *)strdup(argv[i+1]);
@ -684,7 +707,7 @@ int MODULEMAINFUNC (int argc, char** argv){
return 2; return 2;
}; };
*newparam = defparam; *newparam = defparam;
return((*srv.pf)((void *)newparam)? 1:0); return(childfunc(newparam)? 1:0);
} }
#endif #endif
@ -917,7 +940,7 @@ int MODULEMAINFUNC (int argc, char** argv){
#ifndef _WIN32 #ifndef _WIN32
pthread_attr_init(&pa); pthread_attr_init(&pa);
pthread_attr_setstacksize(&pa,PTHREAD_STACK_MIN + (32768 + srv.stacksize)); pthread_attr_setstacksize(&pa,threadstacksize(srv.stacksize));
pthread_attr_setdetachstate(&pa,PTHREAD_CREATE_DETACHED); pthread_attr_setdetachstate(&pa,PTHREAD_CREATE_DETACHED);
#endif #endif
@ -1051,9 +1074,12 @@ int MODULEMAINFUNC (int argc, char** argv){
else { else {
struct clientparam *toparam; struct clientparam *toparam;
srv.udplen = sockrecvfrom(NULL, srv.srvsock, (struct sockaddr *)&defparam.sincr, srv.udpbuf, UDPBUFSIZE, 0);
if(srv.udplen <= 0) continue;
_3proxy_sem_lock(udpinit); _3proxy_sem_lock(udpinit);
srv.udplen = sockrecvfrom(NULL, srv.srvsock, (struct sockaddr *)&defparam.sincr, srv.udpbuf, UDPBUFSIZE, 0);
if(srv.udplen <= 0) {
_3proxy_sem_unlock(udpinit);
continue;
}
if(hashresolv(&udp_table, &defparam, &toparam, NULL)) { if(hashresolv(&udp_table, &defparam, &toparam, NULL)) {
int i, len=0; int i, len=0;
@ -1172,12 +1198,7 @@ int MODULEMAINFUNC (int argc, char** argv){
#ifndef NOUDPMAIN #ifndef NOUDPMAIN
int udpinited = 0; int udpinited = 0;
#ifdef _WIN32 _3proxy_sem_t udpinit;
HANDLE udpinit;
#else
_3proxy_mutex_t udpinit;
#endif
#endif #endif
void srvinit(struct srvparam * srv, struct clientparam *param){ void srvinit(struct srvparam * srv, struct clientparam *param){
@ -1216,11 +1237,7 @@ void srvinit(struct srvparam * srv, struct clientparam *param){
_3proxy_mutex_init(&srv->counter_mutex); _3proxy_mutex_init(&srv->counter_mutex);
#ifndef NOUDPMAIN #ifndef NOUDPMAIN
if(!udpinited){ if(!udpinited){
#ifdef _WIN32 (void)_3proxy_sem_init(udpinit, 1, 1);
udpinit = CreateSemaphore(NULL, 1, 1, NULL);
#else
_3proxy_mutex_init(&udpinit);
#endif
} }
udpinited = 1; udpinited = 1;
#endif #endif

View File

@ -132,7 +132,7 @@ void * smtppchild(struct clientparam* param) {
if(socksend(param, param->clisock, (unsigned char *)"220 2.0.0 Ready to start TLS\r\n", 30, conf.timeouts[STRING_S])!=30) {RETURN(673);} if(socksend(param, param->clisock, (unsigned char *)"220 2.0.0 Ready to start TLS\r\n", 30, conf.timeouts[STRING_S])!=30) {RETURN(673);}
param->clientstarttls = S_SMTPP; param->clientstarttls = S_SMTPP;
if(!param->srv->targetport) param->srv->targetport = htons(587); if(!param->srv->targetport) param->srv->targetport = htons(587);
return tlsprchild(param); return (void *)tlsprchild;
} }
#endif #endif
else if(!param->hostname) socksend(param, param->clisock, (unsigned char *)"571 need AUTH first\r\n", 22, conf.timeouts[STRING_S]); else if(!param->hostname) socksend(param, param->clisock, (unsigned char *)"571 need AUTH first\r\n", 22, conf.timeouts[STRING_S]);
@ -319,7 +319,6 @@ CLEANRET:
if ((param->res > 0 && param->res < 100) || (param->res > 661 && param->res <700)) socksend(param, param->clisock, (unsigned char *)"571 \r\n", 6,conf.timeouts[STRING_S]); if ((param->res > 0 && param->res < 100) || (param->res > 661 && param->res <700)) socksend(param, param->clisock, (unsigned char *)"571 \r\n", 6,conf.timeouts[STRING_S]);
} }
if(command) free(command); if(command) free(command);
freeparam(param);
return (NULL); return (NULL);
} }

View File

@ -385,9 +385,8 @@ fflush(stderr);
switch(command) { switch(command) {
case 1: case 1:
if(param->redirectfunc){ if(param->redirectfunc){
void *ret = (*param->redirectfunc)(param);
if(buf)free(buf); if(buf)free(buf);
return ret; return (void *)param->redirectfunc;
} }
param->res = mapsocket(param, conf.timeouts[CONNECTION_L]); param->res = mapsocket(param, conf.timeouts[CONNECTION_L]);
break; break;
@ -467,7 +466,6 @@ fflush(stderr);
dolog(param, buf); dolog(param, buf);
free(buf); free(buf);
} }
freeparam(param);
return (NULL); return (NULL);
} }

View File

@ -490,6 +490,24 @@ static int verify_callback(int preverify_ok, X509_STORE_CTX *ctx){
return preverify_ok; return preverify_ok;
} }
#ifdef WITH_WOLFSSL
/* wolfSSL's SSL_CTX_use_PrivateKey(EVP_PKEY*) compat is unreliable: it
* silently fails (returns 0, no error queued) for keys loaded via
* PEM_read_bio_PrivateKey. Export the key to DER and load via the
* native buffer API instead. */
static int ssl_ctx_use_pkey(SSL_CTX *ctx, EVP_PKEY *key){
unsigned char *der = NULL;
int len, rc;
len = i2d_PrivateKey(key, &der);
if(len <= 0 || !der) return 0;
rc = wolfSSL_CTX_use_PrivateKey_buffer(ctx, der, (long)len, SSL_FILETYPE_ASN1);
OPENSSL_free(der);
return rc;
}
#else
#define ssl_ctx_use_pkey(ctx, key) SSL_CTX_use_PrivateKey((ctx), (key))
#endif
SSL_CTX * ssl_cli_ctx(SSL_CONFIG *config, X509 *server_cert, EVP_PKEY *server_key, char** errSSL){ SSL_CTX * ssl_cli_ctx(SSL_CONFIG *config, X509 *server_cert, EVP_PKEY *server_key, char** errSSL){
SSL_CTX *ctx; SSL_CTX *ctx;
@ -516,12 +534,19 @@ SSL_CTX * ssl_cli_ctx(SSL_CONFIG *config, X509 *server_cert, EVP_PKEY *server_ke
} }
} }
err = SSL_CTX_use_PrivateKey(ctx, server_key); /* Load the private key only when a cert is already loaded into ctx.
* wolfSSL validates the key against the cert and requires the cert
* to be present first; OpenSSL tolerates either order but is happy
* with cert-first too. When server_cert is NULL (serv path), the
* caller loads the cert chain and the key itself after this call. */
if(server_cert) {
err = ssl_ctx_use_pkey(ctx, server_key);
if ( err <= 0 ) { if ( err <= 0 ) {
*errSSL = getSSLErr(); *errSSL = getSSLErr();
SSL_CTX_free(ctx); SSL_CTX_free(ctx);
return NULL; return NULL;
} }
}
SSL_CTX_set_session_id_context(ctx, (const unsigned char *)"3proxy", 6); SSL_CTX_set_session_id_context(ctx, (const unsigned char *)"3proxy", 6);
if(config->server_min_proto_version)SSL_CTX_set_min_proto_version(ctx, config->server_min_proto_version); if(config->server_min_proto_version)SSL_CTX_set_min_proto_version(ctx, config->server_min_proto_version);
if(config->server_max_proto_version)SSL_CTX_set_max_proto_version(ctx, config->server_max_proto_version); if(config->server_max_proto_version)SSL_CTX_set_max_proto_version(ctx, config->server_max_proto_version);
@ -655,6 +680,10 @@ static void* ssl_filter_open(void * idata, struct srvparam * srv){
fprintf(stderr, "failed to read server cert: %s\n", srvcert); fprintf(stderr, "failed to read server cert: %s\n", srvcert);
return sc; return sc;
} }
if(ssl_ctx_use_pkey(sc->cli_ctx, sc->server_key) <= 0){
fprintf(stderr, "failed to use server key\n");
return sc;
}
sc->serv = 1; sc->serv = 1;
} }
if(mitm || cli || serv){ if(mitm || cli || serv){
@ -678,7 +707,7 @@ static void* ssl_filter_open(void * idata, struct srvparam * srv){
} }
if(sc->client_cert){ if(sc->client_cert){
SSL_CTX_use_certificate(sc->srv_ctx, (X509 *) sc->client_cert); SSL_CTX_use_certificate(sc->srv_ctx, (X509 *) sc->client_cert);
SSL_CTX_use_PrivateKey(sc->srv_ctx, sc->client_key); ssl_ctx_use_pkey(sc->srv_ctx, sc->client_key);
} }
if(sc->client_min_proto_version)SSL_CTX_set_min_proto_version(sc->srv_ctx, sc->client_min_proto_version); if(sc->client_min_proto_version)SSL_CTX_set_min_proto_version(sc->srv_ctx, sc->client_min_proto_version);
if(sc->client_max_proto_version)SSL_CTX_set_max_proto_version(sc->srv_ctx, sc->client_max_proto_version); if(sc->client_max_proto_version)SSL_CTX_set_max_proto_version(sc->srv_ctx, sc->client_max_proto_version);
@ -705,6 +734,15 @@ static void* ssl_filter_open(void * idata, struct srvparam * srv){
SSL_CTX_set_default_verify_paths(sc->srv_ctx); SSL_CTX_set_default_verify_paths(sc->srv_ctx);
SSL_CTX_set_verify(sc->srv_ctx, SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL); SSL_CTX_set_verify(sc->srv_ctx, SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);
} }
#ifdef WITH_WOLFSSL
else {
/* wolfSSL defaults to peer verification; OpenSSL defaults to
* SSL_VERIFY_NONE. Make the no-verify intent explicit so the
* upstream handshake (ssl_cli / ssl_mitm) succeeds without a
* trusted CA store. */
SSL_CTX_set_verify(sc->srv_ctx, SSL_VERIFY_NONE, NULL);
}
#endif
} }
#ifdef WITHSPLICE #ifdef WITHSPLICE
srv->usesplice = 0; srv->usesplice = 0;

View File

@ -45,8 +45,6 @@ static char hexMap[] = {
'8', '9', 'A', 'B', 'C', 'D', 'E', 'F' '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
}; };
static BIO *bio_err=NULL;
char * getSSLErr(){ char * getSSLErr(){
@ -74,10 +72,23 @@ static size_t bin2hex (const unsigned char* bin, size_t bin_length, char* str, s
return p - str; return p - str;
} }
static int add_ext(X509 *cert, int nid, char *value) static int copy_ext(X509 *dst_cert, X509 *src_cert, int nid)
{
X509_EXTENSION *ext;
int idx;
idx = X509_get_ext_by_NID(src_cert, nid, -1);
if(idx < 0) return 0;
if(!(ext = X509_get_ext(src_cert, idx))) return 0;
return X509_add_ext(dst_cert, ext, -1) > 0;
}
#ifndef WITH_WOLFSSL
static int add_ext(X509 *cert, int nid, const char *value)
{ {
X509_EXTENSION *ex; X509_EXTENSION *ex;
X509V3_CTX ctx; X509V3_CTX ctx;
int err;
/* This sets the 'context' of the extensions. */ /* This sets the 'context' of the extensions. */
/* No configuration database */ /* No configuration database */
X509V3_set_ctx_nodb(&ctx); X509V3_set_ctx_nodb(&ctx);
@ -85,25 +96,16 @@ static int add_ext(X509 *cert, int nid, char *value)
* no request and no CRL * no request and no CRL
*/ */
X509V3_set_ctx(&ctx, cert, cert, NULL, NULL, 0); X509V3_set_ctx(&ctx, cert, cert, NULL, NULL, 0);
ex = X509V3_EXT_conf_nid(NULL, &ctx, nid, value); /* value is char * prior to OpenSSL 1.1.0 */
ex = X509V3_EXT_conf_nid(NULL, &ctx, nid, (char *)value);
if (!ex) if (!ex)
return 0; return 0;
X509_add_ext(cert,ex,-1); err = X509_add_ext(cert,ex,-1);
X509_EXTENSION_free(ex); X509_EXTENSION_free(ex);
return 1; return err > 0;
}
void del_ext(X509 *dst_cert, int nid, int where){
int ex;
ex = X509_get_ext_by_NID(dst_cert, nid, where);
if(ex>=0){
X509_EXTENSION *ext;
if((ext = X509_delete_ext(dst_cert, ex))) X509_EXTENSION_free(ext);
}
} }
#endif
SSL_CERT ssl_copy_cert(SSL_CERT cert, SSL_CONFIG *config) SSL_CERT ssl_copy_cert(SSL_CERT cert, SSL_CONFIG *config)
{ {
@ -112,8 +114,6 @@ SSL_CERT ssl_copy_cert(SSL_CERT cert, SSL_CONFIG *config)
X509 *src_cert = (X509 *) cert; X509 *src_cert = (X509 *) cert;
X509 *dst_cert = NULL; X509 *dst_cert = NULL;
EVP_PKEY *pk = NULL;
unsigned char hash_sha256[32]; unsigned char hash_sha256[32];
char hash_name_sha256[(16*2) + 1]; char hash_name_sha256[(16*2) + 1];
char cache_name[256]; char cache_name[256];
@ -142,28 +142,67 @@ SSL_CERT ssl_copy_cert(SSL_CERT cert, SSL_CONFIG *config)
} }
} }
} }
/* proceed if certificate is not cached */ /* Build a fresh certificate instead of duplicating the source: only
dst_cert = X509_dup(src_cert); * the fields required for a usable server cert are copied (version,
* serial, subject, validity, SAN). This avoids inheriting upstream
* extensions (AKI, CRL dist points, certificate policies, ...) that
* break chain validation, and works around wolfSSL's no-op
* X509_delete_ext compat shim. */
dst_cert = X509_new();
if ( dst_cert == NULL ) { if ( dst_cert == NULL ) {
return NULL; return NULL;
} }
del_ext(dst_cert, NID_crl_distribution_points, -1); /* v3 is required, extensions are added below */
del_ext(dst_cert, NID_info_access, -1); X509_set_version(dst_cert, 2);
del_ext(dst_cert, NID_authority_key_identifier, -1); if(!X509_set_serialNumber(dst_cert, X509_get_serialNumber(src_cert))
del_ext(dst_cert, NID_certificate_policies, 0); || !X509_set_subject_name(dst_cert, X509_get_subject_name(src_cert))
|| !X509_set_issuer_name(dst_cert, X509_get_subject_name(config->CA_cert))){
X509_free(dst_cert);
return NULL;
}
err = X509_set_pubkey(dst_cert, config->server_key?config->server_key:config->CA_key); err = X509_set_pubkey(dst_cert, config->server_key?config->server_key:config->CA_key);
if ( err == 0 ) { if ( err == 0 ) {
X509_free(dst_cert); X509_free(dst_cert);
return NULL; return NULL;
} }
/* wolfSSL has no X509_set1_notBefore/X509_set1_notAfter before 5.7.2,
X509_set_notBefore/X509_set_notAfter are available in every version and
err = X509_set_issuer_name(dst_cert, X509_get_subject_name(config->CA_cert)); copy the time the same way.
if(!err){ */
#if defined(WITH_WOLFSSL) || OPENSSL_VERSION_NUMBER < 0x10100000L
if(!X509_set_notBefore(dst_cert, X509_get_notBefore(src_cert))
|| !X509_set_notAfter(dst_cert, X509_get_notAfter(src_cert))){
#else
if(!X509_set1_notBefore(dst_cert, X509_get0_notBefore(src_cert))
|| !X509_set1_notAfter(dst_cert, X509_get0_notAfter(src_cert))){
#endif
X509_free(dst_cert); X509_free(dst_cert);
return NULL; return NULL;
} }
/* Copy the extensions an end entity certificate is expected to have.
* The extensions which break chain validation (AKI, CRL distribution
* points, certificate policies, ...) are intentionally not copied.
* A copy may fail: wolfSSL keeps extKeyUsage in its own form and can
* not add back the one it returns, it is not fatal.
*/
copy_ext(dst_cert, src_cert, NID_subject_alt_name);
#ifndef WITH_WOLFSSL
/* Without EKU serverAuth Apple's TLS stack (and Chrome on macOS/iOS,
* which uses it) rejects the certificate, generate the extensions the
* server certificate has no usable ones to copy. keyUsage is not set:
* it depends on the type of the key reused for every generated
* certificate, and an absent keyUsage places no restriction.
* wolfSSL_X509V3_EXT_conf_nid() is a stub returning NULL in every
* wolfSSL version, the extensions can not be generated there.
*/
if(!copy_ext(dst_cert, src_cert, NID_basic_constraints))
add_ext(dst_cert, NID_basic_constraints, "critical,CA:FALSE");
if(!copy_ext(dst_cert, src_cert, NID_ext_key_usage))
add_ext(dst_cert, NID_ext_key_usage, "serverAuth");
#else
copy_ext(dst_cert, src_cert, NID_basic_constraints);
copy_ext(dst_cert, src_cert, NID_ext_key_usage);
#endif
err = X509_sign(dst_cert, config->CA_key, EVP_sha256()); err = X509_sign(dst_cert, config->CA_key, EVP_sha256());
if(!err){ if(!err){
X509_free(dst_cert); X509_free(dst_cert);
@ -229,10 +268,18 @@ void _ssl_cert_free(SSL_CERT cert)
/* OpenSSL before 1.1.0 requires the application to install threading
callbacks; OpenSSL >= 1.1.0 and wolfSSL handle locking internally. */
#if !defined(WITH_WOLFSSL) && defined(OPENSSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER < 0x10100000L
#define LEGACY_SSL_THREADING 1
#else
#define LEGACY_SSL_THREADING 0
#endif
#if LEGACY_SSL_THREADING
/* This array will store all of the mutexes available to OpenSSL. */ /* This array will store all of the mutexes available to OpenSSL. */
static _3proxy_mutex_t *mutex_buf= NULL; static _3proxy_mutex_t *mutex_buf= NULL;
static void locking_function(int mode, int n, const char * file, int line) static void locking_function(int mode, int n, const char * file, int line)
{ {
if (mode & CRYPTO_LOCK) if (mode & CRYPTO_LOCK)
@ -249,9 +296,11 @@ static unsigned long id_function(void)
return ((unsigned long)pthread_self()); return ((unsigned long)pthread_self());
#endif #endif
} }
#endif
int thread_setup(void) int thread_setup(void)
{ {
#if LEGACY_SSL_THREADING
int i; int i;
mutex_buf = malloc(CRYPTO_num_locks( ) * sizeof(_3proxy_mutex_t)); mutex_buf = malloc(CRYPTO_num_locks( ) * sizeof(_3proxy_mutex_t));
@ -262,10 +311,14 @@ int thread_setup(void)
CRYPTO_set_id_callback(id_function); CRYPTO_set_id_callback(id_function);
CRYPTO_set_locking_callback(locking_function); CRYPTO_set_locking_callback(locking_function);
return 1; return 1;
#else
return 1;
#endif
} }
int thread_cleanup(void) int thread_cleanup(void)
{ {
#if LEGACY_SSL_THREADING
int i; int i;
if (!mutex_buf) if (!mutex_buf)
@ -277,6 +330,9 @@ int thread_cleanup(void)
free(mutex_buf); free(mutex_buf);
mutex_buf = NULL; mutex_buf = NULL;
return 1; return 1;
#else
return 1;
#endif
} }
@ -291,9 +347,14 @@ void ssl_init()
ssl_init_done = 1; ssl_init_done = 1;
thread_setup(); thread_setup();
#ifdef WITH_WOLFSSL
wolfSSL_Init();
#elif defined(OPENSSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER >= 0x10100000L
OPENSSL_init_ssl(OPENSSL_INIT_LOAD_SSL_STRINGS, NULL);
#else
SSLeay_add_ssl_algorithms(); SSLeay_add_ssl_algorithms();
SSL_load_error_strings(); SSL_load_error_strings();
#endif
_3proxy_mutex_init(&ssl_file_mutex); _3proxy_mutex_init(&ssl_file_mutex);
bio_err=BIO_new_fp(stderr,BIO_NOCLOSE);
} }
} }

View File

@ -42,6 +42,12 @@ extern "C" {
#define _3proxy_mutex_destroy pthread_mutex_destroy #define _3proxy_mutex_destroy pthread_mutex_destroy
#define _3proxy_mutex_lock pthread_mutex_lock #define _3proxy_mutex_lock pthread_mutex_lock
#define _3proxy_mutex_unlock pthread_mutex_unlock #define _3proxy_mutex_unlock pthread_mutex_unlock
typedef struct _3proxy_sem_s {
pthread_mutex_t mutex;
pthread_cond_t cond;
unsigned count;
unsigned maxcount;
} _3proxy_sem_t;
#else #else
#include <winsock2.h> #include <winsock2.h>
#include <ws2tcpip.h> #include <ws2tcpip.h>
@ -50,6 +56,7 @@ extern "C" {
#define _3proxy_mutex_lock(x) EnterCriticalSection(x) #define _3proxy_mutex_lock(x) EnterCriticalSection(x)
#define _3proxy_mutex_unlock(x) LeaveCriticalSection(x) #define _3proxy_mutex_unlock(x) LeaveCriticalSection(x)
#define _3proxy_mutex_destroy(x) DeleteCriticalSection(x) #define _3proxy_mutex_destroy(x) DeleteCriticalSection(x)
#define _3proxy_sem_t HANDLE
#ifdef MSVC #ifdef MSVC
#pragma warning (disable : 4996) #pragma warning (disable : 4996)
#endif #endif
@ -682,11 +689,7 @@ struct filemon {
struct extparam { struct extparam {
#ifdef _WIN32 _3proxy_sem_t threadinit;
HANDLE threadinit;
#else
_3proxy_mutex_t threadinit;
#endif
int *timeouts; int *timeouts;
struct ace * acl; struct ace * acl;
char * conffile; char * conffile;

View File

@ -40,14 +40,13 @@ void * tcppmchild(struct clientparam* param) {
if(action != PASS) RETURN(19); if(action != PASS) RETURN(19);
} }
if(param->redirectfunc){ if(param->redirectfunc){
return (*param->redirectfunc)(param); return (void *)param->redirectfunc;
} }
RETURN (mapsocket(param, conf.timeouts[CONNECTION_L])); RETURN (mapsocket(param, conf.timeouts[CONNECTION_L]));
CLEANRET: CLEANRET:
dolog(param, param->hostname); dolog(param, param->hostname);
freeparam(param);
return (NULL); return (NULL);
} }

View File

@ -190,7 +190,7 @@ int clistarttls(struct clientparam *param, PROXYSERVICE proto){
return 0; return 0;
} }
if(!strncasecmp((char *)buf, "EHLO ", 5)){ if(!strncasecmp((char *)buf, "EHLO ", 5)){
socksend(param, param->clisock, (unsigned char *)"250-Proxy\r\n250 STARTTLS\r\n", 24, conf.timeouts[STRING_S]); socksend(param, param->clisock, (unsigned char *)"250-Proxy\r\n250 STARTTLS\r\n", 25, conf.timeouts[STRING_S]);
continue; continue;
} }
if(!strncasecmp((char *)buf, "HELO ", 5)){ if(!strncasecmp((char *)buf, "HELO ", 5)){
@ -201,8 +201,10 @@ int clistarttls(struct clientparam *param, PROXYSERVICE proto){
socksend(param, param->clisock, (unsigned char *)"221 Proxy\r\n", 11, conf.timeouts[STRING_S]); socksend(param, param->clisock, (unsigned char *)"221 Proxy\r\n", 11, conf.timeouts[STRING_S]);
return -1; return -1;
} }
socksend(param, param->clisock, (unsigned char *)"530 5.7.0 Must issue a STARTTLS command first\r\n", 45, conf.timeouts[STRING_S]); socksend(param, param->clisock, (unsigned char *)"530 5.7.0 Must issue a STARTTLS command first\r\n", 47, conf.timeouts[STRING_S]);
} }
default:
break;
} }
return 1; return 1;
} }
@ -394,7 +396,7 @@ void * tlsprchild(struct clientparam* param) {
if(action != PASS) RETURN(19); if(action != PASS) RETURN(19);
} }
if(param->redirectfunc && param->redirectfunc != tlsprchild){ if(param->redirectfunc && param->redirectfunc != tlsprchild){
return (*param->redirectfunc)(param); return (void *)param->redirectfunc;
} }
if(stlsproto){ if(stlsproto){
@ -469,7 +471,6 @@ CLEANRET:
sprintf(req, "%sv%d.%d %s %s", lv<0?"NONE":lv?"TLS":"SSL", lv<0?0:lv?1:3, lv<0?0:lv?lv-1:0, param->hostname?(char *)param->hostname:"-", proto); sprintf(req, "%sv%d.%d %s %s", lv<0?"NONE":lv?"TLS":"SSL", lv<0?0:lv?1:3, lv<0?0:lv?lv-1:0, param->hostname?(char *)param->hostname:"-", proto);
dolog(param, (unsigned char *)req); dolog(param, (unsigned char *)req);
freeparam(param);
return (NULL); return (NULL);
} }

View File

@ -87,7 +87,6 @@ CLEANRET:
_3proxy_sem_unlock(udpinit); _3proxy_sem_unlock(udpinit);
dolog(param, NULL); dolog(param, NULL);
param->clisock = INVALID_SOCKET; param->clisock = INVALID_SOCKET;
freeparam(param);
return (NULL); return (NULL);
} }

View File

@ -615,6 +615,5 @@ CLEANRET:
if(buf) free(buf); if(buf) free(buf);
dolog(param, (unsigned char *)req); dolog(param, (unsigned char *)req);
if(req)free(req); if(req)free(req);
freeparam(param);
return (NULL); return (NULL);
} }