Package: release.debian.org Severity: normal Tags: trixie X-Debbugs-Cc: [email protected] Control: affects -1 + src:apache2 User: [email protected] Usertags: pu
[ Reason ] Apache2 is vulnerable to various medium CVEs (CVE-2026-29167, CVE-2026-29170, CVE-2026-34355, CVE-2026-34356, CVE-2026-42535, CVE-2026-42536, CVE-2026-43951, CVE-2026-44119, CVE-2026-44185, CVE-2026-44186, CVE-2026-44631, CVE-2026-48913). [ Impact ] Medium security issues [ Tests ] Diff contains a test-framework update [ Risks ] As usual with Apache, low but not null risk [ Checklist ] [X] *all* changes are documented in the d/changelog [X] I reviewed all changes and I approve them [X] attach debdiff against the package in (old)stable [X] the issue is verified as fixed in unstable [ Changes ] This release contains only fixes (a lot...) [ Other info ] I put here just the debian/ diff. The whole diff is big
diff --git a/debian/changelog b/debian/changelog index a04158c1..8e4b38fa 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,15 @@ +apache2 (2.4.68-1~deb13u1) trixie; urgency=medium + + * New upstream version (Closes: CVE-2026-29167, CVE-2026-29170, + CVE-2026-34355, CVE-2026-34356, CVE-2026-42535, CVE-2026-42536, + CVE-2026-43951, CVE-2026-44119, CVE-2026-44185, CVE-2026-44186, + CVE-2026-44631, CVE-2026-48913) + * Drop CVE-2026-49975_*, now included in upstream + * Update debian/convert_docs + * Update test framework + + -- Xavier Guimard <[email protected]> Thu, 11 Jun 2026 20:28:43 +0200 + apache2 (2.4.67-1~deb13u3) trixie-security; urgency=medium * Fix CVE-2026-49975 (HTTP/2 Bomb) diff --git a/debian/convert_docs b/debian/convert_docs index b39a58fa..b8c07370 100755 --- a/debian/convert_docs +++ b/debian/convert_docs @@ -49,7 +49,7 @@ foreach my $h (@html) { elsif ( -f "$SRC/$h.$l" ) { conv("$SRC/$h.$l", "$tdir$h", $h, $updir); } - else { + elsif ( ! -e "$tdir$h" ) { symlink("$updir/en/$h", "$tdir$h"); } diff --git a/debian/patches/CVE-2026-49975_1.patch b/debian/patches/CVE-2026-49975_1.patch deleted file mode 100644 index 28ec9897..00000000 --- a/debian/patches/CVE-2026-49975_1.patch +++ /dev/null @@ -1,26 +0,0 @@ -From: Stefan Eissing <[email protected]> -Date: Wed, 27 May 2026 10:50:32 +0200 -Subject: cookie reqest header counting - -Account merged cookie headers as an "add" to keep LimitRequestFields effective. - -origin: backport, https://github.com/icing/mod_h2/commit/cb7cd2eec3b6ef02dea62d77de2a38a108af66ee -bug: https://github.com/icing/mod_h2/pull/324 -bug-security: https://blog.calif.io/p/codex-discovered-a-hidden-http2-bomb ---- - modules/http2/h2_util.c | 2 ++ - 1 file changed, 2 insertions(+) - -diff --git a/modules/http2/h2_util.c b/modules/http2/h2_util.c -index b377ff7..b265bc9 100644 ---- a/modules/http2/h2_util.c -+++ b/modules/http2/h2_util.c -@@ -1719,6 +1719,8 @@ static apr_status_t req_add_header(apr_table_t *headers, apr_pool_t *pool, - apr_table_setn(headers, "Cookie", - apr_psprintf(pool, "%s; %.*s", existing, - (int)nv->valuelen, nv->value)); -+ /* Treat the merge as an "add" to not escape LimitRequestFields */ -+ *pwas_added = 1; - return APR_SUCCESS; - } - } diff --git a/debian/patches/CVE-2026-49975_2.patch b/debian/patches/CVE-2026-49975_2.patch deleted file mode 100644 index 851e5b36..00000000 --- a/debian/patches/CVE-2026-49975_2.patch +++ /dev/null @@ -1,24 +0,0 @@ -From: Stefan Eissing <[email protected]> -Date: Wed, 27 May 2026 11:05:44 +0200 -Subject: ignore duplicate empty cookie headers - -origin: backport, https://github.com/icing/mod_h2/commit/b5c211e7010d31224ac2621664479177314aec96 -bug: https://github.com/icing/mod_h2/pull/324 -bug-security: https://blog.calif.io/p/codex-discovered-a-hidden-http2-bomb ---- - modules/http2/h2_util.c | 2 ++ - 1 file changed, 2 insertions(+) - -diff --git a/modules/http2/h2_util.c b/modules/http2/h2_util.c -index b265bc9..b303945 100644 ---- a/modules/http2/h2_util.c -+++ b/modules/http2/h2_util.c -@@ -1708,6 +1708,8 @@ static apr_status_t req_add_header(apr_table_t *headers, apr_pool_t *pool, - && !ap_cstr_casecmpn("cookie", (const char *)nv->name, nv->namelen)) { - existing = apr_table_get(headers, "cookie"); - if (existing) { -+ if (!nv->valuelen) -+ return APR_SUCCESS; - /* Cookie header come separately in HTTP/2, but need - * to be merged by "; " (instead of default ", ") - */ diff --git a/debian/patches/fhs_compliance.patch b/debian/patches/fhs_compliance.patch index ee4715fd..b0c3b185 100644 --- a/debian/patches/fhs_compliance.patch +++ b/debian/patches/fhs_compliance.patch @@ -6,7 +6,7 @@ Last-Update: 2026-05-05 --- a/configure +++ b/configure -@@ -42443,16 +42443,17 @@ +@@ -42455,16 +42455,17 @@ cat >>confdefs.h <<_ACEOF #define HTTPD_ROOT "${ap_prefix}" @@ -28,7 +28,7 @@ Last-Update: 2026-05-05 --- a/configure.in +++ b/configure.in -@@ -934,11 +934,11 @@ +@@ -936,11 +936,11 @@ echo $MODLIST | $AWK -f $srcdir/build/build-modules-c.awk > modules.c APR_EXPAND_VAR(ap_prefix, $prefix) diff --git a/debian/patches/series b/debian/patches/series index 2aabdc69..450653aa 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -5,5 +5,3 @@ customize_apxs.patch build_suexec-custom.patch reproducible_builds.diff fix-macro.patch -CVE-2026-49975_1.patch -CVE-2026-49975_2.patch diff --git a/debian/perl-framework/t/apache/expr.t b/debian/perl-framework/t/apache/expr.t index 7d62bc04..06e031d0 100644 --- a/debian/perl-framework/t/apache/expr.t +++ b/debian/perl-framework/t/apache/expr.t @@ -115,7 +115,6 @@ my @test_cases = ( [ q[toupper(escape('?')) = '%3F' ] => 1 ], [ q[tolower(toupper(escape('?'))) = '%3f' ] => 1 ], [ q[%{toupper:%{escape:?}} = '%3F' ] => 1 ], - [ q[file('] . $file_foo . q[') = 'foo\n' ] => 1 ], # unary operators [ q[-n ''] => 0 ], [ q[-z ''] => 1 ], @@ -198,11 +197,13 @@ push @test_cases, @bool_test_cases; push @test_cases, map { ["!($_->[0])" => neg($_->[1]) ] } @bool_test_cases; if (have_min_apache_version("2.3.13")) { + my $restrict2_result = have_min_apache_version('2.4.68') ? undef : 1; push(@test_cases, ( # functions - [ q[filesize('] . $file_foo . q[') = 4 ] => 1 ], - [ q[filesize('] . $file_notexist . q[') = 0 ] => 1 ], - [ q[filesize('] . $file_zero . q[') = 0 ] => 1 ], + [ q[filesize('] . $file_foo . q[') = 4 ] => $restrict2_result ], + [ q[filesize('] . $file_notexist . q[') = 0 ] => $restrict2_result ], + [ q[filesize('] . $file_zero . q[') = 0 ] => $restrict2_result ], + [ q[file('] . $file_foo . q[') = 'foo\n' ] => $restrict2_result ], # unary operators [ qq[-d '$file_foo' ] => 0 ], [ qq[-e '$file_foo' ] => 1 ], diff --git a/debian/perl-framework/t/apache/pr64339.t b/debian/perl-framework/t/apache/pr64339.t index 00097e66..4915418c 100644 --- a/debian/perl-framework/t/apache/pr64339.t +++ b/debian/perl-framework/t/apache/pr64339.t @@ -18,7 +18,7 @@ my @testcases = ( ['/doc.notxml', "application/notreallyxml", "f\xf3\xf3\n" ], # Sent with charset=ISO-8859-1 - should be transformed to utf-8 - ['/doc.isohtml', "text/html;charset=utf-8", "<html><body><p>fóó\n</p></body></html>" ], + ['/doc.isohtml', "text/html;charset=utf-8", "<html><body>.*fóó\n.*</body></html>" ], ); # mod_xml2enc on trunk behaves quite differently to the 2.4.x version @@ -42,5 +42,5 @@ foreach my $t (@testcases) { ok t_cmp($r->code, 200, "fetching ".$t->[0]); ok t_cmp($r->header('Content-Type'), $t->[1], "content-type header test for ".$t->[0]); - ok t_cmp($r->content, $t->[2], "content test for ".$t->[0]); + ok t_cmp($r->content, qr/$t->[2]/, "content test for ".$t->[0]); } diff --git a/debian/perl-framework/t/htdocs/modules/include/apexpr/restrict_func.shtml b/debian/perl-framework/t/htdocs/modules/include/apexpr/restrict_func.shtml new file mode 100644 index 00000000..31401606 --- /dev/null +++ b/debian/perl-framework/t/htdocs/modules/include/apexpr/restrict_func.shtml @@ -0,0 +1,5 @@ +<!--#if expr="file('%{DOCUMENT_ROOT}/foobar.html') =~ /foobar/" --> +IF_FUNC_TRUE +<!--#else --> +IF_FUNC_FALSE +<!--#endif --> diff --git a/debian/perl-framework/t/modules/dav.t b/debian/perl-framework/t/modules/dav.t index a1b6248b..8ff990d8 100644 --- a/debian/perl-framework/t/modules/dav.t +++ b/debian/perl-framework/t/modules/dav.t @@ -10,7 +10,7 @@ use HTTP::Date; ## mod_dav tests ## -plan tests => 21, [qw(dav HTTP::DAV)]; +plan tests => 22, [qw(dav HTTP::DAV)]; require HTTP::DAV; my $vars = Apache::Test::vars(); @@ -178,6 +178,15 @@ print "PR 49825: expect 400 bad request got: $actual\n"; ok $actual == 400; $user_agent->default_header('Content-Range' => undef); +## Test PUT to .DAV subdirectory (should be blocked in 2.4.68+) ## +my $dav_uri = "/$dir/.DAV/test.html"; +my $dav_resource = $dav->new_resource( -uri => "http://$server$dav_uri"); +my $expected = have_min_apache_version('2.4.68') ? 403 : 201; +$response = $dav_resource->put($body); +$actual = $response->code; +ok t_cmp($actual, $expected); + ## clean up ## +unlink "$htdocs/$dir/.DAV/test.html"; rmdir "$htdocs/$dir/.DAV" or print "warning: could not remove .DAV dir: $!"; rmdir "$htdocs/$dir" or print "warning: could not remove dav dir: $!"; diff --git a/debian/perl-framework/t/modules/headers.t b/debian/perl-framework/t/modules/headers.t index 4892b95c..40bd5c32 100644 --- a/debian/perl-framework/t/modules/headers.t +++ b/debian/perl-framework/t/modules/headers.t @@ -115,7 +115,27 @@ my @testcases = ( [ ], [ 'Test-Header' => 'foo' ], ], + # 500 error test - invalid regex pattern + [ + "Header edit Test-Header (unclosed bar", # malformed regex (unmatched parenthesis) + [ ], + [ ], + 500, + ], ); +if (have_min_apache_version('2.4.68')) { + push(@testcases, + ( + # edit* + [ + "Header set Test-Header \"expr=%{base64:%{file:$htaccess}}\"", # no file() in htaccess + [ ], + [ ], + 500, + ], + ) + ); +} if (have_min_apache_version('2.5.1')) { push(@testcases, ( @@ -297,6 +317,9 @@ sub test_header2 { my @test = @_; my $h = HTTP::Headers->new; + # Extract expected status code (default to 200 if not specified) + my $expected_status = $test[0][3] // 200; + print "\n\n\n"; for (my $i = 0; $i < scalar @{$test[0][1]}; $i += 2) { print "Header sent n°" . $i/2 . ":\n"; @@ -312,22 +335,30 @@ sub test_header2 { ## my $r = HTTP::Request->new('GET', "http://$hostport/modules/headers/htaccess/", $h); my $res = $ua->request($r); - ok t_cmp($res->code, 200, "Checking return code is '200'"); + ok t_cmp($res->code, $expected_status, "Checking return code is '$expected_status'"); - my $isok = 1; - for (my $i = 0; $i < scalar @{$test[0][2]}; $i += 2) { - print "\n"; - print "Header received n°" . $i/2 . ":\n"; - print " header: " . $test[0][2][$i] . "\n"; - print " expected: " . $test[0][2][$i+1] . "\n"; - if ($res->header($test[0][2][$i])) { - print " received: " . $res->header($test[0][2][$i]) . "\n"; - } else { - print " received: <undefined>\n"; + # Only validate headers if we expect a successful response + if ($expected_status == 200) { + my $isok = 1; + for (my $i = 0; $i < scalar @{$test[0][2]}; $i += 2) { + print "\n"; + print "Header received n°" . $i/2 . ":\n"; + print " header: " . $test[0][2][$i] . "\n"; + print " expected: " . $test[0][2][$i+1] . "\n"; + if ($res->header($test[0][2][$i])) { + print " received: " . $res->header($test[0][2][$i]) . "\n"; + } else { + print " received: <undefined>\n"; + } + $isok = $isok && $res->header($test[0][2][$i]) && $test[0][2][$i+1] eq $res->header($test[0][2][$i]); } - $isok = $isok && $res->header($test[0][2][$i]) && $test[0][2][$i+1] eq $res->header($test[0][2][$i]); - } - print "\nResponse received is:\n" . $res->as_string; + print "\nResponse received is:\n" . $res->as_string; - ok $isok; + ok $isok; + } else { + # For error responses, skip header validation + print "\nExpected error response received (status $expected_status)\n"; + print "Response received is:\n" . $res->as_string; + ok 1; + } } diff --git a/debian/perl-framework/t/modules/include.t b/debian/perl-framework/t/modules/include.t index 99644971..a4b1e80b 100644 --- a/debian/perl-framework/t/modules/include.t +++ b/debian/perl-framework/t/modules/include.t @@ -85,7 +85,7 @@ my %test = ( "echo3.shtml" => ['<!--#echo var="DOCUMENT_NAME" -->', "retagged1"], "notreal.shtml" => "pass <!--", "malformed.shtml" => "[an error occurred while processing this ". - "directive] malformed.shtml", + "directive]", "exec/off/cmd.shtml" => "[an error occurred while processing this ". "directive]", "exec/on/cmd.shtml" => "pass", @@ -107,9 +107,11 @@ my %test = ( my %ap_expr_test = ( "apexpr/if1.shtml" => "pass", "apexpr/err.shtml" => "[an error occurred while processing this ". - "directive] err.shtml", + "directive]", "apexpr/restrict.shtml" => "[an error occurred while processing this ". - "directive] restrict.shtml", + "directive]", +"apexpr/restrict_func.shtml" => "[an error occurred while processing this ". + "directive]", "apexpr/var.shtml" => "pass pass pass", "apexpr/lazyvar.shtml" => "pass", ); @@ -303,6 +305,12 @@ foreach $doc (sort keys %tests) { skip "Skipping 'exec cgi' test; no cgi module.", 2; } } + elsif ($doc =~ m/malformed|apexpr/) { + ok t_cmp(super_chomp(GET_BODY "$dir$doc"), + qr/\Q$tests{$doc}\E/, + "GET $dir$doc" + ); + } else { ok t_cmp(super_chomp(GET_BODY "$dir$doc"), $tests{$doc}, diff --git a/debian/perl-framework/t/modules/proxy_fcgi.t b/debian/perl-framework/t/modules/proxy_fcgi.t index dce4a804..6da6253f 100644 --- a/debian/perl-framework/t/modules/proxy_fcgi.t +++ b/debian/perl-framework/t/modules/proxy_fcgi.t @@ -154,7 +154,7 @@ sub run_fcgi_envvar_request } if(defined($fcgi_port)) { - if ($r->code ge '500') { + if ($r->code ge '400') { # Unknown failure, probably the request didn't hit the FCGI child # process, so it will hang waiting for our request kill 'TERM', $child; @@ -328,6 +328,11 @@ foreach my $url (@udstests) { } for my $t (@balancertests) { + if (!have_module("proxy_balancer")) { + skip "no proxy_balancer"; + skip "no proxy_balancer"; + next; + } my $url = $t->{"url"}; my $pathinfo = $t->{"pathinfo"}; $envs = run_fcgi_envvar_request($fcgi_port, $url); diff --git a/debian/perl-framework/t/modules/setenvif.t b/debian/perl-framework/t/modules/setenvif.t index cb561c28..0b13fb81 100644 --- a/debian/perl-framework/t/modules/setenvif.t +++ b/debian/perl-framework/t/modules/setenvif.t @@ -56,7 +56,7 @@ my @var = qw(VAR_ONE VAR_TWO VAR_THREE); my $htaccess = "$htdocs/modules/setenvif/htaccess/.htaccess"; -plan tests => @var * 10 + (keys %var_att) * 6 * @var + 4, +plan tests => @var * 10 + (keys %var_att) * 6 * @var + 5, have_module qw(setenvif include); sub write_htaccess { @@ -178,6 +178,16 @@ else { skip "skipping inverted match test with version <2.4.38" } +if (need_min_apache_version("2.4.67")) { + # file() access should be disallowed in htaccess context + write_htaccess("SetEnvIfExpr \"file('$htdocs/foobar.html') =~ /(.+)/\" VAR_ONE=\$0"); + $body = GET_BODY $page; + ok ! t_cmp($body, qr/^1:foobar/); +} +else { + skip "skipping test for CVE-2026-24072 in version <2.4.67"; +} + ## i think this should work, but it doesnt. ## leaving it commented now pending investigation. ## seems you cant override variables that have been previously set.

