Compare commits

...

No commits in common. "c8-stream-2.4" and "c9s" have entirely different histories.

176 changed files with 9323 additions and 14789 deletions

1
.fmf/version Normal file
View File

@ -0,0 +1 @@
1

47
.gitignore vendored
View File

@ -1,2 +1,45 @@
SOURCES/apache-poweredby.png
SOURCES/httpd-2.4.37.tar.bz2
httpd-2.2.16.tar.gz
/httpd-2.2.17.tar.bz2
x86_64
/httpd-2.2.17
/*.rpm
/.build*.log
/noarch
/httpd-2.2.19.tar.gz
/httpd-2.2.20.tar.gz
/httpd-2.2.21.tar.bz2
/clog
/httpd-2.2.22.tar.bz2
/httpd-2.4.??/
/httpd-2.4.2.tar.bz2
/httpd-2.4.3.tar.bz2
/httpd-2.4.4.tar.bz2
/httpd-2.4.6.tar.bz2
/httpd-2.4.7.tar.bz2
/httpd-2.4.9.tar.bz2
/httpd-2.4.10.tar.bz2
/httpd-2.4.12.tar.bz2
/httpd-2.4.16.tar.bz2
/httpd-2.4.17.tar.bz2
/httpd-2.4.18.tar.bz2
/httpd-2.4.23.tar.bz2
/httpd-2.4.25.tar.bz2
/httpd.socket.8
/httpd.service.8
/httpd-2.4.26.tar.bz2
/httpd-2.4.27.tar.bz2
/httpd-*.tar.bz2
/httpd*.8
/results_httpd
/htcacheclean.service.8
/httpd.conf.5
/httpd-2.4.41.tar.bz2.asc
/apachectl.8
/httpd-2.4.43.tar.bz2.asc
/KEYS
/httpd-2.4.46.tar.bz2.asc
/httpd-2.4.48.tar.bz2.asc
/httpd-2.4.49.tar.bz2.asc
/httpd-2.4.51.tar.bz2.asc
/httpd-2.4.53.tar.bz2.asc
/httpd-2.4.57.tar.bz2.asc

View File

@ -1,2 +1,3 @@
3a7449d6cff00e5ccb3ed8571f34c0528555d38f SOURCES/apache-poweredby.png
4a38471de821288b0300148016f2b03dfee8adf2 SOURCES/httpd-2.4.37.tar.bz2
01044512374941fad939ec4b1537428cc7edc769 httpd-2.4.57.tar.bz2
5cac6152cf2f175cc35ca0cf9d00b797c949b273 httpd-2.4.57.tar.bz2.asc
b2457e3ce46a7634bf9272a92b4214974b9bc9e0 KEYS

View File

@ -23,7 +23,6 @@ LoadModule authz_host_module modules/mod_authz_host.so
LoadModule authz_owner_module modules/mod_authz_owner.so
LoadModule authz_user_module modules/mod_authz_user.so
LoadModule autoindex_module modules/mod_autoindex.so
LoadModule brotli_module modules/mod_brotli.so
LoadModule cache_module modules/mod_cache.so
LoadModule cache_disk_module modules/mod_cache_disk.so
LoadModule cache_socache_module modules/mod_cache_socache.so
@ -55,6 +54,7 @@ LoadModule slotmem_plain_module modules/mod_slotmem_plain.so
LoadModule slotmem_shm_module modules/mod_slotmem_shm.so
LoadModule socache_dbm_module modules/mod_socache_dbm.so
LoadModule socache_memcache_module modules/mod_socache_memcache.so
LoadModule socache_redis_module modules/mod_socache_redis.so
LoadModule socache_shmcb_module modules/mod_socache_shmcb.so
LoadModule status_module modules/mod_status.so
LoadModule substitute_module modules/mod_substitute.so

1
00-brotli.conf Normal file
View File

@ -0,0 +1 @@
LoadModule brotli_module modules/mod_brotli.so

View File

@ -4,6 +4,7 @@
#
#LoadModule asis_module modules/mod_asis.so
#LoadModule authnz_fcgi_module modules/mod_authnz_fcgi.so
#LoadModule buffer_module modules/mod_buffer.so
#LoadModule heartbeat_module modules/mod_heartbeat.so
#LoadModule heartmonitor_module modules/mod_heartmonitor.so

View File

@ -2,10 +2,7 @@
# which has been configured in 00-mpm.conf. mod_cgid should be used
# with a threaded MPM; mod_cgi with the prefork MPM.
<IfModule mpm_worker_module>
LoadModule cgid_module modules/mod_cgid.so
</IfModule>
<IfModule mpm_event_module>
<IfModule !mpm_prefork_module>
LoadModule cgid_module modules/mod_cgid.so
</IfModule>
<IfModule mpm_prefork_module>

View File

@ -1,2 +0,0 @@
#!/bin/sh
exec /sbin/apachectl configtest "$@"

View File

@ -1,2 +0,0 @@
#!/bin/sh
exec /sbin/apachectl graceful "$@"

View File

@ -1,29 +0,0 @@
- Fix config for /icons/ dir to allow symlink to poweredby.png
- Avoid using coredump GIF for a directory called "core"
Upstream-Status: vendor specific patch
diff --git a/docs/conf/extra/httpd-autoindex.conf.in b/docs/conf/extra/httpd-autoindex.conf.in
index 51b02ed..dd6f2c6 100644
--- a/docs/conf/extra/httpd-autoindex.conf.in
+++ b/docs/conf/extra/httpd-autoindex.conf.in
@@ -21,7 +21,7 @@ IndexOptions FancyIndexing HTMLTable VersionSort
Alias /icons/ "@exp_iconsdir@/"
<Directory "@exp_iconsdir@">
- Options Indexes MultiViews
+ Options Indexes MultiViews FollowSymlinks
AllowOverride None
Require all granted
</Directory>
@@ -53,7 +53,8 @@ AddIcon /icons/dvi.gif .dvi
AddIcon /icons/uuencoded.gif .uu
AddIcon /icons/script.gif .conf .sh .shar .csh .ksh .tcl
AddIcon /icons/tex.gif .tex
-AddIcon /icons/bomb.gif core
+AddIcon /icons/bomb.gif /core
+AddIcon /icons/bomb.gif */core.*
AddIcon /icons/back.gif ..
AddIcon /icons/hand.right.gif README

View File

@ -1,16 +0,0 @@
diff --git a/modules/aaa/mod_access_compat.c b/modules/aaa/mod_access_compat.c
index 3023803..2edf440 100644
--- a/modules/aaa/mod_access_compat.c
+++ b/modules/aaa/mod_access_compat.c
@@ -152,6 +152,11 @@ static const char *allow_cmd(cmd_parms *cmd, void *dv, const char *from,
if (strcasecmp(from, "from"))
return "allow and deny must be followed by 'from'";
+ s = ap_strchr(where, '#');
+ if (s) {
+ *s = '\0';
+ }
+
a = (allowdeny *) apr_array_push(cmd->info ? d->allows : d->denys);
a->x.from = where;
a->limited = cmd->limited;

View File

@ -1,22 +0,0 @@
There is no need to "suck in" the apr/apr-util symbols when using
a shared libapr{,util}, it just bloats the symbol table; so don't.
Upstream-HEAD: needed
Upstream-2.0: omit
Upstream-Status: EXPORT_DIRS change is conditional on using shared apr
diff --git a/server/Makefile.in b/server/Makefile.in
index 1fa3344..f635d76 100644
--- a/server/Makefile.in
+++ b/server/Makefile.in
@@ -60,9 +60,6 @@ export_files:
ls $$dir/*.h ; \
done; \
echo "$(top_srcdir)/server/mpm_fdqueue.h"; \
- for dir in $(EXPORT_DIRS_APR); do \
- ls $$dir/ap[ru].h $$dir/ap[ru]_*.h 2>/dev/null; \
- done; \
) | sed -e s,//,/,g | sort -u > $@
exports.c: export_files

View File

@ -1,19 +0,0 @@
diff --git a/server/mpm/event/event.c b/server/mpm/event/event.c
index 16e39be..2543693 100644
--- a/server/mpm/event/event.c
+++ b/server/mpm/event/event.c
@@ -1111,10 +1111,11 @@ read_request:
"network write failure in core output filter");
cs->pub.state = CONN_STATE_LINGER;
}
- else if (c->data_in_output_filters) {
+ else if (c->data_in_output_filters ||
+ cs->pub.sense == CONN_SENSE_WANT_READ) {
/* Still in WRITE_COMPLETION_STATE:
- * Set a write timeout for this connection, and let the
- * event thread poll for writeability.
+ * Set a read/write timeout for this connection, and let the
+ * event thread poll for read/writeability.
*/
cs->queue_timestamp = apr_time_now();
notify_suspend(cs);

View File

@ -1,113 +0,0 @@
diff --git a/docs/man/apachectl.8 b/docs/man/apachectl.8
index 870a048..32d3ee5 100644
--- a/docs/man/apachectl.8
+++ b/docs/man/apachectl.8
@@ -74,7 +74,7 @@ Restarts the Apache httpd daemon\&. If the daemon is not running, it is started\
Displays a full status report from mod_status\&. For this to work, you need to have mod_status enabled on your server and a text-based browser such as \fBlynx\fR available on your system\&. The URL used to access the status report can be set by editing the \fBSTATUSURL\fR variable in the script\&.
.TP
\fBstatus\fR
-Displays a brief status report\&. Similar to the \fBfullstatus\fR option, except that the list of requests currently being served is omitted\&.
+Displays a brief status report using systemd\&.
.TP
\fBgraceful\fR
Gracefully restarts the Apache httpd daemon\&. If the daemon is not running, it is started\&. This differs from a normal restart in that currently open connections are not aborted\&. A side effect is that old log files will not be closed immediately\&. This means that if used in a log rotation script, a substantial delay may be necessary to ensure that the old log files are closed before processing them\&. This command automatically checks the configuration files as in \fBconfigtest\fR before initiating the restart to make sure Apache doesn't die\&. This is equivalent to \fBapachectl -k graceful\fR\&.
diff --git a/support/apachectl.in b/support/apachectl.in
index 3281c2e..8ce6f2b 100644
--- a/support/apachectl.in
+++ b/support/apachectl.in
@@ -44,19 +44,20 @@ ARGV="$@"
# the path to your httpd binary, including options if necessary
HTTPD='@exp_sbindir@/@progname@'
#
-# pick up any necessary environment variables
-if test -f @exp_sbindir@/envvars; then
- . @exp_sbindir@/envvars
-fi
#
# a command that outputs a formatted text version of the HTML at the
# url given on the command line. Designed for lynx, however other
# programs may work.
-LYNX="@LYNX_PATH@ -dump"
+if [ -x "@LYNX_PATH@" ]; then
+ LYNX="@LYNX_PATH@ -dump"
+else
+ LYNX=none
+fi
#
# the URL to your server's mod_status status page. If you do not
# have one, then status and fullstatus will not work.
STATUSURL="http://localhost:@PORT@/server-status"
+
#
# Set this variable to a command that increases the maximum
# number of file descriptors allowed per child process. This is
@@ -76,9 +77,46 @@ if [ "x$ARGV" = "x" ] ; then
ARGV="-h"
fi
+function checklynx() {
+if [ "$LYNX" = "none" ]; then
+ echo "The 'links' package is required for this functionality."
+ exit 8
+fi
+}
+
+function testconfig() {
+# httpd is denied terminal access in SELinux, so run in the
+# current context to get stdout from $HTTPD -t.
+if test -x /usr/sbin/selinuxenabled && /usr/sbin/selinuxenabled; then
+ runcon -- `id -Z` /usr/sbin/httpd $OPTIONS -t
+else
+ /usr/sbin/httpd $OPTIONS -t
+fi
+ERROR=$?
+}
+
+if [ "x$2" != "x" ] ; then
+ echo Passing arguments to httpd using apachectl is no longer supported.
+ echo You can only start/stop/restart httpd using this script.
+ echo If you want to pass extra arguments to httpd, edit the
+ echo /etc/sysconfig/httpd config file.
+fi
+
case $ACMD in
-start|stop|restart|graceful|graceful-stop)
- $HTTPD -k $ARGV
+start|stop|restart|status)
+ /usr/bin/systemctl $ACMD httpd.service
+ ERROR=$?
+ ;;
+graceful)
+ if /usr/bin/systemctl -q is-active httpd.service; then
+ /usr/bin/systemctl reload httpd.service
+ else
+ /usr/bin/systemctl start httpd.service
+ fi
+ ERROR=$?
+ ;;
+graceful-stop)
+ /usr/bin/systemctl stop httpd.service
ERROR=$?
;;
startssl|sslstart|start-SSL)
@@ -88,17 +126,14 @@ startssl|sslstart|start-SSL)
ERROR=2
;;
configtest)
- $HTTPD -t
- ERROR=$?
- ;;
-status)
- $LYNX $STATUSURL | awk ' /process$/ { print; exit } { print } '
+ testconfig
;;
fullstatus)
+ checklynx
$LYNX $STATUSURL
;;
*)
- $HTTPD "$@"
+ /usr/sbin/httpd $OPTIONS "$@"
ERROR=$?
esac

View File

@ -1,77 +0,0 @@
diff --git a/Makefile.in b/Makefile.in
index ea8366e..06b8c5a 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -4,7 +4,7 @@ CLEAN_SUBDIRS = test
PROGRAM_NAME = $(progname)
PROGRAM_SOURCES = modules.c
-PROGRAM_LDADD = buildmark.o $(HTTPD_LDFLAGS) $(PROGRAM_DEPENDENCIES) $(PCRE_LIBS) $(EXTRA_LIBS) $(AP_LIBS) $(LIBS)
+PROGRAM_LDADD = buildmark.o $(HTTPD_LDFLAGS) $(PROGRAM_DEPENDENCIES) $(HTTPD_LIBS) $(EXTRA_LIBS) $(AP_LIBS) $(LIBS)
PROGRAM_PRELINK = $(COMPILE) -c $(top_srcdir)/server/buildmark.c
PROGRAM_DEPENDENCIES = \
server/libmain.la \
diff --git a/acinclude.m4 b/acinclude.m4
index ce1d637..0ad0c13 100644
--- a/acinclude.m4
+++ b/acinclude.m4
@@ -606,6 +606,30 @@ AC_DEFUN([APACHE_CHECK_OPENSSL],[
fi
])
+AC_DEFUN(APACHE_CHECK_SYSTEMD, [
+dnl Check for systemd support for listen.c's socket activation.
+case $host in
+*-linux-*)
+ if test -n "$PKGCONFIG" && $PKGCONFIG --exists libsystemd; then
+ SYSTEMD_LIBS=`$PKGCONFIG --libs libsystemd`
+ elif test -n "$PKGCONFIG" && $PKGCONFIG --exists libsystemd-daemon; then
+ SYSTEMD_LIBS=`$PKGCONFIG --libs libsystemd-daemon`
+ else
+ AC_CHECK_LIB(systemd-daemon, sd_notify, SYSTEMD_LIBS="-lsystemd-daemon")
+ fi
+ if test -n "$SYSTEMD_LIBS"; then
+ AC_CHECK_HEADERS(systemd/sd-daemon.h)
+ if test "${ac_cv_header_systemd_sd_daemon_h}" = "no" || test -z "${SYSTEMD_LIBS}"; then
+ AC_MSG_WARN([Your system does not support systemd.])
+ else
+ APR_ADDTO(HTTPD_LIBS, [$SYSTEMD_LIBS])
+ AC_DEFINE(HAVE_SYSTEMD, 1, [Define if systemd is supported])
+ fi
+ fi
+ ;;
+esac
+])
+
dnl
dnl APACHE_EXPORT_ARGUMENTS
dnl Export (via APACHE_SUBST) the various path-related variables that
diff --git a/configure.in b/configure.in
index 82bfeef..eedba50 100644
--- a/configure.in
+++ b/configure.in
@@ -234,6 +234,7 @@ if test "$PCRE_CONFIG" != "false"; then
AC_MSG_NOTICE([Using external PCRE library from $PCRE_CONFIG])
APR_ADDTO(PCRE_INCLUDES, [`$PCRE_CONFIG --cflags`])
APR_ADDTO(PCRE_LIBS, [`$PCRE_CONFIG --libs`])
+ APR_ADDTO(HTTPD_LIBS, [\$(PCRE_LIBS)])
else
AC_MSG_ERROR([pcre-config for libpcre not found. PCRE is required and available from http://pcre.org/])
fi
@@ -504,6 +505,8 @@ if test "$ac_cv_struct_tm_gmtoff" = "yes"; then
AC_DEFINE(HAVE_GMTOFF, 1, [Define if struct tm has a tm_gmtoff field])
fi
+APACHE_CHECK_SYSTEMD
+
dnl ## Set up any appropriate OS-specific environment variables for apachectl
case $host in
@@ -677,6 +680,7 @@ APACHE_SUBST(OS_DIR)
APACHE_SUBST(BUILTIN_LIBS)
APACHE_SUBST(SHLIBPATH_VAR)
APACHE_SUBST(OS_SPECIFIC_VARS)
+APACHE_SUBST(HTTPD_LIBS)
PRE_SHARED_CMDS='echo ""'
POST_SHARED_CMDS='echo ""'

View File

@ -1,15 +0,0 @@
diff --git a/modules/ssl/ssl_engine_kernel.c b/modules/ssl/ssl_engine_kernel.c
index a5e86e4..6611610 100644
--- a/modules/ssl/ssl_engine_kernel.c
+++ b/modules/ssl/ssl_engine_kernel.c
@@ -1823,8 +1823,8 @@ int ssl_callback_SSLVerify(int ok, X509_STORE_CTX *ctx)
/*
* Perform OCSP-based revocation checks
*/
- if (ok && ((sc->server->ocsp_mask & SSL_OCSPCHECK_CHAIN) ||
- (errdepth == 0 && (sc->server->ocsp_mask & SSL_OCSPCHECK_LEAF)))) {
+ if (ok && ((mctx->ocsp_mask & SSL_OCSPCHECK_CHAIN) ||
+ (errdepth == 0 && (mctx->ocsp_mask & SSL_OCSPCHECK_LEAF)))) {
/* If there was an optional verification error, it's not
* possible to perform OCSP validation since the issuer may be
* missing/untrusted. Fail in that case. */

View File

@ -1,16 +0,0 @@
diff --git a/modules/ssl/ssl_engine_io.c b/modules/ssl/ssl_engine_io.c
index d52d5e3..8a57659 100644
--- a/modules/ssl/ssl_engine_io.c
+++ b/modules/ssl/ssl_engine_io.c
@@ -1415,6 +1415,11 @@ static apr_status_t ssl_io_filter_handshake(ssl_filter_ctx_t *filter_ctx)
"\"SSLVerifyClient optional_no_ca\" "
"configuration");
ssl_log_ssl_error(SSLLOG_MARK, APLOG_INFO, server);
+
+ /* on session resumption ssl_callback_SSLVerify()
+ * will not be called, therefore we have to set it here
+ */
+ sslconn->verify_info = "GENEROUS";
}
else {
const char *error = sslconn->verify_error ?

View File

@ -1,140 +0,0 @@
diff --git a/modules/proxy/ajp.h b/modules/proxy/ajp.h
index c119a7e..267150a 100644
--- a/modules/proxy/ajp.h
+++ b/modules/proxy/ajp.h
@@ -413,12 +413,14 @@ apr_status_t ajp_ilink_receive(apr_socket_t *sock, ajp_msg_t *msg);
* @param sock backend socket
* @param r current request
* @param buffsize max size of the AJP packet.
+ * @param secret authentication secret
* @param uri requested uri
* @return APR_SUCCESS or error
*/
apr_status_t ajp_send_header(apr_socket_t *sock, request_rec *r,
apr_size_t buffsize,
- apr_uri_t *uri);
+ apr_uri_t *uri,
+ const char *secret);
/**
* Read the ajp message and return the type of the message.
diff --git a/modules/proxy/ajp_header.c b/modules/proxy/ajp_header.c
index 67353a7..680a8f3 100644
--- a/modules/proxy/ajp_header.c
+++ b/modules/proxy/ajp_header.c
@@ -213,7 +213,8 @@ AJPV13_REQUEST/AJPV14_REQUEST=
static apr_status_t ajp_marshal_into_msgb(ajp_msg_t *msg,
request_rec *r,
- apr_uri_t *uri)
+ apr_uri_t *uri,
+ const char *secret)
{
int method;
apr_uint32_t i, num_headers = 0;
@@ -293,17 +294,15 @@ static apr_status_t ajp_marshal_into_msgb(ajp_msg_t *msg,
i, elts[i].key, elts[i].val);
}
-/* XXXX need to figure out how to do this
- if (s->secret) {
+ if (secret) {
if (ajp_msg_append_uint8(msg, SC_A_SECRET) ||
- ajp_msg_append_string(msg, s->secret)) {
+ ajp_msg_append_string(msg, secret)) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(03228)
- "Error ajp_marshal_into_msgb - "
+ "ajp_marshal_into_msgb: "
"Error appending secret");
return APR_EGENERAL;
}
}
- */
if (r->user) {
if (ajp_msg_append_uint8(msg, SC_A_REMOTE_USER) ||
@@ -671,7 +670,8 @@ static apr_status_t ajp_unmarshal_response(ajp_msg_t *msg,
apr_status_t ajp_send_header(apr_socket_t *sock,
request_rec *r,
apr_size_t buffsize,
- apr_uri_t *uri)
+ apr_uri_t *uri,
+ const char *secret)
{
ajp_msg_t *msg;
apr_status_t rc;
@@ -683,7 +683,7 @@ apr_status_t ajp_send_header(apr_socket_t *sock,
return rc;
}
- rc = ajp_marshal_into_msgb(msg, r, uri);
+ rc = ajp_marshal_into_msgb(msg, r, uri, secret);
if (rc != APR_SUCCESS) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00988)
"ajp_send_header: ajp_marshal_into_msgb failed");
diff --git a/modules/proxy/mod_proxy.c b/modules/proxy/mod_proxy.c
index 69a35ce..800ede1 100644
--- a/modules/proxy/mod_proxy.c
+++ b/modules/proxy/mod_proxy.c
@@ -327,6 +327,12 @@ static const char *set_worker_param(apr_pool_t *p,
worker->s->response_field_size = (s ? s : HUGE_STRING_LEN);
worker->s->response_field_size_set = 1;
}
+ else if (!strcasecmp(key, "secret")) {
+ if (PROXY_STRNCPY(worker->s->secret, val) != APR_SUCCESS) {
+ return apr_psprintf(p, "Secret length must be < %d characters",
+ (int)sizeof(worker->s->secret));
+ }
+ }
else {
if (set_worker_hc_param_f) {
return set_worker_hc_param_f(p, s, worker, key, val, NULL);
diff --git a/modules/proxy/mod_proxy.h b/modules/proxy/mod_proxy.h
index aabd09f..3419023 100644
--- a/modules/proxy/mod_proxy.h
+++ b/modules/proxy/mod_proxy.h
@@ -357,6 +357,7 @@ PROXY_WORKER_HC_FAIL )
#define PROXY_WORKER_MAX_HOSTNAME_SIZE 64
#define PROXY_BALANCER_MAX_HOSTNAME_SIZE PROXY_WORKER_MAX_HOSTNAME_SIZE
#define PROXY_BALANCER_MAX_STICKY_SIZE 64
+#define PROXY_WORKER_MAX_SECRET_SIZE 64
#define PROXY_RFC1035_HOSTNAME_SIZE 256
@@ -450,6 +451,7 @@ typedef struct {
hcmethod_t method; /* method to use for health check */
apr_interval_time_t interval;
char upgrade[PROXY_WORKER_MAX_SCHEME_SIZE];/* upgrade protocol used by mod_proxy_wstunnel */
+ char secret[PROXY_WORKER_MAX_SECRET_SIZE]; /* authentication secret (e.g. AJP13) */
char hostname_ex[PROXY_RFC1035_HOSTNAME_SIZE]; /* RFC1035 compliant version of the remote backend address */
apr_size_t response_field_size; /* Size of proxy response buffer in bytes. */
unsigned int response_field_size_set:1;
diff --git a/modules/proxy/mod_proxy_ajp.c b/modules/proxy/mod_proxy_ajp.c
index 73716af..6faabea 100644
--- a/modules/proxy/mod_proxy_ajp.c
+++ b/modules/proxy/mod_proxy_ajp.c
@@ -193,6 +193,7 @@ static int ap_proxy_ajp_request(apr_pool_t *p, request_rec *r,
apr_off_t content_length = 0;
int original_status = r->status;
const char *original_status_line = r->status_line;
+ const char *secret = NULL;
if (psf->io_buffer_size_set)
maxsize = psf->io_buffer_size;
@@ -202,12 +203,15 @@ static int ap_proxy_ajp_request(apr_pool_t *p, request_rec *r,
maxsize = AJP_MSG_BUFFER_SZ;
maxsize = APR_ALIGN(maxsize, 1024);
+ if (*conn->worker->s->secret)
+ secret = conn->worker->s->secret;
+
/*
* Send the AJP request to the remote server
*/
/* send request headers */
- status = ajp_send_header(conn->sock, r, maxsize, uri);
+ status = ajp_send_header(conn->sock, r, maxsize, uri, secret);
if (status != APR_SUCCESS) {
conn->close = 1;
ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, APLOGNO(00868)

View File

@ -1,708 +0,0 @@
# ./pullrev.sh 1830819 1830836 1830912 1830913 1830927 1831168 1831173
http://svn.apache.org/viewvc?view=revision&revision=1830819
http://svn.apache.org/viewvc?view=revision&revision=1830912
http://svn.apache.org/viewvc?view=revision&revision=1830913
http://svn.apache.org/viewvc?view=revision&revision=1830927
http://svn.apache.org/viewvc?view=revision&revision=1831168
http://svn.apache.org/viewvc?view=revision&revision=1831173
http://svn.apache.org/viewvc?view=revision&revision=1835240
http://svn.apache.org/viewvc?view=revision&revision=1835242
http://svn.apache.org/viewvc?view=revision&revision=1835615
diff --git a/modules/ssl/ssl_engine_config.c b/modules/ssl/ssl_engine_config.c
index 43397f9..ff8f429 100644
--- httpd-2.4.35/modules/ssl/ssl_engine_config.c.r1830819+
+++ httpd-2.4.35/modules/ssl/ssl_engine_config.c
@@ -899,7 +899,9 @@
SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
const char *err;
- if ((err = ssl_cmd_check_file(cmd, &arg))) {
+ /* Only check for non-ENGINE based certs. */
+ if (!modssl_is_engine_id(arg)
+ && (err = ssl_cmd_check_file(cmd, &arg))) {
return err;
}
@@ -915,7 +917,9 @@
SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
const char *err;
- if ((err = ssl_cmd_check_file(cmd, &arg))) {
+ /* Check keyfile exists for non-ENGINE keys. */
+ if (!modssl_is_engine_id(arg)
+ && (err = ssl_cmd_check_file(cmd, &arg))) {
return err;
}
--- httpd-2.4.35/modules/ssl/ssl_engine_init.c.r1830819+
+++ httpd-2.4.35/modules/ssl/ssl_engine_init.c
@@ -1186,12 +1186,18 @@
(certfile = APR_ARRAY_IDX(mctx->pks->cert_files, i,
const char *));
i++) {
+ EVP_PKEY *pkey;
+ const char *engine_certfile = NULL;
+
key_id = apr_psprintf(ptemp, "%s:%d", vhost_id, i);
ERR_clear_error();
/* first the certificate (public key) */
- if (mctx->cert_chain) {
+ if (modssl_is_engine_id(certfile)) {
+ engine_certfile = certfile;
+ }
+ else if (mctx->cert_chain) {
if ((SSL_CTX_use_certificate_file(mctx->ssl_ctx, certfile,
SSL_FILETYPE_PEM) < 1)) {
ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(02561)
@@ -1220,12 +1226,46 @@
ERR_clear_error();
- if ((SSL_CTX_use_PrivateKey_file(mctx->ssl_ctx, keyfile,
- SSL_FILETYPE_PEM) < 1) &&
- (ERR_GET_FUNC(ERR_peek_last_error())
- != X509_F_X509_CHECK_PRIVATE_KEY)) {
+ if (modssl_is_engine_id(keyfile)) {
+ apr_status_t rv;
+
+ cert = NULL;
+
+ if ((rv = modssl_load_engine_keypair(s, ptemp, vhost_id,
+ engine_certfile, keyfile,
+ &cert, &pkey))) {
+ return rv;
+ }
+
+ if (cert) {
+ if (SSL_CTX_use_certificate(mctx->ssl_ctx, cert) < 1) {
+ ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(10137)
+ "Failed to configure engine certificate %s, check %s",
+ key_id, certfile);
+ ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s);
+ return APR_EGENERAL;
+ }
+
+ /* SSL_CTX now owns the cert. */
+ X509_free(cert);
+ }
+
+ if (SSL_CTX_use_PrivateKey(mctx->ssl_ctx, pkey) < 1) {
+ ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(10130)
+ "Failed to configure private key %s from engine",
+ keyfile);
+ ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s);
+ return APR_EGENERAL;
+ }
+
+ /* SSL_CTX now owns the key */
+ EVP_PKEY_free(pkey);
+ }
+ else if ((SSL_CTX_use_PrivateKey_file(mctx->ssl_ctx, keyfile,
+ SSL_FILETYPE_PEM) < 1)
+ && (ERR_GET_FUNC(ERR_peek_last_error())
+ != X509_F_X509_CHECK_PRIVATE_KEY)) {
ssl_asn1_t *asn1;
- EVP_PKEY *pkey;
const unsigned char *ptr;
ERR_clear_error();
@@ -1312,8 +1352,9 @@
/*
* Try to read DH parameters from the (first) SSLCertificateFile
*/
- if ((certfile = APR_ARRAY_IDX(mctx->pks->cert_files, 0, const char *)) &&
- (dhparams = ssl_dh_GetParamFromFile(certfile))) {
+ certfile = APR_ARRAY_IDX(mctx->pks->cert_files, 0, const char *);
+ if (certfile && !modssl_is_engine_id(certfile)
+ && (dhparams = ssl_dh_GetParamFromFile(certfile))) {
SSL_CTX_set_tmp_dh(mctx->ssl_ctx, dhparams);
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(02540)
"Custom DH parameters (%d bits) for %s loaded from %s",
@@ -1325,10 +1366,10 @@
/*
* Similarly, try to read the ECDH curve name from SSLCertificateFile...
*/
- if ((certfile != NULL) &&
- (ecparams = ssl_ec_GetParamFromFile(certfile)) &&
- (nid = EC_GROUP_get_curve_name(ecparams)) &&
- (eckey = EC_KEY_new_by_curve_name(nid))) {
+ if (certfile && !modssl_is_engine_id(certfile)
+ && (ecparams = ssl_ec_GetParamFromFile(certfile))
+ && (nid = EC_GROUP_get_curve_name(ecparams))
+ && (eckey = EC_KEY_new_by_curve_name(nid))) {
SSL_CTX_set_tmp_ecdh(mctx->ssl_ctx, eckey);
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(02541)
"ECDH curve %s for %s specified in %s",
--- httpd-2.4.35/modules/ssl/ssl_engine_pphrase.c.r1830819+
+++ httpd-2.4.35/modules/ssl/ssl_engine_pphrase.c
@@ -143,9 +143,6 @@
const char *key_id = asn1_table_vhost_key(mc, p, sc->vhost_id, idx);
EVP_PKEY *pPrivateKey = NULL;
ssl_asn1_t *asn1;
- unsigned char *ucp;
- long int length;
- BOOL bReadable;
int nPassPhrase = (*pphrases)->nelts;
int nPassPhraseRetry = 0;
apr_time_t pkey_mtime = 0;
@@ -222,16 +219,12 @@
* is not empty. */
ERR_clear_error();
- bReadable = ((pPrivateKey = modssl_read_privatekey(ppcb_arg.pkey_file,
- NULL, ssl_pphrase_Handle_CB, &ppcb_arg)) != NULL ?
- TRUE : FALSE);
-
- /*
- * when the private key file now was readable,
- * it's fine and we go out of the loop
- */
- if (bReadable)
- break;
+ pPrivateKey = modssl_read_privatekey(ppcb_arg.pkey_file,
+ ssl_pphrase_Handle_CB, &ppcb_arg);
+ /* If the private key was successfully read, nothing more to
+ do here. */
+ if (pPrivateKey != NULL)
+ break;
/*
* when we have more remembered pass phrases
@@ -356,19 +349,12 @@
nPassPhrase++;
}
- /*
- * Insert private key into the global module configuration
- * (we convert it to a stand-alone DER byte sequence
- * because the SSL library uses static variables inside a
- * RSA structure which do not survive DSO reloads!)
- */
- length = i2d_PrivateKey(pPrivateKey, NULL);
- ucp = ssl_asn1_table_set(mc->tPrivateKey, key_id, length);
- (void)i2d_PrivateKey(pPrivateKey, &ucp); /* 2nd arg increments */
+ /* Cache the private key in the global module configuration so it
+ * can be used after subsequent reloads. */
+ asn1 = ssl_asn1_table_set(mc->tPrivateKey, key_id, pPrivateKey);
if (ppcb_arg.nPassPhraseDialogCur != 0) {
/* remember mtime of encrypted keys */
- asn1 = ssl_asn1_table_get(mc->tPrivateKey, key_id);
asn1->source_mtime = pkey_mtime;
}
@@ -619,3 +605,303 @@
*/
return (len);
}
+
+
+#if defined(HAVE_OPENSSL_ENGINE_H) && defined(HAVE_ENGINE_INIT)
+
+/* OpenSSL UI implementation for passphrase entry; largely duplicated
+ * from ssl_pphrase_Handle_CB but adjusted for UI API. TODO: Might be
+ * worth trying to shift pphrase handling over to the UI API
+ * completely. */
+static int passphrase_ui_open(UI *ui)
+{
+ pphrase_cb_arg_t *ppcb = UI_get0_user_data(ui);
+ SSLSrvConfigRec *sc = mySrvConfig(ppcb->s);
+
+ ppcb->nPassPhraseDialog++;
+ ppcb->nPassPhraseDialogCur++;
+
+ /*
+ * Builtin or Pipe dialog
+ */
+ if (sc->server->pphrase_dialog_type == SSL_PPTYPE_BUILTIN
+ || sc->server->pphrase_dialog_type == SSL_PPTYPE_PIPE) {
+ if (sc->server->pphrase_dialog_type == SSL_PPTYPE_PIPE) {
+ if (!readtty) {
+ ap_log_error(APLOG_MARK, APLOG_INFO, 0, ppcb->s,
+ APLOGNO(10143)
+ "Init: Creating pass phrase dialog pipe child "
+ "'%s'", sc->server->pphrase_dialog_path);
+ if (ssl_pipe_child_create(ppcb->p,
+ sc->server->pphrase_dialog_path)
+ != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, ppcb->s,
+ APLOGNO(10144)
+ "Init: Failed to create pass phrase pipe '%s'",
+ sc->server->pphrase_dialog_path);
+ return 0;
+ }
+ }
+ ap_log_error(APLOG_MARK, APLOG_INFO, 0, ppcb->s, APLOGNO(10145)
+ "Init: Requesting pass phrase via piped dialog");
+ }
+ else { /* sc->server->pphrase_dialog_type == SSL_PPTYPE_BUILTIN */
+#ifdef WIN32
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, ppcb->s, APLOGNO(10146)
+ "Init: Failed to create pass phrase pipe '%s'",
+ sc->server->pphrase_dialog_path);
+ return 0;
+#else
+ /*
+ * stderr has already been redirected to the error_log.
+ * rather than attempting to temporarily rehook it to the terminal,
+ * we print the prompt to stdout before EVP_read_pw_string turns
+ * off tty echo
+ */
+ apr_file_open_stdout(&writetty, ppcb->p);
+
+ ap_log_error(APLOG_MARK, APLOG_INFO, 0, ppcb->s, APLOGNO(10147)
+ "Init: Requesting pass phrase via builtin terminal "
+ "dialog");
+#endif
+ }
+
+ /*
+ * The first time display a header to inform the user about what
+ * program he actually speaks to, which module is responsible for
+ * this terminal dialog and why to the hell he has to enter
+ * something...
+ */
+ if (ppcb->nPassPhraseDialog == 1) {
+ apr_file_printf(writetty, "%s mod_ssl (Pass Phrase Dialog)\n",
+ AP_SERVER_BASEVERSION);
+ apr_file_printf(writetty,
+ "A pass phrase is required to access the private key.\n");
+ }
+ if (ppcb->bPassPhraseDialogOnce) {
+ ppcb->bPassPhraseDialogOnce = FALSE;
+ apr_file_printf(writetty, "\n");
+ apr_file_printf(writetty, "Private key %s (%s)\n",
+ ppcb->key_id, ppcb->pkey_file);
+ }
+ }
+
+ return 1;
+}
+
+static int passphrase_ui_read(UI *ui, UI_STRING *uis)
+{
+ pphrase_cb_arg_t *ppcb = UI_get0_user_data(ui);
+ SSLSrvConfigRec *sc = mySrvConfig(ppcb->s);
+ const char *prompt;
+ int i;
+ int bufsize;
+ int len;
+ char *buf;
+
+ prompt = UI_get0_output_string(uis);
+ if (prompt == NULL) {
+ prompt = "Enter pass phrase:";
+ }
+
+ /*
+ * Get the maximum expected size and allocate the buffer
+ */
+ bufsize = UI_get_result_maxsize(uis);
+ buf = apr_pcalloc(ppcb->p, bufsize);
+
+ if (sc->server->pphrase_dialog_type == SSL_PPTYPE_BUILTIN
+ || sc->server->pphrase_dialog_type == SSL_PPTYPE_PIPE) {
+ /*
+ * Get the pass phrase through a callback.
+ * Empty input is not accepted.
+ */
+ for (;;) {
+ if (sc->server->pphrase_dialog_type == SSL_PPTYPE_PIPE) {
+ i = pipe_get_passwd_cb(buf, bufsize, "", FALSE);
+ }
+ else { /* sc->server->pphrase_dialog_type == SSL_PPTYPE_BUILTIN */
+ i = EVP_read_pw_string(buf, bufsize, "", FALSE);
+ }
+ if (i != 0) {
+ OPENSSL_cleanse(buf, bufsize);
+ return 0;
+ }
+ len = strlen(buf);
+ if (len < 1){
+ apr_file_printf(writetty, "Apache:mod_ssl:Error: Pass phrase"
+ "empty (needs to be at least 1 character).\n");
+ apr_file_puts(prompt, writetty);
+ }
+ else {
+ break;
+ }
+ }
+ }
+ /*
+ * Filter program
+ */
+ else if (sc->server->pphrase_dialog_type == SSL_PPTYPE_FILTER) {
+ const char *cmd = sc->server->pphrase_dialog_path;
+ const char **argv = apr_palloc(ppcb->p, sizeof(char *) * 3);
+ char *result;
+
+ ap_log_error(APLOG_MARK, APLOG_INFO, 0, ppcb->s, APLOGNO(10148)
+ "Init: Requesting pass phrase from dialog filter "
+ "program (%s)", cmd);
+
+ argv[0] = cmd;
+ argv[1] = ppcb->key_id;
+ argv[2] = NULL;
+
+ result = ssl_util_readfilter(ppcb->s, ppcb->p, cmd, argv);
+ apr_cpystrn(buf, result, bufsize);
+ len = strlen(buf);
+ }
+
+ /*
+ * Ok, we now have the pass phrase, so give it back
+ */
+ ppcb->cpPassPhraseCur = apr_pstrdup(ppcb->p, buf);
+ UI_set_result(ui, uis, buf);
+
+ /* Clear sensitive data. */
+ OPENSSL_cleanse(buf, bufsize);
+ return 1;
+}
+
+static int passphrase_ui_write(UI *ui, UI_STRING *uis)
+{
+ pphrase_cb_arg_t *ppcb = UI_get0_user_data(ui);
+ SSLSrvConfigRec *sc;
+ const char *prompt;
+
+ sc = mySrvConfig(ppcb->s);
+
+ if (sc->server->pphrase_dialog_type == SSL_PPTYPE_BUILTIN
+ || sc->server->pphrase_dialog_type == SSL_PPTYPE_PIPE) {
+ prompt = UI_get0_output_string(uis);
+ apr_file_puts(prompt, writetty);
+ }
+
+ return 1;
+}
+
+static int passphrase_ui_close(UI *ui)
+{
+ /*
+ * Close the pipes if they were opened
+ */
+ if (readtty) {
+ apr_file_close(readtty);
+ apr_file_close(writetty);
+ readtty = writetty = NULL;
+ }
+ return 1;
+}
+
+static apr_status_t pp_ui_method_cleanup(void *uip)
+{
+ UI_METHOD *uim = uip;
+
+ UI_destroy_method(uim);
+
+ return APR_SUCCESS;
+}
+
+static UI_METHOD *get_passphrase_ui(apr_pool_t *p)
+{
+ UI_METHOD *ui_method = UI_create_method("Passphrase UI");
+
+ UI_method_set_opener(ui_method, passphrase_ui_open);
+ UI_method_set_reader(ui_method, passphrase_ui_read);
+ UI_method_set_writer(ui_method, passphrase_ui_write);
+ UI_method_set_closer(ui_method, passphrase_ui_close);
+
+ apr_pool_cleanup_register(p, ui_method, pp_ui_method_cleanup,
+ pp_ui_method_cleanup);
+
+ return ui_method;
+}
+
+
+apr_status_t modssl_load_engine_keypair(server_rec *s, apr_pool_t *p,
+ const char *vhostid,
+ const char *certid, const char *keyid,
+ X509 **pubkey, EVP_PKEY **privkey)
+{
+ const char *c, *scheme;
+ ENGINE *e;
+ UI_METHOD *ui_method = get_passphrase_ui(p);
+ pphrase_cb_arg_t ppcb;
+
+ memset(&ppcb, 0, sizeof ppcb);
+ ppcb.s = s;
+ ppcb.p = p;
+ ppcb.bPassPhraseDialogOnce = TRUE;
+ ppcb.key_id = vhostid;
+ ppcb.pkey_file = keyid;
+
+ c = ap_strchr_c(keyid, ':');
+ if (!c || c == keyid) {
+ ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(10131)
+ "Init: Unrecognized private key identifier `%s'",
+ keyid);
+ return ssl_die(s);
+ }
+
+ scheme = apr_pstrmemdup(p, keyid, c - keyid);
+ if (!(e = ENGINE_by_id(scheme))) {
+ ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(10132)
+ "Init: Failed to load engine for private key %s",
+ keyid);
+ ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s);
+ return ssl_die(s);
+ }
+
+ if (!ENGINE_init(e)) {
+ ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(10149)
+ "Init: Failed to initialize engine %s for private key %s",
+ scheme, keyid);
+ ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s);
+ return ssl_die(s);
+ }
+
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
+ "Init: Initialized engine %s for private key %s",
+ scheme, keyid);
+
+ if (APLOGdebug(s)) {
+ ENGINE_ctrl_cmd_string(e, "VERBOSE", NULL, 0);
+ }
+
+ if (certid) {
+ struct {
+ const char *cert_id;
+ X509 *cert;
+ } params = { certid, NULL };
+
+ if (!ENGINE_ctrl_cmd(e, "LOAD_CERT_CTRL", 0, &params, NULL, 1)) {
+ ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(10136)
+ "Init: Unable to get the certificate");
+ ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s);
+ return ssl_die(s);
+ }
+
+ *pubkey = params.cert;
+ }
+
+ *privkey = ENGINE_load_private_key(e, keyid, ui_method, &ppcb);
+ if (*privkey == NULL) {
+ ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(10133)
+ "Init: Unable to get the private key");
+ ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s);
+ return ssl_die(s);
+ }
+
+ ENGINE_finish(e);
+ ENGINE_free(e);
+
+ return APR_SUCCESS;
+}
+#endif
--- httpd-2.4.35/modules/ssl/ssl_private.h.r1830819+
+++ httpd-2.4.35/modules/ssl/ssl_private.h
@@ -986,21 +986,28 @@
apr_status_t ssl_load_encrypted_pkey(server_rec *, apr_pool_t *, int,
const char *, apr_array_header_t **);
+/* Load public and/or private key from the configured ENGINE. Private
+ * key returned as *pkey. certid can be NULL, in which case *pubkey
+ * is not altered. Errors logged on failure. */
+apr_status_t modssl_load_engine_keypair(server_rec *s, apr_pool_t *p,
+ const char *vhostid,
+ const char *certid, const char *keyid,
+ X509 **pubkey, EVP_PKEY **privkey);
+
/** Diffie-Hellman Parameter Support */
DH *ssl_dh_GetParamFromFile(const char *);
#ifdef HAVE_ECC
EC_GROUP *ssl_ec_GetParamFromFile(const char *);
#endif
-unsigned char *ssl_asn1_table_set(apr_hash_t *table,
- const char *key,
- long int length);
-
-ssl_asn1_t *ssl_asn1_table_get(apr_hash_t *table,
- const char *key);
-
-void ssl_asn1_table_unset(apr_hash_t *table,
- const char *key);
+/* Store the EVP_PKEY key (serialized into DER) in the hash table with
+ * key, returning the ssl_asn1_t structure pointer. */
+ssl_asn1_t *ssl_asn1_table_set(apr_hash_t *table, const char *key,
+ EVP_PKEY *pkey);
+/* Retrieve the ssl_asn1_t structure with given key from the hash. */
+ssl_asn1_t *ssl_asn1_table_get(apr_hash_t *table, const char *key);
+/* Remove and free the ssl_asn1_t structure with given key. */
+void ssl_asn1_table_unset(apr_hash_t *table, const char *key);
/** Mutex Support */
int ssl_mutex_init(server_rec *, apr_pool_t *);
@@ -1088,6 +1095,10 @@
int ssl_is_challenge(conn_rec *c, const char *servername,
X509 **pcert, EVP_PKEY **pkey);
+/* Returns non-zero if the cert/key filename should be handled through
+ * the configured ENGINE. */
+int modssl_is_engine_id(const char *name);
+
#endif /* SSL_PRIVATE_H */
/** @} */
--- httpd-2.4.35/modules/ssl/ssl_util.c.r1830819+
+++ httpd-2.4.35/modules/ssl/ssl_util.c
@@ -175,45 +175,37 @@
return TRUE;
}
-/*
- * certain key data needs to survive restarts,
- * which are stored in the user data table of s->process->pool.
- * to prevent "leaking" of this data, we use malloc/free
- * rather than apr_palloc and these wrappers to help make sure
- * we do not leak the malloc-ed data.
- */
-unsigned char *ssl_asn1_table_set(apr_hash_t *table,
- const char *key,
- long int length)
+/* Decrypted private keys are cached to survive restarts. The cached
+ * data must have lifetime of the process (hence malloc/free rather
+ * than pools), and uses raw DER since the EVP_PKEY structure
+ * internals may not survive across a module reload. */
+ssl_asn1_t *ssl_asn1_table_set(apr_hash_t *table, const char *key,
+ EVP_PKEY *pkey)
{
apr_ssize_t klen = strlen(key);
ssl_asn1_t *asn1 = apr_hash_get(table, key, klen);
+ apr_size_t length = i2d_PrivateKey(pkey, NULL);
+ unsigned char *p;
- /*
- * if a value for this key already exists,
- * reuse as much of the already malloc-ed data
- * as possible.
- */
+ /* Re-use structure if cached previously. */
if (asn1) {
if (asn1->nData != length) {
- free(asn1->cpData); /* XXX: realloc? */
- asn1->cpData = NULL;
+ asn1->cpData = ap_realloc(asn1->cpData, length);
}
}
else {
asn1 = ap_malloc(sizeof(*asn1));
asn1->source_mtime = 0; /* used as a note for encrypted private keys */
- asn1->cpData = NULL;
- }
-
- asn1->nData = length;
- if (!asn1->cpData) {
asn1->cpData = ap_malloc(length);
+
+ apr_hash_set(table, key, klen, asn1);
}
- apr_hash_set(table, key, klen, asn1);
+ asn1->nData = length;
+ p = asn1->cpData;
+ i2d_PrivateKey(pkey, &p); /* increases p by length */
- return asn1->cpData; /* caller will assign a value to this */
+ return asn1;
}
ssl_asn1_t *ssl_asn1_table_get(apr_hash_t *table,
@@ -463,3 +455,13 @@
}
#endif /* #if APR_HAS_THREADS && MODSSL_USE_OPENSSL_PRE_1_1_API */
+
+int modssl_is_engine_id(const char *name)
+{
+#if defined(HAVE_OPENSSL_ENGINE_H) && defined(HAVE_ENGINE_INIT)
+ /* ### Can handle any other special ENGINE key names here? */
+ return strncmp(name, "pkcs11:", 7) == 0;
+#else
+ return 0;
+#endif
+}
--- httpd-2.4.35/modules/ssl/ssl_util_ssl.c.r1830819+
+++ httpd-2.4.35/modules/ssl/ssl_util_ssl.c
@@ -74,7 +74,7 @@
** _________________________________________________________________
*/
-EVP_PKEY *modssl_read_privatekey(const char* filename, EVP_PKEY **key, pem_password_cb *cb, void *s)
+EVP_PKEY *modssl_read_privatekey(const char *filename, pem_password_cb *cb, void *s)
{
EVP_PKEY *rc;
BIO *bioS;
@@ -83,7 +83,7 @@
/* 1. try PEM (= DER+Base64+headers) */
if ((bioS=BIO_new_file(filename, "r")) == NULL)
return NULL;
- rc = PEM_read_bio_PrivateKey(bioS, key, cb, s);
+ rc = PEM_read_bio_PrivateKey(bioS, NULL, cb, s);
BIO_free(bioS);
if (rc == NULL) {
@@ -107,41 +107,9 @@
BIO_free(bioS);
}
}
- if (rc != NULL && key != NULL) {
- if (*key != NULL)
- EVP_PKEY_free(*key);
- *key = rc;
- }
return rc;
}
-typedef struct {
- const char *pass;
- int pass_len;
-} pass_ctx;
-
-static int provide_pass(char *buf, int size, int rwflag, void *baton)
-{
- pass_ctx *ctx = baton;
- if (ctx->pass_len > 0) {
- if (ctx->pass_len < size) {
- size = (int)ctx->pass_len;
- }
- memcpy(buf, ctx->pass, size);
- }
- return ctx->pass_len;
-}
-
-EVP_PKEY *modssl_read_encrypted_pkey(const char *filename, EVP_PKEY **key,
- const char *pass, apr_size_t pass_len)
-{
- pass_ctx ctx;
-
- ctx.pass = pass;
- ctx.pass_len = pass_len;
- return modssl_read_privatekey(filename, key, provide_pass, &ctx);
-}
-
/* _________________________________________________________________
**
** Smart shutdown
--- httpd-2.4.35/modules/ssl/ssl_util_ssl.h.r1830819+
+++ httpd-2.4.35/modules/ssl/ssl_util_ssl.h
@@ -64,8 +64,11 @@
void modssl_init_app_data2_idx(void);
void *modssl_get_app_data2(SSL *);
void modssl_set_app_data2(SSL *, void *);
-EVP_PKEY *modssl_read_privatekey(const char *, EVP_PKEY **, pem_password_cb *, void *);
-EVP_PKEY *modssl_read_encrypted_pkey(const char *, EVP_PKEY **, const char *, apr_size_t);
+
+/* Read private key from filename in either PEM or raw base64(DER)
+ * format, using password entry callback cb and userdata. */
+EVP_PKEY *modssl_read_privatekey(const char *filename, pem_password_cb *cb, void *ud);
+
int modssl_smart_shutdown(SSL *ssl);
BOOL modssl_X509_getBC(X509 *, int *, int *);
char *modssl_X509_NAME_ENTRY_to_string(apr_pool_t *p, X509_NAME_ENTRY *xsne,

View File

@ -1,22 +0,0 @@
diff --git a/modules/filters/mod_deflate.c b/modules/filters/mod_deflate.c
index d218bab..9f86b09 100644
--- a/modules/filters/mod_deflate.c
+++ b/modules/filters/mod_deflate.c
@@ -864,7 +864,7 @@ static apr_status_t deflate_out_filter(ap_filter_t *f,
if (c->note_output_name) {
apr_table_setn(r->notes, c->note_output_name,
- (ctx->stream.total_in > 0)
+ (ctx->stream.total_out > 0)
? apr_off_t_toa(r->pool,
ctx->stream.total_out)
: "-");
@@ -1336,8 +1336,6 @@ static apr_status_t deflate_in_filter(ap_filter_t *f,
ctx->stream.next_in = (unsigned char *)data;
ctx->stream.avail_in = (int)len;
- zRC = Z_OK;
-
if (!ctx->validation_buffer) {
while (ctx->stream.avail_in != 0) {
if (ctx->stream.avail_out == 0) {

View File

@ -1,245 +0,0 @@
--- httpd-2.4.33/modules/arch/unix/config5.m4.systemd
+++ httpd-2.4.33/modules/arch/unix/config5.m4
@@ -18,6 +18,16 @@
fi
])
+APACHE_MODULE(systemd, Systemd support, , , all, [
+ if test "${ac_cv_header_systemd_sd_daemon_h}" = "no" || test -z "${SYSTEMD_LIBS}"; then
+ AC_MSG_WARN([Your system does not support systemd.])
+ enable_systemd="no"
+ else
+ APR_ADDTO(MOD_SYSTEMD_LDADD, [$SYSTEMD_LIBS])
+ enable_systemd="yes"
+ fi
+])
+
APR_ADDTO(INCLUDES, [-I\$(top_srcdir)/$modpath_current])
APACHE_MODPATH_FINISH
--- httpd-2.4.33/modules/arch/unix/mod_systemd.c.systemd
+++ httpd-2.4.33/modules/arch/unix/mod_systemd.c
@@ -0,0 +1,223 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdint.h>
+#include <ap_config.h>
+#include "ap_mpm.h"
+#include <http_core.h>
+#include <httpd.h>
+#include <http_log.h>
+#include <apr_version.h>
+#include <apr_pools.h>
+#include <apr_strings.h>
+#include "unixd.h"
+#include "scoreboard.h"
+#include "mpm_common.h"
+
+#include "systemd/sd-daemon.h"
+#include "systemd/sd-journal.h"
+
+#if APR_HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+static int shutdown_timer = 0;
+static int shutdown_counter = 0;
+static unsigned long bytes_served;
+static pid_t mainpid;
+static char describe_listeners[50];
+
+static int systemd_pre_config(apr_pool_t *pconf, apr_pool_t *plog,
+ apr_pool_t *ptemp)
+{
+ sd_notify(0,
+ "RELOADING=1\n"
+ "STATUS=Reading configuration...\n");
+ ap_extended_status = 1;
+ return OK;
+}
+
+static char *dump_listener(ap_listen_rec *lr, apr_pool_t *p)
+{
+ apr_sockaddr_t *sa = lr->bind_addr;
+ char addr[128];
+
+ if (apr_sockaddr_is_wildcard(sa)) {
+ return apr_pstrcat(p, "port ", apr_itoa(p, sa->port), NULL);
+ }
+
+ apr_sockaddr_ip_getbuf(addr, sizeof addr, sa);
+
+ return apr_psprintf(p, "%s port %u", addr, sa->port);
+}
+
+static int systemd_post_config(apr_pool_t *pconf, apr_pool_t *plog,
+ apr_pool_t *ptemp, server_rec *s)
+{
+ ap_listen_rec *lr;
+ apr_size_t plen = sizeof describe_listeners;
+ char *p = describe_listeners;
+
+ if (ap_state_query(AP_SQ_MAIN_STATE) == AP_SQ_MS_CREATE_PRE_CONFIG)
+ return OK;
+
+ for (lr = ap_listeners; lr; lr = lr->next) {
+ char *s = dump_listener(lr, ptemp);
+
+ if (strlen(s) + 3 < plen) {
+ char *newp = apr_cpystrn(p, s, plen);
+ if (lr->next)
+ newp = apr_cpystrn(newp, ", ", 3);
+ plen -= newp - p;
+ p = newp;
+ }
+ else {
+ if (plen < 4) {
+ p = describe_listeners + sizeof describe_listeners - 4;
+ plen = 4;
+ }
+ apr_cpystrn(p, "...", plen);
+ break;
+ }
+ }
+
+ sd_journal_print(LOG_INFO, "Server configured, listening on: %s", describe_listeners);
+
+ return OK;
+}
+
+static int systemd_pre_mpm(apr_pool_t *p, ap_scoreboard_e sb_type)
+{
+ int rv;
+
+ mainpid = getpid();
+
+ rv = sd_notifyf(0, "READY=1\n"
+ "STATUS=Started, listening on: %s\n"
+ "MAINPID=%" APR_PID_T_FMT,
+ describe_listeners, mainpid);
+ if (rv < 0) {
+ ap_log_perror(APLOG_MARK, APLOG_ERR, 0, p, APLOGNO(02395)
+ "sd_notifyf returned an error %d", rv);
+ }
+
+ return OK;
+}
+
+static int systemd_monitor(apr_pool_t *p, server_rec *s)
+{
+ ap_sload_t sload;
+ apr_interval_time_t up_time;
+ char bps[5];
+ int rv;
+
+ if (!ap_extended_status) {
+ /* Nothing useful to report if ExtendedStatus disabled. */
+ return DECLINED;
+ }
+
+ ap_get_sload(&sload);
+
+ if (sload.access_count == 0) {
+ rv = sd_notifyf(0, "READY=1\n"
+ "STATUS=Running, listening on: %s\n",
+ describe_listeners);
+ }
+ else {
+ /* up_time in seconds */
+ up_time = (apr_uint32_t) apr_time_sec(apr_time_now() -
+ ap_scoreboard_image->global->restart_time);
+
+ apr_strfsize((unsigned long)((float) (sload.bytes_served)
+ / (float) up_time), bps);
+
+ rv = sd_notifyf(0, "READY=1\n"
+ "STATUS=Total requests: %lu; Idle/Busy workers %d/%d;"
+ "Requests/sec: %.3g; Bytes served/sec: %sB/sec\n",
+ sload.access_count, sload.idle, sload.busy,
+ ((float) sload.access_count) / (float) up_time, bps);
+ }
+
+ if (rv < 0) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(02396)
+ "sd_notifyf returned an error %d", rv);
+ }
+
+ /* Shutdown httpd when nothing is sent for shutdown_timer seconds. */
+ if (sload.bytes_served == bytes_served) {
+ /* mpm_common.c: INTERVAL_OF_WRITABLE_PROBES is 10 */
+ shutdown_counter += 10;
+ if (shutdown_timer > 0 && shutdown_counter >= shutdown_timer) {
+ rv = sd_notifyf(0, "READY=1\n"
+ "STATUS=Stopped as result of IdleShutdown "
+ "timeout.");
+ if (rv < 0) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(02804)
+ "sd_notifyf returned an error %d", rv);
+ }
+ kill(mainpid, AP_SIG_GRACEFUL);
+ }
+ }
+ else {
+ shutdown_counter = 0;
+ }
+
+ bytes_served = sload.bytes_served;
+
+ return DECLINED;
+}
+
+static void systemd_register_hooks(apr_pool_t *p)
+{
+ /* Enable ap_extended_status. */
+ ap_hook_pre_config(systemd_pre_config, NULL, NULL, APR_HOOK_LAST);
+ /* Grab the listener config. */
+ ap_hook_post_config(systemd_post_config, NULL, NULL, APR_HOOK_LAST);
+ /* We know the PID in this hook ... */
+ ap_hook_pre_mpm(systemd_pre_mpm, NULL, NULL, APR_HOOK_LAST);
+ /* Used to update httpd's status line using sd_notifyf */
+ ap_hook_monitor(systemd_monitor, NULL, NULL, APR_HOOK_MIDDLE);
+}
+
+static const char *set_shutdown_timer(cmd_parms *cmd, void *dummy,
+ const char *arg)
+{
+ const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+ if (err != NULL) {
+ return err;
+ }
+
+ shutdown_timer = atoi(arg);
+ return NULL;
+}
+
+static const command_rec systemd_cmds[] =
+{
+AP_INIT_TAKE1("IdleShutdown", set_shutdown_timer, NULL, RSRC_CONF,
+ "Number of seconds in idle-state after which httpd is shutdown"),
+ {NULL}
+};
+
+AP_DECLARE_MODULE(systemd) = {
+ STANDARD20_MODULE_STUFF,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ systemd_cmds,
+ systemd_register_hooks,
+};

View File

@ -1,20 +0,0 @@
diff --git a/modules/dav/main/util.c b/modules/dav/main/util.c
index 1ae5914027c..3f7822fc931 100644
--- a/modules/dav/main/util.c
+++ b/modules/dav/main/util.c
@@ -801,8 +801,14 @@ static dav_error * dav_process_if_header(request_rec *r, dav_if_header **p_ih)
"for the same state.");
}
condition = DAV_IF_COND_NOT;
+ list += 2;
+ }
+ else {
+ return dav_new_error(r->pool, HTTP_BAD_REQUEST,
+ DAV_ERR_IF_UNK_CHAR, 0,
+ "Invalid \"If:\" header: "
+ "Unexpected character in List");
}
- list += 2;
break;
case ' ':

View File

@ -1,39 +0,0 @@
diff --git a/modules/session/mod_session.c b/modules/session/mod_session.c
index 10e6396..7ee477c 100644
--- a/modules/session/mod_session.c
+++ b/modules/session/mod_session.c
@@ -126,20 +126,23 @@ static apr_status_t ap_session_load(request_rec * r, session_rec ** z)
/* found a session that hasn't expired? */
now = apr_time_now();
+
if (zz) {
- if (zz->expiry && zz->expiry < now) {
+ /* load the session attibutes */
+ rv = ap_run_session_decode(r, zz);
+
+ /* having a session we cannot decode is just as good as having
+ none at all */
+ if (OK != rv) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01817)
+ "error while decoding the session, "
+ "session not loaded: %s", r->uri);
zz = NULL;
}
- else {
- /* having a session we cannot decode is just as good as having
- none at all */
- rv = ap_run_session_decode(r, zz);
- if (OK != rv) {
- ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01817)
- "error while decoding the session, "
- "session not loaded: %s", r->uri);
- zz = NULL;
- }
+
+ /* invalidate session if session is expired */
+ if (zz && zz->expiry && zz->expiry < now) {
+ zz = NULL;
}
}

View File

@ -1,207 +0,0 @@
diff --git a/include/scoreboard.h b/include/scoreboard.h
index 9376da2..92d198d 100644
--- a/include/scoreboard.h
+++ b/include/scoreboard.h
@@ -148,7 +148,9 @@ struct process_score {
apr_uint32_t lingering_close; /* async connections in lingering close */
apr_uint32_t keep_alive; /* async connections in keep alive */
apr_uint32_t suspended; /* connections suspended by some module */
- int bucket; /* Listener bucket used by this child */
+ int bucket; /* Listener bucket used by this child; this field is DEPRECATED
+ * and no longer updated by the MPMs (i.e. always zero).
+ */
};
/* Scoreboard is now in 'local' memory, since it isn't updated once created,
diff --git a/server/mpm/event/event.c b/server/mpm/event/event.c
index ffe8a23..048ae61 100644
--- a/server/mpm/event/event.c
+++ b/server/mpm/event/event.c
@@ -2695,7 +2695,6 @@ static int make_child(server_rec * s, int slot, int bucket)
ap_scoreboard_image->parent[slot].quiescing = 0;
ap_scoreboard_image->parent[slot].not_accepting = 0;
- ap_scoreboard_image->parent[slot].bucket = bucket;
event_note_child_started(slot, pid);
active_daemons++;
retained->total_daemons++;
@@ -2734,6 +2733,7 @@ static void perform_idle_server_maintenance(int child_bucket, int num_buckets)
* that threads_per_child is always > 0 */
int status = SERVER_DEAD;
int child_threads_active = 0;
+ int bucket = i % num_buckets;
if (i >= retained->max_daemons_limit &&
free_length == retained->idle_spawn_rate[child_bucket]) {
@@ -2757,7 +2757,7 @@ static void perform_idle_server_maintenance(int child_bucket, int num_buckets)
*/
if (status <= SERVER_READY && !ps->quiescing && !ps->not_accepting
&& ps->generation == retained->mpm->my_generation
- && ps->bucket == child_bucket)
+ && bucket == child_bucket)
{
++idle_thread_count;
}
@@ -2768,7 +2768,9 @@ static void perform_idle_server_maintenance(int child_bucket, int num_buckets)
last_non_dead = i;
}
active_thread_count += child_threads_active;
- if (!ps->pid && free_length < retained->idle_spawn_rate[child_bucket])
+ if (!ps->pid
+ && bucket == child_bucket
+ && free_length < retained->idle_spawn_rate[child_bucket])
free_slots[free_length++] = i;
else if (child_threads_active == threads_per_child)
had_healthy_child = 1;
@@ -2951,13 +2953,14 @@ static void server_main_loop(int remaining_children_to_start, int num_buckets)
retained->total_daemons--;
if (processed_status == APEXIT_CHILDSICK) {
/* resource shortage, minimize the fork rate */
- retained->idle_spawn_rate[ps->bucket] = 1;
+ retained->idle_spawn_rate[child_slot % num_buckets] = 1;
}
else if (remaining_children_to_start) {
/* we're still doing a 1-for-1 replacement of dead
* children with new children
*/
- make_child(ap_server_conf, child_slot, ps->bucket);
+ make_child(ap_server_conf, child_slot,
+ child_slot % num_buckets);
--remaining_children_to_start;
}
}
diff --git a/server/mpm/prefork/prefork.c b/server/mpm/prefork/prefork.c
index 8efda72..7c00625 100644
--- a/server/mpm/prefork/prefork.c
+++ b/server/mpm/prefork/prefork.c
@@ -637,8 +637,9 @@ static void child_main(int child_num_arg, int child_bucket)
}
-static int make_child(server_rec *s, int slot, int bucket)
+static int make_child(server_rec *s, int slot)
{
+ int bucket = slot % retained->mpm->num_buckets;
int pid;
if (slot + 1 > retained->max_daemons_limit) {
@@ -716,7 +717,6 @@ static int make_child(server_rec *s, int slot, int bucket)
child_main(slot, bucket);
}
- ap_scoreboard_image->parent[slot].bucket = bucket;
prefork_note_child_started(slot, pid);
return 0;
@@ -732,7 +732,7 @@ static void startup_children(int number_to_start)
if (ap_scoreboard_image->servers[i][0].status != SERVER_DEAD) {
continue;
}
- if (make_child(ap_server_conf, i, i % retained->mpm->num_buckets) < 0) {
+ if (make_child(ap_server_conf, i) < 0) {
break;
}
--number_to_start;
@@ -741,8 +741,6 @@ static void startup_children(int number_to_start)
static void perform_idle_server_maintenance(apr_pool_t *p)
{
- static int bucket_make_child_record = -1;
- static int bucket_kill_child_record = -1;
int i;
int idle_count;
worker_score *ws;
@@ -789,6 +787,7 @@ static void perform_idle_server_maintenance(apr_pool_t *p)
}
retained->max_daemons_limit = last_non_dead + 1;
if (idle_count > ap_daemons_max_free) {
+ static int bucket_kill_child_record = -1;
/* kill off one child... we use the pod because that'll cause it to
* shut down gracefully, in case it happened to pick up a request
* while we were counting
@@ -819,10 +818,7 @@ static void perform_idle_server_maintenance(apr_pool_t *p)
idle_count, total_non_dead);
}
for (i = 0; i < free_length; ++i) {
- bucket_make_child_record++;
- bucket_make_child_record %= retained->mpm->num_buckets;
- make_child(ap_server_conf, free_slots[i],
- bucket_make_child_record);
+ make_child(ap_server_conf, free_slots[i]);
}
/* the next time around we want to spawn twice as many if this
* wasn't good enough, but not if we've just done a graceful
@@ -867,7 +863,7 @@ static int prefork_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s)
if (one_process) {
AP_MONCONTROL(1);
- make_child(ap_server_conf, 0, 0);
+ make_child(ap_server_conf, 0);
/* NOTREACHED */
ap_assert(0);
return !OK;
@@ -976,8 +972,7 @@ static int prefork_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s)
/* we're still doing a 1-for-1 replacement of dead
* children with new children
*/
- make_child(ap_server_conf, child_slot,
- ap_get_scoreboard_process(child_slot)->bucket);
+ make_child(ap_server_conf, child_slot);
--remaining_children_to_start;
}
#if APR_HAS_OTHER_CHILD
diff --git a/server/mpm/worker/worker.c b/server/mpm/worker/worker.c
index 8012fe2..a927942 100644
--- a/server/mpm/worker/worker.c
+++ b/server/mpm/worker/worker.c
@@ -1339,7 +1339,6 @@ static int make_child(server_rec *s, int slot, int bucket)
worker_note_child_lost_slot(slot, pid);
}
ap_scoreboard_image->parent[slot].quiescing = 0;
- ap_scoreboard_image->parent[slot].bucket = bucket;
worker_note_child_started(slot, pid);
return 0;
}
@@ -1388,6 +1387,7 @@ static void perform_idle_server_maintenance(int child_bucket, int num_buckets)
int any_dead_threads = 0;
int all_dead_threads = 1;
int child_threads_active = 0;
+ int bucket = i % num_buckets;
if (i >= retained->max_daemons_limit &&
totally_free_length == retained->idle_spawn_rate[child_bucket]) {
@@ -1420,7 +1420,7 @@ static void perform_idle_server_maintenance(int child_bucket, int num_buckets)
if (status <= SERVER_READY &&
!ps->quiescing &&
ps->generation == retained->mpm->my_generation &&
- ps->bucket == child_bucket) {
+ bucket == child_bucket) {
++idle_thread_count;
}
if (status >= SERVER_READY && status < SERVER_GRACEFUL) {
@@ -1430,6 +1430,7 @@ static void perform_idle_server_maintenance(int child_bucket, int num_buckets)
}
active_thread_count += child_threads_active;
if (any_dead_threads
+ && bucket == child_bucket
&& totally_free_length < retained->idle_spawn_rate[child_bucket]
&& free_length < MAX_SPAWN_RATE / num_buckets
&& (!ps->pid /* no process in the slot */
@@ -1615,14 +1616,15 @@ static void server_main_loop(int remaining_children_to_start, int num_buckets)
ps->quiescing = 0;
if (processed_status == APEXIT_CHILDSICK) {
/* resource shortage, minimize the fork rate */
- retained->idle_spawn_rate[ps->bucket] = 1;
+ retained->idle_spawn_rate[child_slot % num_buckets] = 1;
}
else if (remaining_children_to_start
&& child_slot < ap_daemons_limit) {
/* we're still doing a 1-for-1 replacement of dead
* children with new children
*/
- make_child(ap_server_conf, child_slot, ps->bucket);
+ make_child(ap_server_conf, child_slot,
+ child_slot % num_buckets);
--remaining_children_to_start;
}
}

View File

@ -1,20 +0,0 @@
diff --git a/modules/ssl/ssl_engine_kernel.c b/modules/ssl/ssl_engine_kernel.c
index de0ffb0..e6a9f67 100644
--- a/modules/ssl/ssl_engine_kernel.c
+++ b/modules/ssl/ssl_engine_kernel.c
@@ -1154,6 +1154,7 @@ static int ssl_hook_Access_modern(request_rec *r, SSLSrvConfigRec *sc, SSLDirCon
ssl_log_ssl_error(SSLLOG_MARK, APLOG_ERR, r->server);
apr_table_setn(r->notes, "error-notes",
"Reason: Cannot perform Post-Handshake Authentication.<br />");
+ SSL_set_verify(ssl, vmode_inplace, NULL);
return HTTP_FORBIDDEN;
}
@@ -1175,6 +1176,7 @@ static int ssl_hook_Access_modern(request_rec *r, SSLSrvConfigRec *sc, SSLDirCon
* Finally check for acceptable renegotiation results
*/
if (OK != (rc = ssl_check_post_client_verify(r, sc, dc, sslconn, ssl))) {
+ SSL_set_verify(ssl, vmode_inplace, NULL);
return rc;
}
}

View File

@ -1,111 +0,0 @@
--- a/modules/aaa/mod_auth_digest.c 2019/03/12 09:24:19 1855297
+++ b/modules/aaa/mod_auth_digest.c 2019/03/12 09:24:26 1855298
@@ -92,7 +92,6 @@
int check_nc;
const char *algorithm;
char *uri_list;
- const char *ha1;
} digest_config_rec;
@@ -153,6 +152,7 @@
apr_time_t nonce_time;
enum hdr_sts auth_hdr_sts;
int needed_auth;
+ const char *ha1;
client_entry *client;
} digest_header_rec;
@@ -1304,7 +1304,7 @@
*/
static authn_status get_hash(request_rec *r, const char *user,
- digest_config_rec *conf)
+ digest_config_rec *conf, const char **rethash)
{
authn_status auth_result;
char *password;
@@ -1356,7 +1356,7 @@
} while (current_provider);
if (auth_result == AUTH_USER_FOUND) {
- conf->ha1 = password;
+ *rethash = password;
}
return auth_result;
@@ -1483,25 +1483,24 @@
/* RFC-2069 */
static const char *old_digest(const request_rec *r,
- const digest_header_rec *resp, const char *ha1)
+ const digest_header_rec *resp)
{
const char *ha2;
ha2 = ap_md5(r->pool, (unsigned char *)apr_pstrcat(r->pool, resp->method, ":",
resp->uri, NULL));
return ap_md5(r->pool,
- (unsigned char *)apr_pstrcat(r->pool, ha1, ":", resp->nonce,
- ":", ha2, NULL));
+ (unsigned char *)apr_pstrcat(r->pool, resp->ha1, ":",
+ resp->nonce, ":", ha2, NULL));
}
/* RFC-2617 */
static const char *new_digest(const request_rec *r,
- digest_header_rec *resp,
- const digest_config_rec *conf)
+ digest_header_rec *resp)
{
const char *ha1, *ha2, *a2;
- ha1 = conf->ha1;
+ ha1 = resp->ha1;
a2 = apr_pstrcat(r->pool, resp->method, ":", resp->uri, NULL);
ha2 = ap_md5(r->pool, (const unsigned char *)a2);
@@ -1514,7 +1513,6 @@
NULL));
}
-
static void copy_uri_components(apr_uri_t *dst,
apr_uri_t *src, request_rec *r) {
if (src->scheme && src->scheme[0] != '\0') {
@@ -1759,7 +1757,7 @@
return HTTP_UNAUTHORIZED;
}
- return_code = get_hash(r, r->user, conf);
+ return_code = get_hash(r, r->user, conf, &resp->ha1);
if (return_code == AUTH_USER_NOT_FOUND) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01790)
@@ -1789,7 +1787,7 @@
if (resp->message_qop == NULL) {
/* old (rfc-2069) style digest */
- if (strcmp(resp->digest, old_digest(r, resp, conf->ha1))) {
+ if (strcmp(resp->digest, old_digest(r, resp))) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01792)
"user %s: password mismatch: %s", r->user,
r->uri);
@@ -1819,7 +1817,7 @@
return HTTP_UNAUTHORIZED;
}
- exp_digest = new_digest(r, resp, conf);
+ exp_digest = new_digest(r, resp);
if (!exp_digest) {
/* we failed to allocate a client struct */
return HTTP_INTERNAL_SERVER_ERROR;
@@ -1903,7 +1901,7 @@
/* calculate rspauth attribute
*/
- ha1 = conf->ha1;
+ ha1 = resp->ha1;
a2 = apr_pstrcat(r->pool, ":", resp->uri, NULL);
ha2 = ap_md5(r->pool, (const unsigned char *)a2);

View File

@ -1,235 +0,0 @@
diff --git a/docs/manual/mod/core.html.en b/docs/manual/mod/core.html.en
index 0a24bc8..20d1e5a 100644
--- a/docs/manual/mod/core.html.en
+++ b/docs/manual/mod/core.html.en
@@ -97,6 +97,7 @@ available</td></tr>
<li><img alt="" src="../images/down.gif" /> <a href="#maxrangeoverlaps">MaxRangeOverlaps</a></li>
<li><img alt="" src="../images/down.gif" /> <a href="#maxrangereversals">MaxRangeReversals</a></li>
<li><img alt="" src="../images/down.gif" /> <a href="#maxranges">MaxRanges</a></li>
+<li><img alt="" src="../images/down.gif" /> <a href="#mergeslashes">MergeSlashes</a></li>
<li><img alt="" src="../images/down.gif" /> <a href="#mergetrailers">MergeTrailers</a></li>
<li><img alt="" src="../images/down.gif" /> <a href="#mutex">Mutex</a></li>
<li><img alt="" src="../images/down.gif" /> <a href="#namevirtualhost">NameVirtualHost</a></li>
@@ -3465,6 +3466,30 @@ resource </td></tr>
</div>
<div class="top"><a href="#page-header"><img alt="top" src="../images/up.gif" /></a></div>
+<div class="directive-section"><h2><a name="MergeSlashes" id="MergeSlashes">MergeSlashes</a> <a name="mergeslashes" id="mergeslashes">Directive</a></h2>
+<table class="directive">
+<tr><th><a href="directive-dict.html#Description">Description:</a></th><td>Controls whether the server merges consecutive slashes in URLs. </td></tr>
+<tr><th><a href="directive-dict.html#Syntax">Syntax:</a></th><td><code>MergeSlashes ON | OFF</code></td></tr>
+<tr><th><a href="directive-dict.html#Default">Default:</a></th><td><code>MergeSlashes ON</code></td></tr>
+<tr><th><a href="directive-dict.html#Context">Context:</a></th><td>server config, virtual host</td></tr>
+<tr><th><a href="directive-dict.html#Status">Status:</a></th><td>Core</td></tr>
+<tr><th><a href="directive-dict.html#Module">Module:</a></th><td>core</td></tr>
+<tr><th><a href="directive-dict.html#Compatibility">Compatibility:</a></th><td>Available in Apache HTTP Server 2.4.6 in Red Hat Enterprise Linux 7</td></tr>
+</table>
+ <p>By default, the server merges (or collapses) multiple consecutive slash
+ ('/') characters in the path component of the request URL.</p>
+
+ <p>When mapping URL's to the filesystem, these multiple slashes are not
+ significant. However, URL's handled other ways, such as by CGI or proxy,
+ might prefer to retain the significance of multiple consecutive slashes.
+ In these cases <code class="directive">MergeSlashes</code> can be set to
+ <em>OFF</em> to retain the multiple consecutive slashes. In these
+ configurations, regular expressions used in the configuration file that match
+ the path component of the URL (<code class="directive">LocationMatch</code>,
+ <code class="directive">RewriteRule</code>, ...) need to take into account multiple
+ consecutive slashes.</p>
+</div>
+<div class="top"><a href="#page-header"><img alt="top" src="../images/up.gif" /></a></div>
<div class="directive-section"><h2><a name="MergeTrailers" id="MergeTrailers">MergeTrailers</a> <a name="mergetrailers" id="mergetrailers">Directive</a></h2>
<table class="directive">
<tr><th><a href="directive-dict.html#Description">Description:</a></th><td>Determines whether trailers are merged into headers</td></tr>
--- a/include/http_core.h 2019/03/18 08:49:19 1855736
+++ b/include/http_core.h 2019/03/18 08:49:59 1855737
@@ -740,7 +740,7 @@
#define AP_HTTP_METHODS_LENIENT 1
#define AP_HTTP_METHODS_REGISTERED 2
char http_methods;
-
+ unsigned int merge_slashes;
} core_server_config;
/* for AddOutputFiltersByType in core.c */
diff --git a/include/httpd.h b/include/httpd.h
index 65392f8..99f7f04 100644
--- a/include/httpd.h
+++ b/include/httpd.h
@@ -1697,11 +1697,21 @@ AP_DECLARE(int) ap_unescape_url_keep2f(char *url, int decode_slashes);
AP_DECLARE(int) ap_unescape_urlencoded(char *query);
/**
- * Convert all double slashes to single slashes
- * @param name The string to convert
+ * Convert all double slashes to single slashes, except where significant
+ * to the filesystem on the current platform.
+ * @param name The string to convert, assumed to be a filesystem path
*/
AP_DECLARE(void) ap_no2slash(char *name);
+/**
+ * Convert all double slashes to single slashes, except where significant
+ * to the filesystem on the current platform.
+ * @param name The string to convert
+ * @param is_fs_path if set to 0, the significance of any double-slashes is
+ * ignored.
+ */
+AP_DECLARE(void) ap_no2slash_ex(char *name, int is_fs_path);
+
/**
* Remove all ./ and xx/../ substrings from a file name. Also remove
* any leading ../ or /../ substrings.
diff --git a/server/request.c b/server/request.c
index dbe3e07..d5c558a 100644
--- a/server/request.c
+++ b/server/request.c
@@ -167,6 +167,8 @@ AP_DECLARE(int) ap_process_request_internal(request_rec *r)
int file_req = (r->main && r->filename);
int access_status;
core_dir_config *d;
+ core_server_config *sconf =
+ ap_get_core_module_config(r->server->module_config);
/* Ignore embedded %2F's in path for proxy requests */
if (!r->proxyreq && r->parsed_uri.path) {
@@ -191,6 +193,12 @@ AP_DECLARE(int) ap_process_request_internal(request_rec *r)
}
ap_getparents(r->uri); /* OK --- shrinking transformations... */
+ if (sconf->merge_slashes != AP_CORE_CONFIG_OFF) {
+ ap_no2slash(r->uri);
+ if (r->parsed_uri.path) {
+ ap_no2slash(r->parsed_uri.path);
+ }
+ }
/* All file subrequests are a huge pain... they cannot bubble through the
* next several steps. Only file subrequests are allowed an empty uri,
@@ -1411,20 +1419,7 @@ AP_DECLARE(int) ap_location_walk(request_rec *r)
cache = prep_walk_cache(AP_NOTE_LOCATION_WALK, r);
cached = (cache->cached != NULL);
-
- /* Location and LocationMatch differ on their behaviour w.r.t. multiple
- * slashes. Location matches multiple slashes with a single slash,
- * LocationMatch doesn't. An exception, for backwards brokenness is
- * absoluteURIs... in which case neither match multiple slashes.
- */
- if (r->uri[0] != '/') {
- entry_uri = r->uri;
- }
- else {
- char *uri = apr_pstrdup(r->pool, r->uri);
- ap_no2slash(uri);
- entry_uri = uri;
- }
+ entry_uri = r->uri;
/* If we have an cache->cached location that matches r->uri,
* and the vhost's list of locations hasn't changed, we can skip
@@ -1491,7 +1486,7 @@ AP_DECLARE(int) ap_location_walk(request_rec *r)
pmatch = apr_palloc(rxpool, nmatch*sizeof(ap_regmatch_t));
}
- if (ap_regexec(entry_core->r, r->uri, nmatch, pmatch, 0)) {
+ if (ap_regexec(entry_core->r, entry_uri, nmatch, pmatch, 0)) {
continue;
}
@@ -1501,7 +1496,7 @@ AP_DECLARE(int) ap_location_walk(request_rec *r)
apr_table_setn(r->subprocess_env,
((const char **)entry_core->refs->elts)[i],
apr_pstrndup(r->pool,
- r->uri + pmatch[i].rm_so,
+ entry_uri + pmatch[i].rm_so,
pmatch[i].rm_eo - pmatch[i].rm_so));
}
}
diff --git a/server/util.c b/server/util.c
index fd7a0a1..e0c558c 100644
--- a/server/util.c
+++ b/server/util.c
@@ -561,16 +561,20 @@ AP_DECLARE(void) ap_getparents(char *name)
name[l] = '\0';
}
}
-
-AP_DECLARE(void) ap_no2slash(char *name)
+AP_DECLARE(void) ap_no2slash_ex(char *name, int is_fs_path)
{
+
char *d, *s;
+ if (!*name) {
+ return;
+ }
+
s = d = name;
#ifdef HAVE_UNC_PATHS
/* Check for UNC names. Leave leading two slashes. */
- if (s[0] == '/' && s[1] == '/')
+ if (is_fs_path && s[0] == '/' && s[1] == '/')
*d++ = *s++;
#endif
@@ -587,6 +591,10 @@ AP_DECLARE(void) ap_no2slash(char *name)
*d = '\0';
}
+AP_DECLARE(void) ap_no2slash(char *name)
+{
+ ap_no2slash_ex(name, 1);
+}
/*
* copy at most n leading directories of s into d
diff --git a/server/core.c b/server/core.c
index b5ab429..a31f1e4 100644
--- a/server/core.c
+++ b/server/core.c
@@ -493,6 +493,7 @@ static void *create_core_server_config(apr_pool_t *a, server_rec *s)
*/
conf->trace_enable = AP_TRACE_UNSET;
+ conf->merge_slashes = AP_CORE_CONFIG_UNSET;
conf->protocols = apr_array_make(a, 5, sizeof(const char *));
conf->protocols_honor_order = -1;
@@ -561,7 +562,9 @@ static void *merge_core_server_configs(apr_pool_t *p, void *basev, void *virtv)
conf->protocols_honor_order = ((virt->protocols_honor_order < 0)?
base->protocols_honor_order :
virt->protocols_honor_order);
-
+
+ AP_CORE_MERGE_FLAG(merge_slashes, conf, base, virt);
+
return conf;
}
@@ -1872,6 +1875,13 @@ static const char *set_qualify_redirect_url(cmd_parms *cmd, void *d_, int flag)
return NULL;
}
+static const char *set_core_server_flag(cmd_parms *cmd, void *s_, int flag)
+{
+ core_server_config *conf =
+ ap_get_core_module_config(cmd->server->module_config);
+ return ap_set_flag_slot(cmd, conf, flag);
+}
+
static const char *set_override_list(cmd_parms *cmd, void *d_, int argc, char *const argv[])
{
core_dir_config *d = d_;
@@ -4598,6 +4608,10 @@ AP_INIT_ITERATE("HttpProtocolOptions", set_http_protocol_options, NULL, RSRC_CON
"'Unsafe' or 'Strict' (default). Sets HTTP acceptance rules"),
AP_INIT_ITERATE("RegisterHttpMethod", set_http_method, NULL, RSRC_CONF,
"Registers non-standard HTTP methods"),
+AP_INIT_FLAG("MergeSlashes", set_core_server_flag,
+ (void *)APR_OFFSETOF(core_server_config, merge_slashes),
+ RSRC_CONF,
+ "Controls whether consecutive slashes in the URI path are merged"),
{ NULL }
};

View File

@ -1,192 +0,0 @@
diff --git a/modules/http/http_protocol.c b/modules/http/http_protocol.c
index e419eb6..dcafa9c 100644
--- a/modules/http/http_protocol.c
+++ b/modules/http/http_protocol.c
@@ -1132,13 +1132,10 @@ static const char *get_canned_error_string(int status,
"\">here</a>.</p>\n",
NULL));
case HTTP_USE_PROXY:
- return(apr_pstrcat(p,
- "<p>This resource is only accessible "
- "through the proxy\n",
- ap_escape_html(r->pool, location),
- "<br />\nYou will need to configure "
- "your client to use that proxy.</p>\n",
- NULL));
+ return("<p>This resource is only accessible "
+ "through the proxy\n"
+ "<br />\nYou will need to configure "
+ "your client to use that proxy.</p>\n");
case HTTP_PROXY_AUTHENTICATION_REQUIRED:
case HTTP_UNAUTHORIZED:
return("<p>This server could not verify that you\n"
@@ -1154,34 +1151,20 @@ static const char *get_canned_error_string(int status,
"error-notes",
"</p>\n"));
case HTTP_FORBIDDEN:
- s1 = apr_pstrcat(p,
- "<p>You don't have permission to access ",
- ap_escape_html(r->pool, r->uri),
- "\non this server.<br />\n",
- NULL);
- return(add_optional_notes(r, s1, "error-notes", "</p>\n"));
+ return(add_optional_notes(r, "<p>You don't have permission to access this resource.", "error-notes", "</p>\n"));
case HTTP_NOT_FOUND:
- return(apr_pstrcat(p,
- "<p>The requested URL ",
- ap_escape_html(r->pool, r->uri),
- " was not found on this server.</p>\n",
- NULL));
+ return("<p>The requested URL was not found on this server.</p>\n");
case HTTP_METHOD_NOT_ALLOWED:
return(apr_pstrcat(p,
"<p>The requested method ",
ap_escape_html(r->pool, r->method),
- " is not allowed for the URL ",
- ap_escape_html(r->pool, r->uri),
- ".</p>\n",
+ " is not allowed for this URL.</p>\n",
NULL));
case HTTP_NOT_ACCEPTABLE:
- s1 = apr_pstrcat(p,
- "<p>An appropriate representation of the "
- "requested resource ",
- ap_escape_html(r->pool, r->uri),
- " could not be found on this server.</p>\n",
- NULL);
- return(add_optional_notes(r, s1, "variant-list", ""));
+ return(add_optional_notes(r,
+ "<p>An appropriate representation of the requested resource "
+ "could not be found on this server.</p>\n",
+ "variant-list", ""));
case HTTP_MULTIPLE_CHOICES:
return(add_optional_notes(r, "", "variant-list", ""));
case HTTP_LENGTH_REQUIRED:
@@ -1192,18 +1175,13 @@ static const char *get_canned_error_string(int status,
NULL);
return(add_optional_notes(r, s1, "error-notes", "</p>\n"));
case HTTP_PRECONDITION_FAILED:
- return(apr_pstrcat(p,
- "<p>The precondition on the request "
- "for the URL ",
- ap_escape_html(r->pool, r->uri),
- " evaluated to false.</p>\n",
- NULL));
+ return("<p>The precondition on the request "
+ "for this URL evaluated to false.</p>\n");
case HTTP_NOT_IMPLEMENTED:
s1 = apr_pstrcat(p,
"<p>",
- ap_escape_html(r->pool, r->method), " to ",
- ap_escape_html(r->pool, r->uri),
- " not supported.<br />\n",
+ ap_escape_html(r->pool, r->method), " ",
+ " not supported for current URL.<br />\n",
NULL);
return(add_optional_notes(r, s1, "error-notes", "</p>\n"));
case HTTP_BAD_GATEWAY:
@@ -1211,29 +1189,19 @@ static const char *get_canned_error_string(int status,
"response from an upstream server.<br />" CRLF;
return(add_optional_notes(r, s1, "error-notes", "</p>\n"));
case HTTP_VARIANT_ALSO_VARIES:
- return(apr_pstrcat(p,
- "<p>A variant for the requested "
- "resource\n<pre>\n",
- ap_escape_html(r->pool, r->uri),
- "\n</pre>\nis itself a negotiable resource. "
- "This indicates a configuration error.</p>\n",
- NULL));
+ return("<p>A variant for the requested "
+ "resource\n<pre>\n"
+ "\n</pre>\nis itself a negotiable resource. "
+ "This indicates a configuration error.</p>\n");
case HTTP_REQUEST_TIME_OUT:
return("<p>Server timeout waiting for the HTTP request from the client.</p>\n");
case HTTP_GONE:
- return(apr_pstrcat(p,
- "<p>The requested resource<br />",
- ap_escape_html(r->pool, r->uri),
- "<br />\nis no longer available on this server "
- "and there is no forwarding address.\n"
- "Please remove all references to this "
- "resource.</p>\n",
- NULL));
+ return("<p>The requested resource is no longer available on this server"
+ " and there is no forwarding address.\n"
+ "Please remove all references to this resource.</p>\n");
case HTTP_REQUEST_ENTITY_TOO_LARGE:
return(apr_pstrcat(p,
- "The requested resource<br />",
- ap_escape_html(r->pool, r->uri), "<br />\n",
- "does not allow request data with ",
+ "The requested resource does not allow request data with ",
ap_escape_html(r->pool, r->method),
" requests, or the amount of data provided in\n"
"the request exceeds the capacity limit.\n",
@@ -1317,11 +1285,9 @@ static const char *get_canned_error_string(int status,
"the Server Name Indication (SNI) in use for this\n"
"connection.</p>\n");
case HTTP_UNAVAILABLE_FOR_LEGAL_REASONS:
- s1 = apr_pstrcat(p,
- "<p>Access to ", ap_escape_html(r->pool, r->uri),
- "\nhas been denied for legal reasons.<br />\n",
- NULL);
- return(add_optional_notes(r, s1, "error-notes", "</p>\n"));
+ return(add_optional_notes(r,
+ "<p>Access to this URL has been denied for legal reasons.<br />\n",
+ "error-notes", "</p>\n"));
default: /* HTTP_INTERNAL_SERVER_ERROR */
/*
* This comparison to expose error-notes could be modified to
diff --git a/modules/proxy/mod_proxy.c b/modules/proxy/mod_proxy.c
index 800ede1..de48735 100644
--- a/modules/proxy/mod_proxy.c
+++ b/modules/proxy/mod_proxy.c
@@ -1055,9 +1055,10 @@ static int proxy_handler(request_rec *r)
char *end;
maxfwd = apr_strtoi64(str, &end, 10);
if (maxfwd < 0 || maxfwd == APR_INT64_MAX || *end) {
- return ap_proxyerror(r, HTTP_BAD_REQUEST,
- apr_psprintf(r->pool,
- "Max-Forwards value '%s' could not be parsed", str));
+ ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO()
+ "Max-Forwards value '%s' could not be parsed", str);
+ return ap_proxyerror(r, HTTP_BAD_REQUEST,
+ "Max-Forwards request header could not be parsed");
}
else if (maxfwd == 0) {
switch (r->method_number) {
diff --git a/modules/proxy/mod_proxy_ftp.c b/modules/proxy/mod_proxy_ftp.c
index 4a10987..8f6f853 100644
--- a/modules/proxy/mod_proxy_ftp.c
+++ b/modules/proxy/mod_proxy_ftp.c
@@ -1024,8 +1024,9 @@ static int proxy_ftp_handler(request_rec *r, proxy_worker *worker,
/* We break the URL into host, port, path-search */
if (r->parsed_uri.hostname == NULL) {
if (APR_SUCCESS != apr_uri_parse(p, url, &uri)) {
- return ap_proxyerror(r, HTTP_BAD_REQUEST,
- apr_psprintf(p, "URI cannot be parsed: %s", url));
+ ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO()
+ "URI cannot be parsed: %s", url);
+ return ap_proxyerror(r, HTTP_BAD_REQUEST, "URI cannot be parsed");
}
connectname = uri.hostname;
connectport = uri.port;
diff --git a/modules/proxy/proxy_util.c b/modules/proxy/proxy_util.c
index 6501c68..0bbfa59 100644
--- a/modules/proxy/proxy_util.c
+++ b/modules/proxy/proxy_util.c
@@ -368,12 +368,9 @@ PROXY_DECLARE(char *)
PROXY_DECLARE(int) ap_proxyerror(request_rec *r, int statuscode, const char *message)
{
- const char *uri = ap_escape_html(r->pool, r->uri);
apr_table_setn(r->notes, "error-notes",
apr_pstrcat(r->pool,
- "The proxy server could not handle the request <em><a href=\"",
- uri, "\">", ap_escape_html(r->pool, r->method), "&nbsp;", uri,
- "</a></em>.<p>\n"
+ "The proxy server could not handle the request<p>"
"Reason: <strong>", ap_escape_html(r->pool, message),
"</strong></p>",
NULL));

View File

@ -1,66 +0,0 @@
diff --git a/modules/metadata/mod_remoteip.c b/modules/metadata/mod_remoteip.c
index 4572ce1..a0cbc0f 100644
--- a/modules/metadata/mod_remoteip.c
+++ b/modules/metadata/mod_remoteip.c
@@ -987,15 +987,13 @@ static remoteip_parse_status_t remoteip_process_v2_header(conn_rec *c,
return HDR_ERROR;
#endif
default:
- /* unsupported protocol, keep local connection address */
- return HDR_DONE;
+ /* unsupported protocol */
+ ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, APLOGNO(10183)
+ "RemoteIPProxyProtocol: unsupported protocol %.2hx",
+ (unsigned short)hdr->v2.fam);
+ return HDR_ERROR;
}
break; /* we got a sockaddr now */
-
- case 0x00: /* LOCAL command */
- /* keep local connection address for LOCAL */
- return HDR_DONE;
-
default:
/* not a supported command */
ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, APLOGNO(03507)
@@ -1087,11 +1085,24 @@ static apr_status_t remoteip_input_filter(ap_filter_t *f,
/* try to read a header's worth of data */
while (!ctx->done) {
if (APR_BRIGADE_EMPTY(ctx->bb)) {
- ret = ap_get_brigade(f->next, ctx->bb, ctx->mode, block,
- ctx->need - ctx->rcvd);
+ apr_off_t got, want = ctx->need - ctx->rcvd;
+
+ ret = ap_get_brigade(f->next, ctx->bb, ctx->mode, block, want);
if (ret != APR_SUCCESS) {
+ ap_log_cerror(APLOG_MARK, APLOG_ERR, ret, f->c, APLOGNO(10184)
+ "failed reading input");
return ret;
}
+
+ ret = apr_brigade_length(ctx->bb, 1, &got);
+ if (ret || got > want) {
+ ap_log_cerror(APLOG_MARK, APLOG_ERR, ret, f->c, APLOGNO(10185)
+ "RemoteIPProxyProtocol header too long, "
+ "got %" APR_OFF_T_FMT " expected %" APR_OFF_T_FMT,
+ got, want);
+ f->c->aborted = 1;
+ return APR_ECONNABORTED;
+ }
}
if (APR_BRIGADE_EMPTY(ctx->bb)) {
return block == APR_NONBLOCK_READ ? APR_SUCCESS : APR_EOF;
@@ -1139,6 +1150,13 @@ static apr_status_t remoteip_input_filter(ap_filter_t *f,
if (ctx->rcvd >= MIN_V2_HDR_LEN) {
ctx->need = MIN_V2_HDR_LEN +
remoteip_get_v2_len((proxy_header *) ctx->header);
+ if (ctx->need > sizeof(proxy_v2)) {
+ ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, f->c, APLOGNO(10186)
+ "RemoteIPProxyProtocol protocol header length too long");
+ f->c->aborted = 1;
+ apr_brigade_destroy(ctx->bb);
+ return APR_ECONNABORTED;
+ }
}
if (ctx->rcvd >= ctx->need) {
psts = remoteip_process_v2_header(f->c, conn_conf,

View File

@ -1,91 +0,0 @@
diff --git a/include/ap_regex.h b/include/ap_regex.h
index 7d8df79..7af2f99 100644
--- a/include/ap_regex.h
+++ b/include/ap_regex.h
@@ -84,7 +84,11 @@ extern "C" {
#define AP_REG_DOLLAR_ENDONLY 0x200 /* '$' matches at end of subject string only */
-#define AP_REG_MATCH "MATCH_" /** suggested prefix for ap_regname */
+#define AP_REG_NO_DEFAULT 0x400 /**< Don't implicitely add AP_REG_DEFAULT options */
+
+#define AP_REG_MATCH "MATCH_" /**< suggested prefix for ap_regname */
+
+#define AP_REG_DEFAULT (AP_REG_DOTALL|AP_REG_DOLLAR_ENDONLY)
/* Error values: */
enum {
diff --git a/modules/filters/mod_substitute.c b/modules/filters/mod_substitute.c
index b7d5296..e976c51 100644
--- a/modules/filters/mod_substitute.c
+++ b/modules/filters/mod_substitute.c
@@ -667,8 +667,10 @@ static const char *set_pattern(cmd_parms *cmd, void *cfg, const char *line)
/* first see if we can compile the regex */
if (!is_pattern) {
- r = ap_pregcomp(cmd->pool, from, AP_REG_EXTENDED |
- (ignore_case ? AP_REG_ICASE : 0));
+ int flags = AP_REG_NO_DEFAULT
+ | (ap_regcomp_get_default_cflags() & AP_REG_DOLLAR_ENDONLY)
+ | (ignore_case ? AP_REG_ICASE : 0);
+ r = ap_pregcomp(cmd->pool, from, flags);
if (!r)
return "Substitute could not compile regex";
}
diff --git a/server/core.c b/server/core.c
index 76432ce..6d00777 100644
--- a/server/core.c
+++ b/server/core.c
@@ -4973,7 +4973,7 @@ static int core_pre_config(apr_pool_t *pconf, apr_pool_t *plog, apr_pool_t *ptem
init_config_defines(pconf);
apr_pool_cleanup_register(pconf, NULL, reset_config, apr_pool_cleanup_null);
- ap_regcomp_set_default_cflags(AP_REG_DOLLAR_ENDONLY);
+ ap_regcomp_set_default_cflags(AP_REG_DEFAULT);
mpm_common_pre_config(pconf);
diff --git a/server/util_pcre.c b/server/util_pcre.c
index f2cb1bb..2a665c8 100644
--- a/server/util_pcre.c
+++ b/server/util_pcre.c
@@ -120,7 +120,7 @@ AP_DECLARE(void) ap_regfree(ap_regex_t *preg)
* Compile a regular expression *
*************************************************/
-static int default_cflags = AP_REG_DOLLAR_ENDONLY;
+static int default_cflags = AP_REG_DEFAULT;
AP_DECLARE(int) ap_regcomp_get_default_cflags(void)
{
@@ -168,7 +168,8 @@ AP_DECLARE(int) ap_regcomp(ap_regex_t * preg, const char *pattern, int cflags)
int errcode = 0;
int options = PCRE_DUPNAMES;
- cflags |= default_cflags;
+ if ((cflags & AP_REG_NO_DEFAULT) == 0)
+ cflags |= default_cflags;
if ((cflags & AP_REG_ICASE) != 0)
options |= PCRE_CASELESS;
if ((cflags & AP_REG_NEWLINE) != 0)
diff --git a/server/util_regex.c b/server/util_regex.c
index 2a30d68..5405f8d 100644
--- a/server/util_regex.c
+++ b/server/util_regex.c
@@ -94,6 +94,7 @@ AP_DECLARE(ap_rxplus_t*) ap_rxplus_compile(apr_pool_t *pool,
}
/* anything after the current delimiter is flags */
+ ret->flags = ap_regcomp_get_default_cflags() & AP_REG_DOLLAR_ENDONLY;
while (*++endp) {
switch (*endp) {
case 'i': ret->flags |= AP_REG_ICASE; break;
@@ -106,7 +107,7 @@ AP_DECLARE(ap_rxplus_t*) ap_rxplus_compile(apr_pool_t *pool,
default: break; /* we should probably be stricter here */
}
}
- if (ap_regcomp(&ret->rx, rxstr, ret->flags) == 0) {
+ if (ap_regcomp(&ret->rx, rxstr, AP_REG_NO_DEFAULT | ret->flags) == 0) {
apr_pool_cleanup_register(pool, &ret->rx, rxplus_cleanup,
apr_pool_cleanup_null);
}

View File

@ -1,36 +0,0 @@
--- a/modules/proxy/mod_proxy_uwsgi.c 2020/07/24 09:31:46 1880250
+++ b/modules/proxy/mod_proxy_uwsgi.c 2020/07/24 09:35:25 1880251
@@ -136,7 +136,7 @@
int j;
apr_size_t headerlen = 4;
- apr_uint16_t pktsize, keylen, vallen;
+ apr_size_t pktsize, keylen, vallen;
const char *script_name;
const char *path_info;
const char *auth;
@@ -178,6 +178,15 @@
headerlen += 2 + strlen(env[j].key) + 2 + strlen(env[j].val);
}
+ pktsize = headerlen - 4;
+ if (pktsize > APR_UINT16_MAX) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10259)
+ "can't send headers to %s:%u: packet size too "
+ "large (%" APR_SIZE_T_FMT ")",
+ conn->hostname, conn->port, pktsize);
+ return HTTP_INTERNAL_SERVER_ERROR;
+ }
+
ptr = buf = apr_palloc(r->pool, headerlen);
ptr += 4;
@@ -196,8 +205,6 @@
ptr += vallen;
}
- pktsize = headerlen - 4;
-
buf[0] = 0;
buf[1] = (apr_byte_t) (pktsize & 0xff);
buf[2] = (apr_byte_t) ((pktsize >> 8) & 0xff);

View File

@ -1,12 +0,0 @@
diff --git a/modules/proxy/mod_proxy_http.c b/modules/proxy/mod_proxy_http.c
index 5786ea8..7da9bde 100644
--- a/modules/proxy/mod_proxy_http.c
+++ b/modules/proxy/mod_proxy_http.c
@@ -637,7 +637,6 @@ static int ap_proxy_http_prefetch(proxy_http_req_t *req,
"chunked body with Content-Length (C-L ignored)",
c->client_ip, c->remote_host ? c->remote_host: "");
req->old_cl_val = NULL;
- origin->keepalive = AP_CONN_CLOSE;
p_conn->close = 1;
}

View File

@ -1,68 +0,0 @@
--- a/modules/proxy/mod_proxy_ftp.c 2020/02/07 17:01:07 1873744
+++ b/modules/proxy/mod_proxy_ftp.c 2020/02/07 17:04:45 1873745
@@ -218,7 +218,7 @@
* (EBCDIC) machines either.
*/
static apr_status_t ftp_string_read(conn_rec *c, apr_bucket_brigade *bb,
- char *buff, apr_size_t bufflen, int *eos)
+ char *buff, apr_size_t bufflen, int *eos, apr_size_t *outlen)
{
apr_bucket *e;
apr_status_t rv;
@@ -230,6 +230,7 @@
/* start with an empty string */
buff[0] = 0;
*eos = 0;
+ *outlen = 0;
/* loop through each brigade */
while (!found) {
@@ -273,6 +274,7 @@
if (len > 0) {
memcpy(pos, response, len);
pos += len;
+ *outlen += len;
}
}
apr_bucket_delete(e);
@@ -385,28 +387,36 @@
char buff[5];
char *mb = msgbuf, *me = &msgbuf[msglen];
apr_status_t rv;
+ apr_size_t nread;
+
int eos;
- if (APR_SUCCESS != (rv = ftp_string_read(ftp_ctrl, bb, response, sizeof(response), &eos))) {
+ if (APR_SUCCESS != (rv = ftp_string_read(ftp_ctrl, bb, response, sizeof(response), &eos, &nread))) {
return -1;
}
/*
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL, APLOGNO(03233)
"<%s", response);
*/
+ if (nread < 4) {
+ ap_log_error(APLOG_MARK, APLOG_INFO, 0, NULL, APLOGNO(10229) "Malformed FTP response '%s'", response);
+ *mb = '\0';
+ return -1;
+ }
+
if (!apr_isdigit(response[0]) || !apr_isdigit(response[1]) ||
- !apr_isdigit(response[2]) || (response[3] != ' ' && response[3] != '-'))
+ !apr_isdigit(response[2]) || (response[3] != ' ' && response[3] != '-'))
status = 0;
else
status = 100 * response[0] + 10 * response[1] + response[2] - 111 * '0';
mb = apr_cpystrn(mb, response + 4, me - mb);
- if (response[3] == '-') {
+ if (response[3] == '-') { /* multi-line reply "123-foo\nbar\n123 baz" */
memcpy(buff, response, 3);
buff[3] = ' ';
do {
- if (APR_SUCCESS != (rv = ftp_string_read(ftp_ctrl, bb, response, sizeof(response), &eos))) {
+ if (APR_SUCCESS != (rv = ftp_string_read(ftp_ctrl, bb, response, sizeof(response), &eos, &nread))) {
return -1;
}
mb = apr_cpystrn(mb, response + (' ' == response[0] ? 1 : 4), me - mb);

View File

@ -1,21 +0,0 @@
diff --git a/modules/aaa/mod_auth_digest.c b/modules/aaa/mod_auth_digest.c
index b760941..0825b1b 100644
--- a/modules/aaa/mod_auth_digest.c
+++ b/modules/aaa/mod_auth_digest.c
@@ -1422,9 +1422,14 @@ static int check_nonce(request_rec *r, digest_header_rec *resp,
time_rec nonce_time;
char tmp, hash[NONCE_HASH_LEN+1];
- if (strlen(resp->nonce) != NONCE_LEN) {
+ /* Since the time part of the nonce is a base64 encoding of an
+ * apr_time_t (8 bytes), it should end with a '=', fail early otherwise.
+ */
+ if (strlen(resp->nonce) != NONCE_LEN
+ || resp->nonce[NONCE_TIME_LEN - 1] != '=') {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01775)
- "invalid nonce %s received - length is not %d",
+ "invalid nonce '%s' received - length is not %d "
+ "or time encoding is incorrect",
resp->nonce, NONCE_LEN);
note_digest_auth_failure(r, conf, resp, 1);
return HTTP_UNAUTHORIZED;

View File

@ -1,14 +0,0 @@
diff --git a/modules/session/mod_session.c b/modules/session/mod_session.c
index 7ee477c..049255d 100644
--- a/modules/session/mod_session.c
+++ b/modules/session/mod_session.c
@@ -404,8 +404,8 @@ static apr_status_t session_identity_decode(request_rec * r, session_rec * z)
char *plast = NULL;
const char *psep = "=";
char *key = apr_strtok(pair, psep, &plast);
- char *val = apr_strtok(NULL, psep, &plast);
if (key && *key) {
+ char *val = apr_strtok(NULL, sep, &plast);
if (!val || !*val) {
apr_table_unset(z->entries, key);
}

View File

@ -1,13 +0,0 @@
diff --git a/modules/session/mod_session.c b/modules/session/mod_session.c
index 049255d..af70f6b 100644
--- a/modules/session/mod_session.c
+++ b/modules/session/mod_session.c
@@ -317,7 +317,7 @@ static apr_status_t ap_session_set(request_rec * r, session_rec * z,
static int identity_count(void *v, const char *key, const char *val)
{
int *count = v;
- *count += strlen(key) * 3 + strlen(val) * 3 + 1;
+ *count += strlen(key) * 3 + strlen(val) * 3 + 2;
return 1;
}

View File

@ -1,44 +0,0 @@
diff --git a/server/request.c b/server/request.c
index d5c558a..18625af 100644
--- a/server/request.c
+++ b/server/request.c
@@ -1419,7 +1419,20 @@ AP_DECLARE(int) ap_location_walk(request_rec *r)
cache = prep_walk_cache(AP_NOTE_LOCATION_WALK, r);
cached = (cache->cached != NULL);
- entry_uri = r->uri;
+
+ /*
+ * When merge_slashes is set to AP_CORE_CONFIG_OFF the slashes in r->uri
+ * have not been merged. But for Location walks we always go with merged
+ * slashes no matter what merge_slashes is set to.
+ */
+ if (sconf->merge_slashes != AP_CORE_CONFIG_OFF) {
+ entry_uri = r->uri;
+ }
+ else {
+ char *uri = apr_pstrdup(r->pool, r->uri);
+ ap_no2slash(uri);
+ entry_uri = uri;
+ }
/* If we have an cache->cached location that matches r->uri,
* and the vhost's list of locations hasn't changed, we can skip
@@ -1486,7 +1499,7 @@ AP_DECLARE(int) ap_location_walk(request_rec *r)
pmatch = apr_palloc(rxpool, nmatch*sizeof(ap_regmatch_t));
}
- if (ap_regexec(entry_core->r, entry_uri, nmatch, pmatch, 0)) {
+ if (ap_regexec(entry_core->r, r->uri, nmatch, pmatch, 0)) {
continue;
}
@@ -1496,7 +1509,7 @@ AP_DECLARE(int) ap_location_walk(request_rec *r)
apr_table_setn(r->subprocess_env,
((const char **)entry_core->refs->elts)[i],
apr_pstrndup(r->pool,
- entry_uri + pmatch[i].rm_so,
+ r->uri + pmatch[i].rm_so,
pmatch[i].rm_eo - pmatch[i].rm_so));
}
}

View File

@ -1,706 +0,0 @@
diff --git a/include/http_core.h b/include/http_core.h
index 8e10988..3ba8069 100644
--- a/include/http_core.h
+++ b/include/http_core.h
@@ -741,6 +741,7 @@ typedef struct {
#define AP_HTTP_METHODS_REGISTERED 2
char http_methods;
unsigned int merge_slashes;
+ unsigned int strict_host_check;
} core_server_config;
/* for AddOutputFiltersByType in core.c */
@@ -769,6 +770,11 @@ AP_DECLARE(void) ap_set_server_protocol(server_rec* s, const char* proto);
typedef struct core_output_filter_ctx core_output_filter_ctx_t;
typedef struct core_filter_ctx core_ctx_t;
+struct core_filter_ctx {
+ apr_bucket_brigade *b;
+ apr_bucket_brigade *tmpbb;
+};
+
typedef struct core_net_rec {
/** Connection to the client */
apr_socket_t *client_socket;
diff --git a/include/http_protocol.h b/include/http_protocol.h
index 11c7b2d..e7abdd9 100644
--- a/include/http_protocol.h
+++ b/include/http_protocol.h
@@ -53,6 +53,13 @@ AP_DECLARE_DATA extern ap_filter_rec_t *ap_old_write_func;
* or control the ones that eventually do.
*/
+/**
+ * Read an empty request and set reasonable defaults.
+ * @param c The current connection
+ * @return The new request_rec
+ */
+AP_DECLARE(request_rec *) ap_create_request(conn_rec *c);
+
/**
* Read a request and fill in the fields.
* @param c The current connection
@@ -60,6 +67,20 @@ AP_DECLARE_DATA extern ap_filter_rec_t *ap_old_write_func;
*/
request_rec *ap_read_request(conn_rec *c);
+/**
+ * Parse and validate the request line.
+ * @param r The current request
+ * @return 1 on success, 0 on failure
+ */
+AP_DECLARE(int) ap_parse_request_line(request_rec *r);
+
+/**
+ * Validate the request header and select vhost.
+ * @param r The current request
+ * @return 1 on success, 0 on failure
+ */
+AP_DECLARE(int) ap_check_request_header(request_rec *r);
+
/**
* Read the mime-encoded headers.
* @param r The current request
diff --git a/include/http_vhost.h b/include/http_vhost.h
index 473c9c7..d2d9c97 100644
--- a/include/http_vhost.h
+++ b/include/http_vhost.h
@@ -99,6 +99,19 @@ AP_DECLARE(void) ap_update_vhost_given_ip(conn_rec *conn);
*/
AP_DECLARE(void) ap_update_vhost_from_headers(request_rec *r);
+/**
+ * Updates r->server with the best name-based virtual host match, within
+ * the chain of matching virtual hosts selected by ap_update_vhost_given_ip.
+ * @param r The current request
+ * @param require_match 1 to return an HTTP error if the requested hostname is
+ * not explicitly matched to a VirtualHost.
+ * @return return HTTP_OK unless require_match was specified and the requested
+ * hostname did not match any ServerName, ServerAlias, or VirtualHost
+ * address-spec.
+ */
+AP_DECLARE(int) ap_update_vhost_from_headers_ex(request_rec *r, int require_match);
+
+
/**
* Match the host in the header with the hostname of the server for this
* request.
diff --git a/server/core.c b/server/core.c
index 84e80f2..23abf57 100644
--- a/server/core.c
+++ b/server/core.c
@@ -498,6 +498,8 @@ static void *create_core_server_config(apr_pool_t *a, server_rec *s)
conf->protocols = apr_array_make(a, 5, sizeof(const char *));
conf->protocols_honor_order = -1;
+ conf->strict_host_check= AP_CORE_CONFIG_UNSET;
+
return (void *)conf;
}
@@ -565,6 +567,12 @@ static void *merge_core_server_configs(apr_pool_t *p, void *basev, void *virtv)
AP_CORE_MERGE_FLAG(merge_slashes, conf, base, virt);
+ conf->strict_host_check = (virt->strict_host_check != AP_CORE_CONFIG_UNSET)
+ ? virt->strict_host_check
+ : base->strict_host_check;
+
+ AP_CORE_MERGE_FLAG(strict_host_check, conf, base, virt);
+
return conf;
}
@@ -4546,7 +4554,10 @@ AP_INIT_TAKE2("CGIVar", set_cgi_var, NULL, OR_FILEINFO,
AP_INIT_FLAG("QualifyRedirectURL", set_qualify_redirect_url, NULL, OR_FILEINFO,
"Controls whether HTTP authorization headers, normally hidden, will "
"be passed to scripts"),
-
+AP_INIT_FLAG("StrictHostCheck", set_core_server_flag,
+ (void *)APR_OFFSETOF(core_server_config, strict_host_check),
+ RSRC_CONF,
+ "Controls whether a hostname match is required"),
AP_INIT_TAKE1("ForceType", ap_set_string_slot_lower,
(void *)APR_OFFSETOF(core_dir_config, mime_type), OR_FILEINFO,
"a mime type that overrides other configured type"),
@@ -5581,4 +5592,3 @@ AP_DECLARE_MODULE(core) = {
core_cmds, /* command apr_table_t */
register_hooks /* register hooks */
};
-
diff --git a/server/core_filters.c b/server/core_filters.c
index a6c2bd6..e08801f 100644
--- a/server/core_filters.c
+++ b/server/core_filters.c
@@ -84,11 +84,6 @@ struct core_output_filter_ctx {
apr_size_t bytes_written;
};
-struct core_filter_ctx {
- apr_bucket_brigade *b;
- apr_bucket_brigade *tmpbb;
-};
-
apr_status_t ap_core_input_filter(ap_filter_t *f, apr_bucket_brigade *b,
ap_input_mode_t mode, apr_read_type_e block,
diff --git a/server/protocol.c b/server/protocol.c
index 8d1fdd2..430d91e 100644
--- a/server/protocol.c
+++ b/server/protocol.c
@@ -609,8 +609,15 @@ AP_CORE_DECLARE(void) ap_parse_uri(request_rec *r, const char *uri)
}
r->args = r->parsed_uri.query;
- r->uri = r->parsed_uri.path ? r->parsed_uri.path
- : apr_pstrdup(r->pool, "/");
+ if (r->parsed_uri.path) {
+ r->uri = r->parsed_uri.path;
+ }
+ else if (r->method_number == M_OPTIONS) {
+ r->uri = apr_pstrdup(r->pool, "*");
+ }
+ else {
+ r->uri = apr_pstrdup(r->pool, "/");
+ }
#if defined(OS2) || defined(WIN32)
/* Handle path translations for OS/2 and plug security hole.
@@ -645,13 +652,6 @@ static int field_name_len(const char *field)
static int read_request_line(request_rec *r, apr_bucket_brigade *bb)
{
- enum {
- rrl_none, rrl_badmethod, rrl_badwhitespace, rrl_excesswhitespace,
- rrl_missinguri, rrl_baduri, rrl_badprotocol, rrl_trailingtext,
- rrl_badmethod09, rrl_reject09
- } deferred_error = rrl_none;
- char *ll;
- char *uri;
apr_size_t len;
int num_blank_lines = DEFAULT_LIMIT_BLANK_LINES;
core_server_config *conf = ap_get_core_module_config(r->server->module_config);
@@ -711,6 +711,20 @@ static int read_request_line(request_rec *r, apr_bucket_brigade *bb)
}
r->request_time = apr_time_now();
+ return 1;
+}
+
+AP_DECLARE(int) ap_parse_request_line(request_rec *r)
+{
+ core_server_config *conf = ap_get_core_module_config(r->server->module_config);
+ int strict = (conf->http_conformance != AP_HTTP_CONFORMANCE_UNSAFE);
+ enum {
+ rrl_none, rrl_badmethod, rrl_badwhitespace, rrl_excesswhitespace,
+ rrl_missinguri, rrl_baduri, rrl_badprotocol, rrl_trailingtext,
+ rrl_badmethod09, rrl_reject09
+ } deferred_error = rrl_none;
+ apr_size_t len = 0;
+ char *uri, *ll;
r->method = r->the_request;
@@ -742,7 +756,6 @@ static int read_request_line(request_rec *r, apr_bucket_brigade *bb)
if (deferred_error == rrl_none)
deferred_error = rrl_missinguri;
r->protocol = uri = "";
- len = 0;
goto rrl_done;
}
else if (strict && ll[0] && apr_isspace(ll[1])
@@ -773,7 +786,6 @@ static int read_request_line(request_rec *r, apr_bucket_brigade *bb)
/* Verify URI terminated with a single SP, or mark as specific error */
if (!ll) {
r->protocol = "";
- len = 0;
goto rrl_done;
}
else if (strict && ll[0] && apr_isspace(ll[1])
@@ -866,6 +878,14 @@ rrl_done:
r->header_only = 1;
ap_parse_uri(r, uri);
+ if (r->status == HTTP_OK
+ && (r->parsed_uri.path != NULL)
+ && (r->parsed_uri.path[0] != '/')
+ && (r->method_number != M_OPTIONS
+ || strcmp(r->parsed_uri.path, "*") != 0)) {
+ /* Invalid request-target per RFC 7230 section 5.3 */
+ r->status = HTTP_BAD_REQUEST;
+ }
/* With the request understood, we can consider HTTP/0.9 specific errors */
if (r->proto_num == HTTP_VERSION(0, 9) && deferred_error == rrl_none) {
@@ -973,6 +993,79 @@ rrl_failed:
return 0;
}
+AP_DECLARE(int) ap_check_request_header(request_rec *r)
+{
+ core_server_config *conf;
+ int strict_host_check;
+ const char *expect;
+ int access_status;
+
+ conf = ap_get_core_module_config(r->server->module_config);
+
+ /* update what we think the virtual host is based on the headers we've
+ * now read. may update status.
+ */
+ strict_host_check = (conf->strict_host_check == AP_CORE_CONFIG_ON);
+ access_status = ap_update_vhost_from_headers_ex(r, strict_host_check);
+ if (strict_host_check && access_status != HTTP_OK) {
+ if (r->server == ap_server_conf) {
+ ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(10156)
+ "Requested hostname '%s' did not match any ServerName/ServerAlias "
+ "in the global server configuration ", r->hostname);
+ }
+ else {
+ ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(10157)
+ "Requested hostname '%s' did not match any ServerName/ServerAlias "
+ "in the matching virtual host (default vhost for "
+ "current connection is %s:%u)",
+ r->hostname, r->server->defn_name, r->server->defn_line_number);
+ }
+ r->status = access_status;
+ }
+ if (r->status != HTTP_OK) {
+ return 0;
+ }
+
+ if ((!r->hostname && (r->proto_num >= HTTP_VERSION(1, 1)))
+ || ((r->proto_num == HTTP_VERSION(1, 1))
+ && !apr_table_get(r->headers_in, "Host"))) {
+ /*
+ * Client sent us an HTTP/1.1 or later request without telling us the
+ * hostname, either with a full URL or a Host: header. We therefore
+ * need to (as per the 1.1 spec) send an error. As a special case,
+ * HTTP/1.1 mentions twice (S9, S14.23) that a request MUST contain
+ * a Host: header, and the server MUST respond with 400 if it doesn't.
+ */
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00569)
+ "client sent HTTP/1.1 request without hostname "
+ "(see RFC2616 section 14.23): %s", r->uri);
+ r->status = HTTP_BAD_REQUEST;
+ return 0;
+ }
+
+ if (((expect = apr_table_get(r->headers_in, "Expect")) != NULL)
+ && (expect[0] != '\0')) {
+ /*
+ * The Expect header field was added to HTTP/1.1 after RFC 2068
+ * as a means to signal when a 100 response is desired and,
+ * unfortunately, to signal a poor man's mandatory extension that
+ * the server must understand or return 417 Expectation Failed.
+ */
+ if (ap_cstr_casecmp(expect, "100-continue") == 0) {
+ r->expecting_100 = 1;
+ }
+ else {
+ ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(00570)
+ "client sent an unrecognized expectation value "
+ "of Expect: %s", expect);
+ r->status = HTTP_EXPECTATION_FAILED;
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
static int table_do_fn_check_lengths(void *r_, const char *key,
const char *value)
{
@@ -1256,16 +1349,10 @@ AP_DECLARE(void) ap_get_mime_headers(request_rec *r)
apr_brigade_destroy(tmp_bb);
}
-request_rec *ap_read_request(conn_rec *conn)
+AP_DECLARE(request_rec *) ap_create_request(conn_rec *conn)
{
request_rec *r;
apr_pool_t *p;
- const char *expect;
- int access_status;
- apr_bucket_brigade *tmp_bb;
- apr_socket_t *csd;
- apr_interval_time_t cur_timeout;
-
apr_pool_create(&p, conn->pool);
apr_pool_tag(p, "request");
@@ -1304,6 +1391,7 @@ request_rec *ap_read_request(conn_rec *conn)
r->read_body = REQUEST_NO_BODY;
r->status = HTTP_OK; /* Until further notice */
+ r->header_only = 0;
r->the_request = NULL;
/* Begin by presuming any module can make its own path_info assumptions,
@@ -1314,12 +1402,33 @@ request_rec *ap_read_request(conn_rec *conn)
r->useragent_addr = conn->client_addr;
r->useragent_ip = conn->client_ip;
+ return r;
+}
+
+/* Apply the server's timeout/config to the connection/request. */
+static void apply_server_config(request_rec *r)
+{
+ apr_socket_t *csd;
+
+ csd = ap_get_conn_socket(r->connection);
+ apr_socket_timeout_set(csd, r->server->timeout);
+
+ r->per_dir_config = r->server->lookup_defaults;
+}
+
+request_rec *ap_read_request(conn_rec *conn)
+{
+ int access_status;
+ apr_bucket_brigade *tmp_bb;
+
+ request_rec *r = ap_create_request(conn);
tmp_bb = apr_brigade_create(r->pool, r->connection->bucket_alloc);
ap_run_pre_read_request(r, conn);
/* Get the request... */
- if (!read_request_line(r, tmp_bb)) {
+ if (!read_request_line(r, tmp_bb) || !ap_parse_request_line(r)) {
+ apr_brigade_cleanup(tmp_bb);
switch (r->status) {
case HTTP_REQUEST_URI_TOO_LARGE:
case HTTP_BAD_REQUEST:
@@ -1335,49 +1444,38 @@ request_rec *ap_read_request(conn_rec *conn)
"request failed: malformed request line");
}
access_status = r->status;
- r->status = HTTP_OK;
- ap_die(access_status, r);
- ap_update_child_status(conn->sbh, SERVER_BUSY_LOG, r);
- ap_run_log_transaction(r);
- r = NULL;
- apr_brigade_destroy(tmp_bb);
- goto traceout;
+ goto die_unusable_input;
+
case HTTP_REQUEST_TIME_OUT:
+ /* Just log, no further action on this connection. */
ap_update_child_status(conn->sbh, SERVER_BUSY_LOG, NULL);
if (!r->connection->keepalives)
ap_run_log_transaction(r);
- apr_brigade_destroy(tmp_bb);
- goto traceout;
- default:
- apr_brigade_destroy(tmp_bb);
- r = NULL;
- goto traceout;
+ break;
}
+ /* Not worth dying with. */
+ conn->keepalive = AP_CONN_CLOSE;
+ apr_pool_destroy(r->pool);
+ goto ignore;
}
+ apr_brigade_cleanup(tmp_bb);
/* We may have been in keep_alive_timeout mode, so toggle back
* to the normal timeout mode as we fetch the header lines,
* as necessary.
*/
- csd = ap_get_conn_socket(conn);
- apr_socket_timeout_get(csd, &cur_timeout);
- if (cur_timeout != conn->base_server->timeout) {
- apr_socket_timeout_set(csd, conn->base_server->timeout);
- cur_timeout = conn->base_server->timeout;
- }
+ apply_server_config(r);
if (!r->assbackwards) {
const char *tenc;
ap_get_mime_headers_core(r, tmp_bb);
+ apr_brigade_cleanup(tmp_bb);
if (r->status != HTTP_OK) {
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00567)
"request failed: error reading the headers");
- ap_send_error_response(r, 0);
- ap_update_child_status(conn->sbh, SERVER_BUSY_LOG, r);
- ap_run_log_transaction(r);
- apr_brigade_destroy(tmp_bb);
- goto traceout;
+ access_status = r->status;
+ goto die_unusable_input;
}
tenc = apr_table_get(r->headers_in, "Transfer-Encoding");
@@ -1393,13 +1491,8 @@ request_rec *ap_read_request(conn_rec *conn)
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02539)
"client sent unknown Transfer-Encoding "
"(%s): %s", tenc, r->uri);
- r->status = HTTP_BAD_REQUEST;
- conn->keepalive = AP_CONN_CLOSE;
- ap_send_error_response(r, 0);
- ap_update_child_status(conn->sbh, SERVER_BUSY_LOG, r);
- ap_run_log_transaction(r);
- apr_brigade_destroy(tmp_bb);
- goto traceout;
+ access_status = HTTP_BAD_REQUEST;
+ goto die_unusable_input;
}
/* http://tools.ietf.org/html/draft-ietf-httpbis-p1-messaging-23
@@ -1412,88 +1505,81 @@ request_rec *ap_read_request(conn_rec *conn)
}
}
- apr_brigade_destroy(tmp_bb);
-
- /* update what we think the virtual host is based on the headers we've
- * now read. may update status.
- */
- ap_update_vhost_from_headers(r);
- access_status = r->status;
-
- /* Toggle to the Host:-based vhost's timeout mode to fetch the
- * request body and send the response body, if needed.
- */
- if (cur_timeout != r->server->timeout) {
- apr_socket_timeout_set(csd, r->server->timeout);
- cur_timeout = r->server->timeout;
- }
-
- /* we may have switched to another server */
- r->per_dir_config = r->server->lookup_defaults;
-
- if ((!r->hostname && (r->proto_num >= HTTP_VERSION(1, 1)))
- || ((r->proto_num == HTTP_VERSION(1, 1))
- && !apr_table_get(r->headers_in, "Host"))) {
- /*
- * Client sent us an HTTP/1.1 or later request without telling us the
- * hostname, either with a full URL or a Host: header. We therefore
- * need to (as per the 1.1 spec) send an error. As a special case,
- * HTTP/1.1 mentions twice (S9, S14.23) that a request MUST contain
- * a Host: header, and the server MUST respond with 400 if it doesn't.
- */
- access_status = HTTP_BAD_REQUEST;
- ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00569)
- "client sent HTTP/1.1 request without hostname "
- "(see RFC2616 section 14.23): %s", r->uri);
- }
-
/*
* Add the HTTP_IN filter here to ensure that ap_discard_request_body
* called by ap_die and by ap_send_error_response works correctly on
* status codes that do not cause the connection to be dropped and
* in situations where the connection should be kept alive.
*/
-
ap_add_input_filter_handle(ap_http_input_filter_handle,
NULL, r, r->connection);
- if (access_status != HTTP_OK
- || (access_status = ap_run_post_read_request(r))) {
- ap_die(access_status, r);
- ap_update_child_status(conn->sbh, SERVER_BUSY_LOG, r);
- ap_run_log_transaction(r);
- r = NULL;
- goto traceout;
+ /* Validate Host/Expect headers and select vhost. */
+ if (!ap_check_request_header(r)) {
+ /* we may have switched to another server still */
+ apply_server_config(r);
+ access_status = r->status;
+ goto die_before_hooks;
}
- if (((expect = apr_table_get(r->headers_in, "Expect")) != NULL)
- && (expect[0] != '\0')) {
- /*
- * The Expect header field was added to HTTP/1.1 after RFC 2068
- * as a means to signal when a 100 response is desired and,
- * unfortunately, to signal a poor man's mandatory extension that
- * the server must understand or return 417 Expectation Failed.
- */
- if (strcasecmp(expect, "100-continue") == 0) {
- r->expecting_100 = 1;
- }
- else {
- r->status = HTTP_EXPECTATION_FAILED;
- ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(00570)
- "client sent an unrecognized expectation value of "
- "Expect: %s", expect);
- ap_send_error_response(r, 0);
- ap_update_child_status(conn->sbh, SERVER_BUSY_LOG, r);
- ap_run_log_transaction(r);
- goto traceout;
- }
+ /* we may have switched to another server */
+ apply_server_config(r);
+
+ if ((access_status = ap_run_post_read_request(r))) {
+ goto die;
}
- AP_READ_REQUEST_SUCCESS((uintptr_t)r, (char *)r->method, (char *)r->uri, (char *)r->server->defn_name, r->status);
+ AP_READ_REQUEST_SUCCESS((uintptr_t)r, (char *)r->method,
+ (char *)r->uri, (char *)r->server->defn_name,
+ r->status);
+
return r;
- traceout:
+
+ /* Everything falls through on failure */
+
+die_unusable_input:
+ /* Input filters are in an undeterminate state, cleanup (including
+ * CORE_IN's socket) such that any further attempt to read is EOF.
+ */
+ {
+ ap_filter_t *f = conn->input_filters;
+ while (f) {
+ if (f->frec == ap_core_input_filter_handle) {
+ core_net_rec *net = f->ctx;
+ apr_brigade_cleanup(net->in_ctx->b);
+ break;
+ }
+ ap_remove_input_filter(f);
+ f = f->next;
+ }
+ conn->input_filters = r->input_filters = f;
+ conn->keepalive = AP_CONN_CLOSE;
+ }
+
+die_before_hooks:
+ /* First call to ap_die() (non recursive) */
+ r->status = HTTP_OK;
+
+die:
+ ap_die(access_status, r);
+
+ /* ap_die() sent the response through the output filters, we must now
+ * end the request with an EOR bucket for stream/pipeline accounting.
+ */
+ {
+ apr_bucket_brigade *eor_bb;
+ eor_bb = apr_brigade_create(conn->pool, conn->bucket_alloc);
+ APR_BRIGADE_INSERT_TAIL(eor_bb,
+ ap_bucket_eor_create(conn->bucket_alloc, r));
+ ap_pass_brigade(conn->output_filters, eor_bb);
+ apr_brigade_cleanup(eor_bb);
+ }
+
+ignore:
+ r = NULL;
+
AP_READ_REQUEST_FAILURE((uintptr_t)r);
- return r;
+ return NULL;
}
/* if a request with a body creates a subrequest, remove original request's
diff --git a/server/vhost.c b/server/vhost.c
index b23b2dd..6e233b5 100644
--- a/server/vhost.c
+++ b/server/vhost.c
@@ -34,6 +34,7 @@
#include "http_vhost.h"
#include "http_protocol.h"
#include "http_core.h"
+#include "http_main.h"
#if APR_HAVE_ARPA_INET_H
#include <arpa/inet.h>
@@ -973,7 +974,13 @@ AP_DECLARE(int) ap_matches_request_vhost(request_rec *r, const char *host,
}
-static void check_hostalias(request_rec *r)
+/*
+ * Updates r->server from ServerName/ServerAlias. Per the interaction
+ * of ip and name-based vhosts, it only looks in the best match from the
+ * connection-level ip-based matching.
+ * Returns HTTP_BAD_REQUEST if there was no match.
+ */
+static int update_server_from_aliases(request_rec *r)
{
/*
* Even if the request has a Host: header containing a port we ignore
@@ -1050,11 +1057,18 @@ static void check_hostalias(request_rec *r)
goto found;
}
- return;
+ if (!r->connection->vhost_lookup_data) {
+ if (matches_aliases(r->server, host)) {
+ s = r->server;
+ goto found;
+ }
+ }
+ return HTTP_BAD_REQUEST;
found:
/* s is the first matching server, we're done */
r->server = s;
+ return HTTP_OK;
}
@@ -1071,7 +1085,7 @@ static void check_serverpath(request_rec *r)
* This is in conjunction with the ServerPath code in http_core, so we
* get the right host attached to a non- Host-sending request.
*
- * See the comment in check_hostalias about how each vhost can be
+ * See the comment in update_server_from_aliases about how each vhost can be
* listed multiple times.
*/
@@ -1134,11 +1148,17 @@ static APR_INLINE const char *construct_host_header(request_rec *r,
}
AP_DECLARE(void) ap_update_vhost_from_headers(request_rec *r)
+{
+ ap_update_vhost_from_headers_ex(r, 0);
+}
+
+AP_DECLARE(int) ap_update_vhost_from_headers_ex(request_rec *r, int require_match)
{
core_server_config *conf = ap_get_core_module_config(r->server->module_config);
const char *host_header = apr_table_get(r->headers_in, "Host");
int is_v6literal = 0;
int have_hostname_from_url = 0;
+ int rc = HTTP_OK;
if (r->hostname) {
/*
@@ -1151,8 +1171,8 @@ AP_DECLARE(void) ap_update_vhost_from_headers(request_rec *r)
else if (host_header != NULL) {
is_v6literal = fix_hostname(r, host_header, conf->http_conformance);
}
- if (r->status != HTTP_OK)
- return;
+ if (!require_match && r->status != HTTP_OK)
+ return HTTP_OK;
if (conf->http_conformance != AP_HTTP_CONFORMANCE_UNSAFE) {
/*
@@ -1173,10 +1193,16 @@ AP_DECLARE(void) ap_update_vhost_from_headers(request_rec *r)
/* check if we tucked away a name_chain */
if (r->connection->vhost_lookup_data) {
if (r->hostname)
- check_hostalias(r);
+ rc = update_server_from_aliases(r);
else
check_serverpath(r);
}
+ else if (require_match && r->hostname) {
+ /* check the base server config */
+ rc = update_server_from_aliases(r);
+ }
+
+ return rc;
}
/**

View File

@ -1,13 +0,0 @@
diff --git a/server/scoreboard.c b/server/scoreboard.c
index 23e3d70..7b01bdf 100644
--- a/server/scoreboard.c
+++ b/server/scoreboard.c
@@ -376,7 +376,7 @@ AP_DECLARE(void) ap_increment_counts(ap_sb_handle_t *sb, request_rec *r)
if (pfn_ap_logio_get_last_bytes != NULL) {
bytes = pfn_ap_logio_get_last_bytes(r->connection);
}
- else if (r->method_number == M_GET && r->method[0] == 'H') {
+ else if (r->method_number == M_GET && r->method && r->method[0] == 'H') {
bytes = 0;
}
else {

View File

@ -1,45 +0,0 @@
diff --git a/modules/proxy/mod_proxy_uwsgi.c b/modules/proxy/mod_proxy_uwsgi.c
index 792d35e..9dcbed1 100644
--- a/modules/proxy/mod_proxy_uwsgi.c
+++ b/modules/proxy/mod_proxy_uwsgi.c
@@ -453,11 +453,8 @@ static int uwsgi_handler(request_rec *r, proxy_worker * worker,
const char *proxyname, apr_port_t proxyport)
{
int status;
- int delta = 0;
- int decode_status;
proxy_conn_rec *backend = NULL;
apr_pool_t *p = r->pool;
- size_t w_len;
char server_portstr[32];
char *u_path_info;
apr_uri_t *uri;
@@ -469,23 +466,14 @@ static int uwsgi_handler(request_rec *r, proxy_worker * worker,
uri = apr_palloc(r->pool, sizeof(*uri));
- /* ADD PATH_INFO */
-#if AP_MODULE_MAGIC_AT_LEAST(20111130,0)
- w_len = strlen(worker->s->name);
-#else
- w_len = strlen(worker->name);
-#endif
- u_path_info = r->filename + 6 + w_len;
- if (u_path_info[0] != '/') {
- delta = 1;
- }
- decode_status = ap_unescape_url(url + w_len - delta);
- if (decode_status) {
+ /* ADD PATH_INFO (unescaped) */
+ u_path_info = ap_strchr(url + sizeof(UWSGI_SCHEME) + 2, '/');
+ if (!u_path_info || ap_unescape_url(u_path_info) != OK) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10100)
- "unable to decode uri: %s", url + w_len - delta);
+ "unable to decode uwsgi uri: %s", url);
return HTTP_INTERNAL_SERVER_ERROR;
}
- apr_table_add(r->subprocess_env, "PATH_INFO", url + w_len - delta);
+ apr_table_add(r->subprocess_env, "PATH_INFO", u_path_info);
/* Create space for state information */

View File

@ -1,21 +0,0 @@
diff --git a/server/util.c b/server/util.c
index e0c558c..2a5dd04 100644
--- a/server/util.c
+++ b/server/util.c
@@ -2460,13 +2460,12 @@ AP_DECLARE(char *) ap_escape_quotes(apr_pool_t *p, const char *instring)
* in front of every " that doesn't already have one.
*/
while (*inchr != '\0') {
- if ((*inchr == '\\') && (inchr[1] != '\0')) {
- *outchr++ = *inchr++;
- *outchr++ = *inchr++;
- }
if (*inchr == '"') {
*outchr++ = '\\';
}
+ if ((*inchr == '\\') && (inchr[1] != '\0')) {
+ *outchr++ = *inchr++;
+ }
if (*inchr != '\0') {
*outchr++ = *inchr++;
}

View File

@ -1,126 +0,0 @@
diff --git a/modules/mappers/mod_rewrite.c b/modules/mappers/mod_rewrite.c
index fb897a9..38dbb24 100644
--- a/modules/mappers/mod_rewrite.c
+++ b/modules/mappers/mod_rewrite.c
@@ -619,6 +619,13 @@ static unsigned is_absolute_uri(char *uri, int *supportsqs)
return 6;
}
break;
+
+ case 'u':
+ case 'U':
+ if (!ap_cstr_casecmpn(uri, "nix:", 4)) { /* unix: */
+ *sqs = 1;
+ return (uri[4] == '/' && uri[5] == '/') ? 7 : 5;
+ }
}
return 0;
diff --git a/modules/proxy/mod_proxy.c b/modules/proxy/mod_proxy.c
index f383996..6a9ef55 100644
--- a/modules/proxy/mod_proxy.c
+++ b/modules/proxy/mod_proxy.c
@@ -1717,7 +1717,8 @@ PROXY_DECLARE(const char *) ap_proxy_de_socketfy(apr_pool_t *p, const char *url)
* the UDS path... ignore it
*/
if (!strncasecmp(url, "unix:", 5) &&
- ((ptr = ap_strchr_c(url, '|')) != NULL)) {
+ ((ptr = ap_strchr_c(url + 5, '|')) != NULL)) {
+
/* move past the 'unix:...|' UDS path info */
const char *ret, *c;
diff --git a/modules/proxy/proxy_util.c b/modules/proxy/proxy_util.c
index 7714b6c..3dd570c 100644
--- a/modules/proxy/proxy_util.c
+++ b/modules/proxy/proxy_util.c
@@ -2084,33 +2084,45 @@ static int ap_proxy_retry_worker(const char *proxy_function, proxy_worker *worke
* were passed a UDS url (eg: from mod_proxy) and adjust uds_path
* as required.
*/
-static void fix_uds_filename(request_rec *r, char **url)
+static int fix_uds_filename(request_rec *r, char **url)
{
- char *ptr, *ptr2;
- if (!r || !r->filename) return;
+ char *uds_url = r->filename + 6, *origin_url;
if (!strncmp(r->filename, "proxy:", 6) &&
- (ptr2 = ap_strcasestr(r->filename, "unix:")) &&
- (ptr = ap_strchr(ptr2, '|'))) {
+ !ap_cstr_casecmpn(uds_url, "unix:", 5) &&
+ (origin_url = ap_strchr(uds_url + 5, '|'))) {
+ char *uds_path = NULL;
+ apr_size_t url_len;
apr_uri_t urisock;
apr_status_t rv;
- *ptr = '\0';
- rv = apr_uri_parse(r->pool, ptr2, &urisock);
- if (rv == APR_SUCCESS) {
- char *rurl = ptr+1;
- char *sockpath = ap_runtime_dir_relative(r->pool, urisock.path);
- apr_table_setn(r->notes, "uds_path", sockpath);
- *url = apr_pstrdup(r->pool, rurl); /* so we get the scheme for the uds */
- /* r->filename starts w/ "proxy:", so add after that */
- memmove(r->filename+6, rurl, strlen(rurl)+1);
- ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r,
- "*: rewrite of url due to UDS(%s): %s (%s)",
- sockpath, *url, r->filename);
- }
- else {
- *ptr = '|';
- }
- }
+
+ *origin_url = '\0';
+ rv = apr_uri_parse(r->pool, uds_url, &urisock);
+ *origin_url++ = '|';
+
+ if (rv == APR_SUCCESS && urisock.path && (!urisock.hostname
+ || !urisock.hostname[0])) {
+ uds_path = ap_runtime_dir_relative(r->pool, urisock.path);
+ }
+
+ if (!uds_path) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10292)
+ "Invalid proxy UDS filename (%s)", r->filename);
+ return 0;
+ }
+ apr_table_setn(r->notes, "uds_path", uds_path);
+
+ /* Remove the UDS path from *url and r->filename */
+ url_len = strlen(origin_url);
+ *url = apr_pstrmemdup(r->pool, origin_url, url_len);
+ memcpy(uds_url, *url, url_len + 1);
+
+ ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r,
+ "*: rewrite of url due to UDS(%s): %s (%s)",
+ uds_path, *url, r->filename);
+ }
+
+ return 1;
}
PROXY_DECLARE(int) ap_proxy_pre_request(proxy_worker **worker,
@@ -2128,7 +2140,9 @@ PROXY_DECLARE(int) ap_proxy_pre_request(proxy_worker **worker,
"%s: found worker %s for %s",
(*worker)->s->scheme, (*worker)->s->name, *url);
*balancer = NULL;
- fix_uds_filename(r, url);
+ if (!fix_uds_filename(r, url)) {
+ return HTTP_INTERNAL_SERVER_ERROR;
+ }
access_status = OK;
}
else if (r->proxyreq == PROXYREQ_PROXY) {
@@ -2159,7 +2173,9 @@ PROXY_DECLARE(int) ap_proxy_pre_request(proxy_worker **worker,
* regarding the Connection header in the request.
*/
apr_table_setn(r->subprocess_env, "proxy-nokeepalive", "1");
- fix_uds_filename(r, url);
+ if (!fix_uds_filename(r, url)) {
+ return HTTP_INTERNAL_SERVER_ERROR;
+ }
}
}
}

View File

@ -1,315 +0,0 @@
diff --git a/include/http_protocol.h b/include/http_protocol.h
index e7abdd9..e1572dc 100644
--- a/include/http_protocol.h
+++ b/include/http_protocol.h
@@ -96,6 +96,13 @@ AP_DECLARE(void) ap_get_mime_headers(request_rec *r);
AP_DECLARE(void) ap_get_mime_headers_core(request_rec *r,
apr_bucket_brigade *bb);
+/**
+ * Run post_read_request hook and validate.
+ * @param r The current request
+ * @return OK or HTTP_...
+ */
+AP_DECLARE(int) ap_post_read_request(request_rec *r);
+
/* Finish up stuff after a request */
/**
diff --git a/modules/http/http_request.c b/modules/http/http_request.c
index 9e7c4db..e873aab 100644
--- a/modules/http/http_request.c
+++ b/modules/http/http_request.c
@@ -681,7 +681,7 @@ static request_rec *internal_internal_redirect(const char *new_uri,
* to do their thing on internal redirects as well. Perhaps this is a
* misnamed function.
*/
- if ((access_status = ap_run_post_read_request(new))) {
+ if ((access_status = ap_post_read_request(new))) {
ap_die(access_status, new);
return NULL;
}
diff --git a/modules/proxy/mod_proxy.c b/modules/proxy/mod_proxy.c
index 6a9ef55..a6df1b8 100644
--- a/modules/proxy/mod_proxy.c
+++ b/modules/proxy/mod_proxy.c
@@ -584,11 +584,12 @@ static int proxy_detect(request_rec *r)
if (conf->req && r->parsed_uri.scheme) {
/* but it might be something vhosted */
- if (!(r->parsed_uri.hostname
- && !strcasecmp(r->parsed_uri.scheme, ap_http_scheme(r))
- && ap_matches_request_vhost(r, r->parsed_uri.hostname,
- (apr_port_t)(r->parsed_uri.port_str ? r->parsed_uri.port
- : ap_default_port(r))))) {
+ if (!r->parsed_uri.hostname
+ || ap_cstr_casecmp(r->parsed_uri.scheme, ap_http_scheme(r)) != 0
+ || !ap_matches_request_vhost(r, r->parsed_uri.hostname,
+ (apr_port_t)(r->parsed_uri.port_str
+ ? r->parsed_uri.port
+ : ap_default_port(r)))) {
r->proxyreq = PROXYREQ_PROXY;
r->uri = r->unparsed_uri;
r->filename = apr_pstrcat(r->pool, "proxy:", r->uri, NULL);
@@ -1750,6 +1751,7 @@ static const char *
struct proxy_alias *new;
char *f = cmd->path;
char *r = NULL;
+ const char *real;
char *word;
apr_table_t *params = apr_table_make(cmd->pool, 5);
const apr_array_header_t *arr;
@@ -1815,6 +1817,10 @@ static const char *
if (r == NULL) {
return "ProxyPass|ProxyPassMatch needs a path when not defined in a location";
}
+ if (!(real = ap_proxy_de_socketfy(cmd->temp_pool, r))) {
+ return "ProxyPass|ProxyPassMatch uses an invalid \"unix:\" URL";
+ }
+
/* if per directory, save away the single alias */
if (cmd->path) {
@@ -1831,7 +1837,7 @@ static const char *
}
new->fake = apr_pstrdup(cmd->pool, f);
- new->real = apr_pstrdup(cmd->pool, ap_proxy_de_socketfy(cmd->pool, r));
+ new->real = apr_pstrdup(cmd->pool, real);
new->flags = flags;
if (use_regex) {
new->regex = ap_pregcomp(cmd->pool, f, AP_REG_EXTENDED);
@@ -2316,6 +2322,7 @@ static const char *add_member(cmd_parms *cmd, void *dummy, const char *arg)
proxy_worker *worker;
char *path = cmd->path;
char *name = NULL;
+ const char *real;
char *word;
apr_table_t *params = apr_table_make(cmd->pool, 5);
const apr_array_header_t *arr;
@@ -2356,6 +2363,9 @@ static const char *add_member(cmd_parms *cmd, void *dummy, const char *arg)
return "BalancerMember must define balancer name when outside <Proxy > section";
if (!name)
return "BalancerMember must define remote proxy server";
+ if (!(real = ap_proxy_de_socketfy(cmd->temp_pool, name))) {
+ return "BalancerMember uses an invalid \"unix:\" URL";
+ }
ap_str_tolower(path); /* lowercase scheme://hostname */
@@ -2368,7 +2378,7 @@ static const char *add_member(cmd_parms *cmd, void *dummy, const char *arg)
}
/* Try to find existing worker */
- worker = ap_proxy_get_worker(cmd->temp_pool, balancer, conf, ap_proxy_de_socketfy(cmd->temp_pool, name));
+ worker = ap_proxy_get_worker(cmd->temp_pool, balancer, conf, real);
if (!worker) {
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server, APLOGNO(01147)
"Defining worker '%s' for balancer '%s'",
@@ -2457,7 +2467,13 @@ static const char *
}
}
else {
- worker = ap_proxy_get_worker(cmd->temp_pool, NULL, conf, ap_proxy_de_socketfy(cmd->temp_pool, name));
+ const char *real;
+
+ if (!(real = ap_proxy_de_socketfy(cmd->temp_pool, name))) {
+ return "ProxySet uses an invalid \"unix:\" URL";
+ }
+
+ worker = ap_proxy_get_worker(cmd->temp_pool, NULL, conf, real);
if (!worker) {
if (in_proxy_section) {
err = ap_proxy_define_worker(cmd->pool, &worker, NULL,
@@ -2599,8 +2615,14 @@ static const char *proxysection(cmd_parms *cmd, void *mconfig, const char *arg)
}
}
else {
+ const char *real;
+
+ if (!(real = ap_proxy_de_socketfy(cmd->temp_pool, conf->p))) {
+ return "<Proxy/ProxyMatch > uses an invalid \"unix:\" URL";
+ }
+
worker = ap_proxy_get_worker(cmd->temp_pool, NULL, sconf,
- ap_proxy_de_socketfy(cmd->temp_pool, (char*)conf->p));
+ real);
if (!worker) {
err = ap_proxy_define_worker(cmd->pool, &worker, NULL,
sconf, conf->p, 0);
diff --git a/modules/proxy/mod_proxy.h b/modules/proxy/mod_proxy.h
index fbbd508..dca6f69 100644
--- a/modules/proxy/mod_proxy.h
+++ b/modules/proxy/mod_proxy.h
@@ -713,6 +713,8 @@ typedef __declspec(dllimport) const char *
proxy_dir_conf *, const char *);
#endif
+#define AP_PROXY_WORKER_NO_UDS (1u << 3)
+
/* Connection pool API */
/**
@@ -725,6 +727,24 @@ typedef __declspec(dllimport) const char *
PROXY_DECLARE(char *) ap_proxy_worker_name(apr_pool_t *p,
proxy_worker *worker);
+
+/**
+ * Get the worker from proxy configuration, looking for either PREFIXED or
+ * MATCHED or both types of workers according to given mask
+ * @param p memory pool used for finding worker
+ * @param balancer the balancer that the worker belongs to
+ * @param conf current proxy server configuration
+ * @param url url to find the worker from
+ * @param mask bitmask of AP_PROXY_WORKER_IS_*
+ * @return proxy_worker or NULL if not found
+ */
+PROXY_DECLARE(proxy_worker *) ap_proxy_get_worker_ex(apr_pool_t *p,
+ proxy_balancer *balancer,
+ proxy_server_conf *conf,
+ const char *url,
+ unsigned int mask);
+
+
/**
* Get the worker from proxy configuration
* @param p memory pool used for finding worker
@@ -737,6 +757,8 @@ PROXY_DECLARE(proxy_worker *) ap_proxy_get_worker(apr_pool_t *p,
proxy_balancer *balancer,
proxy_server_conf *conf,
const char *url);
+
+
/**
* Define and Allocate space for the worker to proxy configuration
* @param p memory pool to allocate worker from
diff --git a/modules/proxy/proxy_util.c b/modules/proxy/proxy_util.c
index 032e0c4..3d5b220 100644
--- a/modules/proxy/proxy_util.c
+++ b/modules/proxy/proxy_util.c
@@ -1643,10 +1643,11 @@ PROXY_DECLARE(char *) ap_proxy_worker_name(apr_pool_t *p,
return apr_pstrcat(p, "unix:", worker->s->uds_path, "|", worker->s->name, NULL);
}
-PROXY_DECLARE(proxy_worker *) ap_proxy_get_worker(apr_pool_t *p,
- proxy_balancer *balancer,
- proxy_server_conf *conf,
- const char *url)
+PROXY_DECLARE(proxy_worker *) ap_proxy_get_worker_ex(apr_pool_t *p,
+ proxy_balancer *balancer,
+ proxy_server_conf *conf,
+ const char *url,
+ unsigned int mask)
{
proxy_worker *worker;
proxy_worker *max_worker = NULL;
@@ -1662,7 +1663,12 @@ PROXY_DECLARE(proxy_worker *) ap_proxy_get_worker(apr_pool_t *p,
return NULL;
}
- url = ap_proxy_de_socketfy(p, url);
+ if (!(mask & AP_PROXY_WORKER_NO_UDS)) {
+ url = ap_proxy_de_socketfy(p, url);
+ if (!url) {
+ return NULL;
+ }
+ }
c = ap_strchr_c(url, ':');
if (c == NULL || c[1] != '/' || c[2] != '/' || c[3] == '\0') {
@@ -1727,6 +1733,14 @@ PROXY_DECLARE(proxy_worker *) ap_proxy_get_worker(apr_pool_t *p,
return max_worker;
}
+PROXY_DECLARE(proxy_worker *) ap_proxy_get_worker(apr_pool_t *p,
+ proxy_balancer *balancer,
+ proxy_server_conf *conf,
+ const char *url)
+{
+ return ap_proxy_get_worker_ex(p, balancer, conf, url, 0);
+}
+
/*
* To create a worker from scratch first we define the
* specifics of the worker; this is all local data.
@@ -2134,22 +2148,22 @@ PROXY_DECLARE(int) ap_proxy_pre_request(proxy_worker **worker,
access_status = proxy_run_pre_request(worker, balancer, r, conf, url);
if (access_status == DECLINED && *balancer == NULL) {
- *worker = ap_proxy_get_worker(r->pool, NULL, conf, *url);
+ const int forward = (r->proxyreq == PROXYREQ_PROXY);
+ *worker = ap_proxy_get_worker_ex(r->pool, NULL, conf, *url,
+ forward ? AP_PROXY_WORKER_NO_UDS : 0);
if (*worker) {
ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r,
"%s: found worker %s for %s",
(*worker)->s->scheme, (*worker)->s->name, *url);
- *balancer = NULL;
- if (!fix_uds_filename(r, url)) {
+ if (!forward && !fix_uds_filename(r, url)) {
return HTTP_INTERNAL_SERVER_ERROR;
}
access_status = OK;
}
- else if (r->proxyreq == PROXYREQ_PROXY) {
+ else if (forward) {
if (conf->forward) {
ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r,
"*: found forward proxy worker for %s", *url);
- *balancer = NULL;
*worker = conf->forward;
access_status = OK;
/*
@@ -2163,8 +2177,8 @@ PROXY_DECLARE(int) ap_proxy_pre_request(proxy_worker **worker,
else if (r->proxyreq == PROXYREQ_REVERSE) {
if (conf->reverse) {
ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r,
- "*: using default reverse proxy worker for %s (no keepalive)", *url);
- *balancer = NULL;
+ "*: using default reverse proxy worker for %s "
+ "(no keepalive)", *url);
*worker = conf->reverse;
access_status = OK;
/*
diff --git a/server/protocol.c b/server/protocol.c
index 430d91e..a2aa081 100644
--- a/server/protocol.c
+++ b/server/protocol.c
@@ -1525,7 +1525,7 @@ request_rec *ap_read_request(conn_rec *conn)
/* we may have switched to another server */
apply_server_config(r);
- if ((access_status = ap_run_post_read_request(r))) {
+ if ((access_status = ap_post_read_request(r))) {
goto die;
}
@@ -1582,6 +1582,27 @@ ignore:
return NULL;
}
+AP_DECLARE(int) ap_post_read_request(request_rec *r)
+{
+ int status;
+
+ if ((status = ap_run_post_read_request(r))) {
+ return status;
+ }
+
+ /* Enforce http(s) only scheme for non-forward-proxy requests */
+ if (!r->proxyreq
+ && r->parsed_uri.scheme
+ && (ap_cstr_casecmpn(r->parsed_uri.scheme, "http", 4) != 0
+ || (r->parsed_uri.scheme[4] != '\0'
+ && (apr_tolower(r->parsed_uri.scheme[4]) != 's'
+ || r->parsed_uri.scheme[5] != '\0')))) {
+ return HTTP_BAD_REQUEST;
+ }
+
+ return OK;
+}
+
/* if a request with a body creates a subrequest, remove original request's
* input headers which pertain to the body which has already been read.
* out-of-line helper function for ap_set_sub_req_protocol.

View File

@ -1,12 +0,0 @@
diff --git a/modules/lua/lua_request.c b/modules/lua/lua_request.c
index 77a88b4..1d8be2e 100644
--- a/modules/lua/lua_request.c
+++ b/modules/lua/lua_request.c
@@ -376,6 +376,7 @@ static int req_parsebody(lua_State *L)
if (end == NULL) break;
key = (char *) apr_pcalloc(r->pool, 256);
filename = (char *) apr_pcalloc(r->pool, 256);
+ if (end - crlf <= 8) break;
vlen = end - crlf - 8;
buffer = (char *) apr_pcalloc(r->pool, vlen+1);
memcpy(buffer, crlf + 4, vlen);

View File

@ -1,70 +0,0 @@
--- a/modules/lua/lua_request.c 2022/03/07 14:48:54 1898693
+++ b/modules/lua/lua_request.c 2022/03/07 14:51:19 1898694
@@ -235,14 +235,16 @@
{
int rc = OK;
+ *rbuf = NULL;
+ *size = 0;
+
if ((rc = ap_setup_client_block(r, REQUEST_CHUNKED_ERROR))) {
return (rc);
}
if (ap_should_client_block(r)) {
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
- char argsbuffer[HUGE_STRING_LEN];
- apr_off_t rsize, len_read, rpos = 0;
+ apr_off_t len_read, rpos = 0;
apr_off_t length = r->remaining;
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
@@ -250,18 +252,18 @@
return APR_EINCOMPLETE; /* Only room for incomplete data chunk :( */
}
*rbuf = (const char *) apr_pcalloc(r->pool, (apr_size_t) (length + 1));
- *size = length;
- while ((len_read = ap_get_client_block(r, argsbuffer, sizeof(argsbuffer))) > 0) {
- if ((rpos + len_read) > length) {
- rsize = length - rpos;
- }
- else {
- rsize = len_read;
- }
-
- memcpy((char *) *rbuf + rpos, argsbuffer, (size_t) rsize);
- rpos += rsize;
+ while ((rpos < length)
+ && (len_read = ap_get_client_block(r, (char *) *rbuf + rpos,
+ length - rpos)) > 0) {
+ rpos += len_read;
}
+ if (len_read < 0) {
+ return APR_EINCOMPLETE;
+ }
+ *size = rpos;
+ }
+ else {
+ rc = DONE;
}
return (rc);
@@ -278,6 +280,8 @@
{
apr_status_t rc = OK;
+ *size = 0;
+
if ((rc = ap_setup_client_block(r, REQUEST_CHUNKED_ERROR)))
return rc;
if (ap_should_client_block(r)) {
@@ -303,6 +307,9 @@
rpos += rsize;
}
}
+ else {
+ rc = DONE;
+ }
return rc;
}

View File

@ -1,154 +0,0 @@
diff --git a/modules/http/http_filters.c b/modules/http/http_filters.c
index 9828cdf..6bedcac 100644
--- a/modules/http/http_filters.c
+++ b/modules/http/http_filters.c
@@ -1605,9 +1605,9 @@ AP_DECLARE(int) ap_map_http_request_error(apr_status_t rv, int status)
*/
AP_DECLARE(int) ap_discard_request_body(request_rec *r)
{
+ int rc = OK;
+ conn_rec *c = r->connection;
apr_bucket_brigade *bb;
- int seen_eos;
- apr_status_t rv;
/* Sometimes we'll get in a state where the input handling has
* detected an error where we want to drop the connection, so if
@@ -1616,54 +1616,57 @@ AP_DECLARE(int) ap_discard_request_body(request_rec *r)
*
* This function is also a no-op on a subrequest.
*/
- if (r->main || r->connection->keepalive == AP_CONN_CLOSE ||
- ap_status_drops_connection(r->status)) {
+ if (r->main || c->keepalive == AP_CONN_CLOSE) {
+ return OK;
+ }
+ if (ap_status_drops_connection(r->status)) {
+ c->keepalive = AP_CONN_CLOSE;
return OK;
}
bb = apr_brigade_create(r->pool, r->connection->bucket_alloc);
- seen_eos = 0;
- do {
- apr_bucket *bucket;
+ for (;;) {
+ apr_status_t rv;
rv = ap_get_brigade(r->input_filters, bb, AP_MODE_READBYTES,
APR_BLOCK_READ, HUGE_STRING_LEN);
-
if (rv != APR_SUCCESS) {
- apr_brigade_destroy(bb);
- return ap_map_http_request_error(rv, HTTP_BAD_REQUEST);
+ rc = ap_map_http_request_error(rv, HTTP_BAD_REQUEST);
+ goto cleanup;
}
- for (bucket = APR_BRIGADE_FIRST(bb);
- bucket != APR_BRIGADE_SENTINEL(bb);
- bucket = APR_BUCKET_NEXT(bucket))
- {
- const char *data;
- apr_size_t len;
+ while (!APR_BRIGADE_EMPTY(bb)) {
+ apr_bucket *b = APR_BRIGADE_FIRST(bb);
- if (APR_BUCKET_IS_EOS(bucket)) {
- seen_eos = 1;
- break;
+ if (APR_BUCKET_IS_EOS(b)) {
+ goto cleanup;
}
- /* These are metadata buckets. */
- if (bucket->length == 0) {
- continue;
- }
-
- /* We MUST read because in case we have an unknown-length
- * bucket or one that morphs, we want to exhaust it.
+ /* There is no need to read empty or metadata buckets or
+ * buckets of known length, but we MUST read buckets of
+ * unknown length in order to exhaust them.
*/
- rv = apr_bucket_read(bucket, &data, &len, APR_BLOCK_READ);
+ if (b->length == (apr_size_t)-1) {
+ apr_size_t len;
+ const char *data;
+
+ rv = apr_bucket_read(b, &data, &len, APR_BLOCK_READ);
if (rv != APR_SUCCESS) {
- apr_brigade_destroy(bb);
- return HTTP_BAD_REQUEST;
+ rc = HTTP_BAD_REQUEST;
+ goto cleanup;
}
}
- apr_brigade_cleanup(bb);
- } while (!seen_eos);
- return OK;
+ apr_bucket_delete(b);
+ }
+ }
+
+cleanup:
+ apr_brigade_cleanup(bb);
+ if (rc != OK) {
+ c->keepalive = AP_CONN_CLOSE;
+ }
+ return rc;
}
/* Here we deal with getting the request message body from the client.
diff --git a/server/protocol.c b/server/protocol.c
index a2aa081..a554970 100644
--- a/server/protocol.c
+++ b/server/protocol.c
@@ -1666,23 +1666,29 @@ AP_DECLARE(void) ap_set_sub_req_protocol(request_rec *rnew,
rnew->main = (request_rec *) r;
}
-static void end_output_stream(request_rec *r)
+static void end_output_stream(request_rec *r, int status)
{
conn_rec *c = r->connection;
apr_bucket_brigade *bb;
apr_bucket *b;
bb = apr_brigade_create(r->pool, c->bucket_alloc);
+ if (status != OK) {
+ b = ap_bucket_error_create(status, NULL, r->pool, c->bucket_alloc);
+ APR_BRIGADE_INSERT_TAIL(bb, b);
+ }
b = apr_bucket_eos_create(c->bucket_alloc);
APR_BRIGADE_INSERT_TAIL(bb, b);
+
ap_pass_brigade(r->output_filters, bb);
+ apr_brigade_cleanup(bb);
}
AP_DECLARE(void) ap_finalize_sub_req_protocol(request_rec *sub)
{
/* tell the filter chain there is no more content coming */
if (!sub->eos_sent) {
- end_output_stream(sub);
+ end_output_stream(sub, OK);
}
}
@@ -1693,11 +1699,11 @@ AP_DECLARE(void) ap_finalize_sub_req_protocol(request_rec *sub)
*/
AP_DECLARE(void) ap_finalize_request_protocol(request_rec *r)
{
- (void) ap_discard_request_body(r);
+ int status = ap_discard_request_body(r);
/* tell the filter chain there is no more content coming */
if (!r->eos_sent) {
- end_output_stream(r);
+ end_output_stream(r, status);
}
}

View File

@ -1,103 +0,0 @@
diff --git a/docs/manual/mod/core.html.en b/docs/manual/mod/core.html.en
index 20d1e5a..e1ec8d0 100644
--- a/docs/manual/mod/core.html.en
+++ b/docs/manual/mod/core.html.en
@@ -2935,12 +2935,19 @@ from the client</td></tr>
<tr><th><a href="directive-dict.html#Status">Status:</a></th><td>Core</td></tr>
<tr><th><a href="directive-dict.html#Module">Module:</a></th><td>core</td></tr>
</table>
- <p>Limit (in bytes) on maximum size of an XML-based request
- body. A value of <code>0</code> will disable any checking.</p>
+ <p>Limit (in bytes) on the maximum size of an XML-based request
+ body. A value of <code>0</code> will apply a hard limit (depending on
+ 32bit vs 64bit system) allowing for XML escaping within the bounds of
+ the system addressable memory, but it exists for compatibility only
+ and is not recommended since it does not account for memory consumed
+ elsewhere or concurrent requests, which might result in an overall
+ system out-of-memory.
+ </p>
<p>Example:</p>
- <pre class="prettyprint lang-config">LimitXMLRequestBody 0</pre>
+ <pre class="prettyprint lang-config"># Limit of 1 MiB
+ LimitXMLRequestBody 1073741824</pre>
diff --git a/server/core.c b/server/core.c
index e32613d..8abfa65 100644
--- a/server/core.c
+++ b/server/core.c
@@ -70,6 +70,8 @@
/* LimitXMLRequestBody handling */
#define AP_LIMIT_UNSET ((long) -1)
#define AP_DEFAULT_LIMIT_XML_BODY ((apr_size_t)1000000)
+/* Hard limit for ap_escape_html2() */
+#define AP_MAX_LIMIT_XML_BODY ((apr_size_t)(APR_SIZE_MAX / 6 - 1))
#define AP_MIN_SENDFILE_BYTES (256)
@@ -3689,6 +3691,11 @@ static const char *set_limit_xml_req_body(cmd_parms *cmd, void *conf_,
if (conf->limit_xml_body < 0)
return "LimitXMLRequestBody requires a non-negative integer.";
+ /* zero is AP_MAX_LIMIT_XML_BODY (implicitly) */
+ if ((apr_size_t)conf->limit_xml_body > AP_MAX_LIMIT_XML_BODY)
+ return apr_psprintf(cmd->pool, "LimitXMLRequestBody must not exceed "
+ "%" APR_SIZE_T_FMT, AP_MAX_LIMIT_XML_BODY);
+
return NULL;
}
@@ -3777,6 +3784,8 @@ AP_DECLARE(apr_size_t) ap_get_limit_xml_body(const request_rec *r)
conf = ap_get_core_module_config(r->per_dir_config);
if (conf->limit_xml_body == AP_LIMIT_UNSET)
return AP_DEFAULT_LIMIT_XML_BODY;
+ if (conf->limit_xml_body == 0)
+ return AP_MAX_LIMIT_XML_BODY;
return (apr_size_t)conf->limit_xml_body;
}
diff --git a/server/util.c b/server/util.c
index 2a5dd04..eefdafa 100644
--- a/server/util.c
+++ b/server/util.c
@@ -2037,11 +2037,14 @@ AP_DECLARE(char *) ap_escape_urlencoded(apr_pool_t *p, const char *buffer)
AP_DECLARE(char *) ap_escape_html2(apr_pool_t *p, const char *s, int toasc)
{
- int i, j;
+ apr_size_t i, j;
char *x;
/* first, count the number of extra characters */
- for (i = 0, j = 0; s[i] != '\0'; i++)
+ for (i = 0, j = 0; s[i] != '\0'; i++) {
+ if (i + j > APR_SIZE_MAX - 6) {
+ abort();
+ }
if (s[i] == '<' || s[i] == '>')
j += 3;
else if (s[i] == '&')
@@ -2050,6 +2053,7 @@ AP_DECLARE(char *) ap_escape_html2(apr_pool_t *p, const char *s, int toasc)
j += 5;
else if (toasc && !apr_isascii(s[i]))
j += 5;
+ }
if (j == 0)
return apr_pstrmemdup(p, s, i);
diff --git a/server/util_xml.c b/server/util_xml.c
index 4845194..22806fa 100644
--- a/server/util_xml.c
+++ b/server/util_xml.c
@@ -85,7 +85,7 @@ AP_DECLARE(int) ap_xml_parse_input(request_rec * r, apr_xml_doc **pdoc)
}
total_read += len;
- if (limit_xml_body && total_read > limit_xml_body) {
+ if (total_read > limit_xml_body) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00539)
"XML request body is larger than the configured "
"limit of %lu", (unsigned long)limit_xml_body);

View File

@ -1,377 +0,0 @@
diff --git a/modules/filters/libsed.h b/modules/filters/libsed.h
index 76cbc0c..0256b1e 100644
--- a/modules/filters/libsed.h
+++ b/modules/filters/libsed.h
@@ -60,7 +60,7 @@ struct sed_label_s {
};
typedef apr_status_t (sed_err_fn_t)(void *data, const char *error);
-typedef apr_status_t (sed_write_fn_t)(void *ctx, char *buf, int sz);
+typedef apr_status_t (sed_write_fn_t)(void *ctx, char *buf, apr_size_t sz);
typedef struct sed_commands_s sed_commands_t;
#define NWFILES 11 /* 10 plus one for standard output */
@@ -69,7 +69,7 @@ struct sed_commands_s {
sed_err_fn_t *errfn;
void *data;
- unsigned lsize;
+ apr_size_t lsize;
char *linebuf;
char *lbend;
const char *saveq;
@@ -116,15 +116,15 @@ struct sed_eval_s {
apr_int64_t lnum;
void *fout;
- unsigned lsize;
+ apr_size_t lsize;
char *linebuf;
char *lspend;
- unsigned hsize;
+ apr_size_t hsize;
char *holdbuf;
char *hspend;
- unsigned gsize;
+ apr_size_t gsize;
char *genbuf;
char *lcomend;
@@ -160,7 +160,7 @@ apr_status_t sed_init_eval(sed_eval_t *eval, sed_commands_t *commands,
sed_err_fn_t *errfn, void *data,
sed_write_fn_t *writefn, apr_pool_t *p);
apr_status_t sed_reset_eval(sed_eval_t *eval, sed_commands_t *commands, sed_err_fn_t *errfn, void *data);
-apr_status_t sed_eval_buffer(sed_eval_t *eval, const char *buf, int bufsz, void *fout);
+apr_status_t sed_eval_buffer(sed_eval_t *eval, const char *buf, apr_size_t bufsz, void *fout);
apr_status_t sed_eval_file(sed_eval_t *eval, apr_file_t *fin, void *fout);
apr_status_t sed_finalize_eval(sed_eval_t *eval, void *f);
void sed_destroy_eval(sed_eval_t *eval);
diff --git a/modules/filters/mod_sed.c b/modules/filters/mod_sed.c
index 346c210..8595e41 100644
--- a/modules/filters/mod_sed.c
+++ b/modules/filters/mod_sed.c
@@ -51,7 +51,7 @@ typedef struct sed_filter_ctxt
apr_bucket_brigade *bbinp;
char *outbuf;
char *curoutbuf;
- int bufsize;
+ apr_size_t bufsize;
apr_pool_t *tpool;
int numbuckets;
} sed_filter_ctxt;
@@ -100,7 +100,7 @@ static void alloc_outbuf(sed_filter_ctxt* ctx)
/* append_bucket
* Allocate a new bucket from buf and sz and append to ctx->bb
*/
-static apr_status_t append_bucket(sed_filter_ctxt* ctx, char* buf, int sz)
+static apr_status_t append_bucket(sed_filter_ctxt* ctx, char* buf, apr_size_t sz)
{
apr_status_t status = APR_SUCCESS;
apr_bucket *b;
@@ -133,7 +133,7 @@ static apr_status_t append_bucket(sed_filter_ctxt* ctx, char* buf, int sz)
*/
static apr_status_t flush_output_buffer(sed_filter_ctxt *ctx)
{
- int size = ctx->curoutbuf - ctx->outbuf;
+ apr_size_t size = ctx->curoutbuf - ctx->outbuf;
char *out;
apr_status_t status = APR_SUCCESS;
if ((ctx->outbuf == NULL) || (size <=0))
@@ -147,12 +147,12 @@ static apr_status_t flush_output_buffer(sed_filter_ctxt *ctx)
/* This is a call back function. When libsed wants to generate the output,
* this function will be invoked.
*/
-static apr_status_t sed_write_output(void *dummy, char *buf, int sz)
+static apr_status_t sed_write_output(void *dummy, char *buf, apr_size_t sz)
{
/* dummy is basically filter context. Context is passed during invocation
* of sed_eval_buffer
*/
- int remainbytes = 0;
+ apr_size_t remainbytes = 0;
apr_status_t status = APR_SUCCESS;
sed_filter_ctxt *ctx = (sed_filter_ctxt *) dummy;
if (ctx->outbuf == NULL) {
@@ -168,21 +168,29 @@ static apr_status_t sed_write_output(void *dummy, char *buf, int sz)
}
/* buffer is now full */
status = append_bucket(ctx, ctx->outbuf, ctx->bufsize);
- /* old buffer is now used so allocate new buffer */
- alloc_outbuf(ctx);
- /* if size is bigger than the allocated buffer directly add to output
- * brigade */
- if ((status == APR_SUCCESS) && (sz >= ctx->bufsize)) {
- char* newbuf = apr_pmemdup(ctx->tpool, buf, sz);
- status = append_bucket(ctx, newbuf, sz);
- /* pool might get clear after append_bucket */
- if (ctx->outbuf == NULL) {
+ if (status == APR_SUCCESS) {
+ /* if size is bigger than the allocated buffer directly add to output
+ * brigade */
+ if (sz >= ctx->bufsize) {
+ char* newbuf = apr_pmemdup(ctx->tpool, buf, sz);
+ status = append_bucket(ctx, newbuf, sz);
+ if (status == APR_SUCCESS) {
+ /* old buffer is now used so allocate new buffer */
+ alloc_outbuf(ctx);
+ }
+ else {
+ clear_ctxpool(ctx);
+ }
+ }
+ else {
+ /* old buffer is now used so allocate new buffer */
alloc_outbuf(ctx);
+ memcpy(ctx->curoutbuf, buf, sz);
+ ctx->curoutbuf += sz;
}
}
else {
- memcpy(ctx->curoutbuf, buf, sz);
- ctx->curoutbuf += sz;
+ clear_ctxpool(ctx);
}
}
else {
diff --git a/modules/filters/sed1.c b/modules/filters/sed1.c
index be03506..67a8d06 100644
--- a/modules/filters/sed1.c
+++ b/modules/filters/sed1.c
@@ -71,7 +71,7 @@ static apr_status_t dosub(sed_eval_t *eval, char *rhsbuf, int n,
static char *place(sed_eval_t *eval, char *asp, char *al1, char *al2);
static apr_status_t command(sed_eval_t *eval, sed_reptr_t *ipc,
step_vars_storage *step_vars);
-static apr_status_t wline(sed_eval_t *eval, char *buf, int sz);
+static apr_status_t wline(sed_eval_t *eval, char *buf, apr_size_t sz);
static apr_status_t arout(sed_eval_t *eval);
static void eval_errf(sed_eval_t *eval, const char *fmt, ...)
@@ -92,11 +92,11 @@ static void eval_errf(sed_eval_t *eval, const char *fmt, ...)
* grow_buffer
*/
static void grow_buffer(apr_pool_t *pool, char **buffer,
- char **spend, unsigned int *cursize,
- unsigned int newsize)
+ char **spend, apr_size_t *cursize,
+ apr_size_t newsize)
{
char* newbuffer = NULL;
- int spendsize = 0;
+ apr_size_t spendsize = 0;
if (*cursize >= newsize)
return;
/* Avoid number of times realloc is called. It could cause huge memory
@@ -124,7 +124,7 @@ static void grow_buffer(apr_pool_t *pool, char **buffer,
/*
* grow_line_buffer
*/
-static void grow_line_buffer(sed_eval_t *eval, int newsize)
+static void grow_line_buffer(sed_eval_t *eval, apr_size_t newsize)
{
grow_buffer(eval->pool, &eval->linebuf, &eval->lspend,
&eval->lsize, newsize);
@@ -133,7 +133,7 @@ static void grow_line_buffer(sed_eval_t *eval, int newsize)
/*
* grow_hold_buffer
*/
-static void grow_hold_buffer(sed_eval_t *eval, int newsize)
+static void grow_hold_buffer(sed_eval_t *eval, apr_size_t newsize)
{
grow_buffer(eval->pool, &eval->holdbuf, &eval->hspend,
&eval->hsize, newsize);
@@ -142,7 +142,7 @@ static void grow_hold_buffer(sed_eval_t *eval, int newsize)
/*
* grow_gen_buffer
*/
-static void grow_gen_buffer(sed_eval_t *eval, int newsize,
+static void grow_gen_buffer(sed_eval_t *eval, apr_size_t newsize,
char **gspend)
{
if (gspend == NULL) {
@@ -156,9 +156,9 @@ static void grow_gen_buffer(sed_eval_t *eval, int newsize,
/*
* appendmem_to_linebuf
*/
-static void appendmem_to_linebuf(sed_eval_t *eval, const char* sz, int len)
+static void appendmem_to_linebuf(sed_eval_t *eval, const char* sz, apr_size_t len)
{
- unsigned int reqsize = (eval->lspend - eval->linebuf) + len;
+ apr_size_t reqsize = (eval->lspend - eval->linebuf) + len;
if (eval->lsize < reqsize) {
grow_line_buffer(eval, reqsize);
}
@@ -169,21 +169,36 @@ static void appendmem_to_linebuf(sed_eval_t *eval, const char* sz, int len)
/*
* append_to_linebuf
*/
-static void append_to_linebuf(sed_eval_t *eval, const char* sz)
+static void append_to_linebuf(sed_eval_t *eval, const char* sz,
+ step_vars_storage *step_vars)
{
- int len = strlen(sz);
+ apr_size_t len = strlen(sz);
+ char *old_linebuf = eval->linebuf;
/* Copy string including null character */
appendmem_to_linebuf(eval, sz, len + 1);
--eval->lspend; /* lspend will now point to NULL character */
+ /* Sync step_vars after a possible linebuf expansion */
+ if (step_vars && old_linebuf != eval->linebuf) {
+ if (step_vars->loc1) {
+ step_vars->loc1 = step_vars->loc1 - old_linebuf + eval->linebuf;
+ }
+ if (step_vars->loc2) {
+ step_vars->loc2 = step_vars->loc2 - old_linebuf + eval->linebuf;
+ }
+ if (step_vars->locs) {
+ step_vars->locs = step_vars->locs - old_linebuf + eval->linebuf;
+ }
+ }
}
/*
* copy_to_linebuf
*/
-static void copy_to_linebuf(sed_eval_t *eval, const char* sz)
+static void copy_to_linebuf(sed_eval_t *eval, const char* sz,
+ step_vars_storage *step_vars)
{
eval->lspend = eval->linebuf;
- append_to_linebuf(eval, sz);
+ append_to_linebuf(eval, sz, step_vars);
}
/*
@@ -191,8 +206,8 @@ static void copy_to_linebuf(sed_eval_t *eval, const char* sz)
*/
static void append_to_holdbuf(sed_eval_t *eval, const char* sz)
{
- int len = strlen(sz);
- unsigned int reqsize = (eval->hspend - eval->holdbuf) + len + 1;
+ apr_size_t len = strlen(sz);
+ apr_size_t reqsize = (eval->hspend - eval->holdbuf) + len + 1;
if (eval->hsize <= reqsize) {
grow_hold_buffer(eval, reqsize);
}
@@ -215,8 +230,8 @@ static void copy_to_holdbuf(sed_eval_t *eval, const char* sz)
*/
static void append_to_genbuf(sed_eval_t *eval, const char* sz, char **gspend)
{
- int len = strlen(sz);
- unsigned int reqsize = (*gspend - eval->genbuf) + len + 1;
+ apr_size_t len = strlen(sz);
+ apr_size_t reqsize = (*gspend - eval->genbuf) + len + 1;
if (eval->gsize < reqsize) {
grow_gen_buffer(eval, reqsize, gspend);
}
@@ -230,8 +245,8 @@ static void append_to_genbuf(sed_eval_t *eval, const char* sz, char **gspend)
*/
static void copy_to_genbuf(sed_eval_t *eval, const char* sz)
{
- int len = strlen(sz);
- unsigned int reqsize = len + 1;
+ apr_size_t len = strlen(sz);
+ apr_size_t reqsize = len + 1;
if (eval->gsize < reqsize) {
grow_gen_buffer(eval, reqsize, NULL);
}
@@ -353,7 +368,7 @@ apr_status_t sed_eval_file(sed_eval_t *eval, apr_file_t *fin, void *fout)
/*
* sed_eval_buffer
*/
-apr_status_t sed_eval_buffer(sed_eval_t *eval, const char *buf, int bufsz, void *fout)
+apr_status_t sed_eval_buffer(sed_eval_t *eval, const char *buf, apr_size_t bufsz, void *fout)
{
apr_status_t rv;
@@ -383,7 +398,7 @@ apr_status_t sed_eval_buffer(sed_eval_t *eval, const char *buf, int bufsz, void
while (bufsz) {
char *n;
- int llen;
+ apr_size_t llen;
n = memchr(buf, '\n', bufsz);
if (n == NULL)
@@ -442,7 +457,7 @@ apr_status_t sed_finalize_eval(sed_eval_t *eval, void *fout)
* buffer is not a newline.
*/
/* Assure space for NULL */
- append_to_linebuf(eval, "");
+ append_to_linebuf(eval, "", NULL);
}
*eval->lspend = '\0';
@@ -666,7 +681,7 @@ static apr_status_t dosub(sed_eval_t *eval, char *rhsbuf, int n,
lp = step_vars->loc2;
step_vars->loc2 = sp - eval->genbuf + eval->linebuf;
append_to_genbuf(eval, lp, &sp);
- copy_to_linebuf(eval, eval->genbuf);
+ copy_to_linebuf(eval, eval->genbuf, step_vars);
return rv;
}
@@ -676,8 +691,8 @@ static apr_status_t dosub(sed_eval_t *eval, char *rhsbuf, int n,
static char *place(sed_eval_t *eval, char *asp, char *al1, char *al2)
{
char *sp = asp;
- int n = al2 - al1;
- unsigned int reqsize = (sp - eval->genbuf) + n + 1;
+ apr_size_t n = al2 - al1;
+ apr_size_t reqsize = (sp - eval->genbuf) + n + 1;
if (eval->gsize < reqsize) {
grow_gen_buffer(eval, reqsize, &sp);
@@ -735,7 +750,7 @@ static apr_status_t command(sed_eval_t *eval, sed_reptr_t *ipc,
}
p1++;
- copy_to_linebuf(eval, p1);
+ copy_to_linebuf(eval, p1, step_vars);
eval->jflag++;
break;
@@ -745,12 +760,12 @@ static apr_status_t command(sed_eval_t *eval, sed_reptr_t *ipc,
break;
case GCOM:
- copy_to_linebuf(eval, eval->holdbuf);
+ copy_to_linebuf(eval, eval->holdbuf, step_vars);
break;
case CGCOM:
- append_to_linebuf(eval, "\n");
- append_to_linebuf(eval, eval->holdbuf);
+ append_to_linebuf(eval, "\n", step_vars);
+ append_to_linebuf(eval, eval->holdbuf, step_vars);
break;
case HCOM:
@@ -881,7 +896,7 @@ static apr_status_t command(sed_eval_t *eval, sed_reptr_t *ipc,
if (rv != APR_SUCCESS)
return rv;
}
- append_to_linebuf(eval, "\n");
+ append_to_linebuf(eval, "\n", step_vars);
eval->pending = ipc->next;
break;
@@ -956,7 +971,7 @@ static apr_status_t command(sed_eval_t *eval, sed_reptr_t *ipc,
case XCOM:
copy_to_genbuf(eval, eval->linebuf);
- copy_to_linebuf(eval, eval->holdbuf);
+ copy_to_linebuf(eval, eval->holdbuf, step_vars);
copy_to_holdbuf(eval, eval->genbuf);
break;
@@ -1013,7 +1028,7 @@ static apr_status_t arout(sed_eval_t *eval)
/*
* wline
*/
-static apr_status_t wline(sed_eval_t *eval, char *buf, int sz)
+static apr_status_t wline(sed_eval_t *eval, char *buf, apr_size_t sz)
{
apr_status_t rv = APR_SUCCESS;
rv = eval->writefn(eval->fout, buf, sz);

View File

@ -1,26 +0,0 @@
diff --git a/modules/proxy/mod_proxy_ajp.c b/modules/proxy/mod_proxy_ajp.c
index 6faabea..058b03f 100644
--- a/modules/proxy/mod_proxy_ajp.c
+++ b/modules/proxy/mod_proxy_ajp.c
@@ -249,9 +249,18 @@ static int ap_proxy_ajp_request(apr_pool_t *p, request_rec *r,
/* read the first bloc of data */
input_brigade = apr_brigade_create(p, r->connection->bucket_alloc);
tenc = apr_table_get(r->headers_in, "Transfer-Encoding");
- if (tenc && (strcasecmp(tenc, "chunked") == 0)) {
- /* The AJP protocol does not want body data yet */
- ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00870) "request is chunked");
+ if (tenc) {
+ if (ap_cstr_casecmp(tenc, "chunked") == 0) {
+ /* The AJP protocol does not want body data yet */
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00870)
+ "request is chunked");
+ }
+ else {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10396)
+ "%s Transfer-Encoding is not supported",
+ tenc);
+ return HTTP_INTERNAL_SERVER_ERROR;
+ }
} else {
/* Get client provided Content-Length header */
content_length = get_content_length(r);

View File

@ -1,47 +0,0 @@
diff --git a/include/http_protocol.h b/include/http_protocol.h
index e1572dc..8ed77ac 100644
--- a/include/http_protocol.h
+++ b/include/http_protocol.h
@@ -439,7 +439,27 @@ AP_DECLARE(int) ap_rwrite(const void *buf, int nbyte, request_rec *r);
*/
static APR_INLINE int ap_rputs(const char *str, request_rec *r)
{
- return ap_rwrite(str, (int)strlen(str), r);
+ apr_size_t len;
+
+ len = strlen(str);
+
+ for (;;) {
+ if (len <= INT_MAX) {
+ return ap_rwrite(str, (int)len, r);
+ }
+ else {
+ int rc;
+
+ rc = ap_rwrite(str, INT_MAX, r);
+ if (rc < 0) {
+ return rc;
+ }
+ else {
+ str += INT_MAX;
+ len -= INT_MAX;
+ }
+ }
+ }
}
/**
diff --git a/server/protocol.c b/server/protocol.c
index a554970..ea461a2 100644
--- a/server/protocol.c
+++ b/server/protocol.c
@@ -2107,6 +2107,9 @@ AP_DECLARE(int) ap_rputc(int c, request_rec *r)
AP_DECLARE(int) ap_rwrite(const void *buf, int nbyte, request_rec *r)
{
+ if (nbyte < 0)
+ return -1;
+
if (r->connection->aborted)
return -1;

View File

@ -1,22 +0,0 @@
diff --git a/server/util.c b/server/util.c
index eefdafa..45051b7 100644
--- a/server/util.c
+++ b/server/util.c
@@ -186,7 +186,7 @@ AP_DECLARE(char *) ap_ht_time(apr_pool_t *p, apr_time_t t, const char *fmt,
*/
AP_DECLARE(int) ap_strcmp_match(const char *str, const char *expected)
{
- int x, y;
+ apr_size_t x, y;
for (x = 0, y = 0; expected[y]; ++y, ++x) {
if ((!str[x]) && (expected[y] != '*'))
@@ -210,7 +210,7 @@ AP_DECLARE(int) ap_strcmp_match(const char *str, const char *expected)
AP_DECLARE(int) ap_strcasecmp_match(const char *str, const char *expected)
{
- int x, y;
+ apr_size_t x, y;
for (x = 0, y = 0; expected[y]; ++y, ++x) {
if (!str[x] && expected[y] != '*')

View File

@ -1,90 +0,0 @@
diff --git a/docs/manual/mod/core.html.en b/docs/manual/mod/core.html.en
index e1ec8d0..833fa7b 100644
--- a/docs/manual/mod/core.html.en
+++ b/docs/manual/mod/core.html.en
@@ -2748,16 +2748,16 @@ subrequests</td></tr>
<tr><th><a href="directive-dict.html#Description">Description:</a></th><td>Restricts the total size of the HTTP request body sent
from the client</td></tr>
<tr><th><a href="directive-dict.html#Syntax">Syntax:</a></th><td><code>LimitRequestBody <var>bytes</var></code></td></tr>
-<tr><th><a href="directive-dict.html#Default">Default:</a></th><td><code>LimitRequestBody 0</code></td></tr>
+<tr><th><a href="directive-dict.html#Default">Default:</a></th><td><code>LimitRequestBody 1073741824</code></td></tr>
<tr><th><a href="directive-dict.html#Context">Context:</a></th><td>server config, virtual host, directory, .htaccess</td></tr>
<tr><th><a href="directive-dict.html#Override">Override:</a></th><td>All</td></tr>
<tr><th><a href="directive-dict.html#Status">Status:</a></th><td>Core</td></tr>
<tr><th><a href="directive-dict.html#Module">Module:</a></th><td>core</td></tr>
+<tr><th><a href="directive-dict.html#Compatibility">Compatibility:</a></th><td>In Apache HTTP Server 2.4.53 and earlier, the default value
+ was 0 (unlimited)</td></tr>
</table>
- <p>This directive specifies the number of <var>bytes</var> from 0
- (meaning unlimited) to 2147483647 (2GB) that are allowed in a
- request body. See the note below for the limited applicability
- to proxy requests.</p>
+ <p>This directive specifies the number of <var>bytes</var>
+ that are allowed in a request body. A value of <var>0</var> means unlimited.</p>
<p>The <code class="directive">LimitRequestBody</code> directive allows
the user to set a limit on the allowed size of an HTTP request
@@ -2783,12 +2783,6 @@ from the client</td></tr>
<pre class="prettyprint lang-config">LimitRequestBody 102400</pre>
-
- <div class="note"><p>For a full description of how this directive is interpreted by
- proxy requests, see the <code class="module"><a href="../mod/mod_proxy.html">mod_proxy</a></code> documentation.</p>
- </div>
-
-
</div>
<div class="top"><a href="#page-header"><img alt="top" src="../images/up.gif" /></a></div>
<div class="directive-section"><h2><a name="LimitRequestFields" id="LimitRequestFields">LimitRequestFields</a> <a name="limitrequestfields" id="limitrequestfields">Directive</a></h2>
diff --git a/docs/manual/mod/mod_proxy.html.en b/docs/manual/mod/mod_proxy.html.en
index 2cc6ace..c9e4634 100644
--- a/docs/manual/mod/mod_proxy.html.en
+++ b/docs/manual/mod/mod_proxy.html.en
@@ -459,9 +459,6 @@ ProxyPass "/examples" "http://backend.example.com/examples" timeout=10</pre>
Content-Length header, but the server is configured to filter incoming
request bodies.</p>
- <p><code class="directive"><a href="../mod/core.html#limitrequestbody">LimitRequestBody</a></code> only applies to
- request bodies that the server will spool to disk</p>
-
</div><div class="top"><a href="#page-header"><img alt="top" src="../images/up.gif" /></a></div>
<div class="section">
<h2><a name="x-headers" id="x-headers">Reverse Proxy Request Headers</a></h2>
diff --git a/modules/http/http_filters.c b/modules/http/http_filters.c
index 6bedcac..393343a 100644
--- a/modules/http/http_filters.c
+++ b/modules/http/http_filters.c
@@ -1710,6 +1710,7 @@ AP_DECLARE(int) ap_setup_client_block(request_rec *r, int read_policy)
{
const char *tenc = apr_table_get(r->headers_in, "Transfer-Encoding");
const char *lenp = apr_table_get(r->headers_in, "Content-Length");
+ apr_off_t limit_req_body = ap_get_limit_req_body(r);
r->read_body = read_policy;
r->read_chunked = 0;
@@ -1748,6 +1749,11 @@ AP_DECLARE(int) ap_setup_client_block(request_rec *r, int read_policy)
return HTTP_REQUEST_ENTITY_TOO_LARGE;
}
+ if (limit_req_body > 0 && (r->remaining > limit_req_body)) {
+ /* will be logged when the body is discarded */
+ return HTTP_REQUEST_ENTITY_TOO_LARGE;
+ }
+
#ifdef AP_DEBUG
{
/* Make sure ap_getline() didn't leave any droppings. */
diff --git a/server/core.c b/server/core.c
index a0bfaad..6556f20 100644
--- a/server/core.c
+++ b/server/core.c
@@ -65,7 +65,7 @@
/* LimitRequestBody handling */
#define AP_LIMIT_REQ_BODY_UNSET ((apr_off_t) -1)
-#define AP_DEFAULT_LIMIT_REQ_BODY ((apr_off_t) 0)
+#define AP_DEFAULT_LIMIT_REQ_BODY ((apr_off_t) 1<<30) /* 1GB */
/* LimitXMLRequestBody handling */
#define AP_LIMIT_UNSET ((long) -1)

View File

@ -1,541 +0,0 @@
diff --git a/modules/filters/mod_sed.c b/modules/filters/mod_sed.c
index 8595e41..9b99a6b 100644
--- a/modules/filters/mod_sed.c
+++ b/modules/filters/mod_sed.c
@@ -59,7 +59,7 @@ typedef struct sed_filter_ctxt
module AP_MODULE_DECLARE_DATA sed_module;
/* This function will be call back from libsed functions if there is any error
- * happend during execution of sed scripts
+ * happened during execution of sed scripts
*/
static apr_status_t log_sed_errf(void *data, const char *error)
{
@@ -276,7 +276,7 @@ static apr_status_t sed_response_filter(ap_filter_t *f,
apr_bucket_brigade *bb)
{
apr_bucket *b;
- apr_status_t status;
+ apr_status_t status = APR_SUCCESS;
sed_config *cfg = ap_get_module_config(f->r->per_dir_config,
&sed_module);
sed_filter_ctxt *ctx = f->ctx;
@@ -301,9 +301,9 @@ static apr_status_t sed_response_filter(ap_filter_t *f,
return status;
ctx = f->ctx;
apr_table_unset(f->r->headers_out, "Content-Length");
- }
- ctx->bb = apr_brigade_create(f->r->pool, f->c->bucket_alloc);
+ ctx->bb = apr_brigade_create(f->r->pool, f->c->bucket_alloc);
+ }
/* Here is the main logic. Iterate through all the buckets, read the
* content of the bucket, call sed_eval_buffer on the data.
@@ -325,63 +325,52 @@ static apr_status_t sed_response_filter(ap_filter_t *f,
* in sed's internal buffer which can't be flushed until new line
* character is arrived.
*/
- for (b = APR_BRIGADE_FIRST(bb); b != APR_BRIGADE_SENTINEL(bb);) {
- const char *buf = NULL;
- apr_size_t bytes = 0;
+ while (!APR_BRIGADE_EMPTY(bb)) {
+ b = APR_BRIGADE_FIRST(bb);
if (APR_BUCKET_IS_EOS(b)) {
- apr_bucket *b1 = APR_BUCKET_NEXT(b);
/* Now clean up the internal sed buffer */
sed_finalize_eval(&ctx->eval, ctx);
status = flush_output_buffer(ctx);
if (status != APR_SUCCESS) {
- clear_ctxpool(ctx);
- return status;
+ break;
}
+ /* Move the eos bucket to ctx->bb brigade */
APR_BUCKET_REMOVE(b);
- /* Insert the eos bucket to ctx->bb brigade */
APR_BRIGADE_INSERT_TAIL(ctx->bb, b);
- b = b1;
}
else if (APR_BUCKET_IS_FLUSH(b)) {
- apr_bucket *b1 = APR_BUCKET_NEXT(b);
- APR_BUCKET_REMOVE(b);
status = flush_output_buffer(ctx);
if (status != APR_SUCCESS) {
- clear_ctxpool(ctx);
- return status;
+ break;
}
+ /* Move the flush bucket to ctx->bb brigade */
+ APR_BUCKET_REMOVE(b);
APR_BRIGADE_INSERT_TAIL(ctx->bb, b);
- b = b1;
- }
- else if (APR_BUCKET_IS_METADATA(b)) {
- b = APR_BUCKET_NEXT(b);
}
- else if (apr_bucket_read(b, &buf, &bytes, APR_BLOCK_READ)
- == APR_SUCCESS) {
- apr_bucket *b1 = APR_BUCKET_NEXT(b);
- status = sed_eval_buffer(&ctx->eval, buf, bytes, ctx);
- if (status != APR_SUCCESS) {
- clear_ctxpool(ctx);
- return status;
+ else {
+ if (!APR_BUCKET_IS_METADATA(b)) {
+ const char *buf = NULL;
+ apr_size_t bytes = 0;
+
+ status = apr_bucket_read(b, &buf, &bytes, APR_BLOCK_READ);
+ if (status == APR_SUCCESS) {
+ status = sed_eval_buffer(&ctx->eval, buf, bytes, ctx);
+ }
+ if (status != APR_SUCCESS) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, status, f->r, APLOGNO(10394) "error evaluating sed on output");
+ break;
+ }
}
- APR_BUCKET_REMOVE(b);
apr_bucket_delete(b);
- b = b1;
- }
- else {
- apr_bucket *b1 = APR_BUCKET_NEXT(b);
- APR_BUCKET_REMOVE(b);
- b = b1;
}
}
- apr_brigade_cleanup(bb);
- status = flush_output_buffer(ctx);
- if (status != APR_SUCCESS) {
- clear_ctxpool(ctx);
- return status;
+ if (status == APR_SUCCESS) {
+ status = flush_output_buffer(ctx);
}
if (!APR_BRIGADE_EMPTY(ctx->bb)) {
- status = ap_pass_brigade(f->next, ctx->bb);
+ if (status == APR_SUCCESS) {
+ status = ap_pass_brigade(f->next, ctx->bb);
+ }
apr_brigade_cleanup(ctx->bb);
}
clear_ctxpool(ctx);
@@ -432,7 +421,7 @@ static apr_status_t sed_request_filter(ap_filter_t *f,
* the buckets in bbinp and read the data from buckets and invoke
* sed_eval_buffer on the data. libsed will generate its output using
* sed_write_output which will add data in ctx->bb. Do it until it have
- * atleast one bucket in ctx->bb. At the end of data eos bucket
+ * at least one bucket in ctx->bb. At the end of data eos bucket
* should be there.
*
* Once eos bucket is seen, then invoke sed_finalize_eval to clear the
@@ -474,8 +463,10 @@ static apr_status_t sed_request_filter(ap_filter_t *f,
if (apr_bucket_read(b, &buf, &bytes, APR_BLOCK_READ)
== APR_SUCCESS) {
status = sed_eval_buffer(&ctx->eval, buf, bytes, ctx);
- if (status != APR_SUCCESS)
+ if (status != APR_SUCCESS) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, status, f->r, APLOGNO(10395) "error evaluating sed on input");
return status;
+ }
flush_output_buffer(ctx);
}
}
diff --git a/modules/filters/sed1.c b/modules/filters/sed1.c
index 67a8d06..047f49b 100644
--- a/modules/filters/sed1.c
+++ b/modules/filters/sed1.c
@@ -87,18 +87,20 @@ static void eval_errf(sed_eval_t *eval, const char *fmt, ...)
}
#define INIT_BUF_SIZE 1024
+#define MAX_BUF_SIZE 1024*8192
/*
* grow_buffer
*/
-static void grow_buffer(apr_pool_t *pool, char **buffer,
+static apr_status_t grow_buffer(apr_pool_t *pool, char **buffer,
char **spend, apr_size_t *cursize,
apr_size_t newsize)
{
char* newbuffer = NULL;
apr_size_t spendsize = 0;
- if (*cursize >= newsize)
- return;
+ if (*cursize >= newsize) {
+ return APR_SUCCESS;
+ }
/* Avoid number of times realloc is called. It could cause huge memory
* requirement if line size is huge e.g 2 MB */
if (newsize < *cursize * 2) {
@@ -107,6 +109,9 @@ static void grow_buffer(apr_pool_t *pool, char **buffer,
/* Align it to 4 KB boundary */
newsize = (newsize + ((1 << 12) - 1)) & ~((1 << 12) - 1);
+ if (newsize > MAX_BUF_SIZE) {
+ return APR_ENOMEM;
+ }
newbuffer = apr_pcalloc(pool, newsize);
if (*spend && *buffer && (*cursize > 0)) {
spendsize = *spend - *buffer;
@@ -119,63 +124,77 @@ static void grow_buffer(apr_pool_t *pool, char **buffer,
if (spend != buffer) {
*spend = *buffer + spendsize;
}
+ return APR_SUCCESS;
}
/*
* grow_line_buffer
*/
-static void grow_line_buffer(sed_eval_t *eval, apr_size_t newsize)
+static apr_status_t grow_line_buffer(sed_eval_t *eval, apr_size_t newsize)
{
- grow_buffer(eval->pool, &eval->linebuf, &eval->lspend,
+ return grow_buffer(eval->pool, &eval->linebuf, &eval->lspend,
&eval->lsize, newsize);
}
/*
* grow_hold_buffer
*/
-static void grow_hold_buffer(sed_eval_t *eval, apr_size_t newsize)
+static apr_status_t grow_hold_buffer(sed_eval_t *eval, apr_size_t newsize)
{
- grow_buffer(eval->pool, &eval->holdbuf, &eval->hspend,
+ return grow_buffer(eval->pool, &eval->holdbuf, &eval->hspend,
&eval->hsize, newsize);
}
/*
* grow_gen_buffer
*/
-static void grow_gen_buffer(sed_eval_t *eval, apr_size_t newsize,
+static apr_status_t grow_gen_buffer(sed_eval_t *eval, apr_size_t newsize,
char **gspend)
{
+ apr_status_t rc = 0;
if (gspend == NULL) {
gspend = &eval->genbuf;
}
- grow_buffer(eval->pool, &eval->genbuf, gspend,
- &eval->gsize, newsize);
- eval->lcomend = &eval->genbuf[71];
+ rc = grow_buffer(eval->pool, &eval->genbuf, gspend,
+ &eval->gsize, newsize);
+ if (rc == APR_SUCCESS) {
+ eval->lcomend = &eval->genbuf[71];
+ }
+ return rc;
}
/*
* appendmem_to_linebuf
*/
-static void appendmem_to_linebuf(sed_eval_t *eval, const char* sz, apr_size_t len)
+static apr_status_t appendmem_to_linebuf(sed_eval_t *eval, const char* sz, apr_size_t len)
{
+ apr_status_t rc = 0;
apr_size_t reqsize = (eval->lspend - eval->linebuf) + len;
if (eval->lsize < reqsize) {
- grow_line_buffer(eval, reqsize);
+ rc = grow_line_buffer(eval, reqsize);
+ if (rc != APR_SUCCESS) {
+ return rc;
+ }
}
memcpy(eval->lspend, sz, len);
eval->lspend += len;
+ return APR_SUCCESS;
}
/*
* append_to_linebuf
*/
-static void append_to_linebuf(sed_eval_t *eval, const char* sz,
+static apr_status_t append_to_linebuf(sed_eval_t *eval, const char* sz,
step_vars_storage *step_vars)
{
apr_size_t len = strlen(sz);
char *old_linebuf = eval->linebuf;
+ apr_status_t rc = 0;
/* Copy string including null character */
- appendmem_to_linebuf(eval, sz, len + 1);
+ rc = appendmem_to_linebuf(eval, sz, len + 1);
+ if (rc != APR_SUCCESS) {
+ return rc;
+ }
--eval->lspend; /* lspend will now point to NULL character */
/* Sync step_vars after a possible linebuf expansion */
if (step_vars && old_linebuf != eval->linebuf) {
@@ -189,68 +208,84 @@ static void append_to_linebuf(sed_eval_t *eval, const char* sz,
step_vars->locs = step_vars->locs - old_linebuf + eval->linebuf;
}
}
+ return APR_SUCCESS;
}
/*
* copy_to_linebuf
*/
-static void copy_to_linebuf(sed_eval_t *eval, const char* sz,
+static apr_status_t copy_to_linebuf(sed_eval_t *eval, const char* sz,
step_vars_storage *step_vars)
{
eval->lspend = eval->linebuf;
- append_to_linebuf(eval, sz, step_vars);
+ return append_to_linebuf(eval, sz, step_vars);
}
/*
* append_to_holdbuf
*/
-static void append_to_holdbuf(sed_eval_t *eval, const char* sz)
+static apr_status_t append_to_holdbuf(sed_eval_t *eval, const char* sz)
{
apr_size_t len = strlen(sz);
apr_size_t reqsize = (eval->hspend - eval->holdbuf) + len + 1;
+ apr_status_t rc = 0;
if (eval->hsize <= reqsize) {
- grow_hold_buffer(eval, reqsize);
+ rc = grow_hold_buffer(eval, reqsize);
+ if (rc != APR_SUCCESS) {
+ return rc;
+ }
}
memcpy(eval->hspend, sz, len + 1);
/* hspend will now point to NULL character */
eval->hspend += len;
+ return APR_SUCCESS;
}
/*
* copy_to_holdbuf
*/
-static void copy_to_holdbuf(sed_eval_t *eval, const char* sz)
+static apr_status_t copy_to_holdbuf(sed_eval_t *eval, const char* sz)
{
eval->hspend = eval->holdbuf;
- append_to_holdbuf(eval, sz);
+ return append_to_holdbuf(eval, sz);
}
/*
* append_to_genbuf
*/
-static void append_to_genbuf(sed_eval_t *eval, const char* sz, char **gspend)
+static apr_status_t append_to_genbuf(sed_eval_t *eval, const char* sz, char **gspend)
{
apr_size_t len = strlen(sz);
apr_size_t reqsize = (*gspend - eval->genbuf) + len + 1;
+ apr_status_t rc = 0;
if (eval->gsize < reqsize) {
- grow_gen_buffer(eval, reqsize, gspend);
+ rc = grow_gen_buffer(eval, reqsize, gspend);
+ if (rc != APR_SUCCESS) {
+ return rc;
+ }
}
memcpy(*gspend, sz, len + 1);
/* *gspend will now point to NULL character */
*gspend += len;
+ return APR_SUCCESS;
}
/*
* copy_to_genbuf
*/
-static void copy_to_genbuf(sed_eval_t *eval, const char* sz)
+static apr_status_t copy_to_genbuf(sed_eval_t *eval, const char* sz)
{
apr_size_t len = strlen(sz);
apr_size_t reqsize = len + 1;
+ apr_status_t rc = APR_SUCCESS;;
if (eval->gsize < reqsize) {
- grow_gen_buffer(eval, reqsize, NULL);
+ rc = grow_gen_buffer(eval, reqsize, NULL);
+ if (rc != APR_SUCCESS) {
+ return rc;
+ }
}
memcpy(eval->genbuf, sz, len + 1);
+ return rc;
}
/*
@@ -397,6 +432,7 @@ apr_status_t sed_eval_buffer(sed_eval_t *eval, const char *buf, apr_size_t bufsz
}
while (bufsz) {
+ apr_status_t rc = 0;
char *n;
apr_size_t llen;
@@ -411,7 +447,10 @@ apr_status_t sed_eval_buffer(sed_eval_t *eval, const char *buf, apr_size_t bufsz
break;
}
- appendmem_to_linebuf(eval, buf, llen + 1);
+ rc = appendmem_to_linebuf(eval, buf, llen + 1);
+ if (rc != APR_SUCCESS) {
+ return rc;
+ }
--eval->lspend;
/* replace new line character with NULL */
*eval->lspend = '\0';
@@ -426,7 +465,10 @@ apr_status_t sed_eval_buffer(sed_eval_t *eval, const char *buf, apr_size_t bufsz
/* Save the leftovers for later */
if (bufsz) {
- appendmem_to_linebuf(eval, buf, bufsz);
+ apr_status_t rc = appendmem_to_linebuf(eval, buf, bufsz);
+ if (rc != APR_SUCCESS) {
+ return rc;
+ }
}
return APR_SUCCESS;
@@ -448,6 +490,7 @@ apr_status_t sed_finalize_eval(sed_eval_t *eval, void *fout)
/* Process leftovers */
if (eval->lspend > eval->linebuf) {
apr_status_t rv;
+ apr_status_t rc = 0;
if (eval->lreadyflag) {
eval->lreadyflag = 0;
@@ -457,7 +500,10 @@ apr_status_t sed_finalize_eval(sed_eval_t *eval, void *fout)
* buffer is not a newline.
*/
/* Assure space for NULL */
- append_to_linebuf(eval, "", NULL);
+ rc = append_to_linebuf(eval, "", NULL);
+ if (rc != APR_SUCCESS) {
+ return rc;
+ }
}
*eval->lspend = '\0';
@@ -655,11 +701,15 @@ static apr_status_t dosub(sed_eval_t *eval, char *rhsbuf, int n,
sp = eval->genbuf;
rp = rhsbuf;
sp = place(eval, sp, lp, step_vars->loc1);
+ if (sp == NULL) {
+ return APR_EGENERAL;
+ }
while ((c = *rp++) != 0) {
if (c == '&') {
sp = place(eval, sp, step_vars->loc1, step_vars->loc2);
- if (sp == NULL)
+ if (sp == NULL) {
return APR_EGENERAL;
+ }
}
else if (c == '\\') {
c = *rp++;
@@ -675,13 +725,19 @@ static apr_status_t dosub(sed_eval_t *eval, char *rhsbuf, int n,
*sp++ = c;
if (sp >= eval->genbuf + eval->gsize) {
/* expand genbuf and set the sp appropriately */
- grow_gen_buffer(eval, eval->gsize + 1024, &sp);
+ rv = grow_gen_buffer(eval, eval->gsize + 1024, &sp);
+ if (rv != APR_SUCCESS) {
+ return rv;
+ }
}
}
lp = step_vars->loc2;
step_vars->loc2 = sp - eval->genbuf + eval->linebuf;
- append_to_genbuf(eval, lp, &sp);
- copy_to_linebuf(eval, eval->genbuf, step_vars);
+ rv = append_to_genbuf(eval, lp, &sp);
+ if (rv != APR_SUCCESS) {
+ return rv;
+ }
+ rv = copy_to_linebuf(eval, eval->genbuf, step_vars);
return rv;
}
@@ -695,7 +751,10 @@ static char *place(sed_eval_t *eval, char *asp, char *al1, char *al2)
apr_size_t reqsize = (sp - eval->genbuf) + n + 1;
if (eval->gsize < reqsize) {
- grow_gen_buffer(eval, reqsize, &sp);
+ apr_status_t rc = grow_gen_buffer(eval, reqsize, &sp);
+ if (rc != APR_SUCCESS) {
+ return NULL;
+ }
}
memcpy(sp, al1, n);
return sp + n;
@@ -750,7 +809,8 @@ static apr_status_t command(sed_eval_t *eval, sed_reptr_t *ipc,
}
p1++;
- copy_to_linebuf(eval, p1, step_vars);
+ rv = copy_to_linebuf(eval, p1, step_vars);
+ if (rv != APR_SUCCESS) return rv;
eval->jflag++;
break;
@@ -760,21 +820,27 @@ static apr_status_t command(sed_eval_t *eval, sed_reptr_t *ipc,
break;
case GCOM:
- copy_to_linebuf(eval, eval->holdbuf, step_vars);
+ rv = copy_to_linebuf(eval, eval->holdbuf, step_vars);
+ if (rv != APR_SUCCESS) return rv;
break;
case CGCOM:
- append_to_linebuf(eval, "\n", step_vars);
- append_to_linebuf(eval, eval->holdbuf, step_vars);
+ rv = append_to_linebuf(eval, "\n", step_vars);
+ if (rv != APR_SUCCESS) return rv;
+ rv = append_to_linebuf(eval, eval->holdbuf, step_vars);
+ if (rv != APR_SUCCESS) return rv;
break;
case HCOM:
- copy_to_holdbuf(eval, eval->linebuf);
+ rv = copy_to_holdbuf(eval, eval->linebuf);
+ if (rv != APR_SUCCESS) return rv;
break;
case CHCOM:
- append_to_holdbuf(eval, "\n");
- append_to_holdbuf(eval, eval->linebuf);
+ rv = append_to_holdbuf(eval, "\n");
+ if (rv != APR_SUCCESS) return rv;
+ rv = append_to_holdbuf(eval, eval->linebuf);
+ if (rv != APR_SUCCESS) return rv;
break;
case ICOM:
@@ -896,7 +962,8 @@ static apr_status_t command(sed_eval_t *eval, sed_reptr_t *ipc,
if (rv != APR_SUCCESS)
return rv;
}
- append_to_linebuf(eval, "\n", step_vars);
+ rv = append_to_linebuf(eval, "\n", step_vars);
+ if (rv != APR_SUCCESS) return rv;
eval->pending = ipc->next;
break;
@@ -970,9 +1037,12 @@ static apr_status_t command(sed_eval_t *eval, sed_reptr_t *ipc,
break;
case XCOM:
- copy_to_genbuf(eval, eval->linebuf);
- copy_to_linebuf(eval, eval->holdbuf, step_vars);
- copy_to_holdbuf(eval, eval->genbuf);
+ rv = copy_to_genbuf(eval, eval->linebuf);
+ if (rv != APR_SUCCESS) return rv;
+ rv = copy_to_linebuf(eval, eval->holdbuf, step_vars);
+ if (rv != APR_SUCCESS) return rv;
+ rv = copy_to_holdbuf(eval, eval->genbuf);
+ if (rv != APR_SUCCESS) return rv;
break;
case YCOM:

View File

@ -1,233 +0,0 @@
diff --git a/modules/lua/lua_request.c b/modules/lua/lua_request.c
index ba63584..c1ba74a 100644
--- a/modules/lua/lua_request.c
+++ b/modules/lua/lua_request.c
@@ -2193,23 +2193,20 @@ static int lua_websocket_greet(lua_State *L)
return 0;
}
-static apr_status_t lua_websocket_readbytes(conn_rec* c, char* buffer,
- apr_off_t len)
+static apr_status_t lua_websocket_readbytes(conn_rec* c,
+ apr_bucket_brigade *brigade,
+ char* buffer, apr_off_t len)
{
- apr_bucket_brigade *brigade = apr_brigade_create(c->pool, c->bucket_alloc);
+ apr_size_t delivered;
apr_status_t rv;
+
rv = ap_get_brigade(c->input_filters, brigade, AP_MODE_READBYTES,
APR_BLOCK_READ, len);
if (rv == APR_SUCCESS) {
- if (!APR_BRIGADE_EMPTY(brigade)) {
- apr_bucket* bucket = APR_BRIGADE_FIRST(brigade);
- const char* data = NULL;
- apr_size_t data_length = 0;
- rv = apr_bucket_read(bucket, &data, &data_length, APR_BLOCK_READ);
- if (rv == APR_SUCCESS) {
- memcpy(buffer, data, len);
- }
- apr_bucket_delete(bucket);
+ delivered = len;
+ rv = apr_brigade_flatten(brigade, buffer, &delivered);
+ if ((rv == APR_SUCCESS) && (delivered < len)) {
+ rv = APR_INCOMPLETE;
}
}
apr_brigade_cleanup(brigade);
@@ -2239,35 +2236,28 @@ static int lua_websocket_peek(lua_State *L)
static int lua_websocket_read(lua_State *L)
{
- apr_socket_t *sock;
apr_status_t rv;
int do_read = 1;
int n = 0;
- apr_size_t len = 1;
apr_size_t plen = 0;
unsigned short payload_short = 0;
apr_uint64_t payload_long = 0;
unsigned char *mask_bytes;
char byte;
- int plaintext;
-
-
+ apr_bucket_brigade *brigade;
+ conn_rec* c;
+
request_rec *r = ap_lua_check_request_rec(L, 1);
- plaintext = ap_lua_ssl_is_https(r->connection) ? 0 : 1;
+ c = r->connection;
-
mask_bytes = apr_pcalloc(r->pool, 4);
- sock = ap_get_conn_socket(r->connection);
+
+ brigade = apr_brigade_create(r->pool, c->bucket_alloc);
while (do_read) {
do_read = 0;
/* Get opcode and FIN bit */
- if (plaintext) {
- rv = apr_socket_recv(sock, &byte, &len);
- }
- else {
- rv = lua_websocket_readbytes(r->connection, &byte, 1);
- }
+ rv = lua_websocket_readbytes(c, brigade, &byte, 1);
if (rv == APR_SUCCESS) {
unsigned char ubyte, fin, opcode, mask, payload;
ubyte = (unsigned char)byte;
@@ -2277,12 +2267,7 @@ static int lua_websocket_read(lua_State *L)
opcode = ubyte & 0xf;
/* Get the payload length and mask bit */
- if (plaintext) {
- rv = apr_socket_recv(sock, &byte, &len);
- }
- else {
- rv = lua_websocket_readbytes(r->connection, &byte, 1);
- }
+ rv = lua_websocket_readbytes(c, brigade, &byte, 1);
if (rv == APR_SUCCESS) {
ubyte = (unsigned char)byte;
/* Mask is the first bit */
@@ -2293,40 +2278,25 @@ static int lua_websocket_read(lua_State *L)
/* Extended payload? */
if (payload == 126) {
- len = 2;
- if (plaintext) {
- /* XXX: apr_socket_recv does not receive len bits, only up to len bits! */
- rv = apr_socket_recv(sock, (char*) &payload_short, &len);
- }
- else {
- rv = lua_websocket_readbytes(r->connection,
- (char*) &payload_short, 2);
- }
- payload_short = ntohs(payload_short);
+ rv = lua_websocket_readbytes(c, brigade,
+ (char*) &payload_short, 2);
- if (rv == APR_SUCCESS) {
- plen = payload_short;
- }
- else {
+ if (rv != APR_SUCCESS) {
return 0;
}
+
+ plen = ntohs(payload_short);
}
/* Super duper extended payload? */
if (payload == 127) {
- len = 8;
- if (plaintext) {
- rv = apr_socket_recv(sock, (char*) &payload_long, &len);
- }
- else {
- rv = lua_websocket_readbytes(r->connection,
- (char*) &payload_long, 8);
- }
- if (rv == APR_SUCCESS) {
- plen = ap_ntoh64(&payload_long);
- }
- else {
+ rv = lua_websocket_readbytes(c, brigade,
+ (char*) &payload_long, 8);
+
+ if (rv != APR_SUCCESS) {
return 0;
}
+
+ plen = ap_ntoh64(&payload_long);
}
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(03210)
"Websocket: Reading %" APR_SIZE_T_FMT " (%s) bytes, masking is %s. %s",
@@ -2335,46 +2305,27 @@ static int lua_websocket_read(lua_State *L)
mask ? "on" : "off",
fin ? "This is a final frame" : "more to follow");
if (mask) {
- len = 4;
- if (plaintext) {
- rv = apr_socket_recv(sock, (char*) mask_bytes, &len);
- }
- else {
- rv = lua_websocket_readbytes(r->connection,
- (char*) mask_bytes, 4);
- }
+ rv = lua_websocket_readbytes(c, brigade,
+ (char*) mask_bytes, 4);
+
if (rv != APR_SUCCESS) {
return 0;
}
}
if (plen < (HUGE_STRING_LEN*1024) && plen > 0) {
apr_size_t remaining = plen;
- apr_size_t received;
- apr_off_t at = 0;
char *buffer = apr_palloc(r->pool, plen+1);
buffer[plen] = 0;
- if (plaintext) {
- while (remaining > 0) {
- received = remaining;
- rv = apr_socket_recv(sock, buffer+at, &received);
- if (received > 0 ) {
- remaining -= received;
- at += received;
- }
- }
- ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r,
- "Websocket: Frame contained %" APR_OFF_T_FMT " bytes, pushed to Lua stack",
- at);
- }
- else {
- rv = lua_websocket_readbytes(r->connection, buffer,
- remaining);
- ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r,
- "Websocket: SSL Frame contained %" APR_SIZE_T_FMT " bytes, "\
- "pushed to Lua stack",
- remaining);
+ rv = lua_websocket_readbytes(c, brigade, buffer, remaining);
+
+ if (rv != APR_SUCCESS) {
+ return 0;
}
+
+ ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r,
+ "Websocket: Frame contained %" APR_SIZE_T_FMT \
+ " bytes, pushed to Lua stack", remaining);
if (mask) {
for (n = 0; n < plen; n++) {
buffer[n] ^= mask_bytes[n%4];
@@ -2386,14 +2337,25 @@ static int lua_websocket_read(lua_State *L)
return 2;
}
-
/* Decide if we need to react to the opcode or not */
if (opcode == 0x09) { /* ping */
char frame[2];
- plen = 2;
+ apr_bucket *b;
+
frame[0] = 0x8A;
frame[1] = 0;
- apr_socket_send(sock, frame, &plen); /* Pong! */
+
+ /* Pong! */
+ b = apr_bucket_transient_create(frame, 2, c->bucket_alloc);
+ APR_BRIGADE_INSERT_TAIL(brigade, b);
+
+ rv = ap_pass_brigade(c->output_filters, brigade);
+ apr_brigade_cleanup(brigade);
+
+ if (rv != APR_SUCCESS) {
+ return 0;
+ }
+
do_read = 1;
}
}

View File

@ -1,229 +0,0 @@
diff --git a/modules/proxy/proxy_util.c b/modules/proxy/proxy_util.c
index efcc6ca..6626ea0 100644
--- a/modules/proxy/proxy_util.c
+++ b/modules/proxy/proxy_util.c
@@ -3631,12 +3631,14 @@ PROXY_DECLARE(int) ap_proxy_create_hdrbrgd(apr_pool_t *p,
char **old_cl_val,
char **old_te_val)
{
+ int rc = OK;
conn_rec *c = r->connection;
int counter;
char *buf;
+ apr_table_t *saved_headers_in = r->headers_in;
+ const char *saved_host = apr_table_get(saved_headers_in, "Host");
const apr_array_header_t *headers_in_array;
const apr_table_entry_t *headers_in;
- apr_table_t *saved_headers_in;
apr_bucket *e;
int do_100_continue;
conn_rec *origin = p_conn->connection;
@@ -3672,6 +3674,52 @@ PROXY_DECLARE(int) ap_proxy_create_hdrbrgd(apr_pool_t *p,
ap_xlate_proto_to_ascii(buf, strlen(buf));
e = apr_bucket_pool_create(buf, strlen(buf), p, c->bucket_alloc);
APR_BRIGADE_INSERT_TAIL(header_brigade, e);
+
+ /*
+ * Make a copy on r->headers_in for the request we make to the backend,
+ * modify the copy in place according to our configuration and connection
+ * handling, use it to fill in the forwarded headers' brigade, and finally
+ * restore the saved/original ones in r->headers_in.
+ *
+ * Note: We need to take r->pool for apr_table_copy as the key / value
+ * pairs in r->headers_in have been created out of r->pool and
+ * p might be (and actually is) a longer living pool.
+ * This would trigger the bad pool ancestry abort in apr_table_copy if
+ * apr is compiled with APR_POOL_DEBUG.
+ *
+ * icing: if p indeed lives longer than r->pool, we should allocate
+ * all new header values from r->pool as well and avoid leakage.
+ */
+ r->headers_in = apr_table_copy(r->pool, saved_headers_in);
+
+ /* Return the original Transfer-Encoding and/or Content-Length values
+ * then drop the headers, they must be set by the proxy handler based
+ * on the actual body being forwarded.
+ */
+ if ((*old_te_val = (char *)apr_table_get(r->headers_in,
+ "Transfer-Encoding"))) {
+ apr_table_unset(r->headers_in, "Transfer-Encoding");
+ }
+ if ((*old_cl_val = (char *)apr_table_get(r->headers_in,
+ "Content-Length"))) {
+ apr_table_unset(r->headers_in, "Content-Length");
+ }
+
+ /* Clear out hop-by-hop request headers not to forward */
+ if (ap_proxy_clear_connection(r, r->headers_in) < 0) {
+ rc = HTTP_BAD_REQUEST;
+ goto cleanup;
+ }
+
+ /* RFC2616 13.5.1 says we should strip these */
+ apr_table_unset(r->headers_in, "Keep-Alive");
+ apr_table_unset(r->headers_in, "Upgrade");
+ apr_table_unset(r->headers_in, "Trailer");
+ apr_table_unset(r->headers_in, "TE");
+
+ /* We used to send `Host: ` always first, so let's keep it that
+ * way. No telling which legacy backend is relying no this.
+ */
if (dconf->preserve_host == 0) {
if (ap_strchr_c(uri->hostname, ':')) { /* if literal IPv6 address */
if (uri->port_str && uri->port != DEFAULT_HTTP_PORT) {
@@ -3693,7 +3741,7 @@ PROXY_DECLARE(int) ap_proxy_create_hdrbrgd(apr_pool_t *p,
/* don't want to use r->hostname, as the incoming header might have a
* port attached
*/
- const char* hostname = apr_table_get(r->headers_in,"Host");
+ const char* hostname = saved_host;
if (!hostname) {
hostname = r->server->server_hostname;
ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(01092)
@@ -3707,21 +3755,7 @@ PROXY_DECLARE(int) ap_proxy_create_hdrbrgd(apr_pool_t *p,
ap_xlate_proto_to_ascii(buf, strlen(buf));
e = apr_bucket_pool_create(buf, strlen(buf), p, c->bucket_alloc);
APR_BRIGADE_INSERT_TAIL(header_brigade, e);
-
- /*
- * Save the original headers in here and restore them when leaving, since
- * we will apply proxy purpose only modifications (eg. clearing hop-by-hop
- * headers, add Via or X-Forwarded-* or Expect...), whereas the originals
- * will be needed later to prepare the correct response and logging.
- *
- * Note: We need to take r->pool for apr_table_copy as the key / value
- * pairs in r->headers_in have been created out of r->pool and
- * p might be (and actually is) a longer living pool.
- * This would trigger the bad pool ancestry abort in apr_table_copy if
- * apr is compiled with APR_POOL_DEBUG.
- */
- saved_headers_in = r->headers_in;
- r->headers_in = apr_table_copy(r->pool, saved_headers_in);
+ apr_table_unset(r->headers_in, "Host");
/* handle Via */
if (conf->viaopt == via_block) {
@@ -3788,8 +3822,6 @@ PROXY_DECLARE(int) ap_proxy_create_hdrbrgd(apr_pool_t *p,
*/
if (dconf->add_forwarded_headers) {
if (PROXYREQ_REVERSE == r->proxyreq) {
- const char *buf;
-
/* Add X-Forwarded-For: so that the upstream has a chance to
* determine, where the original request came from.
*/
@@ -3799,8 +3831,9 @@ PROXY_DECLARE(int) ap_proxy_create_hdrbrgd(apr_pool_t *p,
/* Add X-Forwarded-Host: so that upstream knows what the
* original request hostname was.
*/
- if ((buf = apr_table_get(r->headers_in, "Host"))) {
- apr_table_mergen(r->headers_in, "X-Forwarded-Host", buf);
+ if (saved_host) {
+ apr_table_mergen(r->headers_in, "X-Forwarded-Host",
+ saved_host);
}
/* Add X-Forwarded-Server: so that upstream knows what the
@@ -3812,10 +3845,27 @@ PROXY_DECLARE(int) ap_proxy_create_hdrbrgd(apr_pool_t *p,
}
}
+ /* Do we want to strip Proxy-Authorization ?
+ * If we haven't used it, then NO
+ * If we have used it then MAYBE: RFC2616 says we MAY propagate it.
+ * So let's make it configurable by env.
+ */
+ if (r->user != NULL /* we've authenticated */
+ && !apr_table_get(r->subprocess_env, "Proxy-Chain-Auth")) {
+ apr_table_unset(r->headers_in, "Proxy-Authorization");
+ }
+
+ /* for sub-requests, ignore freshness/expiry headers */
+ if (r->main) {
+ apr_table_unset(r->headers_in, "If-Match");
+ apr_table_unset(r->headers_in, "If-Modified-Since");
+ apr_table_unset(r->headers_in, "If-Range");
+ apr_table_unset(r->headers_in, "If-Unmodified-Since");
+ apr_table_unset(r->headers_in, "If-None-Match");
+ }
+
+ /* run hook to fixup the request we are about to send */
proxy_run_fixups(r);
- if (ap_proxy_clear_connection(r, r->headers_in) < 0) {
- return HTTP_BAD_REQUEST;
- }
creds = apr_table_get(r->notes, "proxy-basic-creds");
if (creds) {
@@ -3827,55 +3877,8 @@ PROXY_DECLARE(int) ap_proxy_create_hdrbrgd(apr_pool_t *p,
headers_in = (const apr_table_entry_t *) headers_in_array->elts;
for (counter = 0; counter < headers_in_array->nelts; counter++) {
if (headers_in[counter].key == NULL
- || headers_in[counter].val == NULL
-
- /* Already sent */
- || !strcasecmp(headers_in[counter].key, "Host")
-
- /* Clear out hop-by-hop request headers not to send
- * RFC2616 13.5.1 says we should strip these headers
- */
- || !strcasecmp(headers_in[counter].key, "Keep-Alive")
- || !strcasecmp(headers_in[counter].key, "TE")
- || !strcasecmp(headers_in[counter].key, "Trailer")
- || !strcasecmp(headers_in[counter].key, "Upgrade")
-
- ) {
- continue;
- }
- /* Do we want to strip Proxy-Authorization ?
- * If we haven't used it, then NO
- * If we have used it then MAYBE: RFC2616 says we MAY propagate it.
- * So let's make it configurable by env.
- */
- if (!strcasecmp(headers_in[counter].key,"Proxy-Authorization")) {
- if (r->user != NULL) { /* we've authenticated */
- if (!apr_table_get(r->subprocess_env, "Proxy-Chain-Auth")) {
- continue;
- }
- }
- }
-
- /* Skip Transfer-Encoding and Content-Length for now.
- */
- if (!strcasecmp(headers_in[counter].key, "Transfer-Encoding")) {
- *old_te_val = headers_in[counter].val;
- continue;
- }
- if (!strcasecmp(headers_in[counter].key, "Content-Length")) {
- *old_cl_val = headers_in[counter].val;
- continue;
- }
-
- /* for sub-requests, ignore freshness/expiry headers */
- if (r->main) {
- if ( !strcasecmp(headers_in[counter].key, "If-Match")
- || !strcasecmp(headers_in[counter].key, "If-Modified-Since")
- || !strcasecmp(headers_in[counter].key, "If-Range")
- || !strcasecmp(headers_in[counter].key, "If-Unmodified-Since")
- || !strcasecmp(headers_in[counter].key, "If-None-Match")) {
- continue;
- }
+ || headers_in[counter].val == NULL) {
+ continue;
}
buf = apr_pstrcat(p, headers_in[counter].key, ": ",
@@ -3886,11 +3889,9 @@ PROXY_DECLARE(int) ap_proxy_create_hdrbrgd(apr_pool_t *p,
APR_BRIGADE_INSERT_TAIL(header_brigade, e);
}
- /* Restore the original headers in (see comment above),
- * we won't modify them anymore.
- */
+cleanup:
r->headers_in = saved_headers_in;
- return OK;
+ return rc;
}
PROXY_DECLARE(int) ap_proxy_prefetch_input(request_rec *r,

View File

@ -1,23 +0,0 @@
From 5efc9507c487c37dfe2a279a4a0335cad701cd5f Mon Sep 17 00:00:00 2001
From: Eric Covener <covener@apache.org>
Date: Tue, 10 Jan 2023 13:19:07 +0000
Subject: [PATCH] cleanup on error
git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1906540 13f79535-47bb-0310-9956-ffa450edef68
---
modules/proxy/mod_proxy_ajp.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/modules/proxy/mod_proxy_ajp.c b/modules/proxy/mod_proxy_ajp.c
index 9cd7adbcbbf..07f37392d88 100644
--- a/modules/proxy/mod_proxy_ajp.c
+++ b/modules/proxy/mod_proxy_ajp.c
@@ -255,6 +255,8 @@ static int ap_proxy_ajp_request(apr_pool_t *p, request_rec *r,
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10396)
"%s Transfer-Encoding is not supported",
tenc);
+ /* We had a failure: Close connection to backend */
+ conn->close = 1;
return HTTP_INTERNAL_SERVER_ERROR;
}
} else {

View File

@ -1,129 +0,0 @@
From 8b6d55f6a047acf62675e32606b037f5eea8ccc7 Mon Sep 17 00:00:00 2001
From: Eric Covener <covener@apache.org>
Date: Tue, 10 Jan 2023 13:20:09 +0000
Subject: [PATCH] Merge r1906539 from trunk:
fail on bad header
Submitted By: covener
Reviewed By: covener, rpluem, gbechis
git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x@1906541 13f79535-47bb-0310-9956-ffa450edef68
---
modules/proxy/mod_proxy_http.c | 46 ++++++++++++++++++++--------------
server/protocol.c | 2 ++
2 files changed, 29 insertions(+), 19 deletions(-)
diff --git a/modules/proxy/mod_proxy_http.c b/modules/proxy/mod_proxy_http.c
index d74ae054ac9..ec4e7fb06b5 100644
--- a/modules/proxy/mod_proxy_http.c
+++ b/modules/proxy/mod_proxy_http.c
@@ -788,7 +788,7 @@ static void process_proxy_header(request_rec *r, proxy_dir_conf *c,
* any sense at all, since we depend on buffer still containing
* what was read by ap_getline() upon return.
*/
-static void ap_proxy_read_headers(request_rec *r, request_rec *rr,
+static apr_status_t ap_proxy_read_headers(request_rec *r, request_rec *rr,
char *buffer, int size,
conn_rec *c, int *pread_len)
{
@@ -820,19 +820,26 @@ static void ap_proxy_read_headers(request_rec *r, request_rec *rr,
rc = ap_proxygetline(tmp_bb, buffer, size, rr,
AP_GETLINE_FOLD | AP_GETLINE_NOSPC_EOL, &len);
- if (len <= 0)
- break;
- if (APR_STATUS_IS_ENOSPC(rc)) {
- /* The header could not fit in the provided buffer, warn.
- * XXX: falls through with the truncated header, 5xx instead?
- */
- int trunc = (len > 128 ? 128 : len) / 2;
- ap_log_rerror(APLOG_MARK, APLOG_WARNING, rc, r, APLOGNO(10124)
- "header size is over the limit allowed by "
- "ResponseFieldSize (%d bytes). "
- "Bad response header: '%.*s[...]%s'",
- size, trunc, buffer, buffer + len - trunc);
+ if (rc != APR_SUCCESS) {
+ if (APR_STATUS_IS_ENOSPC(rc)) {
+ int trunc = (len > 128 ? 128 : len) / 2;
+ ap_log_rerror(APLOG_MARK, APLOG_WARNING, rc, r, APLOGNO(10124)
+ "header size is over the limit allowed by "
+ "ResponseFieldSize (%d bytes). "
+ "Bad response header: '%.*s[...]%s'",
+ size, trunc, buffer, buffer + len - trunc);
+ }
+ else {
+ ap_log_rerror(APLOG_MARK, APLOG_WARNING, rc, r, APLOGNO(10404)
+ "Error reading headers from backend");
+ }
+ r->headers_out = NULL;
+ return rc;
+ }
+
+ if (len <= 0) {
+ break;
}
else {
ap_log_rerror(APLOG_MARK, APLOG_TRACE4, 0, r, "%s", buffer);
@@ -855,7 +862,7 @@ static void ap_proxy_read_headers(request_rec *r, request_rec *rr,
if (psc->badopt == bad_error) {
/* Nope, it wasn't even an extra HTTP header. Give up. */
r->headers_out = NULL;
- return;
+ return APR_EINVAL;
}
else if (psc->badopt == bad_body) {
/* if we've already started loading headers_out, then
@@ -869,13 +876,13 @@ static void ap_proxy_read_headers(request_rec *r, request_rec *rr,
"in headers returned by %s (%s)",
r->uri, r->method);
*pread_len = len;
- return;
+ return APR_SUCCESS;
}
else {
ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(01099)
"No HTTP headers returned by %s (%s)",
r->uri, r->method);
- return;
+ return APR_SUCCESS;
}
}
}
@@ -905,6 +912,7 @@ static void ap_proxy_read_headers(request_rec *r, request_rec *rr,
process_proxy_header(r, dconf, buffer, value);
saw_headers = 1;
}
+ return APR_SUCCESS;
}
@@ -1218,10 +1226,10 @@ int ap_proxy_http_process_response(proxy_http_req_t *req)
"Set-Cookie", NULL);
/* shove the headers direct into r->headers_out */
- ap_proxy_read_headers(r, backend->r, buffer, response_field_size,
- origin, &pread_len);
+ rc = ap_proxy_read_headers(r, backend->r, buffer, response_field_size,
+ origin, &pread_len);
- if (r->headers_out == NULL) {
+ if (rc != APR_SUCCESS || r->headers_out == NULL) {
ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(01106)
"bad HTTP/%d.%d header returned by %s (%s)",
major, minor, r->uri, r->method);
diff --git a/server/protocol.c b/server/protocol.c
index 7adc7f75c10..6f9540ad1de 100644
--- a/server/protocol.c
+++ b/server/protocol.c
@@ -508,6 +508,8 @@ AP_DECLARE(apr_status_t) ap_rgetline_core(char **s, apr_size_t n,
/* PR#43039: We shouldn't accept NULL bytes within the line */
bytes_handled = strlen(*s);
if (bytes_handled < *read) {
+ ap_log_data(APLOG_MARK, APLOG_DEBUG, ap_server_conf,
+ "NULL bytes in header", *s, *read, 0);
*read = bytes_handled;
if (rv == APR_SUCCESS) {
rv = APR_EINVAL;

View File

@ -1,586 +0,0 @@
diff --git a/docs/manual/mod/mod_rewrite.html.en b/docs/manual/mod/mod_rewrite.html.en
index 815ec72..2b8ed35 100644
--- a/docs/manual/mod/mod_rewrite.html.en
+++ b/docs/manual/mod/mod_rewrite.html.en
@@ -1265,7 +1265,17 @@ cannot use <code>$N</code> in the substitution string!
<td>B</td>
<td>Escape non-alphanumeric characters in backreferences <em>before</em>
applying the transformation. <em><a href="../rewrite/flags.html#flag_b">details ...</a></em></td>
- </tr>
+ </tr>
+<tr class="odd">
+ <td>BCTLS</td>
+ <td>Like [B], but only escape control characters and spaces.
+ <em><a href="../rewrite/flags.html#flag_bctls">details ...</a></em></td>
+</tr>
+ <tr>
+ <td>BNE</td>
+ <td>Characters of [B] or [BCTLS] which should <strong>not</strong> be escaped.
+ <em><a href="../rewrite/flags.html#flag_bne">details ...</a></em></td>
+ </tr>
<tr class="odd">
<td>backrefnoplus|BNP</td>
<td>If backreferences are being escaped, spaces should be escaped to
diff --git a/docs/manual/rewrite/flags.html.en b/docs/manual/rewrite/flags.html.en
index 80d0759..734809a 100644
--- a/docs/manual/rewrite/flags.html.en
+++ b/docs/manual/rewrite/flags.html.en
@@ -85,10 +85,6 @@ of how you might use them.</p>
<h2><a name="flag_b" id="flag_b">B (escape backreferences)</a></h2>
<p>The [B] flag instructs <code class="directive"><a href="../mod/mod_rewrite.html#rewriterule">RewriteRule</a></code> to escape non-alphanumeric
characters before applying the transformation.</p>
-<p>In 2.4.26 and later, you can limit the escaping to specific characters
-in backreferences by listing them: <code>[B=#?;]</code>. Note: The space
-character can be used in the list of characters to escape, but it cannot be
-the last character in the list.</p>
<p><code>mod_rewrite</code> has to unescape URLs before mapping them,
so backreferences are unescaped at the time they are applied.
@@ -120,6 +116,20 @@ when the backend may break if presented with an unescaped URL.</p>
<p>An alternative to this flag is using a <code class="directive"><a href="../mod/mod_rewrite.html#rewritecond">RewriteCond</a></code> to capture against %{THE_REQUEST} which will capture
strings in the encoded form.</p>
+
+<p>In 2.4.26 and later, you can limit the escaping to specific characters
+in backreferences by listing them: <code>[B=#?;]</code>. Note: The space
+character can be used in the list of characters to escape, but you must quote
+the entire third argument of <code class="directive"><a href="../mod/mod_rewrite.html#rewriterule">RewriteRule</a></code>
+and the space must not be the last character in the list.</p>
+
+<pre class="prettyprint lang-config"># Escape spaces and question marks. The quotes around the final argument
+# are required when a space is included.
+RewriteRule "^search/(.*)$" "/search.php?term=$1" "[B= ?]"</pre>
+
+<p>To limit the characters escaped this way, see <a href="#flag_bne">#flag_bne</a>
+and <a href="#flag_bctls">#flag_bctls</a></p>
+
</div><div class="top"><a href="#page-header"><img alt="top" src="../images/up.gif" /></a></div>
<div class="section">
<h2><a name="flag_bnp" id="flag_bnp">BNP|backrefnoplus (don't escape space to +)</a></h2>
diff --git a/modules/mappers/mod_rewrite.c b/modules/mappers/mod_rewrite.c
index 38dbb24..b71c67c 100644
--- a/modules/mappers/mod_rewrite.c
+++ b/modules/mappers/mod_rewrite.c
@@ -101,6 +101,8 @@
#include "mod_rewrite.h"
#include "ap_expr.h"
+#include "test_char.h"
+
static ap_dbd_t *(*dbd_acquire)(request_rec*) = NULL;
static void (*dbd_prepare)(server_rec*, const char*, const char*) = NULL;
static const char* really_last_key = "rewrite_really_last";
@@ -168,6 +170,8 @@ static const char* really_last_key = "rewrite_really_last";
#define RULEFLAG_END (1<<17)
#define RULEFLAG_ESCAPENOPLUS (1<<18)
#define RULEFLAG_QSLAST (1<<19)
+#define RULEFLAG_QSNONE (1<<20) /* programattic only */
+#define RULEFLAG_ESCAPECTLS (1<<21)
/* return code of the rewrite rule
* the result may be escaped - or not
@@ -321,7 +325,8 @@ typedef struct {
data_item *cookie; /* added cookies */
int skip; /* number of next rules to skip */
int maxrounds; /* limit on number of loops with N flag */
- char *escapes; /* specific backref escapes */
+ const char *escapes; /* specific backref escapes */
+ const char *noescapes; /* specific backref chars not to escape */
} rewriterule_entry;
typedef struct {
@@ -422,7 +427,9 @@ static const char *rewritemap_mutex_type = "rewrite-map";
/* Optional functions imported from mod_ssl when loaded: */
static APR_OPTIONAL_FN_TYPE(ssl_var_lookup) *rewrite_ssl_lookup = NULL;
static APR_OPTIONAL_FN_TYPE(ssl_is_https) *rewrite_is_https = NULL;
-static char *escape_backref(apr_pool_t *p, const char *path, const char *escapeme, int noplus);
+static char *escape_backref(apr_pool_t *p, const char *path,
+ const char *escapeme, const char *noescapeme,
+ int flags);
/*
* +-------------------------------------------------------+
@@ -645,18 +652,26 @@ static APR_INLINE unsigned char *c2x(unsigned what, unsigned char prefix,
return where;
}
+
/*
* Escapes a backreference in a similar way as php's urlencode does.
* Based on ap_os_escape_path in server/util.c
*/
-static char *escape_backref(apr_pool_t *p, const char *path, const char *escapeme, int noplus) {
- char *copy = apr_palloc(p, 3 * strlen(path) + 3);
+static char *escape_backref(apr_pool_t *p, const char *path,
+ const char *escapeme, const char *noescapeme,
+ int flags)
+{
+ char *copy = apr_palloc(p, 3 * strlen(path) + 1);
const unsigned char *s = (const unsigned char *)path;
unsigned char *d = (unsigned char *)copy;
- unsigned c;
+ int noplus = (flags & RULEFLAG_ESCAPENOPLUS) != 0;
+ int ctls = (flags & RULEFLAG_ESCAPECTLS) != 0;
+ unsigned char c;
while ((c = *s)) {
- if (!escapeme) {
+ if (((ctls ? !TEST_CHAR(c, T_VCHAR_OBSTEXT) : !escapeme)
+ || (escapeme && ap_strchr_c(escapeme, c)))
+ && (!noescapeme || !ap_strchr_c(noescapeme, c))) {
if (apr_isalnum(c) || c == '_') {
*d++ = c;
}
@@ -667,23 +682,8 @@ static char *escape_backref(apr_pool_t *p, const char *path, const char *escapem
d = c2x(c, '%', d);
}
}
- else {
- const char *esc = escapeme;
- while (*esc) {
- if (c == *esc) {
- if (c == ' ' && !noplus) {
- *d++ = '+';
- }
- else {
- d = c2x(c, '%', d);
- }
- break;
- }
- ++esc;
- }
- if (!*esc) {
- *d++ = c;
- }
+ else {
+ *d++ = c;
}
++s;
}
@@ -761,15 +761,24 @@ static char *escape_absolute_uri(apr_pool_t *p, char *uri, unsigned scheme)
ap_escape_uri(p, cp), NULL);
}
+
/*
* split out a QUERY_STRING part from
* the current URI string
*/
-static void splitout_queryargs(request_rec *r, int qsappend, int qsdiscard,
- int qslast)
+static void splitout_queryargs(request_rec *r, int flags)
{
char *q;
int split;
+ int qsappend = flags & RULEFLAG_QSAPPEND;
+ int qsdiscard = flags & RULEFLAG_QSDISCARD;
+ int qslast = flags & RULEFLAG_QSLAST;
+
+ if (flags & RULEFLAG_QSNONE) {
+ rewritelog((r, 2, NULL, "discarding query string, no parse from substitution"));
+ r->args = NULL;
+ return;
+ }
/* don't touch, unless it's a scheme for which a query string makes sense.
* See RFC 1738 and RFC 2368.
@@ -794,7 +803,7 @@ static void splitout_queryargs(request_rec *r, int qsappend, int qsdiscard,
olduri = apr_pstrdup(r->pool, r->filename);
*q++ = '\0';
if (qsappend) {
- if (*q) {
+ if (*q) {
r->args = apr_pstrcat(r->pool, q, "&" , r->args, NULL);
}
}
@@ -802,9 +811,9 @@ static void splitout_queryargs(request_rec *r, int qsappend, int qsdiscard,
r->args = apr_pstrdup(r->pool, q);
}
- if (r->args) {
+ if (r->args) {
len = strlen(r->args);
-
+
if (!len) {
r->args = NULL;
}
@@ -2436,7 +2445,8 @@ static char *do_expand(char *input, rewrite_ctx *ctx, rewriterule_entry *entry)
/* escape the backreference */
char *tmp2, *tmp;
tmp = apr_pstrmemdup(pool, bri->source + bri->regmatch[n].rm_so, span);
- tmp2 = escape_backref(pool, tmp, entry->escapes, entry->flags & RULEFLAG_ESCAPENOPLUS);
+ tmp2 = escape_backref(pool, tmp, entry->escapes, entry->noescapes,
+ entry->flags);
rewritelog((ctx->r, 5, ctx->perdir, "escaping backreference '%s' to '%s'",
tmp, tmp2));
@@ -2733,7 +2743,7 @@ static apr_status_t rewritelock_remove(void *data)
* XXX: what an inclined parser. Seems we have to leave it so
* for backwards compat. *sigh*
*/
-static int parseargline(char *str, char **a1, char **a2, char **a3)
+static int parseargline(char *str, char **a1, char **a2, char **a2_end, char **a3)
{
char quote;
@@ -2784,8 +2794,10 @@ static int parseargline(char *str, char **a1, char **a2, char **a3)
if (!*str) {
*a3 = NULL; /* 3rd argument is optional */
+ *a2_end = str;
return 0;
}
+ *a2_end = str;
*str++ = '\0';
while (apr_isspace(*str)) {
@@ -3323,7 +3335,7 @@ static const char *cmd_rewritecond(cmd_parms *cmd, void *in_dconf,
rewrite_server_conf *sconf;
rewritecond_entry *newcond;
ap_regex_t *regexp;
- char *a1 = NULL, *a2 = NULL, *a3 = NULL;
+ char *a1 = NULL, *a2 = NULL, *a2_end, *a3 = NULL;
const char *err;
sconf = ap_get_module_config(cmd->server->module_config, &rewrite_module);
@@ -3341,7 +3353,7 @@ static const char *cmd_rewritecond(cmd_parms *cmd, void *in_dconf,
* of the argument line. So we can use a1 .. a3 without
* copying them again.
*/
- if (parseargline(str, &a1, &a2, &a3)) {
+ if (parseargline(str, &a1, &a2, &a2_end, &a3)) {
return apr_pstrcat(cmd->pool, "RewriteCond: bad argument line '", str,
"'", NULL);
}
@@ -3500,13 +3512,24 @@ static const char *cmd_rewriterule_setflag(apr_pool_t *p, void *_cfg,
case 'B':
if (!*key || !strcasecmp(key, "ackrefescaping")) {
cfg->flags |= RULEFLAG_ESCAPEBACKREF;
- if (val && *val) {
+ if (val && *val) {
cfg->escapes = val;
}
}
+ else if (!strcasecmp(key, "NE")) {
+ if (val && *val) {
+ cfg->noescapes = val;
+ }
+ else {
+ return "flag 'BNE' wants a list of characters (i.e. [BNE=...])";
+ }
+ }
else if (!strcasecmp(key, "NP") || !strcasecmp(key, "ackrefernoplus")) {
cfg->flags |= RULEFLAG_ESCAPENOPLUS;
}
+ else if (!strcasecmp(key, "CTLS")) {
+ cfg->flags |= RULEFLAG_ESCAPECTLS|RULEFLAG_ESCAPEBACKREF;
+ }
else {
++error;
}
@@ -3749,7 +3772,7 @@ static const char *cmd_rewriterule(cmd_parms *cmd, void *in_dconf,
rewrite_server_conf *sconf;
rewriterule_entry *newrule;
ap_regex_t *regexp;
- char *a1 = NULL, *a2 = NULL, *a3 = NULL;
+ char *a1 = NULL, *a2 = NULL, *a2_end, *a3 = NULL;
const char *err;
sconf = ap_get_module_config(cmd->server->module_config, &rewrite_module);
@@ -3763,12 +3786,11 @@ static const char *cmd_rewriterule(cmd_parms *cmd, void *in_dconf,
}
/* parse the argument line ourself */
- if (parseargline(str, &a1, &a2, &a3)) {
+ if (parseargline(str, &a1, &a2, &a2_end, &a3)) {
return apr_pstrcat(cmd->pool, "RewriteRule: bad argument line '", str,
"'", NULL);
}
- /* arg3: optional flags field */
newrule->forced_mimetype = NULL;
newrule->forced_handler = NULL;
newrule->forced_responsecode = HTTP_MOVED_TEMPORARILY;
@@ -3777,6 +3799,9 @@ static const char *cmd_rewriterule(cmd_parms *cmd, void *in_dconf,
newrule->cookie = NULL;
newrule->skip = 0;
newrule->maxrounds = REWRITE_MAX_ROUNDS;
+ newrule->escapes = newrule->noescapes = NULL;
+
+ /* arg3: optional flags field */
if (a3 != NULL) {
if ((err = cmd_parseflagfield(cmd->pool, newrule, a3,
cmd_rewriterule_setflag)) != NULL) {
@@ -3810,6 +3835,17 @@ static const char *cmd_rewriterule(cmd_parms *cmd, void *in_dconf,
newrule->flags |= RULEFLAG_NOSUB;
}
+ if (*(a2_end-1) == '?') {
+ /* a literal ? at the end of the unsubstituted rewrite rule */
+ newrule->flags |= RULEFLAG_QSNONE;
+ *(a2_end-1) = '\0'; /* trailing ? has done its job */
+ }
+ else if (newrule->flags & RULEFLAG_QSDISCARD) {
+ if (NULL == ap_strchr(newrule->output, '?')) {
+ newrule->flags |= RULEFLAG_QSNONE;
+ }
+ }
+
/* now, if the server or per-dir config holds an
* array of RewriteCond entries, we take it for us
* and clear the array
@@ -4215,9 +4251,7 @@ static int apply_rewrite_rule(rewriterule_entry *p, rewrite_ctx *ctx)
r->path_info = NULL;
}
- splitout_queryargs(r, p->flags & RULEFLAG_QSAPPEND,
- p->flags & RULEFLAG_QSDISCARD,
- p->flags & RULEFLAG_QSLAST);
+ splitout_queryargs(r, p->flags);
/* Add the previously stripped per-directory location prefix, unless
* (1) it's an absolute URL path and
@@ -4696,8 +4730,25 @@ static int hook_uri2file(request_rec *r)
}
if (rulestatus) {
- unsigned skip;
- apr_size_t flen;
+ unsigned skip_absolute = is_absolute_uri(r->filename, NULL);
+ apr_size_t flen = r->filename ? strlen(r->filename) : 0;
+ int to_proxyreq = (flen > 6 && strncmp(r->filename, "proxy:", 6) == 0);
+ int will_escape = skip_absolute && (rulestatus != ACTION_NOESCAPE);
+
+ if (r->args
+ && !will_escape
+ && *(ap_scan_vchar_obstext(r->args))) {
+ /*
+ * We have a raw control character or a ' ' in r->args.
+ * Correct encoding was missed.
+ * Correct encoding was missed and we're not going to escape
+ * it before returning.
+ */
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10410)
+ "Rewritten query string contains control "
+ "characters or spaces");
+ return HTTP_FORBIDDEN;
+ }
if (ACTION_STATUS == rulestatus) {
int n = r->status;
@@ -4706,8 +4757,7 @@ static int hook_uri2file(request_rec *r)
return n;
}
- flen = r->filename ? strlen(r->filename) : 0;
- if (flen > 6 && strncmp(r->filename, "proxy:", 6) == 0) {
+ if (to_proxyreq) {
/* it should be go on as an internal proxy request */
/* check if the proxy module is enabled, so
@@ -4749,7 +4799,7 @@ static int hook_uri2file(request_rec *r)
r->filename));
return OK;
}
- else if ((skip = is_absolute_uri(r->filename, NULL)) > 0) {
+ else if (skip_absolute > 0) {
int n;
/* it was finally rewritten to a remote URL */
@@ -4757,7 +4807,7 @@ static int hook_uri2file(request_rec *r)
if (rulestatus != ACTION_NOESCAPE) {
rewritelog((r, 1, NULL, "escaping %s for redirect",
r->filename));
- r->filename = escape_absolute_uri(r->pool, r->filename, skip);
+ r->filename = escape_absolute_uri(r->pool, r->filename, skip_absolute);
}
/* append the QUERY_STRING part */
@@ -4981,7 +5031,26 @@ static int hook_fixup(request_rec *r)
*/
rulestatus = apply_rewrite_list(r, dconf->rewriterules, dconf->directory);
if (rulestatus) {
- unsigned skip;
+ unsigned skip_absolute = is_absolute_uri(r->filename, NULL);
+ int to_proxyreq = 0;
+ int will_escape = 0;
+
+ l = strlen(r->filename);
+ to_proxyreq = l > 6 && strncmp(r->filename, "proxy:", 6) == 0;
+ will_escape = skip_absolute && (rulestatus != ACTION_NOESCAPE);
+
+ if (r->args
+ && !will_escape
+ && *(ap_scan_vchar_obstext(r->args))) {
+ /*
+ * We have a raw control character or a ' ' in r->args.
+ * Correct encoding was missed.
+ */
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10411)
+ "Rewritten query string contains control "
+ "characters or spaces");
+ return HTTP_FORBIDDEN;
+ }
if (ACTION_STATUS == rulestatus) {
int n = r->status;
@@ -4990,8 +5059,7 @@ static int hook_fixup(request_rec *r)
return n;
}
- l = strlen(r->filename);
- if (l > 6 && strncmp(r->filename, "proxy:", 6) == 0) {
+ if (to_proxyreq) {
/* it should go on as an internal proxy request */
/* make sure the QUERY_STRING and
@@ -5015,7 +5083,7 @@ static int hook_fixup(request_rec *r)
"%s [OK]", r->filename));
return OK;
}
- else if ((skip = is_absolute_uri(r->filename, NULL)) > 0) {
+ else if (skip_absolute > 0) {
/* it was finally rewritten to a remote URL */
/* because we are in a per-dir context
@@ -5024,7 +5092,7 @@ static int hook_fixup(request_rec *r)
*/
if (dconf->baseurl != NULL) {
/* skip 'scheme://' */
- cp = r->filename + skip;
+ cp = r->filename + skip_absolute;
if ((cp = ap_strchr(cp, '/')) != NULL && *(++cp)) {
rewritelog((r, 2, dconf->directory,
@@ -5069,7 +5137,7 @@ static int hook_fixup(request_rec *r)
if (rulestatus != ACTION_NOESCAPE) {
rewritelog((r, 1, dconf->directory, "escaping %s for redirect",
r->filename));
- r->filename = escape_absolute_uri(r->pool, r->filename, skip);
+ r->filename = escape_absolute_uri(r->pool, r->filename, skip_absolute);
}
/* append the QUERY_STRING part */
diff --git a/modules/proxy/mod_proxy_ajp.c b/modules/proxy/mod_proxy_ajp.c
index cbb0872..873ccf1 100644
--- a/modules/proxy/mod_proxy_ajp.c
+++ b/modules/proxy/mod_proxy_ajp.c
@@ -69,6 +69,16 @@ static int proxy_ajp_canon(request_rec *r, char *url)
path = ap_proxy_canonenc(r->pool, url, strlen(url), enc_path, 0,
r->proxyreq);
search = r->args;
+ if (search && *(ap_scan_vchar_obstext(search))) {
+ /*
+ * We have a raw control character or a ' ' in r->args.
+ * Correct encoding was missed.
+ */
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10406)
+ "To be forwarded query string contains control "
+ "characters or spaces");
+ return HTTP_FORBIDDEN;
+ }
}
if (path == NULL)
return HTTP_BAD_REQUEST;
diff --git a/modules/proxy/mod_proxy_balancer.c b/modules/proxy/mod_proxy_balancer.c
index 3a28038..c599e1a 100644
--- a/modules/proxy/mod_proxy_balancer.c
+++ b/modules/proxy/mod_proxy_balancer.c
@@ -106,6 +106,16 @@ static int proxy_balancer_canon(request_rec *r, char *url)
path = ap_proxy_canonenc(r->pool, url, strlen(url), enc_path, 0,
r->proxyreq);
search = r->args;
+ if (search && *(ap_scan_vchar_obstext(search))) {
+ /*
+ * We have a raw control character or a ' ' in r->args.
+ * Correct encoding was missed.
+ */
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10407)
+ "To be forwarded query string contains control "
+ "characters or spaces");
+ return HTTP_FORBIDDEN;
+ }
}
if (path == NULL)
return HTTP_BAD_REQUEST;
diff --git a/modules/proxy/mod_proxy_http.c b/modules/proxy/mod_proxy_http.c
index 7573638..fe7b322 100644
--- a/modules/proxy/mod_proxy_http.c
+++ b/modules/proxy/mod_proxy_http.c
@@ -90,6 +90,16 @@ static int proxy_http_canon(request_rec *r, char *url)
path = ap_proxy_canonenc(r->pool, url, strlen(url),
enc_path, 0, r->proxyreq);
search = r->args;
+ if (search && *(ap_scan_vchar_obstext(search))) {
+ /*
+ * We have a raw control character or a ' ' in r->args.
+ * Correct encoding was missed.
+ */
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10408)
+ "To be forwarded query string contains control "
+ "characters or spaces");
+ return HTTP_FORBIDDEN;
+ }
}
break;
case PROXYREQ_PROXY:
diff --git a/modules/proxy/mod_proxy_wstunnel.c b/modules/proxy/mod_proxy_wstunnel.c
index e005a94..f5e27d9 100644
--- a/modules/proxy/mod_proxy_wstunnel.c
+++ b/modules/proxy/mod_proxy_wstunnel.c
@@ -77,6 +77,16 @@ static int proxy_wstunnel_canon(request_rec *r, char *url)
path = ap_proxy_canonenc(r->pool, url, strlen(url), enc_path, 0,
r->proxyreq);
search = r->args;
+ if (search && *(ap_scan_vchar_obstext(search))) {
+ /*
+ * We have a raw control character or a ' ' in r->args.
+ * Correct encoding was missed.
+ */
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10409)
+ "To be forwarded query string contains control "
+ "characters or spaces");
+ return HTTP_FORBIDDEN;
+ }
}
if (path == NULL)
return HTTP_BAD_REQUEST;
diff --git a/server/gen_test_char.c b/server/gen_test_char.c
index 48ae6f4..6a153a3 100644
--- a/server/gen_test_char.c
+++ b/server/gen_test_char.c
@@ -169,5 +169,15 @@ int main(int argc, char *argv[])
printf("\n};\n");
+
+ printf(
+ "/* we assume the folks using this ensure 0 <= c < 256... which means\n"
+ " * you need a cast to (unsigned char) first, you can't just plug a\n"
+ " * char in here and get it to work, because if char is signed then it\n"
+ " * will first be sign extended.\n"
+ " */\n"
+ "#define TEST_CHAR(c, f) (test_char_table[(unsigned char)(c)] & (f))\n"
+ );
+
return 0;
}
diff --git a/server/util.c b/server/util.c
index 45051b7..9d897d4 100644
--- a/server/util.c
+++ b/server/util.c
@@ -74,13 +74,6 @@
*/
#include "test_char.h"
-/* we assume the folks using this ensure 0 <= c < 256... which means
- * you need a cast to (unsigned char) first, you can't just plug a
- * char in here and get it to work, because if char is signed then it
- * will first be sign extended.
- */
-#define TEST_CHAR(c, f) (test_char_table[(unsigned char)(c)] & (f))
-
/* Win32/NetWare/OS2 need to check for both forward and back slashes
* in ap_getparents() and ap_escape_url.
*/

View File

@ -1,89 +0,0 @@
diff --git a/modules/proxy/mod_proxy_uwsgi.c b/modules/proxy/mod_proxy_uwsgi.c
index 9dcbed1..a1b564d 100644
--- a/modules/proxy/mod_proxy_uwsgi.c
+++ b/modules/proxy/mod_proxy_uwsgi.c
@@ -304,18 +304,16 @@ static int uwsgi_response(request_rec *r, proxy_conn_rec * backend,
pass_bb = apr_brigade_create(r->pool, c->bucket_alloc);
len = ap_getline(buffer, sizeof(buffer), rp, 1);
-
if (len <= 0) {
- /* oops */
+ /* invalid or empty */
return HTTP_INTERNAL_SERVER_ERROR;
}
-
backend->worker->s->read += len;
-
- if (len >= sizeof(buffer) - 1) {
- /* oops */
+ if ((apr_size_t)len >= sizeof(buffer)) {
+ /* too long */
return HTTP_INTERNAL_SERVER_ERROR;
}
+
/* Position of http status code */
if (apr_date_checkmask(buffer, "HTTP/#.# ###*")) {
status_start = 9;
@@ -324,8 +322,8 @@ static int uwsgi_response(request_rec *r, proxy_conn_rec * backend,
status_start = 7;
}
else {
- /* oops */
- return HTTP_INTERNAL_SERVER_ERROR;
+ /* not HTTP */
+ return HTTP_BAD_GATEWAY;
}
status_end = status_start + 3;
@@ -345,21 +343,44 @@ static int uwsgi_response(request_rec *r, proxy_conn_rec * backend,
}
r->status_line = apr_pstrdup(r->pool, &buffer[status_start]);
- /* start parsing headers */
+ /* parse headers */
while ((len = ap_getline(buffer, sizeof(buffer), rp, 1)) > 0) {
+ if ((apr_size_t)len >= sizeof(buffer)) {
+ /* too long */
+ len = -1;
+ break;
+ }
value = strchr(buffer, ':');
- /* invalid header skip */
- if (!value)
- continue;
- *value = '\0';
- ++value;
+ if (!value) {
+ /* invalid header */
+ len = -1;
+ break;
+ }
+ *value++ = '\0';
+ if (*ap_scan_http_token(buffer)) {
+ /* invalid name */
+ len = -1;
+ break;
+ }
while (apr_isspace(*value))
++value;
for (end = &value[strlen(value) - 1];
end > value && apr_isspace(*end); --end)
*end = '\0';
+ if (*ap_scan_http_field_content(value)) {
+ /* invalid value */
+ len = -1;
+ break;
+ }
apr_table_add(r->headers_out, buffer, value);
}
+ if (len < 0) {
+ /* Reset headers, but not to NULL because things below the chain expect
+ * this to be non NULL e.g. the ap_content_length_filter.
+ */
+ r->headers_out = apr_table_make(r->pool, 1);
+ return HTTP_BAD_GATEWAY;
+ }
if ((buf = apr_table_get(r->headers_out, "Content-Type"))) {
ap_set_content_type(r, apr_pstrdup(r->pool, buf));

View File

@ -1,92 +0,0 @@
commit 4c0e27d7bfbf46f14dfbd5d888e56c64ad8c8de5
Author: Tomas Korbar <tkorbar@redhat.com>
Date: Mon Sep 19 13:22:27 2022 +0200
Backport refactor of SNI support to httpd-2.4.37
diff --git a/modules/http2/mod_proxy_http2.c b/modules/http2/mod_proxy_http2.c
index a7e0dcd..31ccd32 100644
--- a/modules/http2/mod_proxy_http2.c
+++ b/modules/http2/mod_proxy_http2.c
@@ -591,16 +591,6 @@ run_connect:
}
if (!ctx->p_conn->data) {
- /* New conection: set a note on the connection what CN is
- * requested and what protocol we want */
- if (ctx->p_conn->ssl_hostname) {
- ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, ctx->owner,
- "set SNI to %s for (%s)",
- ctx->p_conn->ssl_hostname,
- ctx->p_conn->hostname);
- apr_table_setn(ctx->p_conn->connection->notes,
- "proxy-request-hostname", ctx->p_conn->ssl_hostname);
- }
if (ctx->is_ssl) {
apr_table_setn(ctx->p_conn->connection->notes,
"proxy-request-alpn-protos", "h2");
diff --git a/modules/proxy/mod_proxy_http.c b/modules/proxy/mod_proxy_http.c
index 1b7bb81..c1c591a 100644
--- a/modules/proxy/mod_proxy_http.c
+++ b/modules/proxy/mod_proxy_http.c
@@ -2111,19 +2111,6 @@ static int proxy_http_handler(request_rec *r, proxy_worker *worker,
req->origin->keepalive = AP_CONN_CLOSE;
}
- /*
- * On SSL connections set a note on the connection what CN is
- * requested, such that mod_ssl can check if it is requested to do
- * so.
- *
- * https://github.com/apache/httpd/commit/7d272e2628b4ae05f68cdc74b070707250896a34
- */
- if (backend->ssl_hostname) {
- apr_table_setn(backend->connection->notes,
- "proxy-request-hostname",
- backend->ssl_hostname);
- }
-
/* Step Four: Send the Request
* On the off-chance that we forced a 100-Continue as a
* kinda HTTP ping test, allow for retries
diff --git a/modules/proxy/proxy_util.c b/modules/proxy/proxy_util.c
index ec9a414..805820d 100644
--- a/modules/proxy/proxy_util.c
+++ b/modules/proxy/proxy_util.c
@@ -3261,6 +3261,16 @@ static int proxy_connection_create(const char *proxy_function,
backend_addr, conn->hostname);
return HTTP_INTERNAL_SERVER_ERROR;
}
+ if (conn->ssl_hostname) {
+ /* Set a note on the connection about what CN is requested,
+ * such that mod_ssl can check if it is requested to do so.
+ */
+ ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, conn->connection,
+ "%s: set SNI to %s for (%s)", proxy_function,
+ conn->ssl_hostname, conn->hostname);
+ apr_table_setn(conn->connection->notes, "proxy-request-hostname",
+ conn->ssl_hostname);
+ }
}
else {
/* TODO: See if this will break FTP */
diff --git a/modules/ssl/ssl_engine_io.c b/modules/ssl/ssl_engine_io.c
index 4e3875a..9b4280c 100644
--- a/modules/ssl/ssl_engine_io.c
+++ b/modules/ssl/ssl_engine_io.c
@@ -1273,7 +1273,6 @@ static apr_status_t ssl_io_filter_handshake(ssl_filter_ctx_t *filter_ctx)
((dc->proxy->ssl_check_peer_cn != FALSE) ||
(dc->proxy->ssl_check_peer_name == TRUE)) &&
hostname_note) {
- apr_table_unset(c->notes, "proxy-request-hostname");
if (!cert
|| modssl_X509_match_name(c->pool, cert, hostname_note,
TRUE, server) == FALSE) {
@@ -1290,7 +1289,6 @@ static apr_status_t ssl_io_filter_handshake(ssl_filter_ctx_t *filter_ctx)
hostname = ssl_var_lookup(NULL, server, c, NULL,
"SSL_CLIENT_S_DN_CN");
- apr_table_unset(c->notes, "proxy-request-hostname");
/* Do string match or simplest wildcard match if that
* fails. */

View File

@ -1,225 +0,0 @@
diff --git a/modules/proxy/mod_proxy_http.c b/modules/proxy/mod_proxy_http.c
index ec1e042..2c0500f 100644
--- a/modules/proxy/mod_proxy_http.c
+++ b/modules/proxy/mod_proxy_http.c
@@ -310,16 +310,18 @@ static int stream_reqbody_read(proxy_http_req_t *req, apr_bucket_brigade *bb,
return OK;
}
-static int stream_reqbody(proxy_http_req_t *req, rb_methods rb_method)
+static int stream_reqbody(proxy_http_req_t *req)
{
request_rec *r = req->r;
int seen_eos = 0, rv = OK;
apr_size_t hdr_len;
char chunk_hdr[20]; /* must be here due to transient bucket. */
+ conn_rec *origin = req->origin;
proxy_conn_rec *p_conn = req->backend;
apr_bucket_alloc_t *bucket_alloc = req->bucket_alloc;
apr_bucket_brigade *header_brigade = req->header_brigade;
apr_bucket_brigade *input_brigade = req->input_brigade;
+ rb_methods rb_method = req->rb_method;
apr_off_t bytes, bytes_streamed = 0;
apr_bucket *e;
@@ -333,7 +335,7 @@ static int stream_reqbody(proxy_http_req_t *req, rb_methods rb_method)
}
if (!APR_BRIGADE_EMPTY(input_brigade)) {
- /* If this brigade contains EOS, either stop or remove it. */
+ /* If this brigade contains EOS, remove it and be done. */
if (APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(input_brigade))) {
seen_eos = 1;
@@ -375,7 +377,8 @@ static int stream_reqbody(proxy_http_req_t *req, rb_methods rb_method)
APR_BRIGADE_INSERT_TAIL(input_brigade, e);
}
}
- else if (bytes_streamed > req->cl_val) {
+ else if (rb_method == RB_STREAM_CL
+ && bytes_streamed > req->cl_val) {
/* C-L < bytes streamed?!?
* We will error out after the body is completely
* consumed, but we can't stream more bytes at the
@@ -407,7 +410,7 @@ static int stream_reqbody(proxy_http_req_t *req, rb_methods rb_method)
APR_BRIGADE_PREPEND(input_brigade, header_brigade);
/* Flush here on EOS because we won't stream_reqbody_read() again */
- rv = ap_proxy_pass_brigade(bucket_alloc, r, p_conn, req->origin,
+ rv = ap_proxy_pass_brigade(bucket_alloc, r, p_conn, origin,
input_brigade, seen_eos);
if (rv != OK) {
return rv;
@@ -454,10 +457,6 @@ static int spool_reqbody_cl(proxy_http_req_t *req, apr_off_t *bytes_spooled)
/* If this brigade contains EOS, either stop or remove it. */
if (APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(input_brigade))) {
seen_eos = 1;
-
- /* We can't pass this EOS to the output_filters. */
- e = APR_BRIGADE_LAST(input_brigade);
- apr_bucket_delete(e);
}
apr_brigade_length(input_brigade, 1, &bytes);
@@ -644,7 +643,18 @@ static int ap_proxy_http_prefetch(proxy_http_req_t *req,
*/
temp_brigade = apr_brigade_create(p, bucket_alloc);
block = req->prefetch_nonblocking ? APR_NONBLOCK_READ : APR_BLOCK_READ;
- do {
+
+ /* Account for saved input, if any. */
+ apr_brigade_length(input_brigade, 0, &bytes_read);
+
+ /* Ensure we don't hit a wall where we have a buffer too small
+ * for ap_get_brigade's filters to fetch us another bucket,
+ * surrender once we hit 80 bytes less than MAX_MEM_SPOOL
+ * (an arbitrary value).
+ */
+ while (bytes_read < MAX_MEM_SPOOL - 80
+ && (APR_BRIGADE_EMPTY(input_brigade)
+ || !APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(input_brigade)))) {
status = ap_get_brigade(r->input_filters, temp_brigade,
AP_MODE_READBYTES, block,
MAX_MEM_SPOOL - bytes_read);
@@ -686,15 +696,7 @@ static int ap_proxy_http_prefetch(proxy_http_req_t *req,
c->client_ip, c->remote_host ? c->remote_host: "");
return HTTP_INTERNAL_SERVER_ERROR;
}
-
- /* Ensure we don't hit a wall where we have a buffer too small
- * for ap_get_brigade's filters to fetch us another bucket,
- * surrender once we hit 80 bytes less than MAX_MEM_SPOOL
- * (an arbitrary value.)
- */
- } while ((bytes_read < MAX_MEM_SPOOL - 80)
- && !APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(input_brigade))
- && !req->prefetch_nonblocking);
+ }
/* Use chunked request body encoding or send a content-length body?
*
@@ -838,35 +840,21 @@ static int ap_proxy_http_request(proxy_http_req_t *req)
{
int rv;
request_rec *r = req->r;
- apr_bucket_alloc_t *bucket_alloc = req->bucket_alloc;
- apr_bucket_brigade *header_brigade = req->header_brigade;
- apr_bucket_brigade *input_brigade = req->input_brigade;
/* send the request header/body, if any. */
switch (req->rb_method) {
+ case RB_SPOOL_CL:
case RB_STREAM_CL:
case RB_STREAM_CHUNKED:
if (req->do_100_continue) {
- rv = ap_proxy_pass_brigade(bucket_alloc, r, req->backend,
- req->origin, header_brigade, 1);
+ rv = ap_proxy_pass_brigade(req->bucket_alloc, r, req->backend,
+ req->origin, req->header_brigade, 1);
}
else {
- rv = stream_reqbody(req, req->rb_method);
+ rv = stream_reqbody(req);
}
break;
- case RB_SPOOL_CL:
- /* Prefetch has built the header and spooled the whole body;
- * if we don't expect 100-continue we can flush both all at once,
- * otherwise flush the header only.
- */
- if (!req->do_100_continue) {
- APR_BRIGADE_CONCAT(header_brigade, input_brigade);
- }
- rv = ap_proxy_pass_brigade(bucket_alloc, r, req->backend,
- req->origin, header_brigade, 1);
- break;
-
default:
/* shouldn't be possible */
rv = HTTP_INTERNAL_SERVER_ERROR;
@@ -1577,15 +1565,10 @@ int ap_proxy_http_process_response(proxy_http_req_t *req)
/* Send the request body (fully). */
switch(req->rb_method) {
+ case RB_SPOOL_CL:
case RB_STREAM_CL:
case RB_STREAM_CHUNKED:
- status = stream_reqbody(req, req->rb_method);
- break;
- case RB_SPOOL_CL:
- /* Prefetch has spooled the whole body, flush it. */
- status = ap_proxy_pass_brigade(req->bucket_alloc, r,
- backend, origin,
- req->input_brigade, 1);
+ status = stream_reqbody(req);
break;
default:
/* Shouldn't happen */
@@ -1940,6 +1923,7 @@ static int proxy_http_handler(request_rec *r, proxy_worker *worker,
const char *u;
proxy_http_req_t *req = NULL;
proxy_conn_rec *backend = NULL;
+ apr_bucket_brigade *input_brigade = NULL;
int is_ssl = 0;
conn_rec *c = r->connection;
proxy_dir_conf *dconf;
@@ -2005,8 +1989,20 @@ static int proxy_http_handler(request_rec *r, proxy_worker *worker,
dconf = ap_get_module_config(r->per_dir_config, &proxy_module);
+ /* We possibly reuse input data prefetched in previous call(s), e.g. for a
+ * balancer fallback scenario, and in this case the 100 continue settings
+ * should be consistent between balancer members. If not, we need to ignore
+ * Proxy100Continue on=>off once we tried to prefetch already, otherwise
+ * the HTTP_IN filter won't send 100 Continue for us anymore, and we might
+ * deadlock with the client waiting for each other. Note that off=>on is
+ * not an issue because in this case r->expecting_100 is false (the 100
+ * Continue is out already), but we make sure that prefetch will be
+ * nonblocking to avoid passing more time there.
+ */
+ apr_pool_userdata_get((void **)&input_brigade, "proxy-req-input", p);
+
/* Should we handle end-to-end or ping 100-continue? */
- if ((r->expecting_100 && dconf->forward_100_continue)
+ if ((r->expecting_100 && (dconf->forward_100_continue || input_brigade))
|| PROXY_DO_100_CONTINUE(worker, r)) {
/* We need to reset r->expecting_100 or prefetching will cause
* ap_http_filter() to send "100 Continue" response by itself. So
@@ -2023,7 +2019,8 @@ static int proxy_http_handler(request_rec *r, proxy_worker *worker,
/* Should we block while prefetching the body or try nonblocking and flush
* data to the backend ASAP?
*/
- else if (apr_table_get(r->subprocess_env, "proxy-prefetch-nonblocking")) {
+ else if (input_brigade || apr_table_get(r->subprocess_env,
+ "proxy-prefetch-nonblocking")) {
req->prefetch_nonblocking = 1;
}
@@ -2048,6 +2045,17 @@ static int proxy_http_handler(request_rec *r, proxy_worker *worker,
sizeof(req->server_portstr))))
goto cleanup;
+ /* The header is always (re-)built since it depends on worker settings,
+ * but the body can be fetched only once (even partially), so it's saved
+ * in between proxy_http_handler() calls should we come back here.
+ */
+ req->header_brigade = apr_brigade_create(p, req->bucket_alloc);
+ if (input_brigade == NULL) {
+ input_brigade = apr_brigade_create(p, req->bucket_alloc);
+ apr_pool_userdata_setn(input_brigade, "proxy-req-input", NULL, p);
+ }
+ req->input_brigade = input_brigade;
+
/* Prefetch (nonlocking) the request body so to increase the chance to get
* the whole (or enough) body and determine Content-Length vs chunked or
* spooled. By doing this before connecting or reusing the backend, we want
@@ -2058,8 +2066,6 @@ static int proxy_http_handler(request_rec *r, proxy_worker *worker,
* to reduce to the minimum the unavoidable local is_socket_connected() vs
* remote keepalive race condition.
*/
- req->input_brigade = apr_brigade_create(p, req->bucket_alloc);
- req->header_brigade = apr_brigade_create(p, req->bucket_alloc);
if ((status = ap_proxy_http_prefetch(req, uri, locurl)) != OK)
goto cleanup;

View File

@ -1,42 +0,0 @@
diff --git a/modules/ssl/mod_ssl.c b/modules/ssl/mod_ssl.c
index 37947e7..b50c259 100644
--- a/modules/ssl/mod_ssl.c
+++ b/modules/ssl/mod_ssl.c
@@ -331,9 +331,6 @@ static apr_status_t ssl_cleanup_pre_config(void *data)
/*
* Try to kill the internals of the SSL library.
*/
-#ifdef HAVE_FIPS
- FIPS_mode_set(0);
-#endif
/* Corresponds to OBJ_create()s */
OBJ_cleanup();
/* Corresponds to OPENSSL_load_builtin_modules() */
diff --git a/modules/ssl/ssl_engine_init.c b/modules/ssl/ssl_engine_init.c
index 5063a72..21e41e2 100644
--- a/modules/ssl/ssl_engine_init.c
+++ b/modules/ssl/ssl_engine_init.c
@@ -183,6 +183,14 @@ int ssl_is_challenge(conn_rec *c, const char *servername,
return 0;
}
+#ifdef HAVE_FIPS
+static apr_status_t ssl_fips_cleanup(void *data)
+{
+ FIPS_mode_set(0);
+ return APR_SUCCESS;
+}
+#endif
+
/*
* Per-module initialization
*/
@@ -316,6 +324,8 @@ apr_status_t ssl_init_Module(apr_pool_t *p, apr_pool_t *plog,
if (FIPS_mode_set(1)) {
ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, s, APLOGNO(01884)
"Operating in SSL FIPS mode");
+ apr_pool_cleanup_register(p, NULL, ssl_fips_cleanup,
+ apr_pool_cleanup_null);
}
else {
ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(01885) "FIPS mode failed");

View File

@ -1,199 +0,0 @@
diff --git a/modules/proxy/mod_proxy_hcheck.c b/modules/proxy/mod_proxy_hcheck.c
index bd89779..d7c0a68 100644
--- a/modules/proxy/mod_proxy_hcheck.c
+++ b/modules/proxy/mod_proxy_hcheck.c
@@ -33,7 +33,6 @@ module AP_MODULE_DECLARE_DATA proxy_hcheck_module;
#endif
#else
#define HC_USE_THREADS 0
-typedef void apr_thread_pool_t;
#endif
typedef struct {
@@ -73,7 +72,7 @@ typedef struct {
proxy_balancer *balancer;
proxy_worker *worker;
proxy_worker *hc;
- apr_time_t now;
+ apr_time_t *now;
} baton_t;
static void *hc_create_config(apr_pool_t *p, server_rec *s)
@@ -89,7 +88,10 @@ static void *hc_create_config(apr_pool_t *p, server_rec *s)
}
static ap_watchdog_t *watchdog;
-static int tpsize = HC_THREADPOOL_SIZE;
+#if HC_USE_THREADS
+static apr_thread_pool_t *hctp;
+static int tpsize;
+#endif
/*
* This serves double duty by not only validating (and creating)
@@ -825,29 +827,28 @@ static void * APR_THREAD_FUNC hc_check(apr_thread_t *thread, void *b)
server_rec *s = baton->ctx->s;
proxy_worker *worker = baton->worker;
proxy_worker *hc = baton->hc;
- apr_time_t now = baton->now;
+ apr_time_t now;
apr_status_t rv;
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(03256)
"%sHealth checking %s", (thread ? "Threaded " : ""),
worker->s->name);
- worker->s->updated = now;
if (hc->s->method == TCP) {
rv = hc_check_tcp(baton);
}
else {
rv = hc_check_http(baton);
}
+
+ now = apr_time_now();
if (rv == APR_ENOTIMPL) {
ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(03257)
"Somehow tried to use unimplemented hcheck method: %d",
(int)hc->s->method);
- apr_pool_destroy(baton->ptemp);
- return NULL;
}
/* what state are we in ? */
- if (PROXY_WORKER_IS_HCFAILED(worker)) {
+ else if (PROXY_WORKER_IS_HCFAILED(worker)) {
if (rv == APR_SUCCESS) {
worker->s->pcount += 1;
if (worker->s->pcount >= worker->s->passes) {
@@ -860,7 +861,8 @@ static void * APR_THREAD_FUNC hc_check(apr_thread_t *thread, void *b)
}
}
- } else {
+ }
+ else {
if (rv != APR_SUCCESS) {
worker->s->error_time = now;
worker->s->fcount += 1;
@@ -873,7 +875,12 @@ static void * APR_THREAD_FUNC hc_check(apr_thread_t *thread, void *b)
}
}
}
+ if (baton->now) {
+ *baton->now = now;
+ }
apr_pool_destroy(baton->ptemp);
+ worker->s->updated = now;
+
return NULL;
}
@@ -881,12 +888,10 @@ static apr_status_t hc_watchdog_callback(int state, void *data,
apr_pool_t *pool)
{
apr_status_t rv = APR_SUCCESS;
- apr_time_t now = apr_time_now();
proxy_balancer *balancer;
sctx_t *ctx = (sctx_t *)data;
server_rec *s = ctx->s;
proxy_server_conf *conf;
- static apr_thread_pool_t *hctp = NULL;
switch (state) {
case AP_WATCHDOG_STATE_STARTING:
@@ -913,7 +918,6 @@ static apr_status_t hc_watchdog_callback(int state, void *data,
"Skipping apr_thread_pool_create()");
hctp = NULL;
}
-
#endif
break;
@@ -929,45 +933,53 @@ static apr_status_t hc_watchdog_callback(int state, void *data,
ctx->s = s;
for (i = 0; i < conf->balancers->nelts; i++, balancer++) {
int n;
+ apr_time_t now;
proxy_worker **workers;
proxy_worker *worker;
/* Have any new balancers or workers been added dynamically? */
ap_proxy_sync_balancer(balancer, s, conf);
workers = (proxy_worker **)balancer->workers->elts;
+ now = apr_time_now();
for (n = 0; n < balancer->workers->nelts; n++) {
worker = *workers;
if (!PROXY_WORKER_IS(worker, PROXY_WORKER_STOPPED) &&
- (worker->s->method != NONE) &&
- (now > worker->s->updated + worker->s->interval)) {
+ (worker->s->method != NONE) &&
+ (worker->s->updated != 0) &&
+ (now > worker->s->updated + worker->s->interval)) {
baton_t *baton;
apr_pool_t *ptemp;
+
ap_log_error(APLOG_MARK, APLOG_TRACE3, 0, s,
"Checking %s worker: %s [%d] (%pp)", balancer->s->name,
worker->s->name, worker->s->method, worker);
if ((rv = hc_init_worker(ctx, worker)) != APR_SUCCESS) {
+ worker->s->updated = now;
return rv;
}
- /* This pool must last the lifetime of the (possible) thread */
+ worker->s->updated = 0;
+
+ /* This pool has the lifetime of the check */
apr_pool_create(&ptemp, ctx->p);
apr_pool_tag(ptemp, "hc_request");
- baton = apr_palloc(ptemp, sizeof(baton_t));
+ baton = apr_pcalloc(ptemp, sizeof(baton_t));
baton->ctx = ctx;
- baton->now = now;
baton->balancer = balancer;
baton->worker = worker;
baton->ptemp = ptemp;
baton->hc = hc_get_hcworker(ctx, worker, ptemp);
-
- if (!hctp) {
- hc_check(NULL, baton);
- }
#if HC_USE_THREADS
- else {
- rv = apr_thread_pool_push(hctp, hc_check, (void *)baton,
- APR_THREAD_TASK_PRIORITY_NORMAL, NULL);
+ if (hctp) {
+ apr_thread_pool_push(hctp, hc_check, (void *)baton,
+ APR_THREAD_TASK_PRIORITY_NORMAL,
+ NULL);
}
+ else
#endif
+ {
+ baton->now = &now;
+ hc_check(NULL, baton);
+ }
}
workers++;
}
@@ -986,9 +998,9 @@ static apr_status_t hc_watchdog_callback(int state, void *data,
ap_log_error(APLOG_MARK, APLOG_INFO, rv, s, APLOGNO(03315)
"apr_thread_pool_destroy() failed");
}
+ hctp = NULL;
}
#endif
- hctp = NULL;
break;
}
return rv;
@@ -996,7 +1008,10 @@ static apr_status_t hc_watchdog_callback(int state, void *data,
static int hc_pre_config(apr_pool_t *pconf, apr_pool_t *plog,
apr_pool_t *ptemp)
{
+#if HC_USE_THREADS
+ hctp = NULL;
tpsize = HC_THREADPOOL_SIZE;
+#endif
return OK;
}
static int hc_post_config(apr_pool_t *p, apr_pool_t *plog,

View File

@ -1,544 +0,0 @@
diff --git a/modules/ssl/mod_ssl.h b/modules/ssl/mod_ssl.h
index 24a65a0..a360911 100644
--- a/modules/ssl/mod_ssl.h
+++ b/modules/ssl/mod_ssl.h
@@ -29,6 +29,7 @@
#include "httpd.h"
#include "http_config.h"
#include "apr_optional.h"
+#include "apr_tables.h" /* for apr_array_header_t */
/* Create a set of SSL_DECLARE(type), SSL_DECLARE_NONSTD(type) and
* SSL_DECLARE_DATA with appropriate export and import tags for the platform
@@ -86,6 +87,34 @@ APR_DECLARE_OPTIONAL_FN(int, ssl_engine_disable, (conn_rec *));
APR_DECLARE_OPTIONAL_FN(int, ssl_engine_set, (conn_rec *,
ap_conf_vector_t *,
int proxy, int enable));
+
+/* Check for availability of new hooks */
+#define SSL_CERT_HOOKS
+#ifdef SSL_CERT_HOOKS
+
+/** Lets others add certificate and key files to the given server.
+ * For each cert a key must also be added.
+ * @param cert_file and array of const char* with the path to the certificate chain
+ * @param key_file and array of const char* with the path to the private key file
+ */
+APR_DECLARE_EXTERNAL_HOOK(ssl, SSL, int, add_cert_files,
+ (server_rec *s, apr_pool_t *p,
+ apr_array_header_t *cert_files,
+ apr_array_header_t *key_files))
+
+/** In case no certificates are available for a server, this
+ * lets other modules add a fallback certificate for the time
+ * being. Regular requests against this server will be answered
+ * with a 503.
+ * @param cert_file and array of const char* with the path to the certificate chain
+ * @param key_file and array of const char* with the path to the private key file
+ */
+APR_DECLARE_EXTERNAL_HOOK(ssl, SSL, int, add_fallback_cert_files,
+ (server_rec *s, apr_pool_t *p,
+ apr_array_header_t *cert_files,
+ apr_array_header_t *key_files))
+
+#endif /* SSL_CERT_HOOKS */
#endif /* __MOD_SSL_H__ */
/** @} */
diff --git a/modules/ssl/mod_ssl_openssl.h b/modules/ssl/mod_ssl_openssl.h
index 0fa654a..d4f684f 100644
--- a/modules/ssl/mod_ssl_openssl.h
+++ b/modules/ssl/mod_ssl_openssl.h
@@ -69,5 +69,45 @@ APR_DECLARE_EXTERNAL_HOOK(ssl, SSL, int, pre_handshake,
APR_DECLARE_EXTERNAL_HOOK(ssl, SSL, int, proxy_post_handshake,
(conn_rec *c, SSL *ssl))
+/** On TLS connections that do not relate to a configured virtual host,
+ * allow other modules to provide a X509 certificate and EVP_PKEY to
+ * be used on the connection. This first hook which does not
+ * return DECLINED will determine the outcome. */
+APR_DECLARE_EXTERNAL_HOOK(ssl, SSL, int, answer_challenge,
+ (conn_rec *c, const char *server_name,
+ X509 **pcert, EVP_PKEY **pkey))
+
+/** During post_config phase, ask around if someone wants to provide
+ * OCSP stapling status information for the given cert (with the also
+ * provided issuer certificate). The first hook which does not
+ * return DECLINED promises to take responsibility (and respond
+ * in later calls via hook ssl_get_stapling_status).
+ * If no hook takes over, mod_ssl's own stapling implementation will
+ * be applied (if configured).
+ */
+APR_DECLARE_EXTERNAL_HOOK(ssl, SSL, int, init_stapling_status,
+ (server_rec *s, apr_pool_t *p,
+ X509 *cert, X509 *issuer))
+
+/** Anyone answering positive to ssl_init_stapling_status for a
+ * certificate, needs to register here and supply the actual OCSP stapling
+ * status data (OCSP_RESP) for a new connection.
+ * A hook supplying the response data must return APR_SUCCESS.
+ * The data is returned in DER encoded bytes via pder and pderlen. The
+ * returned pointer may be NULL, which indicates that data is (currently)
+ * unavailable.
+ * If DER data is returned, it MUST come from a response with
+ * status OCSP_RESPONSE_STATUS_SUCCESSFUL and V_OCSP_CERTSTATUS_GOOD
+ * or V_OCSP_CERTSTATUS_REVOKED, not V_OCSP_CERTSTATUS_UNKNOWN. This means
+ * errors in OCSP retrieval are to be handled/logged by the hook and
+ * are not done by mod_ssl.
+ * Any DER bytes returned MUST be allocated via malloc() and ownership
+ * passes to mod_ssl. Meaning, the hook must return a malloced copy of
+ * the data it has. mod_ssl (or OpenSSL) will free it.
+ */
+APR_DECLARE_EXTERNAL_HOOK(ssl, SSL, int, get_stapling_status,
+ (unsigned char **pder, int *pderlen,
+ conn_rec *c, server_rec *s, X509 *cert))
+
#endif /* __MOD_SSL_OPENSSL_H__ */
/** @} */
diff --git a/modules/ssl/ssl_engine_init.c b/modules/ssl/ssl_engine_init.c
index 21e41e2..ef631c1 100644
--- a/modules/ssl/ssl_engine_init.c
+++ b/modules/ssl/ssl_engine_init.c
@@ -36,6 +36,25 @@ APR_IMPLEMENT_OPTIONAL_HOOK_RUN_ALL(ssl, SSL, int, init_server,
(server_rec *s,apr_pool_t *p,int is_proxy,SSL_CTX *ctx),
(s,p,is_proxy,ctx), OK, DECLINED)
+APR_IMPLEMENT_OPTIONAL_HOOK_RUN_ALL(ssl, SSL, int, add_cert_files,
+ (server_rec *s, apr_pool_t *p,
+ apr_array_header_t *cert_files, apr_array_header_t *key_files),
+ (s, p, cert_files, key_files),
+ OK, DECLINED)
+
+APR_IMPLEMENT_OPTIONAL_HOOK_RUN_ALL(ssl, SSL, int, add_fallback_cert_files,
+ (server_rec *s, apr_pool_t *p,
+ apr_array_header_t *cert_files, apr_array_header_t *key_files),
+ (s, p, cert_files, key_files),
+ OK, DECLINED)
+
+APR_IMPLEMENT_OPTIONAL_HOOK_RUN_ALL(ssl, SSL, int, answer_challenge,
+ (conn_rec *c, const char *server_name,
+ X509 **pcert, EVP_PKEY **pkey),
+ (c, server_name, pcert, pkey),
+ DECLINED, DECLINED)
+
+
/* _________________________________________________________________
**
** Module Initialization
@@ -165,18 +184,18 @@ static void ssl_add_version_components(apr_pool_t *p,
modver, AP_SERVER_BASEVERSION, incver);
}
-/**************************************************************************************************/
-/* Managed Domains Interface */
-
-static APR_OPTIONAL_FN_TYPE(md_is_managed) *md_is_managed;
-static APR_OPTIONAL_FN_TYPE(md_get_certificate) *md_get_certificate;
-static APR_OPTIONAL_FN_TYPE(md_is_challenge) *md_is_challenge;
+/* _________________________________________________________________
+**
+** Let other answer special connection attempts.
+** Used in ACME challenge handling by mod_md.
+** _________________________________________________________________
+*/
int ssl_is_challenge(conn_rec *c, const char *servername,
X509 **pcert, EVP_PKEY **pkey)
{
- if (md_is_challenge) {
- return md_is_challenge(c, servername, pcert, pkey);
+ if (APR_SUCCESS == ssl_run_answer_challenge(c, servername, pcert, pkey)) {
+ return 1;
}
*pcert = NULL;
*pkey = NULL;
@@ -231,16 +250,6 @@ apr_status_t ssl_init_Module(apr_pool_t *p, apr_pool_t *plog,
ssl_config_global_create(base_server); /* just to avoid problems */
ssl_config_global_fix(mc);
- /* Initialize our interface to mod_md, if it is loaded
- */
- md_is_managed = APR_RETRIEVE_OPTIONAL_FN(md_is_managed);
- md_get_certificate = APR_RETRIEVE_OPTIONAL_FN(md_get_certificate);
- md_is_challenge = APR_RETRIEVE_OPTIONAL_FN(md_is_challenge);
- if (!md_is_managed || !md_get_certificate) {
- md_is_managed = NULL;
- md_get_certificate = NULL;
- }
-
/*
* try to fix the configuration and open the dedicated SSL
* logfile as early as possible
@@ -1392,8 +1401,7 @@ static apr_status_t ssl_init_server_certs(server_rec *s,
* loaded via SSLOpenSSLConfCmd Certificate), so for 1.0.2 and
* later, we defer to the code in ssl_init_server_ctx.
*/
- if ((mctx->stapling_enabled == TRUE) &&
- !ssl_stapling_init_cert(s, p, ptemp, mctx, cert)) {
+ if (!ssl_stapling_init_cert(s, p, ptemp, mctx, cert)) {
ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(02567)
"Unable to configure certificate %s for stapling",
key_id);
@@ -1788,11 +1796,13 @@ static apr_status_t ssl_init_server_ctx(server_rec *s,
apr_array_header_t *pphrases)
{
apr_status_t rv;
+ modssl_pk_server_t *pks;
#ifdef HAVE_SSL_CONF_CMD
ssl_ctx_param_t *param = (ssl_ctx_param_t *)sc->server->ssl_ctx_param->elts;
SSL_CONF_CTX *cctx = sc->server->ssl_ctx_config;
int i;
#endif
+ int n;
/*
* Check for problematic re-initializations
@@ -1804,50 +1814,24 @@ static apr_status_t ssl_init_server_ctx(server_rec *s,
return APR_EGENERAL;
}
- ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(10083)
- "Init: (%s) mod_md support is %s.", ssl_util_vhostid(p, s),
- md_is_managed? "available" : "unavailable");
- if (md_is_managed && md_is_managed(s)) {
- modssl_pk_server_t *const pks = sc->server->pks;
- if (pks->cert_files->nelts > 0 || pks->key_files->nelts > 0) {
- ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s, APLOGNO(10084)
- "Init: (%s) You configured certificate/key files on this host, but "
- "is is covered by a Managed Domain. You need to remove these directives "
- "for the Managed Domain to take over.", ssl_util_vhostid(p, s));
- }
- else {
- const char *key_file, *cert_file, *chain_file;
-
- key_file = cert_file = chain_file = NULL;
-
- if (md_get_certificate) {
- rv = md_get_certificate(s, p, &key_file, &cert_file);
- }
- else {
- rv = APR_ENOTIMPL;
- }
-
- if (key_file && cert_file) {
- ap_log_error(APLOG_MARK, APLOG_TRACE1, 0, s,
- "%s: installing key=%s, cert=%s, chain=%s",
- ssl_util_vhostid(p, s), key_file, cert_file, chain_file);
- APR_ARRAY_PUSH(pks->key_files, const char *) = key_file;
- APR_ARRAY_PUSH(pks->cert_files, const char *) = cert_file;
- sc->server->cert_chain = chain_file;
- }
-
- if (APR_STATUS_IS_EAGAIN(rv)) {
- /* Managed Domain not ready yet. This is not a reason to fail the config */
- ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s, APLOGNO(10085)
- "Init: %s will respond with '503 Service Unavailable' for now. This "
- "host is part of a Managed Domain, but no SSL certificate is "
- "available (yet).", ssl_util_vhostid(p, s));
- pks->service_unavailable = 1;
- }
- else if (rv != APR_SUCCESS) {
- return rv;
- }
- }
+ /* Allow others to provide certificate files */
+ pks = sc->server->pks;
+ n = pks->cert_files->nelts;
+ ssl_run_add_cert_files(s, p, pks->cert_files, pks->key_files);
+
+ if (n < pks->cert_files->nelts) {
+ /* this overrides any old chain configuration */
+ sc->server->cert_chain = NULL;
+ }
+
+ if (apr_is_empty_array(pks->cert_files) && !sc->server->cert_chain) {
+ ssl_run_add_fallback_cert_files(s, p, pks->cert_files, pks->key_files);
+
+ pks->service_unavailable = 1;
+ ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s, APLOGNO(10085)
+ "Init: %s will respond with '503 Service Unavailable' for now. There "
+ "are no SSL certificates configured and no other module contributed any.",
+ ssl_util_vhostid(p, s));
}
if ((rv = ssl_init_ctx(s, p, ptemp, sc->server)) != APR_SUCCESS) {
@@ -1900,7 +1884,7 @@ static apr_status_t ssl_init_server_ctx(server_rec *s,
* (late) point makes sure that we catch both certificates loaded
* via SSLCertificateFile and SSLOpenSSLConfCmd Certificate.
*/
- if (sc->server->stapling_enabled == TRUE) {
+ do {
X509 *cert;
int i = 0;
int ret = SSL_CTX_set_current_cert(sc->server->ssl_ctx,
@@ -1917,7 +1901,7 @@ static apr_status_t ssl_init_server_ctx(server_rec *s,
SSL_CERT_SET_NEXT);
i++;
}
- }
+ } while(0);
#endif
#ifdef HAVE_TLS_SESSION_TICKETS
diff --git a/modules/ssl/ssl_engine_kernel.c b/modules/ssl/ssl_engine_kernel.c
index e6a9f67..a5e86e4 100644
--- a/modules/ssl/ssl_engine_kernel.c
+++ b/modules/ssl/ssl_engine_kernel.c
@@ -2303,6 +2303,37 @@ void ssl_callback_Info(const SSL *ssl, int where, int rc)
}
#ifdef HAVE_TLSEXT
+
+static apr_status_t set_challenge_creds(conn_rec *c, const char *servername,
+ SSL *ssl, X509 *cert, EVP_PKEY *key)
+{
+ SSLConnRec *sslcon = myConnConfig(c);
+
+ sslcon->service_unavailable = 1;
+ if ((SSL_use_certificate(ssl, cert) < 1)) {
+ ap_log_cerror(APLOG_MARK, APLOG_WARNING, 0, c, APLOGNO(10086)
+ "Failed to configure challenge certificate %s",
+ servername);
+ return APR_EGENERAL;
+ }
+
+ if (!SSL_use_PrivateKey(ssl, key)) {
+ ap_log_cerror(APLOG_MARK, APLOG_WARNING, 0, c, APLOGNO(10087)
+ "error '%s' using Challenge key: %s",
+ ERR_error_string(ERR_peek_last_error(), NULL),
+ servername);
+ return APR_EGENERAL;
+ }
+
+ if (SSL_check_private_key(ssl) < 1) {
+ ap_log_cerror(APLOG_MARK, APLOG_WARNING, 0, c, APLOGNO(10088)
+ "Challenge certificate and private key %s "
+ "do not match", servername);
+ return APR_EGENERAL;
+ }
+ return APR_SUCCESS;
+}
+
/*
* This function sets the virtual host from an extended
* client hello with a server name indication extension ("SNI", cf. RFC 6066).
@@ -2332,30 +2363,12 @@ static apr_status_t init_vhost(conn_rec *c, SSL *ssl)
return APR_SUCCESS;
}
else if (ssl_is_challenge(c, servername, &cert, &key)) {
-
- sslcon->service_unavailable = 1;
- if ((SSL_use_certificate(ssl, cert) < 1)) {
- ap_log_cerror(APLOG_MARK, APLOG_WARNING, 0, c, APLOGNO(10086)
- "Failed to configure challenge certificate %s",
- servername);
+ /* With ACMEv1 we can have challenge connections to a unknown domains
+ * that need to be answered with a special certificate and will
+ * otherwise not answer any requests. */
+ if (set_challenge_creds(c, servername, ssl, cert, key) != APR_SUCCESS) {
return APR_EGENERAL;
}
-
- if (!SSL_use_PrivateKey(ssl, key)) {
- ap_log_cerror(APLOG_MARK, APLOG_WARNING, 0, c, APLOGNO(10087)
- "error '%s' using Challenge key: %s",
- ERR_error_string(ERR_peek_last_error(), NULL),
- servername);
- return APR_EGENERAL;
- }
-
- if (SSL_check_private_key(ssl) < 1) {
- ap_log_cerror(APLOG_MARK, APLOG_WARNING, 0, c, APLOGNO(10088)
- "Challenge certificate and private key %s "
- "do not match", servername);
- return APR_EGENERAL;
- }
-
}
else {
ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, APLOGNO(02044)
@@ -2648,6 +2661,23 @@ int ssl_callback_alpn_select(SSL *ssl,
proposed);
return SSL_TLSEXT_ERR_ALERT_FATAL;
}
+
+ /* protocol was switched, this could be a challenge protocol such as "acme-tls/1".
+ * For that to work, we need to allow overrides to our ssl certificate.
+ * However, exclude challenge checks on our best known traffic protocol.
+ * (http/1.1 is the default, we never switch to it anyway.)
+ */
+ if (strcmp("h2", proposed)) {
+ const char *servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
+ X509 *cert;
+ EVP_PKEY *key;
+
+ if (ssl_is_challenge(c, servername, &cert, &key)) {
+ if (set_challenge_creds(c, servername, ssl, cert, key) != APR_SUCCESS) {
+ return SSL_TLSEXT_ERR_ALERT_FATAL;
+ }
+ }
+ }
}
return SSL_TLSEXT_ERR_OK;
diff --git a/modules/ssl/ssl_util_stapling.c b/modules/ssl/ssl_util_stapling.c
index c3e2cfa..4df0a9a 100644
--- a/modules/ssl/ssl_util_stapling.c
+++ b/modules/ssl/ssl_util_stapling.c
@@ -31,12 +31,28 @@
#include "ssl_private.h"
#include "ap_mpm.h"
#include "apr_thread_mutex.h"
+#include "mod_ssl_openssl.h"
+
+APR_IMPLEMENT_OPTIONAL_HOOK_RUN_ALL(ssl, SSL, int, init_stapling_status,
+ (server_rec *s, apr_pool_t *p,
+ X509 *cert, X509 *issuer),
+ (s, p, cert, issuer),
+ DECLINED, DECLINED)
+
+APR_IMPLEMENT_OPTIONAL_HOOK_RUN_ALL(ssl, SSL, int, get_stapling_status,
+ (unsigned char **pder, int *pderlen,
+ conn_rec *c, server_rec *s, X509 *cert),
+ (pder, pderlen, c, s, cert),
+ DECLINED, DECLINED)
+
#ifdef HAVE_OCSP_STAPLING
static int stapling_cache_mutex_on(server_rec *s);
static int stapling_cache_mutex_off(server_rec *s);
+static int stapling_cb(SSL *ssl, void *arg);
+
/**
* Maxiumum OCSP stapling response size. This should be the response for a
* single certificate and will typically include the responder certificate chain
@@ -119,7 +135,38 @@ int ssl_stapling_init_cert(server_rec *s, apr_pool_t *p, apr_pool_t *ptemp,
OCSP_CERTID *cid = NULL;
STACK_OF(OPENSSL_STRING) *aia = NULL;
- if ((x == NULL) || (X509_digest(x, EVP_sha1(), idx, NULL) != 1))
+ if (x == NULL)
+ return 0;
+
+ if (!(issuer = stapling_get_issuer(mctx, x))) {
+ /* In Apache pre 2.4.40, we use to come here only when mod_ssl stapling
+ * was enabled. With the new hooks, we give other modules the chance
+ * to provide stapling status. However, we do not want to log ssl errors
+ * where we did not do so in the past. */
+ if (mctx->stapling_enabled == TRUE) {
+ ssl_log_xerror(SSLLOG_MARK, APLOG_ERR, 0, ptemp, s, x, APLOGNO(02217)
+ "ssl_stapling_init_cert: can't retrieve issuer "
+ "certificate!");
+ return 0;
+ }
+ return 1;
+ }
+
+ if (ssl_run_init_stapling_status(s, p, x, issuer) == APR_SUCCESS) {
+ /* Someone's taken over or mod_ssl's own implementation is not enabled */
+ if (mctx->stapling_enabled != TRUE) {
+ SSL_CTX_set_tlsext_status_cb(mctx->ssl_ctx, stapling_cb);
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO() "OCSP stapling added via hook");
+ }
+ return 1;
+ }
+
+ if (mctx->stapling_enabled != TRUE) {
+ /* mod_ssl's own implementation is not enabled */
+ return 1;
+ }
+
+ if (X509_digest(x, EVP_sha1(), idx, NULL) != 1)
return 0;
cinf = apr_hash_get(stapling_certinfo, idx, sizeof(idx));
@@ -139,13 +186,6 @@ int ssl_stapling_init_cert(server_rec *s, apr_pool_t *p, apr_pool_t *ptemp,
return 1;
}
- if (!(issuer = stapling_get_issuer(mctx, x))) {
- ssl_log_xerror(SSLLOG_MARK, APLOG_ERR, 0, ptemp, s, x, APLOGNO(02217)
- "ssl_stapling_init_cert: can't retrieve issuer "
- "certificate!");
- return 0;
- }
-
cid = OCSP_cert_to_id(NULL, x, issuer);
X509_free(issuer);
if (!cid) {
@@ -182,18 +222,16 @@ int ssl_stapling_init_cert(server_rec *s, apr_pool_t *p, apr_pool_t *ptemp,
mctx->sc->vhost_id);
apr_hash_set(stapling_certinfo, cinf->idx, sizeof(cinf->idx), cinf);
-
+
return 1;
}
-static certinfo *stapling_get_certinfo(server_rec *s, modssl_ctx_t *mctx,
+static certinfo *stapling_get_certinfo(server_rec *s, X509 *x, modssl_ctx_t *mctx,
SSL *ssl)
{
certinfo *cinf;
- X509 *x;
UCHAR idx[SHA_DIGEST_LENGTH];
- x = SSL_get_certificate(ssl);
- if ((x == NULL) || (X509_digest(x, EVP_sha1(), idx, NULL) != 1))
+ if (X509_digest(x, EVP_sha1(), idx, NULL) != 1)
return NULL;
cinf = apr_hash_get(stapling_certinfo, idx, sizeof(idx));
if (cinf && cinf->cid)
@@ -750,18 +788,34 @@ static int stapling_cb(SSL *ssl, void *arg)
OCSP_RESPONSE *rsp = NULL;
int rv;
BOOL ok = TRUE;
+ X509 *x;
+ unsigned char *rspder = NULL;
+ int rspderlen;
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(01951)
+ "stapling_cb: OCSP Stapling callback called");
+
+ x = SSL_get_certificate(ssl);
+ if (x == NULL) {
+ return SSL_TLSEXT_ERR_NOACK;
+ }
+
+ if (ssl_run_get_stapling_status(&rspder, &rspderlen, conn, s, x) == APR_SUCCESS) {
+ /* a hook handles stapling for this certicate and determines the response */
+ if (rspder == NULL || rspderlen <= 0) {
+ return SSL_TLSEXT_ERR_NOACK;
+ }
+ SSL_set_tlsext_status_ocsp_resp(ssl, rspder, rspderlen);
+ return SSL_TLSEXT_ERR_OK;
+ }
+
if (sc->server->stapling_enabled != TRUE) {
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(01950)
"stapling_cb: OCSP Stapling disabled");
return SSL_TLSEXT_ERR_NOACK;
}
- ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(01951)
- "stapling_cb: OCSP Stapling callback called");
-
- cinf = stapling_get_certinfo(s, mctx, ssl);
- if (cinf == NULL) {
+ if ((cinf = stapling_get_certinfo(s, x, mctx, ssl)) == NULL) {
return SSL_TLSEXT_ERR_NOACK;
}
@@ -864,9 +918,10 @@ apr_status_t modssl_init_stapling(server_rec *s, apr_pool_t *p,
if (mctx->stapling_responder_timeout == UNSET) {
mctx->stapling_responder_timeout = 10 * APR_USEC_PER_SEC;
}
+
SSL_CTX_set_tlsext_status_cb(ctx, stapling_cb);
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(01960) "OCSP stapling initialized");
-
+
return APR_SUCCESS;
}

View File

@ -1,44 +0,0 @@
diff --git a/modules/md/mod_md_os.c b/modules/md/mod_md_os.c
index f96d566..8df0248 100644
--- a/modules/md/mod_md_os.c
+++ b/modules/md/mod_md_os.c
@@ -41,14 +41,20 @@
apr_status_t md_try_chown(const char *fname, unsigned int uid, int gid, apr_pool_t *p)
{
-#if AP_NEED_SET_MUTEX_PERMS
- if (-1 == chown(fname, (uid_t)uid, (gid_t)gid)) {
- apr_status_t rv = APR_FROM_OS_ERROR(errno);
- if (!APR_STATUS_IS_ENOENT(rv)) {
- ap_log_perror(APLOG_MARK, APLOG_ERR, rv, p, APLOGNO(10082)
- "Can't change owner of %s", fname);
+#if AP_NEED_SET_MUTEX_PERMS && HAVE_UNISTD_H
+ /* Since we only switch user when running as root, we only need to chown directories
+ * in that case. Otherwise, the server will ignore any "user/group" directives and
+ * child processes have the same privileges as the parent.
+ */
+ if (!geteuid()) {
+ if (-1 == chown(fname, (uid_t)uid, (gid_t)gid)) {
+ apr_status_t rv = APR_FROM_OS_ERROR(errno);
+ if (!APR_STATUS_IS_ENOENT(rv)) {
+ ap_log_perror(APLOG_MARK, APLOG_ERR, rv, p, APLOGNO(10082)
+ "Can't change owner of %s", fname);
+ }
+ return rv;
}
- return rv;
}
return APR_SUCCESS;
#else
@@ -58,11 +64,7 @@ apr_status_t md_try_chown(const char *fname, unsigned int uid, int gid, apr_pool
apr_status_t md_make_worker_accessible(const char *fname, apr_pool_t *p)
{
-#if AP_NEED_SET_MUTEX_PERMS
return md_try_chown(fname, ap_unixd_config.user_id, -1, p);
-#else
- return APR_ENOTIMPL;
-#endif
}
#ifdef WIN32

View File

@ -1,24 +0,0 @@
diff --git a/docs/conf/magic b/docs/conf/magic
index 7c56119..bc891d9 100644
--- a/docs/conf/magic
+++ b/docs/conf/magic
@@ -87,7 +87,7 @@
# Microsoft WAVE format (*.wav)
# [GRR 950115: probably all of the shorts and longs should be leshort/lelong]
# Microsoft RIFF
-0 string RIFF audio/unknown
+0 string RIFF
# - WAVE format
>8 string WAVE audio/x-wav
# MPEG audio.
--- a/modules/metadata/mod_mime_magic.c 2013/06/11 07:36:13 1491699
+++ b/modules/metadata/mod_mime_magic.c 2013/06/11 07:41:40 1491700
@@ -606,7 +606,7 @@
/* high overhead for 1 char - just hope they don't do this much */
str[0] = c;
str[1] = '\0';
- return magic_rsl_add(r, str);
+ return magic_rsl_add(r, apr_pstrdup(r->pool, str));
}
/* allocate and copy a contiguous string from a result string list */

File diff suppressed because it is too large Load Diff

View File

@ -1,35 +0,0 @@
diff --git a/modules/arch/unix/mod_systemd.c b/modules/arch/unix/mod_systemd.c
index 7a82a90..6c244b6 100644
--- a/modules/arch/unix/mod_systemd.c
+++ b/modules/arch/unix/mod_systemd.c
@@ -100,6 +100,21 @@ static int systemd_post_config(apr_pool_t *pconf, apr_pool_t *plog,
return OK;
}
+/* Report the service is ready in post_config, which could be during
+ * startup or after a reload. The server could still hit a fatal
+ * startup error after this point during ap_run_mpm(), so this is
+ * perhaps too early, but by post_config listen() has been called on
+ * the TCP ports so new connections will not be rejected. There will
+ * always be a possible async failure event simultaneous to the
+ * service reporting "ready", so this should be good enough. */
+static int systemd_post_config_last(apr_pool_t *p, apr_pool_t *plog,
+ apr_pool_t *ptemp, server_rec *main_server)
+{
+ sd_notify(0, "READY=1\n"
+ "STATUS=Configuration loaded.\n");
+ return OK;
+}
+
static int systemd_pre_mpm(apr_pool_t *p, ap_scoreboard_e sb_type)
{
int rv;
@@ -187,6 +202,8 @@ static void systemd_register_hooks(apr_pool_t *p)
ap_hook_pre_config(systemd_pre_config, NULL, NULL, APR_HOOK_LAST);
/* Grab the listener config. */
ap_hook_post_config(systemd_post_config, NULL, NULL, APR_HOOK_LAST);
+ /* Signal service is ready. */
+ ap_hook_post_config(systemd_post_config_last, NULL, NULL, APR_HOOK_REALLY_LAST);
/* We know the PID in this hook ... */
ap_hook_pre_mpm(systemd_pre_mpm, NULL, NULL, APR_HOOK_LAST);
/* Used to update httpd's status line using sd_notifyf */

View File

@ -1,48 +0,0 @@
diff --git a/modules/ssl/ssl_engine_init.c b/modules/ssl/ssl_engine_init.c
index 70d151e..e4f5fc8 100644
--- a/modules/ssl/ssl_engine_init.c
+++ b/modules/ssl/ssl_engine_init.c
@@ -1095,7 +1095,9 @@ static apr_status_t ssl_init_ctx_crl(server_rec *s,
/*
* Read a file that optionally contains the server certificate in PEM
* format, possibly followed by a sequence of CA certificates that
- * should be sent to the peer in the SSL Certificate message.
+ * should be sent to the peer in the SSL Certificate message. Returns
+ * 0 on success, otherwise the OpenSSL error stack contents should be
+ * reported.
*/
static int use_certificate_chain(
SSL_CTX *ctx, char *file, int skipfirst, pem_password_cb *cb)
@@ -1128,8 +1130,10 @@ static int use_certificate_chain(
ctx->extra_certs = NULL;
}
#endif
+
/* create new extra chain by loading the certs */
n = 0;
+ ERR_clear_error();
while ((x509 = PEM_read_bio_X509(bio, NULL, cb, NULL)) != NULL) {
if (!SSL_CTX_add_extra_chain_cert(ctx, x509)) {
X509_free(x509);
@@ -1190,6 +1194,7 @@ static apr_status_t ssl_init_ctx_cert_chain(server_rec *s,
if (n < 0) {
ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(01903)
"Failed to configure CA certificate chain!");
+ ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s);
return ssl_die(s);
}
diff --git a/modules/ssl/ssl_util_ocsp.c b/modules/ssl/ssl_util_ocsp.c
index b11a6e9..b66e151 100644
--- a/modules/ssl/ssl_util_ocsp.c
+++ b/modules/ssl/ssl_util_ocsp.c
@@ -363,7 +363,9 @@ static STACK_OF(X509) *modssl_read_ocsp_certificates(const char *file)
BIO_free(bio);
return NULL;
}
+
/* create new extra chain by loading the certs */
+ ERR_clear_error();
while ((x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL)) != NULL) {
if (!other_certs) {
other_certs = sk_X509_new_null();

View File

@ -1,44 +0,0 @@
diff --git a/modules/ssl/ssl_engine_io.c b/modules/ssl/ssl_engine_io.c
index 0958135..018b667 100644
--- a/modules/ssl/ssl_engine_io.c
+++ b/modules/ssl/ssl_engine_io.c
@@ -200,6 +200,8 @@ static int bio_filter_out_write(BIO *bio, const char *in, int inl)
apr_bucket *e;
int need_flush;
+ BIO_clear_retry_flags(bio);
+
#ifndef SSL_OP_NO_RENEGOTIATION
/* Abort early if the client has initiated a renegotiation. */
if (outctx->filter_ctx->config->reneg_state == RENEG_ABORT) {
@@ -208,12 +210,6 @@ static int bio_filter_out_write(BIO *bio, const char *in, int inl)
}
#endif
- /* when handshaking we'll have a small number of bytes.
- * max size SSL will pass us here is about 16k.
- * (16413 bytes to be exact)
- */
- BIO_clear_retry_flags(bio);
-
/* Use a transient bucket for the output data - any downstream
* filter must setaside if necessary. */
e = apr_bucket_transient_create(in, inl, outctx->bb->bucket_alloc);
@@ -460,6 +456,8 @@ static int bio_filter_in_read(BIO *bio, char *in, int inlen)
if (!in)
return 0;
+ BIO_clear_retry_flags(bio);
+
#ifndef SSL_OP_NO_RENEGOTIATION
/* Abort early if the client has initiated a renegotiation. */
if (inctx->filter_ctx->config->reneg_state == RENEG_ABORT) {
@@ -468,8 +466,6 @@ static int bio_filter_in_read(BIO *bio, char *in, int inlen)
}
#endif
- BIO_clear_retry_flags(bio);
-
if (!inctx->bb) {
inctx->rc = APR_EOF;
return -1;

View File

@ -1,209 +0,0 @@
diff --git a/configure.in b/configure.in
index de6a8ad..4ca489d 100644
--- a/configure.in
+++ b/configure.in
@@ -465,6 +465,28 @@ LIBS=""
AC_SEARCH_LIBS(crypt, crypt)
CRYPT_LIBS="$LIBS"
APACHE_SUBST(CRYPT_LIBS)
+
+if test "$ac_cv_search_crypt" != "no"; then
+ # Test crypt() with the SHA-512 test vector from https://akkadia.org/drepper/SHA-crypt.txt
+ AC_CACHE_CHECK([whether crypt() supports SHA-2], [ap_cv_crypt_sha2], [
+ AC_RUN_IFELSE([AC_LANG_PROGRAM([[
+#include <crypt.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define PASSWD_0 "Hello world!"
+#define SALT_0 "\$6\$saltstring"
+#define EXPECT_0 "\$6\$saltstring\$svn8UoSVapNtMuq1ukKS4tPQd8iKwSMHWjl/O817G3uBnIFNjnQJu" \
+ "esI68u4OTLiBFdcbYEdFCoEOfaS35inz1"
+]], [char *result = crypt(PASSWD_0, SALT_0);
+ if (!result) return 1;
+ if (strcmp(result, EXPECT_0)) return 2;
+])], [ap_cv_crypt_sha2=yes], [ap_cv_crypt_sha2=no])])
+ if test "$ap_cv_crypt_sha2" = yes; then
+ AC_DEFINE([HAVE_CRYPT_SHA2], 1, [Define if crypt() supports SHA-2 hashes])
+ fi
+fi
+
LIBS="$saved_LIBS"
dnl See Comment #Spoon
diff --git a/support/htpasswd.c b/support/htpasswd.c
index 660a27c..136f62a 100644
--- a/support/htpasswd.c
+++ b/support/htpasswd.c
@@ -98,28 +98,32 @@ static int mkrecord(struct passwd_ctx *ctx, char *user)
static void usage(void)
{
apr_file_printf(errfile, "Usage:" NL
- "\thtpasswd [-cimBdpsDv] [-C cost] passwordfile username" NL
- "\thtpasswd -b[cmBdpsDv] [-C cost] passwordfile username password" NL
+ "\thtpasswd [-cimB25dpsDv] [-C cost] [-r rounds] passwordfile username" NL
+ "\thtpasswd -b[cmB25dpsDv] [-C cost] [-r rounds] passwordfile username password" NL
NL
- "\thtpasswd -n[imBdps] [-C cost] username" NL
- "\thtpasswd -nb[mBdps] [-C cost] username password" NL
+ "\thtpasswd -n[imB25dps] [-C cost] [-r rounds] username" NL
+ "\thtpasswd -nb[mB25dps] [-C cost] [-r rounds] username password" NL
" -c Create a new file." NL
" -n Don't update file; display results on stdout." NL
" -b Use the password from the command line rather than prompting "
"for it." NL
" -i Read password from stdin without verification (for script usage)." NL
" -m Force MD5 encryption of the password (default)." NL
- " -B Force bcrypt encryption of the password (very secure)." NL
+ " -2 Force SHA-256 crypt() hash of the password (secure)." NL
+ " -5 Force SHA-512 crypt() hash of the password (secure)." NL
+ " -B Force bcrypt encryption of the password (very secure)." NL
" -C Set the computing time used for the bcrypt algorithm" NL
" (higher is more secure but slower, default: %d, valid: 4 to 31)." NL
+ " -r Set the number of rounds used for the SHA-256, SHA-512 algorithms" NL
+ " (higher is more secure but slower, default: 5000)." NL
" -d Force CRYPT encryption of the password (8 chars max, insecure)." NL
- " -s Force SHA encryption of the password (insecure)." NL
+ " -s Force SHA-1 encryption of the password (insecure)." NL
" -p Do not encrypt the password (plaintext, insecure)." NL
" -D Delete the specified user." NL
" -v Verify password for the specified user." NL
"On other systems than Windows and NetWare the '-p' flag will "
"probably not work." NL
- "The SHA algorithm does not use a salt and is less secure than the "
+ "The SHA-1 algorithm does not use a salt and is less secure than the "
"MD5 algorithm." NL,
BCRYPT_DEFAULT_COST
);
@@ -178,7 +182,7 @@ static void check_args(int argc, const char *const argv[],
if (rv != APR_SUCCESS)
exit(ERR_SYNTAX);
- while ((rv = apr_getopt(state, "cnmspdBbDiC:v", &opt, &opt_arg)) == APR_SUCCESS) {
+ while ((rv = apr_getopt(state, "cnmspdBbDi25C:r:v", &opt, &opt_arg)) == APR_SUCCESS) {
switch (opt) {
case 'c':
*mask |= APHTP_NEWFILE;
diff --git a/support/passwd_common.c b/support/passwd_common.c
index 664e509..d45657c 100644
--- a/support/passwd_common.c
+++ b/support/passwd_common.c
@@ -185,10 +185,15 @@ int mkhash(struct passwd_ctx *ctx)
#if CRYPT_ALGO_SUPPORTED
char *cbuf;
#endif
+#ifdef HAVE_CRYPT_SHA2
+ const char *setting;
+ char method;
+#endif
- if (ctx->cost != 0 && ctx->alg != ALG_BCRYPT) {
+ if (ctx->cost != 0 && ctx->alg != ALG_BCRYPT
+ && ctx->alg != ALG_CRYPT_SHA256 && ctx->alg != ALG_CRYPT_SHA512 ) {
apr_file_printf(errfile,
- "Warning: Ignoring -C argument for this algorithm." NL);
+ "Warning: Ignoring -C/-r argument for this algorithm." NL);
}
if (ctx->passwd == NULL) {
@@ -246,6 +251,34 @@ int mkhash(struct passwd_ctx *ctx)
break;
#endif /* CRYPT_ALGO_SUPPORTED */
+#ifdef HAVE_CRYPT_SHA2
+ case ALG_CRYPT_SHA256:
+ case ALG_CRYPT_SHA512:
+ ret = generate_salt(salt, 16, &ctx->errstr, ctx->pool);
+ if (ret != 0)
+ break;
+
+ method = ctx->alg == ALG_CRYPT_SHA256 ? '5': '6';
+
+ if (ctx->cost)
+ setting = apr_psprintf(ctx->pool, "$%c$rounds=%d$%s",
+ method, ctx->cost, salt);
+ else
+ setting = apr_psprintf(ctx->pool, "$%c$%s",
+ method, salt);
+
+ cbuf = crypt(pw, setting);
+ if (cbuf == NULL) {
+ rv = APR_FROM_OS_ERROR(errno);
+ ctx->errstr = apr_psprintf(ctx->pool, "crypt() failed: %pm", &rv);
+ ret = ERR_PWMISMATCH;
+ break;
+ }
+
+ apr_cpystrn(ctx->out, cbuf, ctx->out_len - 1);
+ break;
+#endif /* HAVE_CRYPT_SHA2 */
+
#if BCRYPT_ALGO_SUPPORTED
case ALG_BCRYPT:
rv = apr_generate_random_bytes((unsigned char*)salt, 16);
@@ -294,6 +327,19 @@ int parse_common_options(struct passwd_ctx *ctx, char opt,
case 's':
ctx->alg = ALG_APSHA;
break;
+#ifdef HAVE_CRYPT_SHA2
+ case '2':
+ ctx->alg = ALG_CRYPT_SHA256;
+ break;
+ case '5':
+ ctx->alg = ALG_CRYPT_SHA512;
+ break;
+#else
+ case '2':
+ case '5':
+ ctx->errstr = "SHA-2 crypt() algorithms are not supported on this platform.";
+ return ERR_ALG_NOT_SUPP;
+#endif
case 'p':
ctx->alg = ALG_PLAIN;
#if !PLAIN_ALGO_SUPPORTED
@@ -324,11 +370,12 @@ int parse_common_options(struct passwd_ctx *ctx, char opt,
return ERR_ALG_NOT_SUPP;
#endif
break;
- case 'C': {
+ case 'C':
+ case 'r': {
char *endptr;
long num = strtol(opt_arg, &endptr, 10);
if (*endptr != '\0' || num <= 0) {
- ctx->errstr = "argument to -C must be a positive integer";
+ ctx->errstr = "argument to -C/-r must be a positive integer";
return ERR_SYNTAX;
}
ctx->cost = num;
diff --git a/support/passwd_common.h b/support/passwd_common.h
index 660081e..f1b3cd7 100644
--- a/support/passwd_common.h
+++ b/support/passwd_common.h
@@ -28,6 +28,8 @@
#include "apu_version.h"
#endif
+#include "ap_config_auto.h"
+
#define MAX_STRING_LEN 256
#define ALG_PLAIN 0
@@ -35,6 +37,8 @@
#define ALG_APMD5 2
#define ALG_APSHA 3
#define ALG_BCRYPT 4
+#define ALG_CRYPT_SHA256 5
+#define ALG_CRYPT_SHA512 6
#define BCRYPT_DEFAULT_COST 5
@@ -84,7 +88,7 @@ struct passwd_ctx {
apr_size_t out_len;
char *passwd;
int alg;
- int cost;
+ int cost; /* cost for bcrypt, rounds for SHA-2 */
enum {
PW_PROMPT = 0,
PW_ARG,

View File

@ -1,108 +0,0 @@
--- a/modules/dav/main/mod_dav.c
+++ b/modules/dav/main/mod_dav.c
@@ -557,6 +557,7 @@
dav_begin_multistatus(bb, r, status, namespaces);
apr_pool_create(&subpool, r->pool);
+ apr_pool_tag(subpool, "mod_dav-multistatus");
for (; first != NULL; first = first->next) {
apr_pool_clear(subpool);
@@ -1980,8 +1981,9 @@
** Note: we cast to lose the "const". The propdb won't try to change
** the resource, however, since we are opening readonly.
*/
- err = dav_open_propdb(ctx->r, ctx->w.lockdb, wres->resource, 1,
- ctx->doc ? ctx->doc->namespaces : NULL, &propdb);
+ err = dav_popen_propdb(ctx->scratchpool,
+ ctx->r, ctx->w.lockdb, wres->resource, 1,
+ ctx->doc ? ctx->doc->namespaces : NULL, &propdb);
if (err != NULL) {
/* ### do something with err! */
--- a/modules/dav/main/mod_dav.h
+++ b/modules/dav/main/mod_dav.h
@@ -1590,6 +1590,16 @@
apr_array_header_t *ns_xlate,
dav_propdb **propdb);
+DAV_DECLARE(dav_error *) dav_popen_propdb(
+ apr_pool_t *p,
+ request_rec *r,
+ dav_lockdb *lockdb,
+ const dav_resource *resource,
+ int ro,
+ apr_array_header_t *ns_xlate,
+ dav_propdb **propdb);
+
+
DAV_DECLARE(void) dav_close_propdb(dav_propdb *db);
DAV_DECLARE(dav_get_props_result) dav_get_props(
--- a/modules/dav/main/props.c
+++ b/modules/dav/main/props.c
@@ -323,7 +323,7 @@
{
/* need to escape the uri that's in the resource struct because during
* the property walker it's not encoded. */
- const char *e_uri = ap_escape_uri(propdb->resource->pool,
+ const char *e_uri = ap_escape_uri(propdb->p,
propdb->resource->uri);
/* perform a "GET" on the resource's URI (note that the resource
@@ -524,8 +524,21 @@
apr_array_header_t * ns_xlate,
dav_propdb **p_propdb)
{
- dav_propdb *propdb = apr_pcalloc(r->pool, sizeof(*propdb));
+ return dav_popen_propdb(r->pool, r, lockdb, resource, ro, ns_xlate, p_propdb);
+}
+DAV_DECLARE(dav_error *)dav_popen_propdb(apr_pool_t *p,
+ request_rec *r, dav_lockdb *lockdb,
+ const dav_resource *resource,
+ int ro,
+ apr_array_header_t * ns_xlate,
+ dav_propdb **p_propdb)
+{
+ dav_propdb *propdb = NULL;
+
+ propdb = apr_pcalloc(p, sizeof(*propdb));
+ propdb->p = p;
+
*p_propdb = NULL;
#if DAV_DEBUG
@@ -537,7 +550,6 @@
#endif
propdb->r = r;
- apr_pool_create(&propdb->p, r->pool);
propdb->resource = resource;
propdb->ns_xlate = ns_xlate;
@@ -562,10 +574,10 @@
(*propdb->db_hooks->close)(propdb->db);
}
- /* Currently, mod_dav's pool usage doesn't allow clearing this pool. */
-#if 0
- apr_pool_destroy(propdb->p);
-#endif
+ if (propdb->subreq) {
+ ap_destroy_sub_req(propdb->subreq);
+ propdb->subreq = NULL;
+ }
}
DAV_DECLARE(dav_get_props_result) dav_get_allprops(dav_propdb *propdb,
@@ -739,7 +751,8 @@
*/
if (elem->priv == NULL) {
- elem->priv = apr_pcalloc(propdb->p, sizeof(*priv));
+ /* elem->priv outlives propdb->p. Hence use the request pool */
+ elem->priv = apr_pcalloc(propdb->r->pool, sizeof(*priv));
}
priv = elem->priv;

View File

@ -1,40 +0,0 @@
--- a/modules/proxy/mod_proxy_hcheck.c 2019/07/30 13:01:08 1863999
+++ b/modules/proxy/mod_proxy_hcheck.c 2019/07/30 13:01:21 1864000
@@ -110,6 +110,10 @@
if (!worker && !v) {
return "Bad call to set_worker_hc_param()";
}
+ if (!ctx) {
+ ctx = hc_create_config(p, s);
+ ap_set_module_config(s->module_config, &proxy_hcheck_module, ctx);
+ }
temp = (hc_template_t *)v;
if (!strcasecmp(key, "hctemplate")) {
hc_template_t *template;
@@ -1059,6 +1063,8 @@
int i;
sctx_t *ctx = (sctx_t *) ap_get_module_config(r->server->module_config,
&proxy_hcheck_module);
+ if (!ctx)
+ return;
if (apr_is_empty_table(ctx->conditions))
return;
@@ -1088,6 +1094,8 @@
int i;
sctx_t *ctx = (sctx_t *) ap_get_module_config(r->server->module_config,
&proxy_hcheck_module);
+ if (!ctx)
+ return;
if (apr_is_empty_table(ctx->conditions))
return;
@@ -1111,6 +1119,8 @@
int i;
sctx_t *ctx = (sctx_t *) ap_get_module_config(r->server->module_config,
&proxy_hcheck_module);
+ if (!ctx)
+ return 0;
if (apr_is_empty_table(ctx->conditions))
return 0;

View File

@ -1,117 +0,0 @@
# ./pullrev.sh 1870095 1870097
http://svn.apache.org/viewvc?view=revision&revision=1870095
http://svn.apache.org/viewvc?view=revision&revision=1870097
--- httpd-2.4.37/modules/ssl/ssl_engine_kernel.c
+++ httpd-2.4.37/modules/ssl/ssl_engine_kernel.c
@@ -114,6 +114,45 @@
return result;
}
+/* If a renegotiation is required for the location, and the request
+ * includes a message body (and the client has not requested a "100
+ * Continue" response), then the client will be streaming the request
+ * body over the wire already. In that case, it is not possible to
+ * stop and perform a new SSL handshake immediately; once the SSL
+ * library moves to the "accept" state, it will reject the SSL packets
+ * which the client is sending for the request body.
+ *
+ * To allow authentication to complete in the hook, the solution used
+ * here is to fill a (bounded) buffer with the request body, and then
+ * to reinject that request body later.
+ *
+ * This function is called to fill the renegotiation buffer for the
+ * location as required, or fail. Returns zero on success or HTTP_
+ * error code on failure.
+ */
+static int fill_reneg_buffer(request_rec *r, SSLDirConfigRec *dc)
+{
+ int rv;
+ apr_size_t rsize;
+
+ /* ### this is HTTP/1.1 specific, special case for protocol? */
+ if (r->expecting_100 || !ap_request_has_body(r)) {
+ return 0;
+ }
+
+ rsize = dc->nRenegBufferSize == UNSET ? DEFAULT_RENEG_BUFFER_SIZE : dc->nRenegBufferSize;
+ if (rsize > 0) {
+ /* Fill the I/O buffer with the request body if possible. */
+ rv = ssl_io_buffer_fill(r, rsize);
+ }
+ else {
+ /* If the reneg buffer size is set to zero, just fail. */
+ rv = HTTP_REQUEST_ENTITY_TOO_LARGE;
+ }
+
+ return rv;
+}
+
#ifdef HAVE_TLSEXT
static int ap_array_same_str_set(apr_array_header_t *s1, apr_array_header_t *s2)
{
@@ -814,41 +853,14 @@
}
}
- /* If a renegotiation is now required for this location, and the
- * request includes a message body (and the client has not
- * requested a "100 Continue" response), then the client will be
- * streaming the request body over the wire already. In that
- * case, it is not possible to stop and perform a new SSL
- * handshake immediately; once the SSL library moves to the
- * "accept" state, it will reject the SSL packets which the client
- * is sending for the request body.
- *
- * To allow authentication to complete in this auth hook, the
- * solution used here is to fill a (bounded) buffer with the
- * request body, and then to reinject that request body later.
- */
- if (renegotiate && !renegotiate_quick
- && !r->expecting_100
- && ap_request_has_body(r)) {
- int rv;
- apr_size_t rsize;
-
- rsize = dc->nRenegBufferSize == UNSET ? DEFAULT_RENEG_BUFFER_SIZE :
- dc->nRenegBufferSize;
- if (rsize > 0) {
- /* Fill the I/O buffer with the request body if possible. */
- rv = ssl_io_buffer_fill(r, rsize);
- }
- else {
- /* If the reneg buffer size is set to zero, just fail. */
- rv = HTTP_REQUEST_ENTITY_TOO_LARGE;
- }
-
- if (rv) {
+ /* Fill reneg buffer if required. */
+ if (renegotiate && !renegotiate_quick) {
+ rc = fill_reneg_buffer(r, dc);
+ if (rc) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02257)
"could not buffer message body to allow "
"SSL renegotiation to proceed");
- return rv;
+ return rc;
}
}
@@ -1132,6 +1144,17 @@
}
}
+ /* Fill reneg buffer if required. */
+ if (change_vmode) {
+ rc = fill_reneg_buffer(r, dc);
+ if (rc) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10228)
+ "could not buffer message body to allow "
+ "TLS Post-Handshake Authentication to proceed");
+ return rc;
+ }
+ }
+
if (change_vmode) {
char peekbuf[1];

View File

@ -1,636 +0,0 @@
diff --git a/modules/proxy/mod_proxy.h b/modules/proxy/mod_proxy.h
index 57cc92f..fbbd508 100644
--- a/modules/proxy/mod_proxy.h
+++ b/modules/proxy/mod_proxy.h
@@ -288,12 +288,15 @@ typedef struct {
/* Connection pool */
struct proxy_conn_pool {
- apr_pool_t *pool; /* The pool used in constructor and destructor calls */
- apr_sockaddr_t *addr; /* Preparsed remote address info */
- apr_reslist_t *res; /* Connection resource list */
- proxy_conn_rec *conn; /* Single connection for prefork mpm */
+ apr_pool_t *pool; /* The pool used in constructor and destructor calls */
+ apr_sockaddr_t *addr; /* Preparsed remote address info */
+ apr_reslist_t *res; /* Connection resource list */
+ proxy_conn_rec *conn; /* Single connection for prefork mpm */
+ apr_pool_t *dns_pool; /* The pool used for worker scoped DNS resolutions */
};
+#define AP_VOLATILIZE_T(T, x) (*(T volatile *)&(x))
+
/* worker status bits */
/*
* NOTE: Keep up-to-date w/ proxy_wstat_tbl[]
@@ -475,7 +478,9 @@ struct proxy_worker {
proxy_conn_pool *cp; /* Connection pool to use */
proxy_worker_shared *s; /* Shared data */
proxy_balancer *balancer; /* which balancer am I in? */
+#if APR_HAS_THREADS
apr_thread_mutex_t *tmutex; /* Thread lock for updating address cache */
+#endif
void *context; /* general purpose storage */
ap_conf_vector_t *section_config; /* <Proxy>-section wherein defined */
};
@@ -534,7 +539,9 @@ struct proxy_balancer {
apr_time_t wupdated; /* timestamp of last change to workers list */
proxy_balancer_method *lbmethod;
apr_global_mutex_t *gmutex; /* global lock for updating list of workers */
+#if APR_HAS_THREADS
apr_thread_mutex_t *tmutex; /* Thread lock for updating shm */
+#endif
proxy_server_conf *sconf;
void *context; /* general purpose storage */
proxy_balancer_shared *s; /* Shared data */
diff --git a/modules/proxy/mod_proxy_balancer.c b/modules/proxy/mod_proxy_balancer.c
index c59f5e9..3a28038 100644
--- a/modules/proxy/mod_proxy_balancer.c
+++ b/modules/proxy/mod_proxy_balancer.c
@@ -346,23 +346,27 @@ static proxy_worker *find_best_worker(proxy_balancer *balancer,
proxy_worker *candidate = NULL;
apr_status_t rv;
+#if APR_HAS_THREADS
if ((rv = PROXY_THREAD_LOCK(balancer)) != APR_SUCCESS) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01163)
"%s: Lock failed for find_best_worker()",
balancer->s->name);
return NULL;
}
+#endif
candidate = (*balancer->lbmethod->finder)(balancer, r);
if (candidate)
candidate->s->elected++;
+#if APR_HAS_THREADS
if ((rv = PROXY_THREAD_UNLOCK(balancer)) != APR_SUCCESS) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01164)
"%s: Unlock failed for find_best_worker()",
balancer->s->name);
}
+#endif
if (candidate == NULL) {
/* All the workers are in error state or disabled.
@@ -492,11 +496,13 @@ static int proxy_balancer_pre_request(proxy_worker **worker,
/* Step 2: Lock the LoadBalancer
* XXX: perhaps we need the process lock here
*/
+#if APR_HAS_THREADS
if ((rv = PROXY_THREAD_LOCK(*balancer)) != APR_SUCCESS) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01166)
"%s: Lock failed for pre_request", (*balancer)->s->name);
return DECLINED;
}
+#endif
/* Step 3: force recovery */
force_recovery(*balancer, r->server);
@@ -557,20 +563,24 @@ static int proxy_balancer_pre_request(proxy_worker **worker,
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01167)
"%s: All workers are in error state for route (%s)",
(*balancer)->s->name, route);
+#if APR_HAS_THREADS
if ((rv = PROXY_THREAD_UNLOCK(*balancer)) != APR_SUCCESS) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01168)
"%s: Unlock failed for pre_request",
(*balancer)->s->name);
}
+#endif
return HTTP_SERVICE_UNAVAILABLE;
}
}
+#if APR_HAS_THREADS
if ((rv = PROXY_THREAD_UNLOCK(*balancer)) != APR_SUCCESS) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01169)
"%s: Unlock failed for pre_request",
(*balancer)->s->name);
}
+#endif
if (!*worker) {
runtime = find_best_worker(*balancer, r);
if (!runtime) {
@@ -644,12 +654,14 @@ static int proxy_balancer_post_request(proxy_worker *worker,
apr_status_t rv;
+#if APR_HAS_THREADS
if ((rv = PROXY_THREAD_LOCK(balancer)) != APR_SUCCESS) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01173)
"%s: Lock failed for post_request",
balancer->s->name);
return HTTP_INTERNAL_SERVER_ERROR;
}
+#endif
if (!apr_is_empty_array(balancer->errstatuses)
&& !(worker->s->status & PROXY_WORKER_IGNORE_ERRORS)) {
@@ -681,11 +693,12 @@ static int proxy_balancer_post_request(proxy_worker *worker,
worker->s->error_time = apr_time_now();
}
-
+#if APR_HAS_THREADS
if ((rv = PROXY_THREAD_UNLOCK(balancer)) != APR_SUCCESS) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01175)
"%s: Unlock failed for post_request", balancer->s->name);
}
+#endif
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01176)
"proxy_balancer_post_request for (%s)", balancer->s->name);
@@ -945,7 +958,6 @@ static int balancer_post_config(apr_pool_t *pconf, apr_pool_t *plog,
PROXY_STRNCPY(balancer->s->sname, sname); /* We know this will succeed */
balancer->max_workers = balancer->workers->nelts + balancer->growth;
-
/* Create global mutex */
rv = ap_global_mutex_create(&(balancer->gmutex), NULL, balancer_mutex_type,
balancer->s->sname, s, pconf, 0);
@@ -955,7 +967,6 @@ static int balancer_post_config(apr_pool_t *pconf, apr_pool_t *plog,
balancer->s->sname);
return HTTP_INTERNAL_SERVER_ERROR;
}
-
apr_pool_cleanup_register(pconf, (void *)s, lock_remove,
apr_pool_cleanup_null);
@@ -1135,17 +1146,21 @@ static int balancer_handler(request_rec *r)
balancer = (proxy_balancer *)conf->balancers->elts;
for (i = 0; i < conf->balancers->nelts; i++, balancer++) {
+#if APR_HAS_THREADS
if ((rv = PROXY_THREAD_LOCK(balancer)) != APR_SUCCESS) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01189)
"%s: Lock failed for balancer_handler",
balancer->s->name);
}
+#endif
ap_proxy_sync_balancer(balancer, r->server, conf);
+#if APR_HAS_THREADS
if ((rv = PROXY_THREAD_UNLOCK(balancer)) != APR_SUCCESS) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01190)
"%s: Unlock failed for balancer_handler",
balancer->s->name);
}
+#endif
}
if (r->args && (r->method_number == M_GET)) {
@@ -1359,11 +1374,13 @@ static int balancer_handler(request_rec *r)
proxy_worker *nworker;
nworker = ap_proxy_get_worker(r->pool, bsel, conf, val);
if (!nworker && storage->num_free_slots(bsel->wslot)) {
+#if APR_HAS_THREADS
if ((rv = PROXY_GLOBAL_LOCK(bsel)) != APR_SUCCESS) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01194)
"%s: Lock failed for adding worker",
bsel->s->name);
}
+#endif
ret = ap_proxy_define_worker(conf->pool, &nworker, bsel, conf, val, 0);
if (!ret) {
unsigned int index;
@@ -1372,53 +1389,76 @@ static int balancer_handler(request_rec *r)
if ((rv = storage->grab(bsel->wslot, &index)) != APR_SUCCESS) {
ap_log_rerror(APLOG_MARK, APLOG_EMERG, rv, r, APLOGNO(01195)
"worker slotmem_grab failed");
+#if APR_HAS_THREADS
if ((rv = PROXY_GLOBAL_UNLOCK(bsel)) != APR_SUCCESS) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01196)
"%s: Unlock failed for adding worker",
bsel->s->name);
}
+#endif
return HTTP_BAD_REQUEST;
}
if ((rv = storage->dptr(bsel->wslot, index, (void *)&shm)) != APR_SUCCESS) {
ap_log_rerror(APLOG_MARK, APLOG_EMERG, rv, r, APLOGNO(01197)
"worker slotmem_dptr failed");
+#if APR_HAS_THREADS
if ((rv = PROXY_GLOBAL_UNLOCK(bsel)) != APR_SUCCESS) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01198)
"%s: Unlock failed for adding worker",
bsel->s->name);
}
+#endif
return HTTP_BAD_REQUEST;
}
if ((rv = ap_proxy_share_worker(nworker, shm, index)) != APR_SUCCESS) {
ap_log_rerror(APLOG_MARK, APLOG_EMERG, rv, r, APLOGNO(01199)
"Cannot share worker");
+#if APR_HAS_THREADS
if ((rv = PROXY_GLOBAL_UNLOCK(bsel)) != APR_SUCCESS) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01200)
"%s: Unlock failed for adding worker",
bsel->s->name);
}
+#endif
return HTTP_BAD_REQUEST;
}
if ((rv = ap_proxy_initialize_worker(nworker, r->server, conf->pool)) != APR_SUCCESS) {
ap_log_rerror(APLOG_MARK, APLOG_EMERG, rv, r, APLOGNO(01201)
"Cannot init worker");
+#if APR_HAS_THREADS
if ((rv = PROXY_GLOBAL_UNLOCK(bsel)) != APR_SUCCESS) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01202)
"%s: Unlock failed for adding worker",
bsel->s->name);
}
+#endif
return HTTP_BAD_REQUEST;
}
/* sync all timestamps */
bsel->wupdated = bsel->s->wupdated = nworker->s->updated = apr_time_now();
/* by default, all new workers are disabled */
ap_proxy_set_wstatus(PROXY_WORKER_DISABLED_FLAG, 1, nworker);
+ } else {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10163)
+ "%s: failed to add worker %s",
+ bsel->s->name, val);
+#if APR_HAS_THREADS
+ PROXY_GLOBAL_UNLOCK(bsel);
+#endif
+ return HTTP_BAD_REQUEST;
}
+#if APR_HAS_THREADS
if ((rv = PROXY_GLOBAL_UNLOCK(bsel)) != APR_SUCCESS) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01203)
"%s: Unlock failed for adding worker",
bsel->s->name);
}
+#endif
+ } else {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10164)
+ "%s: failed to add worker %s",
+ bsel->s->name, val);
+ return HTTP_BAD_REQUEST;
}
}
diff --git a/modules/proxy/mod_proxy_ftp.c b/modules/proxy/mod_proxy_ftp.c
index 5d9175e..5c4d641 100644
--- a/modules/proxy/mod_proxy_ftp.c
+++ b/modules/proxy/mod_proxy_ftp.c
@@ -979,8 +979,10 @@ static int proxy_ftp_handler(request_rec *r, proxy_worker *worker,
apr_status_t rv;
conn_rec *origin, *data = NULL;
apr_status_t err = APR_SUCCESS;
+#if APR_HAS_THREADS
apr_status_t uerr = APR_SUCCESS;
- apr_bucket_brigade *bb = apr_brigade_create(p, c->bucket_alloc);
+#endif
+ apr_bucket_brigade *bb;
char *buf, *connectname;
apr_port_t connectport;
char *ftpmessage = NULL;
@@ -1120,13 +1122,15 @@ static int proxy_ftp_handler(request_rec *r, proxy_worker *worker,
if (worker->s->is_address_reusable) {
if (!worker->cp->addr) {
+#if APR_HAS_THREADS
if ((err = PROXY_THREAD_LOCK(worker->balancer)) != APR_SUCCESS) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, err, r, APLOGNO(01037) "lock");
return HTTP_INTERNAL_SERVER_ERROR;
}
+#endif
}
- connect_addr = worker->cp->addr;
- address_pool = worker->cp->pool;
+ connect_addr = AP_VOLATILIZE_T(apr_sockaddr_t *, worker->cp->addr);
+ address_pool = worker->cp->dns_pool;
}
else
address_pool = r->pool;
@@ -1139,9 +1143,11 @@ static int proxy_ftp_handler(request_rec *r, proxy_worker *worker,
address_pool);
if (worker->s->is_address_reusable && !worker->cp->addr) {
worker->cp->addr = connect_addr;
+#if APR_HAS_THREADS
if ((uerr = PROXY_THREAD_UNLOCK(worker->balancer)) != APR_SUCCESS) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, uerr, r, APLOGNO(01038) "unlock");
}
+#endif
}
/*
* get all the possible IP addresses for the destname and loop through
@@ -1212,6 +1218,7 @@ static int proxy_ftp_handler(request_rec *r, proxy_worker *worker,
* correct directory...
*/
+ bb = apr_brigade_create(p, c->bucket_alloc);
/* possible results: */
/* 120 Service ready in nnn minutes. */
diff --git a/modules/proxy/proxy_util.c b/modules/proxy/proxy_util.c
index 2bfc8f0..7714b6c 100644
--- a/modules/proxy/proxy_util.c
+++ b/modules/proxy/proxy_util.c
@@ -1167,8 +1167,10 @@ PROXY_DECLARE(char *) ap_proxy_define_balancer(apr_pool_t *p,
lbmethod = ap_lookup_provider(PROXY_LBMETHOD, "byrequests", "0");
(*balancer)->workers = apr_array_make(p, 5, sizeof(proxy_worker *));
+#if APR_HAS_THREADS
(*balancer)->gmutex = NULL;
(*balancer)->tmutex = NULL;
+#endif
(*balancer)->lbmethod = lbmethod;
if (do_malloc)
@@ -1257,7 +1259,9 @@ PROXY_DECLARE(apr_status_t) ap_proxy_share_balancer(proxy_balancer *balancer,
PROXY_DECLARE(apr_status_t) ap_proxy_initialize_balancer(proxy_balancer *balancer, server_rec *s, apr_pool_t *p)
{
+#if APR_HAS_THREADS
apr_status_t rv = APR_SUCCESS;
+#endif
ap_slotmem_provider_t *storage = balancer->storage;
apr_size_t size;
unsigned int num;
@@ -1297,6 +1301,7 @@ PROXY_DECLARE(apr_status_t) ap_proxy_initialize_balancer(proxy_balancer *balance
if (balancer->lbmethod && balancer->lbmethod->reset)
balancer->lbmethod->reset(balancer, s);
+#if APR_HAS_THREADS
if (balancer->tmutex == NULL) {
rv = apr_thread_mutex_create(&(balancer->tmutex), APR_THREAD_MUTEX_DEFAULT, p);
if (rv != APR_SUCCESS) {
@@ -1305,6 +1310,7 @@ PROXY_DECLARE(apr_status_t) ap_proxy_initialize_balancer(proxy_balancer *balance
return rv;
}
}
+#endif
return APR_SUCCESS;
}
@@ -1446,16 +1452,14 @@ static void socket_cleanup(proxy_conn_rec *conn)
static apr_status_t conn_pool_cleanup(void *theworker)
{
- proxy_worker *worker = (proxy_worker *)theworker;
- if (worker->cp->res) {
- worker->cp->pool = NULL;
- }
+ ((proxy_worker *)theworker)->cp = NULL;
return APR_SUCCESS;
}
static void init_conn_pool(apr_pool_t *p, proxy_worker *worker)
{
apr_pool_t *pool;
+ apr_pool_t *dns_pool;
proxy_conn_pool *cp;
/*
@@ -1466,12 +1470,21 @@ static void init_conn_pool(apr_pool_t *p, proxy_worker *worker)
*/
apr_pool_create(&pool, p);
apr_pool_tag(pool, "proxy_worker_cp");
+ /*
+ * Create a subpool of the connection pool for worker
+ * scoped DNS resolutions. This is needed to avoid race
+ * conditions in using the connection pool by multiple
+ * threads during ramp up.
+ */
+ apr_pool_create(&dns_pool, pool);
+ apr_pool_tag(dns_pool, "proxy_worker_dns");
/*
* Alloc from the same pool as worker.
* proxy_conn_pool is permanently attached to the worker.
*/
cp = (proxy_conn_pool *)apr_pcalloc(p, sizeof(proxy_conn_pool));
cp->pool = pool;
+ cp->dns_pool = dns_pool;
worker->cp = cp;
}
@@ -1487,14 +1500,6 @@ static apr_status_t connection_cleanup(void *theconn)
proxy_conn_rec *conn = (proxy_conn_rec *)theconn;
proxy_worker *worker = conn->worker;
- /*
- * If the connection pool is NULL the worker
- * cleanup has been run. Just return.
- */
- if (!worker->cp->pool) {
- return APR_SUCCESS;
- }
-
if (conn->r) {
apr_pool_destroy(conn->r->pool);
conn->r = NULL;
@@ -1616,7 +1621,7 @@ static apr_status_t connection_destructor(void *resource, void *params,
proxy_worker *worker = params;
/* Destroy the pool only if not called from reslist_destroy */
- if (worker->cp->pool) {
+ if (worker->cp) {
proxy_conn_rec *conn = resource;
apr_pool_destroy(conn->pool);
}
@@ -1972,67 +1977,73 @@ PROXY_DECLARE(apr_status_t) ap_proxy_initialize_worker(proxy_worker *worker, ser
ap_proxy_worker_name(p, worker));
}
else {
- ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00927)
- "initializing worker %s local",
- ap_proxy_worker_name(p, worker));
apr_global_mutex_lock(proxy_mutex);
- /* Now init local worker data */
- if (worker->tmutex == NULL) {
- rv = apr_thread_mutex_create(&(worker->tmutex), APR_THREAD_MUTEX_DEFAULT, p);
- if (rv != APR_SUCCESS) {
- ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(00928)
- "can not create worker thread mutex");
+ /* Check again after we got the lock if we are still uninitialized */
+ if (!(AP_VOLATILIZE_T(unsigned int, worker->local_status) & PROXY_WORKER_INITIALIZED)) {
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00927)
+ "initializing worker %s local",
+ ap_proxy_worker_name(p, worker));
+ /* Now init local worker data */
+#if APR_HAS_THREADS
+ if (worker->tmutex == NULL) {
+ rv = apr_thread_mutex_create(&(worker->tmutex), APR_THREAD_MUTEX_DEFAULT, p);
+ if (rv != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO(00928)
+ "can not create worker thread mutex");
+ apr_global_mutex_unlock(proxy_mutex);
+ return rv;
+ }
+ }
+#endif
+ if (worker->cp == NULL)
+ init_conn_pool(p, worker);
+ if (worker->cp == NULL) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(00929)
+ "can not create connection pool");
apr_global_mutex_unlock(proxy_mutex);
- return rv;
+ return APR_EGENERAL;
}
- }
- if (worker->cp == NULL)
- init_conn_pool(p, worker);
- if (worker->cp == NULL) {
- ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(00929)
- "can not create connection pool");
- apr_global_mutex_unlock(proxy_mutex);
- return APR_EGENERAL;
- }
- if (worker->s->hmax) {
- rv = apr_reslist_create(&(worker->cp->res),
- worker->s->min, worker->s->smax,
- worker->s->hmax, worker->s->ttl,
- connection_constructor, connection_destructor,
- worker, worker->cp->pool);
+ if (worker->s->hmax) {
+ rv = apr_reslist_create(&(worker->cp->res),
+ worker->s->min, worker->s->smax,
+ worker->s->hmax, worker->s->ttl,
+ connection_constructor, connection_destructor,
+ worker, worker->cp->pool);
- apr_pool_cleanup_register(worker->cp->pool, (void *)worker,
- conn_pool_cleanup,
- apr_pool_cleanup_null);
+ apr_pool_pre_cleanup_register(worker->cp->pool, worker,
+ conn_pool_cleanup);
- ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00930)
- "initialized pool in child %" APR_PID_T_FMT " for (%s) min=%d max=%d smax=%d",
- getpid(), worker->s->hostname_ex, worker->s->min,
- worker->s->hmax, worker->s->smax);
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00930)
+ "initialized pool in child %" APR_PID_T_FMT " for (%s) min=%d max=%d smax=%d",
+ getpid(), worker->s->hostname_ex, worker->s->min,
+ worker->s->hmax, worker->s->smax);
- /* Set the acquire timeout */
- if (rv == APR_SUCCESS && worker->s->acquire_set) {
- apr_reslist_timeout_set(worker->cp->res, worker->s->acquire);
- }
+ /* Set the acquire timeout */
+ if (rv == APR_SUCCESS && worker->s->acquire_set) {
+ apr_reslist_timeout_set(worker->cp->res, worker->s->acquire);
+ }
- }
- else {
- void *conn;
+ }
+ else {
+ void *conn;
- rv = connection_constructor(&conn, worker, worker->cp->pool);
- worker->cp->conn = conn;
+ rv = connection_constructor(&conn, worker, worker->cp->pool);
+ worker->cp->conn = conn;
- ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00931)
- "initialized single connection worker in child %" APR_PID_T_FMT " for (%s)",
- getpid(), worker->s->hostname_ex);
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, s, APLOGNO(00931)
+ "initialized single connection worker in child %" APR_PID_T_FMT " for (%s)",
+ getpid(), worker->s->hostname_ex);
+ }
+ if (rv == APR_SUCCESS) {
+ worker->local_status |= (PROXY_WORKER_INITIALIZED);
+ }
}
apr_global_mutex_unlock(proxy_mutex);
}
if (rv == APR_SUCCESS) {
worker->s->status |= (PROXY_WORKER_INITIALIZED);
- worker->local_status |= (PROXY_WORKER_INITIALIZED);
}
return rv;
}
@@ -2292,13 +2303,13 @@ PROXY_DECLARE(int) ap_proxy_acquire_connection(const char *proxy_function,
else {
/* create the new connection if the previous was destroyed */
if (!worker->cp->conn) {
- connection_constructor((void **)conn, worker, worker->cp->pool);
+ rv = connection_constructor((void **)conn, worker, worker->cp->pool);
}
else {
*conn = worker->cp->conn;
worker->cp->conn = NULL;
+ rv = APR_SUCCESS;
}
- rv = APR_SUCCESS;
}
if (rv != APR_SUCCESS) {
@@ -2344,7 +2355,9 @@ ap_proxy_determine_connection(apr_pool_t *p, request_rec *r,
{
int server_port;
apr_status_t err = APR_SUCCESS;
+#if APR_HAS_THREADS
apr_status_t uerr = APR_SUCCESS;
+#endif
const char *uds_path;
/*
@@ -2481,25 +2494,39 @@ ap_proxy_determine_connection(apr_pool_t *p, request_rec *r,
* we can reuse the address.
*/
if (!worker->cp->addr) {
+#if APR_HAS_THREADS
if ((err = PROXY_THREAD_LOCK(worker)) != APR_SUCCESS) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, err, r, APLOGNO(00945) "lock");
return HTTP_INTERNAL_SERVER_ERROR;
}
+#endif
/*
- * Worker can have the single constant backend address.
- * The single DNS lookup is used once per worker.
- * If dynamic change is needed then set the addr to NULL
- * inside dynamic config to force the lookup.
+ * Recheck addr after we got the lock. This may have changed
+ * while waiting for the lock.
*/
- err = apr_sockaddr_info_get(&(worker->cp->addr),
- conn->hostname, APR_UNSPEC,
- conn->port, 0,
- worker->cp->pool);
+ if (!AP_VOLATILIZE_T(apr_sockaddr_t *, worker->cp->addr)) {
+
+ apr_sockaddr_t *addr;
+
+ /*
+ * Worker can have the single constant backend address.
+ * The single DNS lookup is used once per worker.
+ * If dynamic change is needed then set the addr to NULL
+ * inside dynamic config to force the lookup.
+ */
+ err = apr_sockaddr_info_get(&addr,
+ conn->hostname, APR_UNSPEC,
+ conn->port, 0,
+ worker->cp->dns_pool);
+ worker->cp->addr = addr;
+ }
conn->addr = worker->cp->addr;
+#if APR_HAS_THREADS
if ((uerr = PROXY_THREAD_UNLOCK(worker)) != APR_SUCCESS) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, uerr, r, APLOGNO(00946) "unlock");
}
+#endif
}
else {
conn->addr = worker->cp->addr;
@@ -3422,7 +3449,9 @@ PROXY_DECLARE(apr_status_t) ap_proxy_sync_balancer(proxy_balancer *b, server_rec
(*runtime)->cp = NULL;
(*runtime)->balancer = b;
(*runtime)->s = shm;
+#if APR_HAS_THREADS
(*runtime)->tmutex = NULL;
+#endif
rv = ap_proxy_initialize_worker(*runtime, s, conf->pool);
if (rv != APR_SUCCESS) {
ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s, APLOGNO(00966) "Cannot init worker");

View File

@ -1,265 +0,0 @@
diff --git a/docs/manual/mod/mod_ssl.html.en b/docs/manual/mod/mod_ssl.html.en
index b543150..ab72d4f 100644
--- a/docs/manual/mod/mod_ssl.html.en
+++ b/docs/manual/mod/mod_ssl.html.en
@@ -1524,6 +1524,32 @@ The available (case-insensitive) <em>protocol</em>s are:</p>
<div class="example"><h3>Example</h3><pre class="prettyprint lang-config">SSLProtocol TLSv1</pre>
</div>
+<div class="note">
+<h3><code class="directive">SSLProtocol</code> for name-based virtual hosts</h3>
+<p>
+Before OpenSSL 1.1.1, even though the Server Name Indication (SNI) allowed to
+determine the targeted virtual host early in the TLS handshake, it was not
+possible to switch the TLS protocol version of the connection at this point,
+and thus the <code class="directive">SSLProtocol</code> negotiated was always based off
+the one of the <em>base virtual host</em> (first virtual host declared on the
+listening <code>IP:port</code> of the connection).
+</p>
+<p>
+Beginning with Apache HTTP server version 2.4.42, when built/linked against
+OpenSSL 1.1.1 or later, and when the SNI is provided by the client in the TLS
+handshake, the <code class="directive">SSLProtocol</code> of each (name-based) virtual
+host can and will be honored.
+</p>
+<p>
+For compatibility with previous versions, if no
+<code class="directive">SSLProtocol</code> is configured in a name-based virtual host,
+the one from the base virtual host still applies, <strong>unless</strong>
+<code class="directive">SSLProtocol</code> is configured globally in which case the
+global value applies (this latter exception is more sensible than compatible,
+though).
+</p>
+</div>
+
</div>
<div class="top"><a href="#page-header"><img alt="top" src="../images/up.gif" /></a></div>
<div class="directive-section"><h2><a name="SSLProxyCACertificateFile" id="SSLProxyCACertificateFile">SSLProxyCACertificateFile</a> <a name="sslproxycacertificatefile" id="sslproxycacertificatefile">Directive</a></h2>
diff --git a/modules/ssl/ssl_engine_config.c b/modules/ssl/ssl_engine_config.c
index 0c4bf1f..ca5f702 100644
--- a/modules/ssl/ssl_engine_config.c
+++ b/modules/ssl/ssl_engine_config.c
@@ -269,6 +269,7 @@ static void modssl_ctx_cfg_merge(apr_pool_t *p,
mrg->protocol_set = 1;
}
else {
+ mrg->protocol_set = base->protocol_set;
mrg->protocol = base->protocol;
}
diff --git a/modules/ssl/ssl_engine_init.c b/modules/ssl/ssl_engine_init.c
index 31062bc..70d151e 100644
--- a/modules/ssl/ssl_engine_init.c
+++ b/modules/ssl/ssl_engine_init.c
@@ -520,7 +520,9 @@ static apr_status_t ssl_init_ctx_tls_extensions(server_rec *s,
"Configuring TLS extension handling");
/*
- * Server name indication (SNI)
+ * The Server Name Indication (SNI) provided by the ClientHello can be
+ * used to select the right (name-based-)vhost and its SSL configuration
+ * before the handshake takes place.
*/
if (!SSL_CTX_set_tlsext_servername_callback(mctx->ssl_ctx,
ssl_callback_ServerNameIndication) ||
@@ -532,6 +534,16 @@ static apr_status_t ssl_init_ctx_tls_extensions(server_rec *s,
return ssl_die(s);
}
+#if OPENSSL_VERSION_NUMBER >= 0x10101000L && !defined(LIBRESSL_VERSION_NUMBER)
+ /*
+ * The ClientHello callback also allows to retrieve the SNI, but since it
+ * runs at the earliest possible connection stage we can even set the TLS
+ * protocol version(s) according to the selected (name-based-)vhost, which
+ * is not possible at the SNI callback stage (due to OpenSSL internals).
+ */
+ SSL_CTX_set_client_hello_cb(mctx->ssl_ctx, ssl_callback_ClientHello, NULL);
+#endif
+
#ifdef HAVE_OCSP_STAPLING
/*
* OCSP Stapling support, status_request extension
@@ -708,7 +720,7 @@ static apr_status_t ssl_init_ctx_protocol(server_rec *s,
#else /* #if OPENSSL_VERSION_NUMBER < 0x10100000L */
/* We first determine the maximum protocol version we should provide */
#if SSL_HAVE_PROTOCOL_TLSV1_3
- if (SSL_HAVE_PROTOCOL_TLSV1_3 && (protocol & SSL_PROTOCOL_TLSV1_3)) {
+ if (protocol & SSL_PROTOCOL_TLSV1_3) {
prot = TLS1_3_VERSION;
} else
#endif
diff --git a/modules/ssl/ssl_engine_kernel.c b/modules/ssl/ssl_engine_kernel.c
index 8b44674..7313a55 100644
--- a/modules/ssl/ssl_engine_kernel.c
+++ b/modules/ssl/ssl_engine_kernel.c
@@ -2357,28 +2357,31 @@ static apr_status_t set_challenge_creds(conn_rec *c, const char *servername,
* This function sets the virtual host from an extended
* client hello with a server name indication extension ("SNI", cf. RFC 6066).
*/
-static apr_status_t init_vhost(conn_rec *c, SSL *ssl)
+static apr_status_t init_vhost(conn_rec *c, SSL *ssl, const char *servername)
{
- const char *servername;
X509 *cert;
EVP_PKEY *key;
if (c) {
SSLConnRec *sslcon = myConnConfig(c);
-
- if (sslcon->server != c->base_server) {
- /* already found the vhost */
- return APR_SUCCESS;
+
+ if (sslcon->vhost_found) {
+ /* already found the vhost? */
+ return sslcon->vhost_found > 0 ? APR_SUCCESS : APR_NOTFOUND;
}
+ sslcon->vhost_found = -1;
- servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
+ if (!servername) {
+ servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
+ }
if (servername) {
if (ap_vhost_iterate_given_conn(c, ssl_find_vhost,
(void *)servername)) {
ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, APLOGNO(02043)
"SSL virtual host for servername %s found",
servername);
-
+
+ sslcon->vhost_found = +1;
return APR_SUCCESS;
}
else if (ssl_is_challenge(c, servername, &cert, &key)) {
@@ -2428,11 +2431,72 @@ static apr_status_t init_vhost(conn_rec *c, SSL *ssl)
int ssl_callback_ServerNameIndication(SSL *ssl, int *al, modssl_ctx_t *mctx)
{
conn_rec *c = (conn_rec *)SSL_get_app_data(ssl);
- apr_status_t status = init_vhost(c, ssl);
+ apr_status_t status = init_vhost(c, ssl, NULL);
return (status == APR_SUCCESS)? SSL_TLSEXT_ERR_OK : SSL_TLSEXT_ERR_NOACK;
}
+#if OPENSSL_VERSION_NUMBER >= 0x10101000L && !defined(LIBRESSL_VERSION_NUMBER)
+/*
+ * This callback function is called when the ClientHello is received.
+ */
+int ssl_callback_ClientHello(SSL *ssl, int *al, void *arg)
+{
+ char *servername = NULL;
+ conn_rec *c = (conn_rec *)SSL_get_app_data(ssl);
+ const unsigned char *pos;
+ size_t len, remaining;
+ (void)arg;
+
+ /* We can't use SSL_get_servername() at this earliest OpenSSL connection
+ * stage, and there is no SSL_client_hello_get0_servername() provided as
+ * of OpenSSL 1.1.1. So the code below, that extracts the SNI from the
+ * ClientHello's TLS extensions, is taken from some test code in OpenSSL,
+ * i.e. client_hello_select_server_ctx() in "test/handshake_helper.c".
+ */
+
+ /*
+ * The server_name extension was given too much extensibility when it
+ * was written, so parsing the normal case is a bit complex.
+ */
+ if (!SSL_client_hello_get0_ext(ssl, TLSEXT_TYPE_server_name, &pos,
+ &remaining)
+ || remaining <= 2)
+ goto give_up;
+
+ /* Extract the length of the supplied list of names. */
+ len = (*(pos++) << 8);
+ len += *(pos++);
+ if (len + 2 != remaining)
+ goto give_up;
+ remaining = len;
+
+ /*
+ * The list in practice only has a single element, so we only consider
+ * the first one.
+ */
+ if (remaining <= 3 || *pos++ != TLSEXT_NAMETYPE_host_name)
+ goto give_up;
+ remaining--;
+
+ /* Now we can finally pull out the byte array with the actual hostname. */
+ len = (*(pos++) << 8);
+ len += *(pos++);
+ if (len + 2 != remaining)
+ goto give_up;
+
+ /* Use the SNI to switch to the relevant vhost, should it differ from
+ * c->base_server.
+ */
+ servername = apr_pstrmemdup(c->pool, (const char *)pos, len);
+
+give_up:
+ init_vhost(c, ssl, servername);
+ return SSL_CLIENT_HELLO_SUCCESS;
+}
+#endif /* OPENSSL_VERSION_NUMBER < 0x10101000L */
+
+
/*
* Find a (name-based) SSL virtual host where either the ServerName
* or one of the ServerAliases matches the supplied name (to be used
@@ -2452,12 +2516,25 @@ static int ssl_find_vhost(void *servername, conn_rec *c, server_rec *s)
if (found && (ssl = sslcon->ssl) &&
(sc = mySrvConfig(s))) {
SSL_CTX *ctx = SSL_set_SSL_CTX(ssl, sc->server->ssl_ctx);
+
/*
* SSL_set_SSL_CTX() only deals with the server cert,
* so we need to duplicate a few additional settings
* from the ctx by hand
*/
SSL_set_options(ssl, SSL_CTX_get_options(ctx));
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L \
+ && (!defined(LIBRESSL_VERSION_NUMBER) \
+ || LIBRESSL_VERSION_NUMBER >= 0x20800000L)
+ /*
+ * Don't switch the protocol if none is configured for this vhost,
+ * the default in this case is still the base server's SSLProtocol.
+ */
+ if (myCtxConfig(sslcon, sc)->protocol_set) {
+ SSL_set_min_proto_version(ssl, SSL_CTX_get_min_proto_version(ctx));
+ SSL_set_max_proto_version(ssl, SSL_CTX_get_max_proto_version(ctx));
+ }
+#endif
if ((SSL_get_verify_mode(ssl) == SSL_VERIFY_NONE) ||
(SSL_num_renegotiations(ssl) == 0)) {
/*
@@ -2654,7 +2731,7 @@ int ssl_callback_alpn_select(SSL *ssl,
* they callback the SNI. We need to make sure that we know which vhost
* we are dealing with so we respect the correct protocols.
*/
- init_vhost(c, ssl);
+ init_vhost(c, ssl, NULL);
proposed = ap_select_protocol(c, NULL, sslconn->server, client_protos);
if (!proposed) {
diff --git a/modules/ssl/ssl_private.h b/modules/ssl/ssl_private.h
index 8055200..f8a1db7 100644
--- a/modules/ssl/ssl_private.h
+++ b/modules/ssl/ssl_private.h
@@ -563,6 +563,7 @@ typedef struct {
const char *cipher_suite; /* cipher suite used in last reneg */
int service_unavailable; /* thouugh we negotiate SSL, no requests will be served */
+ int vhost_found; /* whether we found vhost from SNI already */
} SSLConnRec;
/* BIG FAT WARNING: SSLModConfigRec has unusual memory lifetime: it is
@@ -946,6 +947,9 @@ void ssl_callback_Info(const SSL *, int, int);
#ifdef HAVE_TLSEXT
int ssl_callback_ServerNameIndication(SSL *, int *, modssl_ctx_t *);
#endif
+#if OPENSSL_VERSION_NUMBER >= 0x10101000L && !defined(LIBRESSL_VERSION_NUMBER)
+int ssl_callback_ClientHello(SSL *, int *, void *);
+#endif
#ifdef HAVE_TLS_SESSION_TICKETS
int ssl_callback_SessionTicket(SSL *, unsigned char *, unsigned char *,
EVP_CIPHER_CTX *, HMAC_CTX *, int);

View File

@ -1,47 +0,0 @@
From ced24e032ebe185a2d885fa309d6de47668ba31e Mon Sep 17 00:00:00 2001
From: Yann Ylavic <ylavic@apache.org>
Date: Fri, 26 Jun 2020 10:21:19 +0000
Subject: [PATCH] Merge r1878280 from trunk:
mod_proxy_http: don't strip EOS when spooling request body to file.
To prevent stream_reqbody() from sending the FILE and FLUSH bucket in separate
brigades, and thus apr_file_setaside() to trigger if network congestion occurs
with the backend, restore the EOS in spool_reqbody_cl() which was stripped
when spooling the request body to a file.
Until APR r1878279 is released (and installed by users), apr_file_setaside()
on a temporary file (mktemp) will simply drop the file cleanup, leaking the
fd and inode..
This fixes BZ 64452.
Submitted by: ylavic
Reviewed by: ylavic, jorton, rpluem
git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x@1879226 13f79535-47bb-0310-9956-ffa450edef68
---
modules/proxy/mod_proxy_http.c | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/modules/proxy/mod_proxy_http.c b/modules/proxy/mod_proxy_http.c
index e94bf26d412..7a0f063f705 100644
--- a/modules/proxy/mod_proxy_http.c
+++ b/modules/proxy/mod_proxy_http.c
@@ -548,6 +548,14 @@ static int spool_reqbody_cl(proxy_http_req_t *req, apr_off_t *bytes_spooled)
e = apr_bucket_immortal_create(CRLF_ASCII, 2, bucket_alloc);
APR_BRIGADE_INSERT_TAIL(input_brigade, e);
}
+ if (tmpfile) {
+ /* We dropped metadata buckets when spooling to tmpfile,
+ * terminate with EOS for stream_reqbody() to flush the
+ * whole in one go.
+ */
+ e = apr_bucket_eos_create(bucket_alloc);
+ APR_BRIGADE_INSERT_TAIL(input_brigade, e);
+ }
return OK;
}

View File

@ -1,13 +0,0 @@
diff --git a/modules/ssl/ssl_engine_kernel.c b/modules/ssl/ssl_engine_kernel.c
index c217a0d..d07d0ba 100644
--- a/modules/ssl/ssl_engine_kernel.c
+++ b/modules/ssl/ssl_engine_kernel.c
@@ -1947,7 +1947,7 @@ static void modssl_proxy_info_log(conn_rec *c,
*cert = info->x509; \
CRYPTO_add(&(*cert)->references, +1, CRYPTO_LOCK_X509); \
*pkey = info->x_pkey->dec_pkey; \
- CRYPTO_add(&(*pkey)->references, +1, CRYPTO_LOCK_X509_PKEY)
+ CRYPTO_add(&(*pkey)->references, +1, CRYPTO_LOCK_EVP_PKEY)
#else
#define modssl_set_cert_info(info, cert, pkey) \
*cert = info->x509; \

View File

@ -1,16 +0,0 @@
--- a/modules/generators/cgi_common.h 2020/09/04 13:16:53 1881458
+++ b/modules/generators/cgi_common.h 2020/09/04 13:56:25 1881459
@@ -259,6 +259,13 @@
if ((ret = ap_scan_script_header_err_brigade_ex(r, bb, sbuf,
APLOG_MODULE_INDEX)))
{
+ /* In the case of a timeout reading script output, clear
+ * the brigade to avoid a second attempt to read the
+ * output. */
+ if (ret == HTTP_GATEWAY_TIME_OUT) {
+ apr_brigade_cleanup(bb);
+ }
+
ret = log_script(r, conf, ret, logdata, sbuf, bb, script_err);
/*

View File

@ -1,849 +0,0 @@
diff --git a/modules/proxy/mod_proxy.h b/modules/proxy/mod_proxy.h
index fbbd508..8fcd26d 100644
--- a/modules/proxy/mod_proxy.h
+++ b/modules/proxy/mod_proxy.h
@@ -1168,6 +1168,55 @@ PROXY_DECLARE(int) ap_proxy_create_hdrbrgd(apr_pool_t *p,
char **old_cl_val,
char **old_te_val);
+/**
+ * Prefetch the client request body (in memory), up to a limit.
+ * Read what's in the client pipe. If nonblocking is set and read is EAGAIN,
+ * pass a FLUSH bucket to the backend and read again in blocking mode.
+ * @param r client request
+ * @param backend backend connection
+ * @param input_brigade input brigade to use/fill
+ * @param block blocking or non-blocking mode
+ * @param bytes_read number of bytes read
+ * @param max_read maximum number of bytes to read
+ * @return OK or HTTP_* error code
+ * @note max_read is rounded up to APR_BUCKET_BUFF_SIZE
+ */
+PROXY_DECLARE(int) ap_proxy_prefetch_input(request_rec *r,
+ proxy_conn_rec *backend,
+ apr_bucket_brigade *input_brigade,
+ apr_read_type_e block,
+ apr_off_t *bytes_read,
+ apr_off_t max_read);
+
+/**
+ * Spool the client request body to memory, or disk above given limit.
+ * @param r client request
+ * @param backend backend connection
+ * @param input_brigade input brigade to use/fill
+ * @param bytes_spooled number of bytes spooled
+ * @param max_mem_spool maximum number of in-memory bytes
+ * @return OK or HTTP_* error code
+ */
+PROXY_DECLARE(int) ap_proxy_spool_input(request_rec *r,
+ proxy_conn_rec *backend,
+ apr_bucket_brigade *input_brigade,
+ apr_off_t *bytes_spooled,
+ apr_off_t max_mem_spool);
+
+/**
+ * Read what's in the client pipe. If the read would block (EAGAIN),
+ * pass a FLUSH bucket to the backend and read again in blocking mode.
+ * @param r client request
+ * @param backend backend connection
+ * @param input_brigade brigade to use/fill
+ * @param max_read maximum number of bytes to read
+ * @return OK or HTTP_* error code
+ */
+PROXY_DECLARE(int) ap_proxy_read_input(request_rec *r,
+ proxy_conn_rec *backend,
+ apr_bucket_brigade *input_brigade,
+ apr_off_t max_read);
+
/**
* @param bucket_alloc bucket allocator
* @param r request
diff --git a/modules/proxy/mod_proxy_fcgi.c b/modules/proxy/mod_proxy_fcgi.c
index 2e97408..f9cf716 100644
--- a/modules/proxy/mod_proxy_fcgi.c
+++ b/modules/proxy/mod_proxy_fcgi.c
@@ -521,7 +521,8 @@ static int handle_headers(request_rec *r, int *state,
static apr_status_t dispatch(proxy_conn_rec *conn, proxy_dir_conf *conf,
request_rec *r, apr_pool_t *setaside_pool,
apr_uint16_t request_id, const char **err,
- int *bad_request, int *has_responded)
+ int *bad_request, int *has_responded,
+ apr_bucket_brigade *input_brigade)
{
apr_bucket_brigade *ib, *ob;
int seen_end_of_headers = 0, done = 0, ignore_body = 0;
@@ -583,9 +584,26 @@ static apr_status_t dispatch(proxy_conn_rec *conn, proxy_dir_conf *conf,
int last_stdin = 0;
char *iobuf_cursor;
- rv = ap_get_brigade(r->input_filters, ib,
- AP_MODE_READBYTES, APR_BLOCK_READ,
- iobuf_size);
+ if (APR_BRIGADE_EMPTY(input_brigade)) {
+ rv = ap_get_brigade(r->input_filters, ib,
+ AP_MODE_READBYTES, APR_BLOCK_READ,
+ iobuf_size);
+ }
+ else {
+ apr_bucket *e;
+ APR_BRIGADE_CONCAT(ib, input_brigade);
+ rv = apr_brigade_partition(ib, iobuf_size, &e);
+ if (rv == APR_SUCCESS) {
+ while (e != APR_BRIGADE_SENTINEL(ib)
+ && APR_BUCKET_IS_METADATA(e)) {
+ e = APR_BUCKET_NEXT(e);
+ }
+ apr_brigade_split_ex(ib, e, input_brigade);
+ }
+ else if (rv == APR_INCOMPLETE) {
+ rv = APR_SUCCESS;
+ }
+ }
if (rv != APR_SUCCESS) {
*err = "reading input brigade";
*bad_request = 1;
@@ -924,7 +942,8 @@ static int fcgi_do_request(apr_pool_t *p, request_rec *r,
conn_rec *origin,
proxy_dir_conf *conf,
apr_uri_t *uri,
- char *url, char *server_portstr)
+ char *url, char *server_portstr,
+ apr_bucket_brigade *input_brigade)
{
/* Request IDs are arbitrary numbers that we assign to a
* single request. This would allow multiplex/pipelining of
@@ -960,7 +979,8 @@ static int fcgi_do_request(apr_pool_t *p, request_rec *r,
/* Step 3: Read records from the back end server and handle them. */
rv = dispatch(conn, conf, r, temp_pool, request_id,
- &err, &bad_request, &has_responded);
+ &err, &bad_request, &has_responded,
+ input_brigade);
if (rv != APR_SUCCESS) {
/* If the client aborted the connection during retrieval or (partially)
* sending the response, don't return a HTTP_SERVICE_UNAVAILABLE, since
@@ -996,6 +1016,8 @@ static int fcgi_do_request(apr_pool_t *p, request_rec *r,
#define FCGI_SCHEME "FCGI"
+#define MAX_MEM_SPOOL 16384
+
/*
* This handles fcgi:(dest) URLs
*/
@@ -1008,6 +1030,8 @@ static int proxy_fcgi_handler(request_rec *r, proxy_worker *worker,
char server_portstr[32];
conn_rec *origin = NULL;
proxy_conn_rec *backend = NULL;
+ apr_bucket_brigade *input_brigade;
+ apr_off_t input_bytes = 0;
apr_uri_t *uri;
proxy_dir_conf *dconf = ap_get_module_config(r->per_dir_config,
@@ -1050,6 +1074,101 @@ static int proxy_fcgi_handler(request_rec *r, proxy_worker *worker,
goto cleanup;
}
+ /* We possibly reuse input data prefetched in previous call(s), e.g. for a
+ * balancer fallback scenario.
+ */
+ apr_pool_userdata_get((void **)&input_brigade, "proxy-fcgi-input", p);
+ if (input_brigade == NULL) {
+ const char *old_te = apr_table_get(r->headers_in, "Transfer-Encoding");
+ const char *old_cl = NULL;
+ if (old_te) {
+ apr_table_unset(r->headers_in, "Content-Length");
+ }
+ else {
+ old_cl = apr_table_get(r->headers_in, "Content-Length");
+ }
+
+ input_brigade = apr_brigade_create(p, r->connection->bucket_alloc);
+ apr_pool_userdata_setn(input_brigade, "proxy-fcgi-input", NULL, p);
+
+ /* Prefetch (nonlocking) the request body so to increase the chance
+ * to get the whole (or enough) body and determine Content-Length vs
+ * chunked or spooled. By doing this before connecting or reusing the
+ * backend, we want to minimize the delay between this connection is
+ * considered alive and the first bytes sent (should the client's link
+ * be slow or some input filter retain the data). This is a best effort
+ * to prevent the backend from closing (from under us) what it thinks is
+ * an idle connection, hence to reduce to the minimum the unavoidable
+ * local is_socket_connected() vs remote keepalive race condition.
+ */
+ status = ap_proxy_prefetch_input(r, backend, input_brigade,
+ APR_NONBLOCK_READ, &input_bytes,
+ MAX_MEM_SPOOL);
+ if (status != OK) {
+ goto cleanup;
+ }
+
+ /*
+ * The request body is streamed by default, using either C-L or
+ * chunked T-E, like this:
+ *
+ * The whole body (including no body) was received on prefetch, i.e.
+ * the input brigade ends with EOS => C-L = input_bytes.
+ *
+ * C-L is known and reliable, i.e. only protocol filters in the input
+ * chain thus none should change the body => use C-L from client.
+ *
+ * The administrator has not "proxy-sendcl" which prevents T-E => use
+ * T-E and chunks.
+ *
+ * Otherwise we need to determine and set a content-length, so spool
+ * the entire request body to memory/temporary file (MAX_MEM_SPOOL),
+ * such that we finally know its length => C-L = input_bytes.
+ */
+ if (!APR_BRIGADE_EMPTY(input_brigade)
+ && APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(input_brigade))) {
+ /* The whole thing fit, so our decision is trivial, use the input
+ * bytes for the Content-Length. If we expected no body, and read
+ * no body, do not set the Content-Length.
+ */
+ if (old_cl || old_te || input_bytes) {
+ apr_table_setn(r->headers_in, "Content-Length",
+ apr_off_t_toa(p, input_bytes));
+ if (old_te) {
+ apr_table_unset(r->headers_in, "Transfer-Encoding");
+ }
+ }
+ }
+ else if (old_cl && r->input_filters == r->proto_input_filters) {
+ /* Streaming is possible by preserving the existing C-L */
+ }
+ else if (!apr_table_get(r->subprocess_env, "proxy-sendcl")) {
+ /* Streaming is possible using T-E: chunked */
+ }
+ else {
+ /* No streaming, C-L is the only option so spool to memory/file */
+ apr_bucket_brigade *tmp_bb;
+ apr_off_t remaining_bytes = 0;
+
+ AP_DEBUG_ASSERT(MAX_MEM_SPOOL >= input_bytes);
+ tmp_bb = apr_brigade_create(p, r->connection->bucket_alloc);
+ status = ap_proxy_spool_input(r, backend, tmp_bb, &remaining_bytes,
+ MAX_MEM_SPOOL - input_bytes);
+ if (status != OK) {
+ goto cleanup;
+ }
+
+ APR_BRIGADE_CONCAT(input_brigade, tmp_bb);
+ input_bytes += remaining_bytes;
+
+ apr_table_setn(r->headers_in, "Content-Length",
+ apr_off_t_toa(p, input_bytes));
+ if (old_te) {
+ apr_table_unset(r->headers_in, "Transfer-Encoding");
+ }
+ }
+ }
+
/* This scheme handler does not reuse connections by default, to
* avoid tying up a fastcgi that isn't expecting to work on
* parallel requests. But if the user went out of their way to
@@ -1074,7 +1193,7 @@ static int proxy_fcgi_handler(request_rec *r, proxy_worker *worker,
/* Step Three: Process the Request */
status = fcgi_do_request(p, r, backend, origin, dconf, uri, url,
- server_portstr);
+ server_portstr, input_brigade);
cleanup:
ap_proxy_release_connection(FCGI_SCHEME, backend, r->server);
diff --git a/modules/proxy/mod_proxy_http.c b/modules/proxy/mod_proxy_http.c
index df10997..7f67f26 100644
--- a/modules/proxy/mod_proxy_http.c
+++ b/modules/proxy/mod_proxy_http.c
@@ -266,50 +266,6 @@ typedef struct {
prefetch_nonblocking:1;
} proxy_http_req_t;
-/* Read what's in the client pipe. If nonblocking is set and read is EAGAIN,
- * pass a FLUSH bucket to the backend and read again in blocking mode.
- */
-static int stream_reqbody_read(proxy_http_req_t *req, apr_bucket_brigade *bb,
- int nonblocking)
-{
- request_rec *r = req->r;
- proxy_conn_rec *p_conn = req->backend;
- apr_bucket_alloc_t *bucket_alloc = req->bucket_alloc;
- apr_read_type_e block = nonblocking ? APR_NONBLOCK_READ : APR_BLOCK_READ;
- apr_status_t status;
- int rv;
-
- for (;;) {
- status = ap_get_brigade(r->input_filters, bb, AP_MODE_READBYTES,
- block, HUGE_STRING_LEN);
- if (block == APR_BLOCK_READ
- || (!APR_STATUS_IS_EAGAIN(status)
- && (status != APR_SUCCESS || !APR_BRIGADE_EMPTY(bb)))) {
- break;
- }
-
- /* Flush and retry (blocking) */
- apr_brigade_cleanup(bb);
- rv = ap_proxy_pass_brigade(bucket_alloc, r, p_conn, req->origin, bb, 1);
- if (rv != OK) {
- return rv;
- }
- block = APR_BLOCK_READ;
- }
-
- if (status != APR_SUCCESS) {
- conn_rec *c = r->connection;
- ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, APLOGNO(02608)
- "read request body failed to %pI (%s)"
- " from %s (%s)", p_conn->addr,
- p_conn->hostname ? p_conn->hostname: "",
- c->client_ip, c->remote_host ? c->remote_host: "");
- return ap_map_http_request_error(status, HTTP_BAD_REQUEST);
- }
-
- return OK;
-}
-
static int stream_reqbody(proxy_http_req_t *req)
{
request_rec *r = req->r;
@@ -328,7 +284,8 @@ static int stream_reqbody(proxy_http_req_t *req)
do {
if (APR_BRIGADE_EMPTY(input_brigade)
&& APR_BRIGADE_EMPTY(header_brigade)) {
- rv = stream_reqbody_read(req, input_brigade, 1);
+ rv = ap_proxy_read_input(r, p_conn, input_brigade,
+ HUGE_STRING_LEN);
if (rv != OK) {
return rv;
}
@@ -409,7 +366,7 @@ static int stream_reqbody(proxy_http_req_t *req)
*/
APR_BRIGADE_PREPEND(input_brigade, header_brigade);
- /* Flush here on EOS because we won't stream_reqbody_read() again */
+ /* Flush here on EOS because we won't ap_proxy_read_input() again. */
rv = ap_proxy_pass_brigade(bucket_alloc, r, p_conn, origin,
input_brigade, seen_eos);
if (rv != OK) {
@@ -427,137 +384,6 @@ static int stream_reqbody(proxy_http_req_t *req)
return OK;
}
-static int spool_reqbody_cl(proxy_http_req_t *req, apr_off_t *bytes_spooled)
-{
- apr_pool_t *p = req->p;
- request_rec *r = req->r;
- int seen_eos = 0, rv = OK;
- apr_status_t status = APR_SUCCESS;
- apr_bucket_alloc_t *bucket_alloc = req->bucket_alloc;
- apr_bucket_brigade *input_brigade = req->input_brigade;
- apr_bucket_brigade *body_brigade;
- apr_bucket *e;
- apr_off_t bytes, fsize = 0;
- apr_file_t *tmpfile = NULL;
- apr_off_t limit;
-
- body_brigade = apr_brigade_create(p, bucket_alloc);
- *bytes_spooled = 0;
-
- limit = ap_get_limit_req_body(r);
-
- do {
- if (APR_BRIGADE_EMPTY(input_brigade)) {
- rv = stream_reqbody_read(req, input_brigade, 0);
- if (rv != OK) {
- return rv;
- }
- }
-
- /* If this brigade contains EOS, either stop or remove it. */
- if (APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(input_brigade))) {
- seen_eos = 1;
- }
-
- apr_brigade_length(input_brigade, 1, &bytes);
-
- if (*bytes_spooled + bytes > MAX_MEM_SPOOL) {
- /*
- * LimitRequestBody does not affect Proxy requests (Should it?).
- * Let it take effect if we decide to store the body in a
- * temporary file on disk.
- */
- if (limit && (*bytes_spooled + bytes > limit)) {
- ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01088)
- "Request body is larger than the configured "
- "limit of %" APR_OFF_T_FMT, limit);
- return HTTP_REQUEST_ENTITY_TOO_LARGE;
- }
- /* can't spool any more in memory; write latest brigade to disk */
- if (tmpfile == NULL) {
- const char *temp_dir;
- char *template;
-
- status = apr_temp_dir_get(&temp_dir, p);
- if (status != APR_SUCCESS) {
- ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, APLOGNO(01089)
- "search for temporary directory failed");
- return HTTP_INTERNAL_SERVER_ERROR;
- }
- apr_filepath_merge(&template, temp_dir,
- "modproxy.tmp.XXXXXX",
- APR_FILEPATH_NATIVE, p);
- status = apr_file_mktemp(&tmpfile, template, 0, p);
- if (status != APR_SUCCESS) {
- ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, APLOGNO(01090)
- "creation of temporary file in directory "
- "%s failed", temp_dir);
- return HTTP_INTERNAL_SERVER_ERROR;
- }
- }
- for (e = APR_BRIGADE_FIRST(input_brigade);
- e != APR_BRIGADE_SENTINEL(input_brigade);
- e = APR_BUCKET_NEXT(e)) {
- const char *data;
- apr_size_t bytes_read, bytes_written;
-
- apr_bucket_read(e, &data, &bytes_read, APR_BLOCK_READ);
- status = apr_file_write_full(tmpfile, data, bytes_read, &bytes_written);
- if (status != APR_SUCCESS) {
- const char *tmpfile_name;
-
- if (apr_file_name_get(&tmpfile_name, tmpfile) != APR_SUCCESS) {
- tmpfile_name = "(unknown)";
- }
- ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, APLOGNO(01091)
- "write to temporary file %s failed",
- tmpfile_name);
- return HTTP_INTERNAL_SERVER_ERROR;
- }
- AP_DEBUG_ASSERT(bytes_read == bytes_written);
- fsize += bytes_written;
- }
- apr_brigade_cleanup(input_brigade);
- }
- else {
-
- /*
- * Save input_brigade in body_brigade. (At least) in the SSL case
- * input_brigade contains transient buckets whose data would get
- * overwritten during the next call of ap_get_brigade in the loop.
- * ap_save_brigade ensures these buckets to be set aside.
- * Calling ap_save_brigade with NULL as filter is OK, because
- * body_brigade already has been created and does not need to get
- * created by ap_save_brigade.
- */
- status = ap_save_brigade(NULL, &body_brigade, &input_brigade, p);
- if (status != APR_SUCCESS) {
- return HTTP_INTERNAL_SERVER_ERROR;
- }
-
- }
-
- *bytes_spooled += bytes;
- } while (!seen_eos);
-
- APR_BRIGADE_CONCAT(input_brigade, body_brigade);
- if (tmpfile) {
- apr_brigade_insert_file(input_brigade, tmpfile, 0, fsize, p);
- }
- if (apr_table_get(r->subprocess_env, "proxy-sendextracrlf")) {
- e = apr_bucket_immortal_create(CRLF_ASCII, 2, bucket_alloc);
- APR_BRIGADE_INSERT_TAIL(input_brigade, e);
- }
- if (tmpfile) {
- /* We dropped metadata buckets when spooling to tmpfile,
- * terminate with EOS for stream_reqbody() to flush the
- * whole in one go.
- */
- e = apr_bucket_eos_create(bucket_alloc);
- APR_BRIGADE_INSERT_TAIL(input_brigade, e);
- }
- return OK;
-}
static int ap_proxy_http_prefetch(proxy_http_req_t *req,
apr_uri_t *uri, char *url)
@@ -569,14 +395,12 @@ static int ap_proxy_http_prefetch(proxy_http_req_t *req,
apr_bucket_alloc_t *bucket_alloc = req->bucket_alloc;
apr_bucket_brigade *header_brigade = req->header_brigade;
apr_bucket_brigade *input_brigade = req->input_brigade;
- apr_bucket_brigade *temp_brigade;
apr_bucket *e;
- char *buf;
apr_status_t status;
+ char *buf;
apr_off_t bytes_read = 0;
apr_off_t bytes;
int force10, rv;
- apr_read_type_e block;
conn_rec *origin = p_conn->connection;
if (apr_table_get(r->subprocess_env, "force-proxy-request-1.0")) {
@@ -641,69 +465,12 @@ static int ap_proxy_http_prefetch(proxy_http_req_t *req,
p_conn->close = 1;
}
- /* Prefetch MAX_MEM_SPOOL bytes
- *
- * This helps us avoid any election of C-L v.s. T-E
- * request bodies, since we are willing to keep in
- * memory this much data, in any case. This gives
- * us an instant C-L election if the body is of some
- * reasonable size.
- */
- temp_brigade = apr_brigade_create(p, bucket_alloc);
- block = req->prefetch_nonblocking ? APR_NONBLOCK_READ : APR_BLOCK_READ;
-
- /* Account for saved input, if any. */
- apr_brigade_length(input_brigade, 0, &bytes_read);
-
- /* Ensure we don't hit a wall where we have a buffer too small
- * for ap_get_brigade's filters to fetch us another bucket,
- * surrender once we hit 80 bytes less than MAX_MEM_SPOOL
- * (an arbitrary value).
- */
- while (bytes_read < MAX_MEM_SPOOL - 80
- && (APR_BRIGADE_EMPTY(input_brigade)
- || !APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(input_brigade)))) {
- status = ap_get_brigade(r->input_filters, temp_brigade,
- AP_MODE_READBYTES, block,
- MAX_MEM_SPOOL - bytes_read);
- /* ap_get_brigade may return success with an empty brigade
- * for a non-blocking read which would block
- */
- if (block == APR_NONBLOCK_READ
- && ((status == APR_SUCCESS && APR_BRIGADE_EMPTY(temp_brigade))
- || APR_STATUS_IS_EAGAIN(status))) {
- break;
- }
- if (status != APR_SUCCESS) {
- ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, APLOGNO(01095)
- "prefetch request body failed to %pI (%s)"
- " from %s (%s)",
- p_conn->addr, p_conn->hostname ? p_conn->hostname: "",
- c->client_ip, c->remote_host ? c->remote_host: "");
- return ap_map_http_request_error(status, HTTP_BAD_REQUEST);
- }
-
- apr_brigade_length(temp_brigade, 1, &bytes);
- bytes_read += bytes;
-
- /*
- * Save temp_brigade in input_brigade. (At least) in the SSL case
- * temp_brigade contains transient buckets whose data would get
- * overwritten during the next call of ap_get_brigade in the loop.
- * ap_save_brigade ensures these buckets to be set aside.
- * Calling ap_save_brigade with NULL as filter is OK, because
- * input_brigade already has been created and does not need to get
- * created by ap_save_brigade.
- */
- status = ap_save_brigade(NULL, &input_brigade, &temp_brigade, p);
- if (status != APR_SUCCESS) {
- ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, APLOGNO(01096)
- "processing prefetched request body failed"
- " to %pI (%s) from %s (%s)",
- p_conn->addr, p_conn->hostname ? p_conn->hostname: "",
- c->client_ip, c->remote_host ? c->remote_host: "");
- return HTTP_INTERNAL_SERVER_ERROR;
- }
+ rv = ap_proxy_prefetch_input(r, req->backend, input_brigade,
+ req->prefetch_nonblocking ? APR_NONBLOCK_READ
+ : APR_BLOCK_READ,
+ &bytes_read, MAX_MEM_SPOOL);
+ if (rv != OK) {
+ return rv;
}
/* Use chunked request body encoding or send a content-length body?
@@ -772,7 +539,7 @@ static int ap_proxy_http_prefetch(proxy_http_req_t *req,
char *endstr;
status = apr_strtoff(&req->cl_val, req->old_cl_val, &endstr, 10);
if (status != APR_SUCCESS || *endstr || req->cl_val < 0) {
- ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, APLOGNO(01085)
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01085)
"could not parse request Content-Length (%s)",
req->old_cl_val);
return HTTP_BAD_REQUEST;
@@ -812,7 +579,8 @@ static int ap_proxy_http_prefetch(proxy_http_req_t *req,
/* If we have to spool the body, do it now, before connecting or
* reusing the backend connection.
*/
- rv = spool_reqbody_cl(req, &bytes);
+ rv = ap_proxy_spool_input(r, p_conn, input_brigade,
+ &bytes, MAX_MEM_SPOOL);
if (rv != OK) {
return rv;
}
diff --git a/modules/proxy/proxy_util.c b/modules/proxy/proxy_util.c
index ab88d8f..973aa83 100644
--- a/modules/proxy/proxy_util.c
+++ b/modules/proxy/proxy_util.c
@@ -3866,6 +3866,268 @@ PROXY_DECLARE(int) ap_proxy_create_hdrbrgd(apr_pool_t *p,
return OK;
}
+PROXY_DECLARE(int) ap_proxy_prefetch_input(request_rec *r,
+ proxy_conn_rec *backend,
+ apr_bucket_brigade *input_brigade,
+ apr_read_type_e block,
+ apr_off_t *bytes_read,
+ apr_off_t max_read)
+{
+ apr_pool_t *p = r->pool;
+ conn_rec *c = r->connection;
+ apr_bucket_brigade *temp_brigade;
+ apr_status_t status;
+ apr_off_t bytes;
+
+ *bytes_read = 0;
+ if (max_read < APR_BUCKET_BUFF_SIZE) {
+ max_read = APR_BUCKET_BUFF_SIZE;
+ }
+
+ /* Prefetch max_read bytes
+ *
+ * This helps us avoid any election of C-L v.s. T-E
+ * request bodies, since we are willing to keep in
+ * memory this much data, in any case. This gives
+ * us an instant C-L election if the body is of some
+ * reasonable size.
+ */
+ temp_brigade = apr_brigade_create(p, input_brigade->bucket_alloc);
+
+ /* Account for saved input, if any. */
+ apr_brigade_length(input_brigade, 0, bytes_read);
+
+ /* Ensure we don't hit a wall where we have a buffer too small for
+ * ap_get_brigade's filters to fetch us another bucket, surrender
+ * once we hit 80 bytes (an arbitrary value) less than max_read.
+ */
+ while (*bytes_read < max_read - 80
+ && (APR_BRIGADE_EMPTY(input_brigade)
+ || !APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(input_brigade)))) {
+ status = ap_get_brigade(r->input_filters, temp_brigade,
+ AP_MODE_READBYTES, block,
+ max_read - *bytes_read);
+ /* ap_get_brigade may return success with an empty brigade
+ * for a non-blocking read which would block
+ */
+ if (block == APR_NONBLOCK_READ
+ && ((status == APR_SUCCESS && APR_BRIGADE_EMPTY(temp_brigade))
+ || APR_STATUS_IS_EAGAIN(status))) {
+ break;
+ }
+ if (status != APR_SUCCESS) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, APLOGNO(01095)
+ "prefetch request body failed to %pI (%s)"
+ " from %s (%s)", backend->addr,
+ backend->hostname ? backend->hostname : "",
+ c->client_ip, c->remote_host ? c->remote_host : "");
+ return ap_map_http_request_error(status, HTTP_BAD_REQUEST);
+ }
+
+ apr_brigade_length(temp_brigade, 1, &bytes);
+ *bytes_read += bytes;
+
+ /*
+ * Save temp_brigade in input_brigade. (At least) in the SSL case
+ * temp_brigade contains transient buckets whose data would get
+ * overwritten during the next call of ap_get_brigade in the loop.
+ * ap_save_brigade ensures these buckets to be set aside.
+ * Calling ap_save_brigade with NULL as filter is OK, because
+ * input_brigade already has been created and does not need to get
+ * created by ap_save_brigade.
+ */
+ status = ap_save_brigade(NULL, &input_brigade, &temp_brigade, p);
+ if (status != APR_SUCCESS) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, APLOGNO(01096)
+ "processing prefetched request body failed"
+ " to %pI (%s) from %s (%s)", backend->addr,
+ backend->hostname ? backend->hostname : "",
+ c->client_ip, c->remote_host ? c->remote_host : "");
+ return HTTP_INTERNAL_SERVER_ERROR;
+ }
+ }
+
+ return OK;
+}
+
+PROXY_DECLARE(int) ap_proxy_read_input(request_rec *r,
+ proxy_conn_rec *backend,
+ apr_bucket_brigade *bb,
+ apr_off_t max_read)
+{
+ apr_bucket_alloc_t *bucket_alloc = bb->bucket_alloc;
+ apr_read_type_e block = (backend->connection) ? APR_NONBLOCK_READ
+ : APR_BLOCK_READ;
+ apr_status_t status;
+ int rv;
+
+ for (;;) {
+ apr_brigade_cleanup(bb);
+ status = ap_get_brigade(r->input_filters, bb, AP_MODE_READBYTES,
+ block, max_read);
+ if (block == APR_BLOCK_READ
+ || (!(status == APR_SUCCESS && APR_BRIGADE_EMPTY(bb))
+ && !APR_STATUS_IS_EAGAIN(status))) {
+ break;
+ }
+
+ /* Flush and retry (blocking) */
+ apr_brigade_cleanup(bb);
+ rv = ap_proxy_pass_brigade(bucket_alloc, r, backend,
+ backend->connection, bb, 1);
+ if (rv != OK) {
+ return rv;
+ }
+ block = APR_BLOCK_READ;
+ }
+
+ if (status != APR_SUCCESS) {
+ conn_rec *c = r->connection;
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, APLOGNO(02608)
+ "read request body failed to %pI (%s)"
+ " from %s (%s)", backend->addr,
+ backend->hostname ? backend->hostname : "",
+ c->client_ip, c->remote_host ? c->remote_host : "");
+ return ap_map_http_request_error(status, HTTP_BAD_REQUEST);
+ }
+
+ return OK;
+}
+
+PROXY_DECLARE(int) ap_proxy_spool_input(request_rec *r,
+ proxy_conn_rec *backend,
+ apr_bucket_brigade *input_brigade,
+ apr_off_t *bytes_spooled,
+ apr_off_t max_mem_spool)
+{
+ apr_pool_t *p = r->pool;
+ int seen_eos = 0, rv = OK;
+ apr_status_t status = APR_SUCCESS;
+ apr_bucket_alloc_t *bucket_alloc = input_brigade->bucket_alloc;
+ apr_bucket_brigade *body_brigade;
+ apr_bucket *e;
+ apr_off_t bytes, fsize = 0;
+ apr_file_t *tmpfile = NULL;
+ apr_off_t limit;
+
+ *bytes_spooled = 0;
+ body_brigade = apr_brigade_create(p, bucket_alloc);
+
+ limit = ap_get_limit_req_body(r);
+
+ do {
+ if (APR_BRIGADE_EMPTY(input_brigade)) {
+ rv = ap_proxy_read_input(r, backend, input_brigade,
+ HUGE_STRING_LEN);
+ if (rv != OK) {
+ return rv;
+ }
+ }
+
+ /* If this brigade contains EOS, either stop or remove it. */
+ if (APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(input_brigade))) {
+ seen_eos = 1;
+ }
+
+ apr_brigade_length(input_brigade, 1, &bytes);
+
+ if (*bytes_spooled + bytes > max_mem_spool) {
+ /*
+ * LimitRequestBody does not affect Proxy requests (Should it?).
+ * Let it take effect if we decide to store the body in a
+ * temporary file on disk.
+ */
+ if (limit && (*bytes_spooled + bytes > limit)) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01088)
+ "Request body is larger than the configured "
+ "limit of %" APR_OFF_T_FMT, limit);
+ return HTTP_REQUEST_ENTITY_TOO_LARGE;
+ }
+ /* can't spool any more in memory; write latest brigade to disk */
+ if (tmpfile == NULL) {
+ const char *temp_dir;
+ char *template;
+
+ status = apr_temp_dir_get(&temp_dir, p);
+ if (status != APR_SUCCESS) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, APLOGNO(01089)
+ "search for temporary directory failed");
+ return HTTP_INTERNAL_SERVER_ERROR;
+ }
+ apr_filepath_merge(&template, temp_dir,
+ "modproxy.tmp.XXXXXX",
+ APR_FILEPATH_NATIVE, p);
+ status = apr_file_mktemp(&tmpfile, template, 0, p);
+ if (status != APR_SUCCESS) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, APLOGNO(01090)
+ "creation of temporary file in directory "
+ "%s failed", temp_dir);
+ return HTTP_INTERNAL_SERVER_ERROR;
+ }
+ }
+ for (e = APR_BRIGADE_FIRST(input_brigade);
+ e != APR_BRIGADE_SENTINEL(input_brigade);
+ e = APR_BUCKET_NEXT(e)) {
+ const char *data;
+ apr_size_t bytes_read, bytes_written;
+
+ apr_bucket_read(e, &data, &bytes_read, APR_BLOCK_READ);
+ status = apr_file_write_full(tmpfile, data, bytes_read, &bytes_written);
+ if (status != APR_SUCCESS) {
+ const char *tmpfile_name;
+
+ if (apr_file_name_get(&tmpfile_name, tmpfile) != APR_SUCCESS) {
+ tmpfile_name = "(unknown)";
+ }
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, APLOGNO(01091)
+ "write to temporary file %s failed",
+ tmpfile_name);
+ return HTTP_INTERNAL_SERVER_ERROR;
+ }
+ AP_DEBUG_ASSERT(bytes_read == bytes_written);
+ fsize += bytes_written;
+ }
+ apr_brigade_cleanup(input_brigade);
+ }
+ else {
+
+ /*
+ * Save input_brigade in body_brigade. (At least) in the SSL case
+ * input_brigade contains transient buckets whose data would get
+ * overwritten during the next call of ap_get_brigade in the loop.
+ * ap_save_brigade ensures these buckets to be set aside.
+ * Calling ap_save_brigade with NULL as filter is OK, because
+ * body_brigade already has been created and does not need to get
+ * created by ap_save_brigade.
+ */
+ status = ap_save_brigade(NULL, &body_brigade, &input_brigade, p);
+ if (status != APR_SUCCESS) {
+ return HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ }
+
+ *bytes_spooled += bytes;
+ } while (!seen_eos);
+
+ APR_BRIGADE_CONCAT(input_brigade, body_brigade);
+ if (tmpfile) {
+ apr_brigade_insert_file(input_brigade, tmpfile, 0, fsize, p);
+ }
+ if (apr_table_get(r->subprocess_env, "proxy-sendextracrlf")) {
+ e = apr_bucket_immortal_create(CRLF_ASCII, 2, bucket_alloc);
+ APR_BRIGADE_INSERT_TAIL(input_brigade, e);
+ }
+ if (tmpfile) {
+ /* We dropped metadata buckets when spooling to tmpfile,
+ * terminate with EOS to allow for flushing in a one go.
+ */
+ e = apr_bucket_eos_create(bucket_alloc);
+ APR_BRIGADE_INSERT_TAIL(input_brigade, e);
+ }
+ return OK;
+}
+
PROXY_DECLARE(int) ap_proxy_pass_brigade(apr_bucket_alloc_t *bucket_alloc,
request_rec *r, proxy_conn_rec *p_conn,
conn_rec *origin, apr_bucket_brigade *bb,

View File

@ -1,188 +0,0 @@
diff --git a/modules/ssl/ssl_engine_io.c b/modules/ssl/ssl_engine_io.c
index 018b667..4e3875a 100644
--- a/modules/ssl/ssl_engine_io.c
+++ b/modules/ssl/ssl_engine_io.c
@@ -1598,18 +1598,32 @@ static apr_status_t ssl_io_filter_input(ap_filter_t *f,
}
-/* ssl_io_filter_output() produces one SSL/TLS message per bucket
+/* ssl_io_filter_output() produces one SSL/TLS record per bucket
* passed down the output filter stack. This results in a high
- * overhead (network packets) for any output comprising many small
- * buckets. SSI page applied through the HTTP chunk filter, for
- * example, may produce many brigades containing small buckets -
- * [chunk-size CRLF] [chunk-data] [CRLF].
+ * overhead (more network packets & TLS processing) for any output
+ * comprising many small buckets. SSI output passed through the HTTP
+ * chunk filter, for example, may produce many brigades containing
+ * small buckets - [chunk-size CRLF] [chunk-data] [CRLF].
*
- * The coalescing filter merges many small buckets into larger buckets
- * where possible, allowing the SSL I/O output filter to handle them
- * more efficiently. */
+ * Sending HTTP response headers as a separate TLS record to the
+ * response body also reveals information to a network observer (the
+ * size of headers) which can be significant.
+ *
+ * The coalescing filter merges data buckets with the aim of producing
+ * fewer, larger TLS records - without copying/buffering all content
+ * and introducing unnecessary overhead.
+ *
+ * ### This buffering could be probably be done more comprehensively
+ * ### in ssl_io_filter_output itself.
+ *
+ * ### Another possible performance optimisation in particular for the
+ * ### [HEAP] [FILE] HTTP response case is using a brigade rather than
+ * ### a char array to buffer; using apr_brigade_write() to append
+ * ### will use already-allocated memory from the HEAP, reducing # of
+ * ### copies.
+ */
-#define COALESCE_BYTES (2048)
+#define COALESCE_BYTES (AP_IOBUFSIZE)
struct coalesce_ctx {
char buffer[COALESCE_BYTES];
@@ -1622,11 +1636,12 @@ static apr_status_t ssl_io_filter_coalesce(ap_filter_t *f,
apr_bucket *e, *upto;
apr_size_t bytes = 0;
struct coalesce_ctx *ctx = f->ctx;
+ apr_size_t buffered = ctx ? ctx->bytes : 0; /* space used on entry */
unsigned count = 0;
/* The brigade consists of zero-or-more small data buckets which
- * can be coalesced (the prefix), followed by the remainder of the
- * brigade.
+ * can be coalesced (referred to as the "prefix"), followed by the
+ * remainder of the brigade.
*
* Find the last bucket - if any - of that prefix. count gives
* the number of buckets in the prefix. The "prefix" must contain
@@ -1641,24 +1656,97 @@ static apr_status_t ssl_io_filter_coalesce(ap_filter_t *f,
e != APR_BRIGADE_SENTINEL(bb)
&& !APR_BUCKET_IS_METADATA(e)
&& e->length != (apr_size_t)-1
- && e->length < COALESCE_BYTES
- && (bytes + e->length) < COALESCE_BYTES
- && (ctx == NULL
- || bytes + ctx->bytes + e->length < COALESCE_BYTES);
+ && e->length <= COALESCE_BYTES
+ && (buffered + bytes + e->length) <= COALESCE_BYTES;
e = APR_BUCKET_NEXT(e)) {
if (e->length) count++; /* don't count zero-length buckets */
bytes += e->length;
}
+
+ /* If there is room remaining and the next bucket is a data
+ * bucket, try to include it in the prefix to coalesce. For a
+ * typical [HEAP] [FILE] HTTP response brigade, this handles
+ * merging the headers and the start of the body into a single TLS
+ * record. */
+ if (bytes + buffered > 0
+ && bytes + buffered < COALESCE_BYTES
+ && e != APR_BRIGADE_SENTINEL(bb)
+ && !APR_BUCKET_IS_METADATA(e)) {
+ apr_status_t rv = APR_SUCCESS;
+
+ /* For an indeterminate length bucket (PIPE/CGI/...), try a
+ * non-blocking read to have it morph into a HEAP. If the
+ * read fails with EAGAIN, it is harmless to try a split
+ * anyway, split is ENOTIMPL for most PIPE-like buckets. */
+ if (e->length == (apr_size_t)-1) {
+ const char *discard;
+ apr_size_t ignore;
+
+ rv = apr_bucket_read(e, &discard, &ignore, APR_NONBLOCK_READ);
+ if (rv != APR_SUCCESS && !APR_STATUS_IS_EAGAIN(rv)) {
+ ap_log_cerror(APLOG_MARK, APLOG_ERR, rv, f->c, APLOGNO(10232)
+ "coalesce failed to read from %s bucket",
+ e->type->name);
+ return AP_FILTER_ERROR;
+ }
+ }
+
+ if (rv == APR_SUCCESS) {
+ /* If the read above made the bucket morph, it may now fit
+ * entirely within the buffer. Otherwise, split it so it does
+ * fit. */
+ if (e->length > COALESCE_BYTES
+ || e->length + buffered + bytes > COALESCE_BYTES) {
+ rv = apr_bucket_split(e, COALESCE_BYTES - (buffered + bytes));
+ }
+
+ if (rv == APR_SUCCESS && e->length == 0) {
+ /* As above, don't count in the prefix if the bucket is
+ * now zero-length. */
+ }
+ else if (rv == APR_SUCCESS) {
+ ap_log_cerror(APLOG_MARK, APLOG_TRACE4, 0, f->c,
+ "coalesce: adding %" APR_SIZE_T_FMT " bytes "
+ "from split %s bucket, total %" APR_SIZE_T_FMT,
+ e->length, e->type->name, bytes + buffered);
+
+ count++;
+ bytes += e->length;
+ e = APR_BUCKET_NEXT(e);
+ }
+ else if (rv != APR_ENOTIMPL) {
+ ap_log_cerror(APLOG_MARK, APLOG_ERR, rv, f->c, APLOGNO(10233)
+ "coalesce: failed to split data bucket");
+ return AP_FILTER_ERROR;
+ }
+ }
+ }
+
+ /* The prefix is zero or more buckets. upto now points to the
+ * bucket AFTER the end of the prefix, which may be the brigade
+ * sentinel. */
upto = e;
- /* Coalesce the prefix, if:
- * a) more than one bucket is found to coalesce, or
- * b) the brigade contains only a single data bucket, or
- * c) the data bucket is not last but we have buffered data already.
+ /* Coalesce the prefix, if any of the following are true:
+ *
+ * a) the prefix is more than one bucket
+ * OR
+ * b) the prefix is the entire brigade, which is a single bucket
+ * AND the prefix length is smaller than the buffer size,
+ * OR
+ * c) the prefix is a single bucket
+ * AND there is buffered data from a previous pass.
+ *
+ * The aim with (b) is to buffer a small bucket so it can be
+ * coalesced with future invocations of this filter. e.g. three
+ * calls each with a single 100 byte HEAP bucket should get
+ * coalesced together. But an invocation with a 8192 byte HEAP
+ * should pass through untouched.
*/
if (bytes > 0
&& (count > 1
- || (upto == APR_BRIGADE_SENTINEL(bb))
+ || (upto == APR_BRIGADE_SENTINEL(bb)
+ && bytes < COALESCE_BYTES)
|| (ctx && ctx->bytes > 0))) {
/* If coalescing some bytes, ensure a context has been
* created. */
@@ -1669,7 +1757,8 @@ static apr_status_t ssl_io_filter_coalesce(ap_filter_t *f,
ap_log_cerror(APLOG_MARK, APLOG_TRACE4, 0, f->c,
"coalesce: have %" APR_SIZE_T_FMT " bytes, "
- "adding %" APR_SIZE_T_FMT " more", ctx->bytes, bytes);
+ "adding %" APR_SIZE_T_FMT " more (buckets=%u)",
+ ctx->bytes, bytes, count);
/* Iterate through the prefix segment. For non-fatal errors
* in this loop it is safe to break out and fall back to the
@@ -1684,7 +1773,8 @@ static apr_status_t ssl_io_filter_coalesce(ap_filter_t *f,
if (APR_BUCKET_IS_METADATA(e)
|| e->length == (apr_size_t)-1) {
ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, f->c, APLOGNO(02012)
- "unexpected bucket type during coalesce");
+ "unexpected %s bucket during coalesce",
+ e->type->name);
break; /* non-fatal error; break out */
}

View File

@ -1,194 +0,0 @@
diff --git a/docs/manual/mod/mod_session.html.en b/docs/manual/mod/mod_session.html.en
index 6834f8e..9f8301f 100644
--- a/docs/manual/mod/mod_session.html.en
+++ b/docs/manual/mod/mod_session.html.en
@@ -82,6 +82,7 @@
<li><img alt="" src="../images/down.gif" /> <a href="#sessionheader">SessionHeader</a></li>
<li><img alt="" src="../images/down.gif" /> <a href="#sessioninclude">SessionInclude</a></li>
<li><img alt="" src="../images/down.gif" /> <a href="#sessionmaxage">SessionMaxAge</a></li>
+<li><img alt="" src="../images/down.gif" /> <a href="#sessionexpiryupdateinterval">SessionExpiryUpdateInterval</a></li>
</ul>
<h3>Bugfix checklist</h3><ul class="seealso"><li><a href="https://www.apache.org/dist/httpd/CHANGES_2.4">httpd changelog</a></li><li><a href="https://bz.apache.org/bugzilla/buglist.cgi?bug_status=__open__&amp;list_id=144532&amp;product=Apache%20httpd-2&amp;query_format=specific&amp;order=changeddate%20DESC%2Cpriority%2Cbug_severity&amp;component=mod_session">Known issues</a></li><li><a href="https://bz.apache.org/bugzilla/enter_bug.cgi?product=Apache%20httpd-2&amp;component=mod_session">Report a bug</a></li></ul><h3>See also</h3>
<ul class="seealso">
@@ -482,6 +483,37 @@ AuthName realm
<p>Setting the maxage to zero disables session expiry.</p>
+</div>
+<div class="top"><a href="#page-header"><img alt="top" src="../images/up.gif" /></a></div>
+<div class="directive-section"><h2><a name="SessionExpiryUpdateInterval" id="sessionexpiryupdateinterval">SessionExpiryUpdateInterval</a> <a name="sessionexpiryupdateinterval" id="sessionexpiryupdateinterval">Directive</a></h2>
+<table class="directive">
+<tr><th><a href="directive-dict.html#Description">Description:</a></th><td>Define the number of seconds a session's expiry may change without the session being updated</td></tr>
+<tr><th><a href="directive-dict.html#Syntax">Syntax:</a></th><td><code>SessionExpiryUpdateInterval <var>interval</var></code></td></tr>
+<tr><th><a href="directive-dict.html#Default">Default:</a></th><td><code>SessionExpiryUpdateInterval 0 (always update)</code></td></tr>
+<tr><th><a href="directive-dict.html#Context">Context:</a></th><td>server config, virtual host, directory, .htaccess</td></tr>
+<tr><th><a href="directive-dict.html#Module">Module:</a></th><td>mod_session</td></tr>
+</table>
+ <p>The <code class="directive">SessionExpiryUpdateInterval</code> directive allows
+ sessions to avoid the cost associated with writing the session each request
+ when only the expiry time has changed. This can be used to make a website
+ more efficient or reduce load on a database when using
+ <module>mod_session_dbd</module>. The session is always written if the data
+ stored in the session has changed or the expiry has changed by more than the
+ configured interval.</p>
+
+ <p>Setting the interval to zero disables this directive, and the session
+ expiry is refreshed for each request.</p>
+
+ <p>This directive only has an effect when combined with <code class="directive"><a href="../mod/mod_session.html#sessionmaxage">SessionMaxAge</a></code> to enable session
+ expiry. Sessions without an expiry are only written when the data stored in
+ the session has changed.</p>
+
+ <div class="warning"><h3>Warning</h3>
+ <p>Because the session expiry may not be refreshed with each request, it's
+ possible for sessions to expire up to <var>interval</var> seconds early.
+ Using a small interval usually provides sufficient savings while having a
+ minimal effect on expiry resolution.</p></div>
+
</div>
</div>
<div class="bottomlang">
diff --git a/modules/session/mod_session.c b/modules/session/mod_session.c
index d517020..10e6396 100644
--- a/modules/session/mod_session.c
+++ b/modules/session/mod_session.c
@@ -177,6 +177,7 @@ static apr_status_t ap_session_save(request_rec * r, session_rec * z)
{
if (z) {
apr_time_t now = apr_time_now();
+ apr_time_t initialExpiry = z->expiry;
int rv = 0;
session_dir_conf *dconf = ap_get_module_config(r->per_dir_config,
@@ -207,6 +208,17 @@ static apr_status_t ap_session_save(request_rec * r, session_rec * z)
z->expiry = now + z->maxage * APR_USEC_PER_SEC;
}
+ /* don't save if the only change is the expiry by a small amount */
+ if (!z->dirty && dconf->expiry_update_time
+ && (z->expiry - initialExpiry < dconf->expiry_update_time)) {
+ return APR_SUCCESS;
+ }
+
+ /* also don't save sessions that didn't change at all */
+ if (!z->dirty && !z->maxage) {
+ return APR_SUCCESS;
+ }
+
/* encode the session */
rv = ap_run_session_encode(r, z);
if (OK != rv) {
@@ -553,6 +565,10 @@ static void *merge_session_dir_config(apr_pool_t * p, void *basev, void *addv)
new->env_set = add->env_set || base->env_set;
new->includes = apr_array_append(p, base->includes, add->includes);
new->excludes = apr_array_append(p, base->excludes, add->excludes);
+ new->expiry_update_time = (add->expiry_update_set == 0)
+ ? base->expiry_update_time
+ : add->expiry_update_time;
+ new->expiry_update_set = add->expiry_update_set || base->expiry_update_set;
return new;
}
@@ -622,6 +638,21 @@ static const char *add_session_exclude(cmd_parms * cmd, void *dconf, const char
return NULL;
}
+static const char *
+ set_session_expiry_update(cmd_parms * parms, void *dconf, const char *arg)
+{
+ session_dir_conf *conf = dconf;
+
+ conf->expiry_update_time = atoi(arg);
+ if (conf->expiry_update_time < 0) {
+ return "SessionExpiryUpdateInterval must be positive or nul";
+ }
+ conf->expiry_update_time = apr_time_from_sec(conf->expiry_update_time);
+ conf->expiry_update_set = 1;
+
+ return NULL;
+}
+
static const command_rec session_cmds[] =
{
@@ -637,6 +668,9 @@ static const command_rec session_cmds[] =
"URL prefixes to include in the session. Defaults to all URLs"),
AP_INIT_TAKE1("SessionExclude", add_session_exclude, NULL, RSRC_CONF|OR_AUTHCFG,
"URL prefixes to exclude from the session. Defaults to no URLs"),
+ AP_INIT_TAKE1("SessionExpiryUpdateInterval", set_session_expiry_update, NULL, RSRC_CONF|OR_AUTHCFG,
+ "time interval for which a session's expiry time may change "
+ "without having to be rewritten. Zero to disable"),
{NULL}
};
diff --git a/modules/session/mod_session.h b/modules/session/mod_session.h
index a6dd5e9..bdeb532 100644
--- a/modules/session/mod_session.h
+++ b/modules/session/mod_session.h
@@ -115,6 +115,9 @@ typedef struct {
* URLs included if empty */
apr_array_header_t *excludes; /* URL prefixes to be excluded. No
* URLs excluded if empty */
+ apr_time_t expiry_update_time; /* seconds the session expiry may change and
+ * not have to be rewritten */
+ int expiry_update_set;
} session_dir_conf;
/**
diff --git a/modules/session/mod_session_cookie.c b/modules/session/mod_session_cookie.c
index 6a02322..4aa75e4 100644
--- a/modules/session/mod_session_cookie.c
+++ b/modules/session/mod_session_cookie.c
@@ -60,9 +60,6 @@ static apr_status_t session_cookie_save(request_rec * r, session_rec * z)
session_cookie_dir_conf *conf = ap_get_module_config(r->per_dir_config,
&session_cookie_module);
- /* don't cache auth protected pages */
- apr_table_addn(r->headers_out, "Cache-Control", "no-cache");
-
/* create RFC2109 compliant cookie */
if (conf->name_set) {
if (z->encoded && z->encoded[0]) {
@@ -162,6 +159,9 @@ static apr_status_t session_cookie_load(request_rec * r, session_rec ** z)
/* put the session in the notes so we don't have to parse it again */
apr_table_setn(m->notes, note, (char *)zz);
+ /* don't cache auth protected pages */
+ apr_table_addn(r->headers_out, "Cache-Control", "no-cache, private");
+
return OK;
}
diff --git a/modules/session/mod_session_dbd.c b/modules/session/mod_session_dbd.c
index 0be7306..f683da2 100644
--- a/modules/session/mod_session_dbd.c
+++ b/modules/session/mod_session_dbd.c
@@ -245,6 +245,9 @@ static apr_status_t session_dbd_load(request_rec * r, session_rec ** z)
/* put the session in the notes so we don't have to parse it again */
apr_table_setn(m->notes, note, (char *)zz);
+ /* don't cache pages with a session */
+ apr_table_addn(r->headers_out, "Cache-Control", "no-cache, private");
+
return OK;
}
@@ -409,9 +412,6 @@ static apr_status_t session_dbd_save(request_rec * r, session_rec * z)
if (conf->name_set || conf->name2_set) {
char *oldkey = NULL, *newkey = NULL;
- /* don't cache pages with a session */
- apr_table_addn(r->headers_out, "Cache-Control", "no-cache");
-
/* if the session is new or changed, make a new session ID */
if (z->uuid) {
oldkey = apr_pcalloc(r->pool, APR_UUID_FORMATTED_LENGTH + 1);
@@ -458,7 +458,7 @@ static apr_status_t session_dbd_save(request_rec * r, session_rec * z)
else if (conf->peruser) {
/* don't cache pages with a session */
- apr_table_addn(r->headers_out, "Cache-Control", "no-cache");
+ apr_table_addn(r->headers_out, "Cache-Control", "no-cache, private");
if (r->user) {
ret = dbd_save(r, r->user, r->user, z->encoded, z->expiry);

View File

@ -1,123 +0,0 @@
diff --git a/modules/ssl/ssl_engine_config.c b/modules/ssl/ssl_engine_config.c
index 1d201d9..0c4bf1f 100644
--- a/modules/ssl/ssl_engine_config.c
+++ b/modules/ssl/ssl_engine_config.c
@@ -75,6 +75,10 @@ SSLModConfigRec *ssl_config_global_create(server_rec *s)
mc->stapling_refresh_mutex = NULL;
#endif
+#ifdef HAVE_OPENSSL_KEYLOG
+ mc->keylog_file = NULL;
+#endif
+
apr_pool_userdata_set(mc, SSL_MOD_CONFIG_KEY,
apr_pool_cleanup_null,
pool);
diff --git a/modules/ssl/ssl_engine_init.c b/modules/ssl/ssl_engine_init.c
index ef631c1..b286053 100644
--- a/modules/ssl/ssl_engine_init.c
+++ b/modules/ssl/ssl_engine_init.c
@@ -437,6 +437,28 @@ apr_status_t ssl_init_Module(apr_pool_t *p, apr_pool_t *plog,
init_bio_methods();
#endif
+#ifdef HAVE_OPENSSL_KEYLOG
+ {
+ const char *logfn = getenv("SSLKEYLOGFILE");
+
+ if (logfn) {
+ rv = apr_file_open(&mc->keylog_file, logfn,
+ APR_FOPEN_CREATE|APR_FOPEN_WRITE|APR_FOPEN_APPEND|APR_FOPEN_LARGEFILE,
+ APR_FPROT_UREAD|APR_FPROT_UWRITE,
+ mc->pPool);
+ if (rv) {
+ ap_log_error(APLOG_MARK, APLOG_NOTICE, rv, s, APLOGNO(10226)
+ "Could not open log file '%s' configured via SSLKEYLOGFILE",
+ logfn);
+ return rv;
+ }
+
+ ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, s, APLOGNO(10227)
+ "Init: Logging SSL private key material to %s", logfn);
+ }
+ }
+#endif
+
return OK;
}
@@ -796,6 +818,12 @@ static apr_status_t ssl_init_ctx_protocol(server_rec *s,
* https://github.com/openssl/openssl/issues/7178 */
SSL_CTX_clear_mode(ctx, SSL_MODE_AUTO_RETRY);
#endif
+
+#ifdef HAVE_OPENSSL_KEYLOG
+ if (mctx->sc->mc->keylog_file) {
+ SSL_CTX_set_keylog_callback(ctx, modssl_callback_keylog);
+ }
+#endif
return APR_SUCCESS;
}
diff --git a/modules/ssl/ssl_engine_kernel.c b/modules/ssl/ssl_engine_kernel.c
index 6611610..7058865 100644
--- a/modules/ssl/ssl_engine_kernel.c
+++ b/modules/ssl/ssl_engine_kernel.c
@@ -2719,3 +2719,17 @@ int ssl_callback_SRPServerParams(SSL *ssl, int *ad, void *arg)
}
#endif /* HAVE_SRP */
+
+
+#ifdef HAVE_OPENSSL_KEYLOG
+/* Callback used with SSL_CTX_set_keylog_callback. */
+void modssl_callback_keylog(const SSL *ssl, const char *line)
+{
+ conn_rec *conn = SSL_get_app_data(ssl);
+ SSLSrvConfigRec *sc = mySrvConfig(conn->base_server);
+
+ if (sc && sc->mc->keylog_file) {
+ apr_file_printf(sc->mc->keylog_file, "%s\n", line);
+ }
+}
+#endif
diff --git a/modules/ssl/ssl_private.h b/modules/ssl/ssl_private.h
index 0fac5d1..2514407 100644
--- a/modules/ssl/ssl_private.h
+++ b/modules/ssl/ssl_private.h
@@ -250,6 +250,10 @@ void free_bio_methods(void);
#endif
#endif
+#if OPENSSL_VERSION_NUMBER >= 0x10101000L && !defined(LIBRESSL_VERSION_NUMBER)
+#define HAVE_OPENSSL_KEYLOG
+#endif
+
/* mod_ssl headers */
#include "ssl_util_ssl.h"
@@ -617,6 +621,12 @@ typedef struct {
apr_global_mutex_t *stapling_cache_mutex;
apr_global_mutex_t *stapling_refresh_mutex;
#endif
+
+#ifdef HAVE_OPENSSL_KEYLOG
+ /* Used for logging if SSLKEYLOGFILE is set at startup. */
+ apr_file_t *keylog_file;
+#endif
+
} SSLModConfigRec;
/** Structure representing configured filenames for certs and keys for
@@ -970,6 +980,11 @@ int ssl_stapling_init_cert(server_rec *, apr_pool_t *, apr_pool_t *,
int ssl_callback_SRPServerParams(SSL *, int *, void *);
#endif
+#ifdef HAVE_OPENSSL_KEYLOG
+/* Callback used with SSL_CTX_set_keylog_callback. */
+void modssl_callback_keylog(const SSL *ssl, const char *line);
+#endif
+
/** I/O */
void ssl_io_filter_init(conn_rec *, request_rec *r, SSL *);
void ssl_io_filter_register(apr_pool_t *);

View File

@ -1,178 +0,0 @@
diff --git a/docs/manual/mod/mod_usertrack.html.en b/docs/manual/mod/mod_usertrack.html.en
index b212747..d2da9b9 100644
--- a/docs/manual/mod/mod_usertrack.html.en
+++ b/docs/manual/mod/mod_usertrack.html.en
@@ -47,7 +47,10 @@
<ul id="toc">
<li><img alt="" src="../images/down.gif" /> <a href="#cookiedomain">CookieDomain</a></li>
<li><img alt="" src="../images/down.gif" /> <a href="#cookieexpires">CookieExpires</a></li>
+<li><img alt="" src="../images/down.gif" /> <a href="#cookiehttponly">CookieHTTPOnly</a></li>
<li><img alt="" src="../images/down.gif" /> <a href="#cookiename">CookieName</a></li>
+<li><img alt="" src="../images/down.gif" /> <a href="#cookiesamesite">CookieSameSite</a></li>
+<li><img alt="" src="../images/down.gif" /> <a href="#cookiesecure">CookieSecure</a></li>
<li><img alt="" src="../images/down.gif" /> <a href="#cookiestyle">CookieStyle</a></li>
<li><img alt="" src="../images/down.gif" /> <a href="#cookietracking">CookieTracking</a></li>
</ul>
@@ -127,6 +130,22 @@ CustomLog "logs/clickstream.log" usertrack</pre>
<pre class="prettyprint lang-config">CookieExpires "3 weeks"</pre>
+</div>
+<div class="top"><a href="#page-header"><img alt="top" src="../images/up.gif" /></a></div>
+<div class="directive-section"><h2><a name="CookieHTTPOnly" id="CookieHTTPOnly">CookieHTTPOnly</a> <a name="cookiehttponly" id="cookiehttponly">Directive</a></h2>
+<table class="directive">
+<tbody><tr><th><a href="directive-dict.html#Description">Description:</a></th><td>Adds the 'HTTPOnly' attribute to the cookie</td></tr>
+<tr><th><a href="directive-dict.html#Syntax">Syntax:</a></th><td><code>CookieHTTPOnly on|off</code></td></tr>
+<tr><th><a href="directive-dict.html#Default">Default:</a></th><td><code>CookieHTTPOnly off</code></td></tr>
+<tr><th><a href="directive-dict.html#Context">Context:</a></th><td>server config, virtual host, directory, .htaccess</td></tr>
+<tr><th><a href="directive-dict.html#Override">Override:</a></th><td>FileInfo</td></tr>
+<tr><th><a href="directive-dict.html#Status">Status:</a></th><td>Extension</td></tr>
+<tr><th><a href="directive-dict.html#Module">Module:</a></th><td>mod_usertrack</td></tr>
+</tbody></table>
+ <p>When set to 'ON', the 'HTTPOnly' cookie attribute is added to this
+ modules tracking cookie. This attribute instructs browsers to block javascript
+ from reading the value of the cookie.</p>
+
</div>
<div class="top"><a href="#page-header"><img alt="top" src="../images/up.gif" /></a></div>
<div class="directive-section"><h2><a name="CookieName" id="CookieName">CookieName</a> <a name="cookiename" id="cookiename">Directive</a></h2>
@@ -150,6 +169,45 @@ CustomLog "logs/clickstream.log" usertrack</pre>
<pre class="prettyprint lang-config">CookieName clicktrack</pre>
+</div>
+<div class="top"><a href="#page-header"><img alt="top" src="../images/up.gif" /></a></div>
+<div class="directive-section"><h2><a name="CookieSameSite" id="CookieSameSite">CookieSameSite</a> <a name="cookiesamesite" id="cookiesamesite">Directive</a></h2>
+<table class="directive">
+<tbody><tr><th><a href="directive-dict.html#Description">Description:</a></th><td>Adds the 'SameSite' attribute to the cookie</td></tr>
+<tr><th><a href="directive-dict.html#Syntax">Syntax:</a></th><td><code>CookieSameSite None|Lax|Strict</code></td></tr>
+<tr><th><a href="directive-dict.html#Default">Default:</a></th><td><code>unset</code></td></tr>
+<tr><th><a href="directive-dict.html#Context">Context:</a></th><td>server config, virtual host, directory, .htaccess</td></tr>
+<tr><th><a href="directive-dict.html#Override">Override:</a></th><td>FileInfo</td></tr>
+<tr><th><a href="directive-dict.html#Status">Status:</a></th><td>Extension</td></tr>
+<tr><th><a href="directive-dict.html#Module">Module:</a></th><td>mod_usertrack</td></tr>
+</tbody></table>
+ <p>When set to 'None', 'Lax', or 'Strict', the 'SameSite' cookie attribute
+ is added to this modules tracking cookie with the corresponding value.
+ This attribute instructs browser on how to treat the cookie when it is
+ requested in a cross-site context.</p>
+
+ <div class="note">
+ <p>A value of 'None' sets 'SameSite=None', which is the most liberal setting. To
+ omit this attribute, omit the directive entirely.</p>
+ </div>
+
+
+</div>
+<div class="top"><a href="#page-header"><img alt="top" src="../images/up.gif" /></a></div>
+<div class="directive-section"><h2><a name="CookieSecure" id="CookieSecure">CookieSecure</a> <a name="cookiesecure" id="cookiesecure">Directive</a></h2>
+<table class="directive">
+<tbody><tr><th><a href="directive-dict.html#Description">Description:</a></th><td>Adds the 'Secure' attribute to the cookie</td></tr>
+<tr><th><a href="directive-dict.html#Syntax">Syntax:</a></th><td><code>CookieSecure on|off</code></td></tr>
+<tr><th><a href="directive-dict.html#Default">Default:</a></th><td><code>CookieSecure off</code></td></tr>
+<tr><th><a href="directive-dict.html#Context">Context:</a></th><td>server config, virtual host, directory, .htaccess</td></tr>
+<tr><th><a href="directive-dict.html#Override">Override:</a></th><td>FileInfo</td></tr>
+<tr><th><a href="directive-dict.html#Status">Status:</a></th><td>Extension</td></tr>
+<tr><th><a href="directive-dict.html#Module">Module:</a></th><td>mod_usertrack</td></tr>
+</tbody></table>
+ <p>When set to 'ON', the 'Secure' cookie attribute is added to this
+ modules tracking cookie. This attribute instructs browsers to only
+ transmit the cookie over HTTPS.</p>
+
</div>
<div class="top"><a href="#page-header"><img alt="top" src="../images/up.gif" /></a></div>
<div class="directive-section"><h2><a name="CookieStyle" id="CookieStyle">CookieStyle</a> <a name="cookiestyle" id="cookiestyle">Directive</a></h2>
diff --git a/modules/metadata/mod_usertrack.c b/modules/metadata/mod_usertrack.c
index 73a9f45..65759c2 100644
--- a/modules/metadata/mod_usertrack.c
+++ b/modules/metadata/mod_usertrack.c
@@ -86,6 +86,9 @@ typedef struct {
const char *cookie_domain;
char *regexp_string; /* used to compile regexp; save for debugging */
ap_regex_t *regexp; /* used to find usertrack cookie in cookie header */
+ int is_secure;
+ int is_httponly;
+ const char *samesite;
} cookie_dir_rec;
/* Make Cookie: Now we have to generate something that is going to be
@@ -143,6 +146,21 @@ static void make_cookie(request_rec *r)
: ""),
NULL);
}
+ if (dcfg->samesite != NULL) {
+ new_cookie = apr_pstrcat(r->pool, new_cookie, "; ",
+ dcfg->samesite,
+ NULL);
+ }
+ if (dcfg->is_secure) {
+ new_cookie = apr_pstrcat(r->pool, new_cookie, "; Secure",
+ NULL);
+ }
+ if (dcfg->is_httponly) {
+ new_cookie = apr_pstrcat(r->pool, new_cookie, "; HttpOnly",
+ NULL);
+ }
+
+
apr_table_addn(r->err_headers_out,
(dcfg->style == CT_COOKIE2 ? "Set-Cookie2" : "Set-Cookie"),
@@ -269,6 +287,7 @@ static void *make_cookie_dir(apr_pool_t *p, char *d)
dcfg->cookie_domain = NULL;
dcfg->style = CT_UNSET;
dcfg->enabled = 0;
+ /* calloc'ed to disabled: samesite, is_secure, is_httponly */
/* In case the user does not use the CookieName directive,
* we need to compile the regexp for the default cookie name. */
@@ -429,6 +448,31 @@ static const char *set_cookie_style(cmd_parms *cmd, void *mconfig,
return NULL;
}
+/*
+ * SameSite enabled disabled
+ */
+
+static const char *set_samesite_value(cmd_parms *cmd, void *mconfig,
+ const char *name)
+{
+ cookie_dir_rec *dcfg;
+
+ dcfg = (cookie_dir_rec *) mconfig;
+
+ if (strcasecmp(name, "strict") == 0) {
+ dcfg->samesite = "SameSite=Strict";
+ } else if (strcasecmp(name, "lax") == 0) {
+ dcfg->samesite = "SameSite=Lax";
+ } else if (strcasecmp(name, "none") == 0) {
+ dcfg->samesite = "SameSite=None";
+ } else {
+ return "CookieSameSite accepts 'Strict', 'Lax', or 'None'";
+ }
+
+
+ return NULL;
+}
+
static const command_rec cookie_log_cmds[] = {
AP_INIT_TAKE1("CookieExpires", set_cookie_exp, NULL, OR_FILEINFO,
"an expiry date code"),
@@ -440,6 +484,17 @@ static const command_rec cookie_log_cmds[] = {
"whether or not to enable cookies"),
AP_INIT_TAKE1("CookieName", set_cookie_name, NULL, OR_FILEINFO,
"name of the tracking cookie"),
+ AP_INIT_FLAG("CookieTracking", set_cookie_enable, NULL, OR_FILEINFO,
+ "whether or not to enable cookies"),
+ AP_INIT_TAKE1("CookieSameSite", set_samesite_value, NULL, OR_FILEINFO,
+ "SameSite setting"),
+ AP_INIT_FLAG("CookieSecure", ap_set_flag_slot,
+ (void *)APR_OFFSETOF(cookie_dir_rec, is_secure), OR_FILEINFO,
+ "is cookie secure"),
+ AP_INIT_FLAG("CookieHttpOnly", ap_set_flag_slot,
+ (void *)APR_OFFSETOF(cookie_dir_rec, is_httponly),OR_FILEINFO,
+ "is cookie http only"),
+
{NULL}
};

2
action-configtest.sh Normal file
View File

@ -0,0 +1,2 @@
#!/bin/sh
exec /usr/sbin/httpd -t

2
action-graceful.sh Normal file
View File

@ -0,0 +1,2 @@
#!/bin/sh
exec /sbin/apachectl graceful

BIN
apache-poweredby.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

74
apachectl.sh Executable file
View File

@ -0,0 +1,74 @@
#!/usr/bin/sh
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
###
### NOTE: This is a replacement version of the "apachectl" script with
### some differences in behaviour to the version distributed with
### Apache httpd. Please read the apachectl(8) man page for more
### information.
###
if [ "x$1" = "x-k" ]; then
shift
fi
ACMD="$1"
ARGV="$@"
SVC='httpd.service'
HTTPD='@HTTPDBIN@'
if [ "x$2" != "x" ] ; then
echo Passing arguments to httpd using apachectl is no longer supported.
echo You can only start/stop/restart httpd using this script.
echo To pass extra arguments to httpd, see the $SVC'(8)'
echo man page.
exit 1
fi
case $ACMD in
start|stop|restart|status)
/usr/bin/systemctl --no-pager $ACMD $SVC
ERROR=$?
;;
graceful)
if /usr/bin/systemctl -q is-active $SVC; then
/usr/bin/systemctl kill --signal=SIGUSR1 --kill-who=main $SVC
else
/usr/bin/systemctl start $SVC
fi
ERROR=$?
;;
graceful-stop)
/usr/bin/systemctl kill --signal=SIGWINCH --kill-who=main $SVC
ERROR=$?
;;
configtest|-t)
$HTTPD -t
ERROR=$?
;;
-v|-V)
$HTTPD $ACMD
ERROR=$?
;;
*)
echo apachectl: The \"$ACMD\" option is not supported. 1>&2
ERROR=2
;;
esac
exit $ERROR

Some files were not shown because too many files have changed in this diff Show More