diff --git a/src/auto.c b/src/auto.c index 91025c2..ddf9e2c 100644 --- a/src/auto.c +++ b/src/auto.c @@ -24,13 +24,13 @@ void * autochild(struct clientparam* param) { } if(*param->clibuf == 4 || *param->clibuf == 5) { param->service = S_SOCKS; - return sockschild(param); + return (void *)sockschild; } if(*param->clibuf == 22) { param->service = S_TLSPR; - return tlsprchild(param); + return (void *)tlsprchild; } param->service = S_PROXY; - return proxychild(param); + return (void *)proxychild; } diff --git a/src/dnspr.c b/src/dnspr.c index 10fc8ed..0eddd72 100644 --- a/src/dnspr.c +++ b/src/dnspr.c @@ -222,7 +222,6 @@ CLEANRET: #ifndef _WIN32 param->clisock = INVALID_SOCKET; #endif - freeparam(param); return (NULL); } diff --git a/src/ftppr.c b/src/ftppr.c index f4a3f63..fbd2b4f 100644 --- a/src/ftppr.c +++ b/src/ftppr.c @@ -325,7 +325,6 @@ CLEANRET: } if(req) free(req); if(buf) free(buf); - freeparam(param); return (NULL); } diff --git a/src/imapp.c b/src/imapp.c index de6402e..f589251 100644 --- a/src/imapp.c +++ b/src/imapp.c @@ -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);} param->clientstarttls = S_IMAPP; if(!param->srv->targetport) param->srv->targetport = htons(143); - return tlsprchild(param); + return (void *)tlsprchild; } #endif if(!strncasecmp((char *)cmd, "LOGIN ", 6)){ @@ -238,7 +238,6 @@ CLEANRET: socksend(param, param->clisock, buf, (int)strlen((char *)buf),conf.timeouts[STRING_S]); } } - freeparam(param); return (NULL); } diff --git a/src/log.c b/src/log.c index ee73769..6749f25 100644 --- a/src/log.c +++ b/src/log.c @@ -338,7 +338,7 @@ void lognone(struct clientparam * param, const unsigned char *s) { clearstat(param); } -void logstdout(struct clientparam * param, const unsigned char *s) { +NOINLINE void logstdout(struct clientparam * param, const unsigned char *s) { FILE *log; unsigned char tmpbuf[8192]; diff --git a/src/pop3p.c b/src/pop3p.c index 933fa9c..9bc1333 100644 --- a/src/pop3p.c +++ b/src/pop3p.c @@ -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);} param->clientstarttls = S_POP3P; if(!param->srv->targetport) param->srv->targetport = htons(110); - return tlsprchild(param); + return (void *)tlsprchild; } #endif 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->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); } diff --git a/src/proxy.c b/src/proxy.c index e0b269a..f8669cb 100644 --- a/src/proxy.c +++ b/src/proxy.c @@ -864,7 +864,7 @@ for(;;){ if(isconnect && param->redirtype != R_HTTP) { if(param->redirectfunc) { freeptr(&req); freeptr(&buf); freeptr(&ftpbase); - return (*param->redirectfunc)(param); + return (void *)param->redirectfunc; } param->res = mapsocket(param, conf.timeouts[CONNECTION_L]); RETURN(param->res); @@ -1226,7 +1226,6 @@ CLEANRET: } logurl(param, (char *)buf, (char *)req, ftp); freeptr(&req); freeptr(&buf); freeptr(&ftpbase); - freeparam(param); return (NULL); } diff --git a/src/proxy.h b/src/proxy.h index b1f0b65..ad7f2d4 100644 --- a/src/proxy.h +++ b/src/proxy.h @@ -112,6 +112,18 @@ void daemonize(void); #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(). */ @@ -213,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 dobuf2(struct clientparam * param, unsigned char * buf, const unsigned char *s, const unsigned char * doublec, struct tm* tm, char * format); 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 lognone(struct clientparam * param, const unsigned char *s); void logradius(struct clientparam * param, const unsigned char *s); @@ -357,6 +369,13 @@ void * udppmchild(struct clientparam * param); void * adminchild(struct clientparam * param); void * ftpprchild(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; diff --git a/src/proxymain.c b/src/proxymain.c index 48976be..298e308 100644 --- a/src/proxymain.c +++ b/src/proxymain.c @@ -13,13 +13,19 @@ /* 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. - NULL is returned by the child which completed the request, it has already - released param and it must not be accessed after the call. + 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; - while(pf) pf = (PROXYFUNC)(*pf)(param); + 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; } @@ -133,7 +139,7 @@ void * threadfunc (void *p) { } } } - ((struct clientparam *) p)->srv->pf((struct clientparam *)p); + childfunc((struct clientparam *)p); } #ifdef _WIN32 return 0; @@ -701,7 +707,7 @@ int MODULEMAINFUNC (int argc, char** argv){ return 2; }; *newparam = defparam; - return((*srv.pf)((void *)newparam)? 1:0); + return(childfunc(newparam)? 1:0); } #endif diff --git a/src/smtpp.c b/src/smtpp.c index e196074..0dca700 100644 --- a/src/smtpp.c +++ b/src/smtpp.c @@ -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);} param->clientstarttls = S_SMTPP; if(!param->srv->targetport) param->srv->targetport = htons(587); - return tlsprchild(param); + return (void *)tlsprchild; } #endif 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(command) free(command); - freeparam(param); return (NULL); } diff --git a/src/socks.c b/src/socks.c index 7597708..99c6624 100644 --- a/src/socks.c +++ b/src/socks.c @@ -385,9 +385,8 @@ fflush(stderr); switch(command) { case 1: if(param->redirectfunc){ - void *ret = (*param->redirectfunc)(param); if(buf)free(buf); - return ret; + return (void *)param->redirectfunc; } param->res = mapsocket(param, conf.timeouts[CONNECTION_L]); break; @@ -467,7 +466,6 @@ fflush(stderr); dolog(param, buf); free(buf); } - freeparam(param); return (NULL); } diff --git a/src/ssllib.c b/src/ssllib.c index e609a0b..d7442c9 100644 --- a/src/ssllib.c +++ b/src/ssllib.c @@ -72,6 +72,18 @@ static size_t bin2hex (const unsigned char* bin, size_t bin_length, char* str, s return p - str; } +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; @@ -93,11 +105,11 @@ static int add_ext(X509 *cert, int nid, const char *value) X509_EXTENSION_free(ex); return err > 0; } +#endif SSL_CERT ssl_copy_cert(SSL_CERT cert, SSL_CONFIG *config) { int err = -1; - int san_idx; BIO *fcache; X509 *src_cert = (X509 *) cert; X509 *dst_cert = NULL; @@ -153,7 +165,11 @@ SSL_CERT ssl_copy_cert(SSL_CERT cert, SSL_CONFIG *config) X509_free(dst_cert); return NULL; } -#if !defined(WITH_WOLFSSL) && OPENSSL_VERSION_NUMBER < 0x10100000L +/* 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 + copy the time the same way. + */ +#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 @@ -163,26 +179,30 @@ SSL_CERT ssl_copy_cert(SSL_CERT cert, SSL_CONFIG *config) X509_free(dst_cert); return NULL; } - san_idx = X509_get_ext_by_NID(src_cert, NID_subject_alt_name, -1); - if(san_idx >= 0){ - X509_EXTENSION *san; - san = X509_get_ext(src_cert, san_idx); - if(san && !X509_add_ext(dst_cert, san, -1)){ - X509_free(dst_cert); - return NULL; - } - } - /* Extensions required from an end entity certificate. Without EKU - * serverAuth Apple's TLS stack (and Chrome on macOS/iOS, which uses it) - * rejects the certificate. keyUsage is intentionally not set: it depends - * on the type of the key being reused for every generated certificate, - * and an absent keyUsage places no restriction. + /* 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. */ - if(!add_ext(dst_cert, NID_basic_constraints, "critical,CA:FALSE") - || !add_ext(dst_cert, NID_ext_key_usage, "serverAuth")){ - X509_free(dst_cert); - return NULL; - } + 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()); if(!err){ X509_free(dst_cert); @@ -256,10 +276,10 @@ void _ssl_cert_free(SSL_CERT cert) #define LEGACY_SSL_THREADING 0 #endif +#if LEGACY_SSL_THREADING /* This array will store all of the mutexes available to OpenSSL. */ static _3proxy_mutex_t *mutex_buf= NULL; - static void locking_function(int mode, int n, const char * file, int line) { if (mode & CRYPTO_LOCK) @@ -276,6 +296,7 @@ static unsigned long id_function(void) return ((unsigned long)pthread_self()); #endif } +#endif int thread_setup(void) { diff --git a/src/tcppm.c b/src/tcppm.c index 3aa6929..bbd4f52 100644 --- a/src/tcppm.c +++ b/src/tcppm.c @@ -40,14 +40,13 @@ void * tcppmchild(struct clientparam* param) { if(action != PASS) RETURN(19); } if(param->redirectfunc){ - return (*param->redirectfunc)(param); + return (void *)param->redirectfunc; } RETURN (mapsocket(param, conf.timeouts[CONNECTION_L])); CLEANRET: dolog(param, param->hostname); - freeparam(param); return (NULL); } diff --git a/src/tlspr.c b/src/tlspr.c index f49d246..bc775cd 100644 --- a/src/tlspr.c +++ b/src/tlspr.c @@ -396,7 +396,7 @@ void * tlsprchild(struct clientparam* param) { if(action != PASS) RETURN(19); } if(param->redirectfunc && param->redirectfunc != tlsprchild){ - return (*param->redirectfunc)(param); + return (void *)param->redirectfunc; } if(stlsproto){ @@ -471,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); dolog(param, (unsigned char *)req); - freeparam(param); return (NULL); } diff --git a/src/udppm.c b/src/udppm.c index 454e8d1..6d6b4bb 100644 --- a/src/udppm.c +++ b/src/udppm.c @@ -87,7 +87,6 @@ CLEANRET: _3proxy_sem_unlock(udpinit); dolog(param, NULL); param->clisock = INVALID_SOCKET; - freeparam(param); return (NULL); } diff --git a/src/webadmin.c b/src/webadmin.c index 98103d4..77277c1 100644 --- a/src/webadmin.c +++ b/src/webadmin.c @@ -615,6 +615,5 @@ CLEANRET: if(buf) free(buf); dolog(param, (unsigned char *)req); if(req)free(req); - freeparam(param); return (NULL); }