Index: head/www/apache22/Makefile =================================================================== --- head/www/apache22/Makefile (revision 392590) +++ head/www/apache22/Makefile (revision 392591) @@ -1,204 +1,204 @@ # $FreeBSD$ PORTNAME= apache22 PORTVERSION= 2.2.29 -PORTREVISION?= 5 +PORTREVISION?= 6 CATEGORIES= www ipv6 MASTER_SITES= APACHE_HTTPD DISTNAME= httpd-${PORTVERSION} DIST_SUBDIR= apache22 MAINTAINER?= apache@FreeBSD.org COMMENT?= Version 2.2.x of Apache web server with ${WITH_MPM} MPM. LIB_DEPENDS= libexpat.so:${PORTSDIR}/textproc/expat2 \ libapr-1.so:${PORTSDIR}/devel/apr1 \ libpcre.so:${PORTSDIR}/devel/pcre CONFLICTS_INSTALL= caudium14-1.* \ apache-*-2.2.* apache22-*-2.2.* \ apache-*-2.4.* apache24-*-2.4.* USE_APACHE= common22 USES= tar:bzip2 iconv perl5 libtool cpe autoreconf USE_PERL5= run GNU_CONFIGURE= yes USE_RC_SUBR= apache22 htcacheclean CPE_VENDOR= apache CPE_PRODUCT= http_server PORTDOCS= * USERS= www GROUPS= www # for slave ports .if !defined(MASTERDIR) APACHEDIR= ${.CURDIR} .else APACHEDIR= ${MASTERDIR} .endif WITH_MPM?= prefork # or worker, event, itk, peruser WITH_HTTP_PORT?= 80 WITH_SSL_PORT?= 443 .include "${APACHEDIR}/Makefile.options" AUTHNZ_LDAP_CONFIGURE_ON= --enable-authnz-ldap # http://httpd.apache.org/docs/2.2/bind.html IPV4_MAPPED_CONFIGURE_ENABLE= v4-mapped LDAP_CONFIGURE_ON= --enable-ldap=shared SSL_CFLAGS= -I${OPENSSLINC} SSL_CONFIGURE_ON= --with-ssl=${OPENSSLBASE} SSL_LDFLAGS= -L${OPENSSLLIB} SSL_USE= OPENSSL=yes SUEXEC_RSRCLIMIT_EXTRA_PATCHES= ${FILESDIR}/extra-patch-suexec_rsrclimit SUEXEC_USERDIR_EXTRA_PATCHES= ${FILESDIR}/extra-patch-suexec_userdir .include ETC_SUBDIRS= Includes envvars.d extra modules.d # PR: 182947 .if ${WITH_MPM} != "peruser" SUB_LIST+= RELOAD_CMD=graceful .else SUB_LIST+= RELOAD_CMD=hrestart .endif APR_CONFIG?= ${LOCALBASE}/bin/apr-1-config APU_CONFIG?= ${LOCALBASE}/bin/apu-1-config # APU modules used by AUTHN_DBD DBD APU_DBD_MYSQL?= ${LOCALBASE}/lib/apr-util-1/apr_dbd_mysql.so APU_DBD_PGSQL?= ${LOCALBASE}/lib/apr-util-1/apr_dbd_pgsql.so APU_DBD_SQLITE3?= ${LOCALBASE}/lib/apr-util-1/apr_dbd_sqlite3.so # APU module used by AUTHNZ_LDAP LDAP APU_LDAP?= ${LOCALBASE}/lib/apr-util-1/apr_ldap.so # APU module used by SESSION_CRYPTO APU_CRYPTO_OPENSSL?= ${LOCALBASE}/lib/apr-util-1/apr_crypto_openssl.so APU_CRYPTO_NSS?= ${LOCALBASE}/lib/apr-util-1/apr_crypto_nss.so CONFIGURE_ARGS+=--prefix=${PREFIX} \ --enable-layout=FreeBSD \ --with-port=${WITH_HTTP_PORT} \ --with-sslport=${WITH_SSL_PORT} \ --with-expat=${LOCALBASE} \ --with-iconv=${ICONV_PREFIX} \ --enable-http \ --with-pcre=${LOCALBASE} \ --with-apr=${APR_CONFIG} \ --with-apr-util=${APU_CONFIG} CONFIGURE_ENV= CONFIG_SHELL="${SH}" \ LOCALBASE="${LOCALBASE}" MAKE_ENV+= EXPR_COMPAT=yes \ INSTALL_MAN="${INSTALL_MAN}" \ DATADIR=${DATADIR} #===================================================== # CONFIGURE_ARGS will be handled in Makefile.modules, # here we do only OPTIONS fixups .if ${PORT_OPTIONS:MSUEXEC_RSRCLIMIT} && !${PORT_OPTIONS:MSUEXEC} IGNORE= suEXEC resource limit patch requires mod_suexec.\ Please (re)run 'make config' and choose SUEXEC option also .endif .if ${PORT_OPTIONS:MSUEXEC_USERDIR} && !${PORT_OPTIONS:MSUEXEC} IGNORE= suEXEC UserDir patch requires mod_suexec.\ Please (re)run 'make config' and choose SUEXEC option also .endif .include .include "${APACHEDIR}/Makefile.modules" show-options: @${SED} -ne 's/^##//p' ${APACHEDIR}/Makefile.doc pre-everything:: @${CAT} ${FILESDIR}/HEADS_UP .if !defined(BATCH) || !defined(PORT_BUILDING) @/bin/sleep 5 .endif post-extract: # remove possible leftover .svn directories in the sources @${FIND} ${WRKSRC} -type d -name .svn -print | ${XARGS} ${RM} -rf # limit grep results ... ${FIND} ${WRKSRC} -type f \( -name 'NWGNU*' -o -name '*.ds?' -o -name '*.dep' -o -name '*.mak' -o -name '*.win' -o -name '*.vbs' -o -name '*.wsf' \) -delete # make qa script happy, it complains on empty dirs even 'PORTDOCS=*' is set # use RMDIR in case upstream ever place some files into this dirs .for d in xsl/util xsl lang -${RMDIR} ${WRKSRC}/docs/manual/style/${d} .endfor post-patch: @${REINPLACE_CMD} -e 's," PLATFORM ",FreeBSD,' ${WRKSRC}/server/core.c # IPv4_mapping fix: https://issues.apache.org/bugzilla/show_bug.cgi?id=53824 @${REINPLACE_CMD} -e 's|freebsd5|freebsd|' \ -e 's|^perlbin=.*|perlbin=${PERL}|' \ ${WRKSRC}/configure.in ${WRKSRC}/configure @${RM} -f ${WRKSRC}/docs/docroot/*.bak ${INSTALL_DATA} ${WRKSRC}/NOTICE ${WRKSRC}/docs/manual # we use devel/apr and devel/pcre @${RM} -rf ${WRKSRC}/srclib @${REINPLACE_CMD} -e 's/srclib//' ${WRKSRC}/Makefile.in pre-configure:: @${ECHO_MSG} "" @${ECHO_MSG} " You can check your modules configuration by using make show-modules" @${ECHO_MSG} "" # Fix build with OpenSSL from ports .if ${PORT_OPTIONS:MSSL} . if ${OPSYS} == FreeBSD . if defined(OPENSSL_INSTALLED) && ${OPENSSL_INSTALLED} != "" @${ECHO_MSG} "===> apply fix for FreeBSD-${OSREL} (${OSVERSION}) for usage with ${OPENSSL_INSTALLED}" @${ECHO_MSG} "" ${REINPLACE_CMD} -e "s|(ALL_CFLAGS)|(ALL_CFLAGS) -L${OPENSSLLIB}|" ${WRKSRC}/build/rules.mk.in . endif . endif .endif post-configure: @FTPUSERS=`${EGREP} -v '^#' /etc/ftpusers| ${TR} -s "\n" " "` ;\ ${REINPLACE_CMD} -e "s,%%FTPUSERS%%,$${FTPUSERS}," ${WRKSRC}/docs/conf/extra/httpd-userdir.conf @${REINPLACE_CMD} -e "s,%%WWWOWN%%,${WWWOWN}," -e "s,%%WWWGRP%%,${WWWGRP}," ${WRKSRC}/docs/conf/httpd.conf @${REINPLACE_CMD} -e "s,%%PREFIX%%,${PREFIX}," ${WRKSRC}/support/envvars-std pre-build: .if ${PORT_OPTIONS:MSSL} @${ECHO_MSG} "===> Generating unique DH group to mitigate Logjam attack (this will take a while)" (cd ${WRKSRC}/modules/ssl && ${SETENV} HOME=${WRKDIR} ${PERL} ssl_engine_dh.c) .endif post-install: @${MKDIR} ${ETC_SUBDIRS:S|^|${STAGEDIR}${ETCDIR}/|} ${INSTALL_DATA} ${FILESDIR}/no-accf.conf ${STAGEDIR}${ETCDIR}/Includes/ # place for 3rd party module configuration ${INSTALL_DATA} ${FILESDIR}/README_modules.d ${STAGEDIR}${ETCDIR}/modules.d/ # strip returns an error for non binary files, but we have a big mix -${STRIP_CMD} ${STAGEDIR}${PREFIX}/sbin/* 2>/dev/null -${STRIP_CMD} ${STAGEDIR}${PREFIX}/libexec/apache22/mod_*.so .if ${PORT_OPTIONS:MLOG_FORENSIC} ${INSTALL_SCRIPT} ${WRKSRC}/support/check_forensic ${STAGEDIR}${PREFIX}/sbin .endif # maintainer only, check for new modules modlist: extract @${AWK} '/: checking whether to enable mod_/ \ {printf "%%%%%s%%%%libexec/apache22/%s.so\n", \ toupper($$8), $$8}' ${WRKSRC}/configure \ | ${TR} -d '"' \ | ${SORT} -u \ | ${GREP} -E -v '^%%MOD_(ECHO|EXAMPLE|HTTP|IDENT|ISAPI|SO)%%' .include Index: head/www/apache22/files/patch-CVE-2015-3183 =================================================================== --- head/www/apache22/files/patch-CVE-2015-3183 (nonexistent) +++ head/www/apache22/files/patch-CVE-2015-3183 (revision 392591) @@ -0,0 +1,777 @@ +diff --git a/modules/http/http_filters.c b/modules/http/http_filters.c +index 347df85..5e190cb 100644 +--- modules/http/http_filters.c ++++ modules/http/http_filters.c +@@ -56,27 +56,31 @@ + #include + #endif + +-#define INVALID_CHAR -2 +- +-static long get_chunk_size(char *); +- +-typedef struct http_filter_ctx { ++typedef struct http_filter_ctx ++{ + apr_off_t remaining; + apr_off_t limit; + apr_off_t limit_used; +- enum { +- BODY_NONE, +- BODY_LENGTH, +- BODY_CHUNK, +- BODY_CHUNK_PART ++ apr_int32_t chunk_used; ++ apr_int32_t chunkbits; ++ enum ++ { ++ BODY_NONE, /* streamed data */ ++ BODY_LENGTH, /* data constrained by content length */ ++ BODY_CHUNK, /* chunk expected */ ++ BODY_CHUNK_PART, /* chunk digits */ ++ BODY_CHUNK_EXT, /* chunk extension */ ++ BODY_CHUNK_LF, /* got CR, expect LF after digits/extension */ ++ BODY_CHUNK_DATA, /* data constrained by chunked encoding */ ++ BODY_CHUNK_END, /* chunked data terminating CRLF */ ++ BODY_CHUNK_END_LF, /* got CR, expect LF after data */ ++ BODY_CHUNK_TRAILER /* trailers */ + } state; +- int eos_sent; +- char chunk_ln[32]; +- char *pos; +- apr_off_t linesize; ++ unsigned int eos_sent :1; + apr_bucket_brigade *bb; + } http_ctx_t; + ++/* bail out if some error in the HTTP input filter happens */ + static apr_status_t bail_out_on_error(http_ctx_t *ctx, + ap_filter_t *f, + int http_error) +@@ -109,119 +113,147 @@ static apr_status_t bail_out_on_error(http_ctx_t *ctx, + e = apr_bucket_eos_create(f->c->bucket_alloc); + APR_BRIGADE_INSERT_TAIL(bb, e); + ctx->eos_sent = 1; ++ /* If chunked encoding / content-length are corrupt, we may treat parts ++ * of this request's body as the next one's headers. ++ * To be safe, disable keep-alive. ++ */ ++ f->r->connection->keepalive = AP_CONN_CLOSE; + return ap_pass_brigade(f->r->output_filters, bb); + } + +-static apr_status_t get_remaining_chunk_line(http_ctx_t *ctx, +- apr_bucket_brigade *b, +- int linelimit) ++/** ++ * Parse a chunk line with optional extension, detect overflow. ++ * There are two error cases: ++ * 1) If the conversion would require too many bits, APR_EGENERAL is returned. ++ * 2) If the conversion used the correct number of bits, but an overflow ++ * caused only the sign bit to flip, then APR_ENOSPC is returned. ++ * In general, any negative number can be considered an overflow error. ++ */ ++static apr_status_t parse_chunk_size(http_ctx_t *ctx, const char *buffer, ++ apr_size_t len, int linelimit) + { +- apr_status_t rv; +- apr_off_t brigade_length; +- apr_bucket *e; +- const char *lineend; +- apr_size_t len; ++ apr_size_t i = 0; + +- /* +- * As the brigade b should have been requested in mode AP_MODE_GETLINE +- * all buckets in this brigade are already some type of memory +- * buckets (due to the needed scanning for LF in mode AP_MODE_GETLINE) +- * or META buckets. +- */ +- rv = apr_brigade_length(b, 0, &brigade_length); +- if (rv != APR_SUCCESS) { +- return rv; +- } +- /* Sanity check. Should never happen. See above. */ +- if (brigade_length == -1) { +- return APR_EGENERAL; +- } +- if (!brigade_length) { +- return APR_EAGAIN; +- } +- ctx->linesize += brigade_length; +- if (ctx->linesize > linelimit) { +- return APR_ENOSPC; +- } +- /* +- * As all buckets are already some type of memory buckets or META buckets +- * (see above), we only need to check the last byte in the last data bucket. +- */ +- for (e = APR_BRIGADE_LAST(b); +- e != APR_BRIGADE_SENTINEL(b); +- e = APR_BUCKET_PREV(e)) { ++ while (i < len) { ++ char c = buffer[i]; + +- if (APR_BUCKET_IS_METADATA(e)) { ++ ap_xlate_proto_from_ascii(&c, 1); ++ ++ /* handle CRLF after the chunk */ ++ if (ctx->state == BODY_CHUNK_END ++ || ctx->state == BODY_CHUNK_END_LF) { ++ if (c == LF) { ++ ctx->state = BODY_CHUNK; ++ } ++ else if (c == CR && ctx->state == BODY_CHUNK_END) { ++ ctx->state = BODY_CHUNK_END_LF; ++ } ++ else { ++ /* ++ * LF expected. ++ */ ++ return APR_EINVAL; ++ } ++ i++; + continue; + } +- rv = apr_bucket_read(e, &lineend, &len, APR_BLOCK_READ); +- if (rv != APR_SUCCESS) { +- return rv; ++ ++ /* handle start of the chunk */ ++ if (ctx->state == BODY_CHUNK) { ++ if (!apr_isxdigit(c)) { ++ /* ++ * Detect invalid character at beginning. This also works for ++ * empty chunk size lines. ++ */ ++ return APR_EINVAL; ++ } ++ else { ++ ctx->state = BODY_CHUNK_PART; ++ } ++ ctx->remaining = 0; ++ ctx->chunkbits = sizeof(apr_off_t) * 8; ++ ctx->chunk_used = 0; ++ } ++ ++ if (c == LF) { ++ if (ctx->remaining) { ++ ctx->state = BODY_CHUNK_DATA; ++ } ++ else { ++ ctx->state = BODY_CHUNK_TRAILER; ++ } + } +- if (len > 0) { +- break; /* we got the data we want */ ++ else if (ctx->state == BODY_CHUNK_LF) { ++ /* ++ * LF expected. ++ */ ++ return APR_EINVAL; + } +- /* If we got a zero-length data bucket, we try the next one */ +- } +- /* We had no data in this brigade */ +- if (!len || e == APR_BRIGADE_SENTINEL(b)) { +- return APR_EAGAIN; +- } +- if (lineend[len - 1] != APR_ASCII_LF) { +- return APR_EAGAIN; +- } +- /* Line is complete. So reset ctx->linesize for next round. */ +- ctx->linesize = 0; +- return APR_SUCCESS; +-} ++ else if (c == CR) { ++ ctx->state = BODY_CHUNK_LF; ++ } ++ else if (c == ';') { ++ ctx->state = BODY_CHUNK_EXT; ++ } ++ else if (ctx->state == BODY_CHUNK_EXT) { ++ /* ++ * Control chars (but tabs) are invalid. ++ */ ++ if (c != '\t' && apr_iscntrl(c)) { ++ return APR_EINVAL; ++ } ++ } ++ else if (ctx->state == BODY_CHUNK_PART) { ++ int xvalue; + +-static apr_status_t get_chunk_line(http_ctx_t *ctx, apr_bucket_brigade *b, +- int linelimit) +-{ +- apr_size_t len; +- int tmp_len; +- apr_status_t rv; ++ /* ignore leading zeros */ ++ if (!ctx->remaining && c == '0') { ++ i++; ++ continue; ++ } + +- tmp_len = sizeof(ctx->chunk_ln) - (ctx->pos - ctx->chunk_ln) - 1; +- /* Saveguard ourselves against underflows */ +- if (tmp_len < 0) { +- len = 0; +- } +- else { +- len = (apr_size_t) tmp_len; +- } +- /* +- * Check if there is space left in ctx->chunk_ln. If not, then either +- * the chunk size is insane or we have chunk-extensions. Ignore both +- * by discarding the remaining part of the line via +- * get_remaining_chunk_line. Only bail out if the line is too long. +- */ +- if (len > 0) { +- rv = apr_brigade_flatten(b, ctx->pos, &len); +- if (rv != APR_SUCCESS) { +- return rv; ++ ctx->chunkbits -= 4; ++ if (ctx->chunkbits < 0) { ++ /* overflow */ ++ return APR_ENOSPC; ++ } ++ ++ if (c >= '0' && c <= '9') { ++ xvalue = c - '0'; ++ } ++ else if (c >= 'A' && c <= 'F') { ++ xvalue = c - 'A' + 0xa; ++ } ++ else if (c >= 'a' && c <= 'f') { ++ xvalue = c - 'a' + 0xa; ++ } ++ else { ++ /* bogus character */ ++ return APR_EINVAL; ++ } ++ ++ ctx->remaining = (ctx->remaining << 4) | xvalue; ++ if (ctx->remaining < 0) { ++ /* overflow */ ++ return APR_ENOSPC; ++ } + } +- ctx->pos += len; +- ctx->linesize += len; +- *(ctx->pos) = '\0'; +- /* +- * Check if we really got a full line. If yes the +- * last char in the just read buffer must be LF. +- * If not advance the buffer and return APR_EAGAIN. +- * We do not start processing until we have the +- * full line. +- */ +- if (ctx->pos[-1] != APR_ASCII_LF) { +- /* Check if the remaining data in the brigade has the LF */ +- return get_remaining_chunk_line(ctx, b, linelimit); ++ else { ++ /* Should not happen */ ++ return APR_EGENERAL; + } +- /* Line is complete. So reset ctx->pos for next round. */ +- ctx->pos = ctx->chunk_ln; +- return APR_SUCCESS; ++ ++ i++; + } +- return get_remaining_chunk_line(ctx, b, linelimit); +-} + ++ /* sanity check */ ++ ctx->chunk_used += len; ++ if (ctx->chunk_used < 0 || ctx->chunk_used > linelimit) { ++ return APR_ENOSPC; ++ } ++ ++ return APR_SUCCESS; ++} + + static apr_status_t read_chunked_trailers(http_ctx_t *ctx, ap_filter_t *f, + apr_bucket_brigade *b, int merge) +@@ -235,7 +267,6 @@ static apr_status_t read_chunked_trailers(http_ctx_t *ctx, ap_filter_t *f, + r->status = HTTP_OK; + r->headers_in = r->trailers_in; + apr_table_clear(r->headers_in); +- ctx->state = BODY_NONE; + ap_get_mime_headers(r); + + if(r->status == HTTP_OK) { +@@ -282,6 +313,7 @@ apr_status_t ap_http_filter(ap_filter_t *f, apr_bucket_brigade *b, + apr_off_t totalread; + int http_error = HTTP_REQUEST_ENTITY_TOO_LARGE; + apr_bucket_brigade *bb; ++ int again; + + conf = (core_server_config *) + ap_get_module_config(f->r->server->module_config, &core_module); +@@ -295,7 +327,6 @@ apr_status_t ap_http_filter(ap_filter_t *f, apr_bucket_brigade *b, + const char *tenc, *lenp; + f->ctx = ctx = apr_pcalloc(f->r->pool, sizeof(*ctx)); + ctx->state = BODY_NONE; +- ctx->pos = ctx->chunk_ln; + ctx->bb = apr_brigade_create(f->r->pool, f->c->bucket_alloc); + bb = ctx->bb; + +@@ -337,7 +368,7 @@ apr_status_t ap_http_filter(ap_filter_t *f, apr_bucket_brigade *b, + */ + ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, f->r, + "Unknown Transfer-Encoding: %s", tenc); +- return bail_out_on_error(ctx, f, HTTP_NOT_IMPLEMENTED); ++ return bail_out_on_error(ctx, f, HTTP_BAD_REQUEST); + } + lenp = NULL; + } +@@ -357,7 +388,7 @@ apr_status_t ap_http_filter(ap_filter_t *f, apr_bucket_brigade *b, + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, f->r, + "Invalid Content-Length"); + +- return bail_out_on_error(ctx, f, HTTP_REQUEST_ENTITY_TOO_LARGE); ++ return bail_out_on_error(ctx, f, HTTP_BAD_REQUEST); + } + + /* If we have a limit in effect and we know the C-L ahead of +@@ -399,7 +430,8 @@ apr_status_t ap_http_filter(ap_filter_t *f, apr_bucket_brigade *b, + if (!ap_is_HTTP_SUCCESS(f->r->status)) { + ctx->state = BODY_NONE; + ctx->eos_sent = 1; +- } else { ++ } ++ else { + char *tmp; + int len; + +@@ -424,285 +456,194 @@ apr_status_t ap_http_filter(ap_filter_t *f, apr_bucket_brigade *b, + } + } + } ++ } + +- /* We can't read the chunk until after sending 100 if required. */ +- if (ctx->state == BODY_CHUNK) { +- apr_brigade_cleanup(bb); ++ /* sanity check in case we're read twice */ ++ if (ctx->eos_sent) { ++ e = apr_bucket_eos_create(f->c->bucket_alloc); ++ APR_BRIGADE_INSERT_TAIL(b, e); ++ return APR_SUCCESS; ++ } ++ ++ do { ++ apr_brigade_cleanup(b); ++ again = 0; /* until further notice */ ++ ++ /* read and handle the brigade */ ++ switch (ctx->state) { ++ case BODY_CHUNK: ++ case BODY_CHUNK_PART: ++ case BODY_CHUNK_EXT: ++ case BODY_CHUNK_LF: ++ case BODY_CHUNK_END: ++ case BODY_CHUNK_END_LF: { + +- rv = ap_get_brigade(f->next, bb, AP_MODE_GETLINE, +- block, 0); ++ rv = ap_get_brigade(f->next, b, AP_MODE_GETLINE, block, 0); + + /* for timeout */ +- if (block == APR_NONBLOCK_READ && +- ( (rv == APR_SUCCESS && APR_BRIGADE_EMPTY(bb)) || +- (APR_STATUS_IS_EAGAIN(rv)) )) { +- ctx->state = BODY_CHUNK_PART; ++ if (block == APR_NONBLOCK_READ ++ && ((rv == APR_SUCCESS && APR_BRIGADE_EMPTY(b)) ++ || (APR_STATUS_IS_EAGAIN(rv)))) { + return APR_EAGAIN; + } + +- if (rv == APR_SUCCESS) { +- rv = get_chunk_line(ctx, bb, f->r->server->limit_req_line); +- if (APR_STATUS_IS_EAGAIN(rv)) { +- apr_brigade_cleanup(bb); +- ctx->state = BODY_CHUNK_PART; +- return rv; +- } +- if (rv == APR_SUCCESS) { +- ctx->remaining = get_chunk_size(ctx->chunk_ln); +- if (ctx->remaining == INVALID_CHAR) { +- rv = APR_EGENERAL; +- http_error = HTTP_SERVICE_UNAVAILABLE; +- } +- } +- } +- apr_brigade_cleanup(bb); +- +- /* Detect chunksize error (such as overflow) */ +- if (rv != APR_SUCCESS || ctx->remaining < 0) { +- ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, f->r, "Error reading first chunk %s ", +- (ctx->remaining < 0) ? "(overflow)" : ""); +- if (APR_STATUS_IS_TIMEUP(rv) || ctx->remaining > 0) { +- http_error = HTTP_REQUEST_TIME_OUT; +- } +- ctx->remaining = 0; /* Reset it in case we have to +- * come back here later */ +- return bail_out_on_error(ctx, f, http_error); ++ if (rv == APR_EOF) { ++ return APR_INCOMPLETE; + } + +- if (!ctx->remaining) { +- return read_chunked_trailers(ctx, f, b, +- conf->merge_trailers == AP_MERGE_TRAILERS_ENABLE); ++ if (rv != APR_SUCCESS) { ++ return rv; + } +- } +- } +- else { +- bb = ctx->bb; +- } + +- if (ctx->eos_sent) { +- e = apr_bucket_eos_create(f->c->bucket_alloc); +- APR_BRIGADE_INSERT_TAIL(b, e); +- return APR_SUCCESS; +- } ++ e = APR_BRIGADE_FIRST(b); ++ while (e != APR_BRIGADE_SENTINEL(b)) { ++ const char *buffer; ++ apr_size_t len; + +- if (!ctx->remaining) { +- switch (ctx->state) { +- case BODY_NONE: +- break; +- case BODY_LENGTH: +- e = apr_bucket_eos_create(f->c->bucket_alloc); +- APR_BRIGADE_INSERT_TAIL(b, e); +- ctx->eos_sent = 1; +- return APR_SUCCESS; +- case BODY_CHUNK: +- case BODY_CHUNK_PART: +- { +- apr_brigade_cleanup(bb); ++ if (!APR_BUCKET_IS_METADATA(e)) { ++ int parsing = 0; + +- /* We need to read the CRLF after the chunk. */ +- if (ctx->state == BODY_CHUNK) { +- rv = ap_get_brigade(f->next, bb, AP_MODE_GETLINE, +- block, 0); +- if (block == APR_NONBLOCK_READ && +- ( (rv == APR_SUCCESS && APR_BRIGADE_EMPTY(bb)) || +- (APR_STATUS_IS_EAGAIN(rv)) )) { +- return APR_EAGAIN; +- } +- /* If we get an error, then leave */ +- if (rv == APR_EOF) { +- return APR_INCOMPLETE; +- } +- if (rv != APR_SUCCESS) { +- return rv; +- } +- /* +- * We really don't care whats on this line. If it is RFC +- * compliant it should be only \r\n. If there is more +- * before we just ignore it as long as we do not get over +- * the limit for request lines. +- */ +- rv = get_remaining_chunk_line(ctx, bb, +- f->r->server->limit_req_line); +- apr_brigade_cleanup(bb); +- if (APR_STATUS_IS_EAGAIN(rv)) { +- return rv; +- } +- } else { +- rv = APR_SUCCESS; +- } ++ rv = apr_bucket_read(e, &buffer, &len, APR_BLOCK_READ); + +- if (rv == APR_SUCCESS) { +- /* Read the real chunk line. */ +- rv = ap_get_brigade(f->next, bb, AP_MODE_GETLINE, +- block, 0); +- /* Test timeout */ +- if (block == APR_NONBLOCK_READ && +- ( (rv == APR_SUCCESS && APR_BRIGADE_EMPTY(bb)) || +- (APR_STATUS_IS_EAGAIN(rv)) )) { +- ctx->state = BODY_CHUNK_PART; +- return APR_EAGAIN; +- } +- ctx->state = BODY_CHUNK; + if (rv == APR_SUCCESS) { +- rv = get_chunk_line(ctx, bb, f->r->server->limit_req_line); +- if (APR_STATUS_IS_EAGAIN(rv)) { +- ctx->state = BODY_CHUNK_PART; +- apr_brigade_cleanup(bb); +- return rv; +- } +- if (rv == APR_SUCCESS) { +- ctx->remaining = get_chunk_size(ctx->chunk_ln); +- if (ctx->remaining == INVALID_CHAR) { +- rv = APR_EGENERAL; +- http_error = HTTP_SERVICE_UNAVAILABLE; ++ parsing = 1; ++ rv = parse_chunk_size(ctx, buffer, len, ++ f->r->server->limit_req_fieldsize); ++ } ++ if (rv != APR_SUCCESS) { ++ ap_log_rerror(APLOG_MARK, APLOG_INFO, rv, f->r, ++ "Error reading/parsing chunk %s ", ++ (APR_ENOSPC == rv) ? "(overflow)" : ""); ++ if (parsing) { ++ if (rv != APR_ENOSPC) { ++ http_error = HTTP_BAD_REQUEST; + } ++ return bail_out_on_error(ctx, f, http_error); + } ++ return rv; + } +- apr_brigade_cleanup(bb); + } + +- /* Detect chunksize error (such as overflow) */ +- if (rv != APR_SUCCESS || ctx->remaining < 0) { +- ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, f->r, "Error reading chunk %s ", +- (ctx->remaining < 0) ? "(overflow)" : ""); +- if (APR_STATUS_IS_TIMEUP(rv) || ctx->remaining > 0) { +- http_error = HTTP_REQUEST_TIME_OUT; +- } +- ctx->remaining = 0; /* Reset it in case we have to +- * come back here later */ +- return bail_out_on_error(ctx, f, http_error); +- } ++ apr_bucket_delete(e); ++ e = APR_BRIGADE_FIRST(b); ++ } ++ again = 1; /* come around again */ + +- if (!ctx->remaining) { +- return read_chunked_trailers(ctx, f, b, ++ if (ctx->state == BODY_CHUNK_TRAILER) { ++ /* Treat UNSET as DISABLE - trailers aren't merged by default */ ++ return read_chunked_trailers(ctx, f, b, + conf->merge_trailers == AP_MERGE_TRAILERS_ENABLE); +- } + } ++ + break; + } +- } ++ case BODY_NONE: ++ case BODY_LENGTH: ++ case BODY_CHUNK_DATA: { + +- /* Ensure that the caller can not go over our boundary point. */ +- if (ctx->state == BODY_LENGTH || ctx->state == BODY_CHUNK) { +- if (ctx->remaining < readbytes) { +- readbytes = ctx->remaining; +- } +- AP_DEBUG_ASSERT(readbytes > 0); +- } ++ /* Ensure that the caller can not go over our boundary point. */ ++ if (ctx->state != BODY_NONE && ctx->remaining < readbytes) { ++ readbytes = ctx->remaining; ++ } ++ if (readbytes > 0) { + +- rv = ap_get_brigade(f->next, b, mode, block, readbytes); ++ rv = ap_get_brigade(f->next, b, mode, block, readbytes); + +- if (rv == APR_EOF && ctx->state != BODY_NONE && +- ctx->remaining > 0) { +- return APR_INCOMPLETE; +- } +- if (rv != APR_SUCCESS) { +- return rv; +- } ++ /* for timeout */ ++ if (block == APR_NONBLOCK_READ ++ && ((rv == APR_SUCCESS && APR_BRIGADE_EMPTY(b)) ++ || (APR_STATUS_IS_EAGAIN(rv)))) { ++ return APR_EAGAIN; ++ } + +- /* How many bytes did we just read? */ +- apr_brigade_length(b, 0, &totalread); ++ if (rv == APR_EOF && ctx->state != BODY_NONE ++ && ctx->remaining > 0) { ++ return APR_INCOMPLETE; ++ } + +- /* If this happens, we have a bucket of unknown length. Die because +- * it means our assumptions have changed. */ +- AP_DEBUG_ASSERT(totalread >= 0); ++ if (rv != APR_SUCCESS) { ++ return rv; ++ } + +- if (ctx->state != BODY_NONE) { +- ctx->remaining -= totalread; +- if (ctx->remaining > 0) { +- e = APR_BRIGADE_LAST(b); +- if (APR_BUCKET_IS_EOS(e)) { +- apr_bucket_delete(e); +- return APR_INCOMPLETE; +- } +- } +- } ++ /* How many bytes did we just read? */ ++ apr_brigade_length(b, 0, &totalread); + +- /* If we have no more bytes remaining on a C-L request, +- * save the callter a roundtrip to discover EOS. +- */ +- if (ctx->state == BODY_LENGTH && ctx->remaining == 0) { +- e = apr_bucket_eos_create(f->c->bucket_alloc); +- APR_BRIGADE_INSERT_TAIL(b, e); +- } ++ /* If this happens, we have a bucket of unknown length. Die because ++ * it means our assumptions have changed. */ ++ AP_DEBUG_ASSERT(totalread >= 0); + +- /* We have a limit in effect. */ +- if (ctx->limit) { +- /* FIXME: Note that we might get slightly confused on chunked inputs +- * as we'd need to compensate for the chunk lengths which may not +- * really count. This seems to be up for interpretation. */ +- ctx->limit_used += totalread; +- if (ctx->limit < ctx->limit_used) { +- ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, f->r, +- "Read content-length of %" APR_OFF_T_FMT +- " is larger than the configured limit" +- " of %" APR_OFF_T_FMT, ctx->limit_used, ctx->limit); +- apr_brigade_cleanup(bb); +- e = ap_bucket_error_create(HTTP_REQUEST_ENTITY_TOO_LARGE, NULL, +- f->r->pool, +- f->c->bucket_alloc); +- APR_BRIGADE_INSERT_TAIL(bb, e); +- e = apr_bucket_eos_create(f->c->bucket_alloc); +- APR_BRIGADE_INSERT_TAIL(bb, e); +- ctx->eos_sent = 1; +- return ap_pass_brigade(f->r->output_filters, bb); +- } +- } ++ if (ctx->state != BODY_NONE) { ++ ctx->remaining -= totalread; ++ if (ctx->remaining > 0) { ++ e = APR_BRIGADE_LAST(b); ++ if (APR_BUCKET_IS_EOS(e)) { ++ apr_bucket_delete(e); ++ return APR_INCOMPLETE; ++ } ++ } ++ else if (ctx->state == BODY_CHUNK_DATA) { ++ /* next chunk please */ ++ ctx->state = BODY_CHUNK_END; ++ ctx->chunk_used = 0; ++ } ++ } + +- return APR_SUCCESS; +-} ++ } + +-/** +- * Parse a chunk extension, detect overflow. +- * There are two error cases: +- * 1) If the conversion would require too many bits, a -1 is returned. +- * 2) If the conversion used the correct number of bits, but an overflow +- * caused only the sign bit to flip, then that negative number is +- * returned. +- * In general, any negative number can be considered an overflow error. +- */ +-static long get_chunk_size(char *b) +-{ +- long chunksize = 0; +- size_t chunkbits = sizeof(long) * 8; ++ /* If we have no more bytes remaining on a C-L request, ++ * save the caller a round trip to discover EOS. ++ */ ++ if (ctx->state == BODY_LENGTH && ctx->remaining == 0) { ++ e = apr_bucket_eos_create(f->c->bucket_alloc); ++ APR_BRIGADE_INSERT_TAIL(b, e); ++ ctx->eos_sent = 1; ++ } + +- ap_xlate_proto_from_ascii(b, strlen(b)); ++ /* We have a limit in effect. */ ++ if (ctx->limit) { ++ /* FIXME: Note that we might get slightly confused on chunked inputs ++ * as we'd need to compensate for the chunk lengths which may not ++ * really count. This seems to be up for interpretation. */ ++ ctx->limit_used += totalread; ++ if (ctx->limit < ctx->limit_used) { ++ ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, f->r, ++ "Read content-length of %" APR_OFF_T_FMT ++ " is larger than the configured limit" ++ " of %" APR_OFF_T_FMT, ctx->limit_used, ctx->limit); ++ return bail_out_on_error(ctx, f, HTTP_REQUEST_ENTITY_TOO_LARGE); ++ } ++ } + +- if (!apr_isxdigit(*b)) { +- /* +- * Detect invalid character at beginning. This also works for empty +- * chunk size lines. +- */ +- return INVALID_CHAR; +- } +- /* Skip leading zeros */ +- while (*b == '0') { +- ++b; +- } ++ break; ++ } ++ case BODY_CHUNK_TRAILER: { ++ ++ rv = ap_get_brigade(f->next, b, mode, block, readbytes); + +- while (apr_isxdigit(*b) && (chunkbits > 0)) { +- int xvalue = 0; ++ /* for timeout */ ++ if (block == APR_NONBLOCK_READ ++ && ((rv == APR_SUCCESS && APR_BRIGADE_EMPTY(b)) ++ || (APR_STATUS_IS_EAGAIN(rv)))) { ++ return APR_EAGAIN; ++ } ++ ++ if (rv != APR_SUCCESS) { ++ return rv; ++ } + +- if (*b >= '0' && *b <= '9') { +- xvalue = *b - '0'; ++ break; + } +- else if (*b >= 'A' && *b <= 'F') { +- xvalue = *b - 'A' + 0xa; ++ default: { ++ /* Should not happen */ ++ ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, f->r, ++ "Unexpected body state (%i)", (int)ctx->state); ++ return APR_EGENERAL; + } +- else if (*b >= 'a' && *b <= 'f') { +- xvalue = *b - 'a' + 0xa; + } + +- chunksize = (chunksize << 4) | xvalue; +- chunkbits -= 4; +- ++b; +- } +- if (apr_isxdigit(*b) && (chunkbits <= 0)) { +- /* overflow */ +- return -1; +- } ++ } while (again); + +- return chunksize; ++ return APR_SUCCESS; + } + + typedef struct header_struct { Property changes on: head/www/apache22/files/patch-CVE-2015-3183 ___________________________________________________________________ Added: fbsd:nokeywords ## -0,0 +1 ## +yes \ No newline at end of property Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property