[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#1016391: marked as done (bullseye-pu: libhttp-daemon-perl/6.12-1+deb11u1 )



Your message dated Sat, 10 Sep 2022 13:36:19 +0100
with message-id <92fe43e7805e82e43100a6471ccbf91cd9a12944.camel@adam-barratt.org.uk>
and subject line Closing requests for updates in 11.5
has caused the Debian Bug report #1016391,
regarding bullseye-pu: libhttp-daemon-perl/6.12-1+deb11u1 
to be marked as done.

This means that you claim that the problem has been dealt with.
If this is not the case it is now your responsibility to reopen the
Bug report if necessary, and/or fix the problem forthwith.

(NB: If you are a system administrator and have no idea what this
message is talking about, this may indicate a serious mail system
misconfiguration somewhere. Please contact owner@bugs.debian.org
immediately.)


-- 
1016391: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1016391
Debian Bug Tracking System
Contact owner@bugs.debian.org with problems
--- Begin Message ---
Package: release.debian.org
Severity: normal
Tags: bulleye
User: release.debian.org@packages.debian.org
Usertags: pu


The attached debdiff for libhttp-daemon-perl fixes CVE-2022-31081 in Bullseye. This CVE has been marked as no-dsa by the security team.

The patch is accompanied by a new test and should not create any issue.
It had been used to fix unstable and will be used for Buster, <Stretch and Jessie as well.

  Thorsten
diff -Nru libhttp-daemon-perl-6.12/debian/changelog libhttp-daemon-perl-6.12/debian/changelog
--- libhttp-daemon-perl-6.12/debian/changelog	2020-06-06 03:12:55.000000000 +0200
+++ libhttp-daemon-perl-6.12/debian/changelog	2022-07-26 20:08:59.000000000 +0200
@@ -1,3 +1,11 @@
+libhttp-daemon-perl (6.12-1+deb11u1) bullseye; urgency=high
+
+  * Non-maintainer upload by the ELTS Team.
+  * CVE-2022-31081 (Closes: #1014808)
+    improved Content-Length: handling in HTTP-header
+
+ -- Thorsten Alteholz <debian@alteholz.de>  Tue, 26 Jul 2022 20:08:59 +0200
+
 libhttp-daemon-perl (6.12-1) unstable; urgency=medium
 
   * Import upstream version 6.12.
diff -Nru libhttp-daemon-perl-6.12/debian/patches/CVE-2022-31081-1.patch libhttp-daemon-perl-6.12/debian/patches/CVE-2022-31081-1.patch
--- libhttp-daemon-perl-6.12/debian/patches/CVE-2022-31081-1.patch	1970-01-01 01:00:00.000000000 +0100
+++ libhttp-daemon-perl-6.12/debian/patches/CVE-2022-31081-1.patch	2022-07-26 20:08:59.000000000 +0200
@@ -0,0 +1,48 @@
+commit e84475de51d6fd7b29354a997413472a99db70b2
+Author: Theo van Hoesel <tvanhoesel@perceptyx.com>
+Date:   Thu Jun 16 08:28:30 2022 +0000
+
+    Fix Content-Length ', '-separated string issues
+    
+    After a security issue, we ensure we comply to
+    RFC-7230 -- HTTP/1.1 Message Syntax and Routing
+    - section 3.3.2 -- Content-Length
+    - section 3.3.3 -- Message Body Length
+
+diff --git a/lib/HTTP/Daemon.pm b/lib/HTTP/Daemon.pm
+index c0cdf76..a5112b3 100644
+--- a/lib/HTTP/Daemon.pm
++++ b/lib/HTTP/Daemon.pm
+@@ -288,6 +288,32 @@ READ_HEADER:
+     }
+     elsif ($ct_len) {
+ 
++        # After a security issue, we ensure we comply to
++        # RFC-7230 -- HTTP/1.1 Message Syntax and Routing
++        # section 3.3.2 -- Content-Length
++        # section 3.3.3 -- Message Body Length
++
++        # split and clean up Content-Length ', ' separated string
++        my @vals = map {my $str = $_; $str =~ s/^\s+//; $str =~ s/\s+$//; $str }
++            split ',', $ct_len;
++        # check that they are all numbers (RFC: Content-Length = 1*DIGIT)
++        my @nums = grep { /^[0-9]+$/} @vals;
++        unless (@vals == @nums) {
++            $self->send_error(400);
++            $self->reason("Content-Length value must be a unsigned integer");
++            return;
++        }
++        # check they are all the same
++        my $ct_len = shift @nums;
++        foreach (@nums) {
++            next if $_ == $ct_len;
++            $self->send_error(400);
++            $self->reason("Content-Length values are not the same");
++            return;
++        }
++        # ensure we have now a fixed header, with only 1 value
++        $r->header('Content-Length' => $ct_len);
++
+         # Plain body specified by "Content-Length"
+         my $missing = $ct_len - length($buf);
+         while ($missing > 0) {
diff -Nru libhttp-daemon-perl-6.12/debian/patches/CVE-2022-31081-2.patch libhttp-daemon-perl-6.12/debian/patches/CVE-2022-31081-2.patch
--- libhttp-daemon-perl-6.12/debian/patches/CVE-2022-31081-2.patch	1970-01-01 01:00:00.000000000 +0100
+++ libhttp-daemon-perl-6.12/debian/patches/CVE-2022-31081-2.patch	2022-07-26 20:08:59.000000000 +0200
@@ -0,0 +1,33 @@
+commit 8dc5269d59e2d5d9eb1647d82c449ccd880f7fd0
+Author: Theo van Hoesel <tvanhoesel@perceptyx.com>
+Date:   Tue Jun 21 20:00:47 2022 +0000
+
+    Include reason in response body content
+
+diff --git a/lib/HTTP/Daemon.pm b/lib/HTTP/Daemon.pm
+index a5112b3..2d022ae 100644
+--- a/lib/HTTP/Daemon.pm
++++ b/lib/HTTP/Daemon.pm
+@@ -299,16 +299,18 @@ READ_HEADER:
+         # check that they are all numbers (RFC: Content-Length = 1*DIGIT)
+         my @nums = grep { /^[0-9]+$/} @vals;
+         unless (@vals == @nums) {
+-            $self->send_error(400);
+-            $self->reason("Content-Length value must be a unsigned integer");
++            my $reason = "Content-Length value must be an unsigned integer";
++            $self->send_error(400, $reason);
++            $self->reason($reason);
+             return;
+         }
+         # check they are all the same
+         my $ct_len = shift @nums;
+         foreach (@nums) {
+             next if $_ == $ct_len;
+-            $self->send_error(400);
+-            $self->reason("Content-Length values are not the same");
++            my $reason = "Content-Length values are not the same";
++            $self->send_error(400, $reason);
++            $self->reason($reason);
+             return;
+         }
+         # ensure we have now a fixed header, with only 1 value
diff -Nru libhttp-daemon-perl-6.12/debian/patches/CVE-2022-31081-rename.patch libhttp-daemon-perl-6.12/debian/patches/CVE-2022-31081-rename.patch
--- libhttp-daemon-perl-6.12/debian/patches/CVE-2022-31081-rename.patch	1970-01-01 01:00:00.000000000 +0100
+++ libhttp-daemon-perl-6.12/debian/patches/CVE-2022-31081-rename.patch	2022-07-26 20:08:59.000000000 +0200
@@ -0,0 +1,102 @@
+commit 331d5c1d1f0e48e6b57ef738c2a8509b1eb53376
+Author: Theo van Hoesel <tvanhoesel@perceptyx.com>
+Date:   Thu Jun 16 08:17:39 2022 +0000
+
+    Rename variables
+    
+    can not remember 2-letter abreviation more than 100 lines below
+
+diff --git a/lib/HTTP/Daemon.pm b/lib/HTTP/Daemon.pm
+index a02486c..c0cdf76 100644
+--- a/lib/HTTP/Daemon.pm
++++ b/lib/HTTP/Daemon.pm
+@@ -192,9 +192,9 @@ READ_HEADER:
+     }
+ 
+     # Find out how much content to read
+-    my $te  = $r->header('Transfer-Encoding');
+-    my $ct  = $r->header('Content-Type');
+-    my $len = $r->header('Content-Length');
++    my $tr_enc  = $r->header('Transfer-Encoding');
++    my $ct_type = $r->header('Content-Type');
++    my $ct_len  = $r->header('Content-Length');
+ 
+     # Act on the Expect header, if it's there
+     for my $e ($r->header('Expect')) {
+@@ -209,7 +209,7 @@ READ_HEADER:
+         }
+     }
+ 
+-    if ($te && lc($te) eq 'chunked') {
++    if ($tr_enc && lc($tr_enc) eq 'chunked') {
+ 
+         # Handle chunked transfer encoding
+         my $body = "";
+@@ -280,32 +280,32 @@ READ_HEADER:
+         $r->push_header($key, $val) if $key;
+ 
+     }
+-    elsif ($te) {
++    elsif ($tr_enc) {
+         $self->send_error(501);    # Unknown transfer encoding
+-        $self->reason("Unknown transfer encoding '$te'");
++        $self->reason("Unknown transfer encoding '$tr_enc'");
+         return;
+ 
+     }
+-    elsif ($len) {
++    elsif ($ct_len) {
+ 
+         # Plain body specified by "Content-Length"
+-        my $missing = $len - length($buf);
++        my $missing = $ct_len - length($buf);
+         while ($missing > 0) {
+             print "Need $missing more bytes of content\n" if $DEBUG;
+             my $n = $self->_need_more($buf, $timeout, $fdset);
+             return unless $n;
+             $missing -= $n;
+         }
+-        if (length($buf) > $len) {
+-            $r->content(substr($buf, 0, $len));
+-            substr($buf, 0, $len) = '';
++        if (length($buf) > $ct_len) {
++            $r->content(substr($buf, 0, $ct_len));
++            substr($buf, 0, $ct_len) = '';
+         }
+         else {
+             $r->content($buf);
+             $buf = '';
+         }
+     }
+-    elsif ($ct && $ct =~ m/^multipart\/\w+\s*;.*boundary\s*=\s*("?)(\w+)\1/i) {
++    elsif ($ct_type && $ct_type =~ m/^multipart\/\w+\s*;.*boundary\s*=\s*("?)(\w+)\1/i) {
+ 
+         # Handle multipart content type
+         my $boundary = "$CRLF--$2--";
+@@ -497,8 +497,8 @@ sub send_redirect {
+     print $self "Location: $loc$CRLF";
+ 
+     if ($content) {
+-        my $ct = $content =~ /^\s*</ ? "text/html" : "text/plain";
+-        print $self "Content-Type: $ct$CRLF";
++        my $ct_type = $content =~ /^\s*</ ? "text/html" : "text/plain";
++        print $self "Content-Type: $ct_type$CRLF";
+     }
+     print $self $CRLF;
+     print $self $content if $content && !$self->head_request;
+@@ -537,12 +537,12 @@ sub send_file_response {
+         local (*F);
+         sysopen(F, $file, 0) or return $self->send_error(RC_FORBIDDEN);
+         binmode(F);
+-        my ($ct, $ce) = guess_media_type($file);
++        my ($mime_type, $file_enc) = guess_media_type($file);
+         my ($size, $mtime) = (stat _)[7, 9];
+         unless ($self->antique_client) {
+             $self->send_basic_header;
+-            print $self "Content-Type: $ct$CRLF";
+-            print $self "Content-Encoding: $ce$CRLF" if $ce;
++            print $self "Content-Type: $mime_type$CRLF";
++            print $self "Content-Encoding: $file_enc$CRLF" if $file_enc;
+             print $self "Content-Length: $size$CRLF" if $size;
+             print $self "Last-Modified: ", time2str($mtime), "$CRLF" if $mtime;
+             print $self $CRLF;
diff -Nru libhttp-daemon-perl-6.12/debian/patches/CVE-2022-31081-testcase.patch libhttp-daemon-perl-6.12/debian/patches/CVE-2022-31081-testcase.patch
--- libhttp-daemon-perl-6.12/debian/patches/CVE-2022-31081-testcase.patch	1970-01-01 01:00:00.000000000 +0100
+++ libhttp-daemon-perl-6.12/debian/patches/CVE-2022-31081-testcase.patch	2022-07-26 20:08:59.000000000 +0200
@@ -0,0 +1,292 @@
+commit faebad54455c2c2919e234202362570925fb99d1
+Author: Theo van Hoesel <tvanhoesel@perceptyx.com>
+Date:   Tue Jun 21 20:30:36 2022 +0000
+
+    Add new test for Content-Length issues
+    
+    prove we fixed CVE-2022-31081
+
+diff --git a/t/content_length.t b/t/content_length.t
+new file mode 100644
+index 0000000..1751845
+--- /dev/null
++++ b/t/content_length.t
+@@ -0,0 +1,278 @@
++use strict;
++use warnings;
++
++use Test::More 0.98;
++
++use Config;
++
++use HTTP::Daemon;
++use HTTP::Response;
++use HTTP::Status;
++use HTTP::Tiny 0.042;
++
++patch_http_tiny(); # do not fix Content-Length, we want to forge something bad
++
++plan skip_all => "This system cannot fork" unless can_fork();
++
++my $BASE_URL;
++my @TESTS = get_tests();
++
++for my $test (@TESTS) {
++    
++    my $http_daemon = HTTP::Daemon->new() or die "HTTP::Daemon->new: $!";
++    $BASE_URL = $http_daemon->url;
++
++    my $pid = fork;
++    die "fork: $!" if !defined $pid;
++    if ($pid == 0) {
++        accept_requests($http_daemon);
++    }
++    
++    my $resp = http_test_request($test);
++    
++    ok $resp, $test->{title};
++    
++    is $resp->{status}, $test->{status},
++        "... and has expected status";
++    
++    like $resp->{content}, $test->{like},
++        "... and body does match"
++        if $test->{like};
++    
++}
++
++done_testing;
++
++
++
++sub get_tests{
++    {
++        title   => "Hello World Request ... it works as expected",
++        path    => "hello-world",
++        status  => 200,
++        like    => qr/^Hello World$/,
++    },
++    {
++        title   => "Positive Content Length",
++        method  => "POST",
++        headers => {
++            'Content-Length' => '+1', # quotes are needed to retain plus-sign
++        },
++        status  => 400,
++        like    => qr/value must be an unsigned integer/,
++    },
++    {
++        title   => "Negative Content Length",
++        method  => "POST",
++        headers => {
++            'Content-Length' => '-1',
++        },
++        status  => 400,
++        like    => qr/value must be an unsigned integer/,
++    },
++    {
++        title   => "Non Integer Content Length",
++        method  => "POST",
++        headers => {
++            'Content-Length' => '3.14',
++        },
++        status  => 400,
++        like    => qr/value must be an unsigned integer/,
++    },
++    {
++        title   => "Explicit Content Length ... with exact length",
++        method  => "POST",
++        headers => {
++            'Content-Length' => '8',
++        },
++        body    => "ABCDEFGH",
++        status  => 200,
++        like    => qr/^ABCDEFGH$/,
++    },
++    {
++        title   => "Implicit Content Length ... will always pass",
++        method  => "POST",
++        body    => "ABCDEFGH",
++        status  => 200,
++        like    => qr/^ABCDEFGH$/,
++    },
++    {
++        title   => "Shorter Content Length ... gets truncated",
++        method  => "POST",
++        headers => {
++            'Content-Length' => '4',
++        },
++        body    => "ABCDEFGH",
++        status  => 200,
++        like    => qr/^ABCD$/,
++    },
++    {
++        title   => "Different Content Length ... must fail",
++        method  => "POST",
++        headers => {
++            'Content-Length' => ['8', '4'],
++        },
++        body    => "ABCDEFGH",
++        status  => 400,
++        like    => qr/values are not the same/,
++    },
++    {
++        title   => "Underscore Content Length ... must match",
++        method  => "POST",
++        headers => {
++            'Content_Length' => '4',
++        },
++        body    => "ABCDEFGH",
++        status  => 400,
++        like    => qr/values are not the same/,
++    },
++    {
++        title   => "Longer Content Length ... gets timeout",
++        method  => "POST",
++        headers => {
++            'Content-Length' => '9',
++        },
++        body    => "ABCDEFGH",
++        status  => 599, # silly code !!!
++        like    => qr/^Timeout/,
++    },
++
++}
++
++
++
++sub router_table {
++    {
++        '/hello-world' => {
++            'GET' => sub {
++                my $resp = HTTP::Response->new(200);
++                $resp->content('Hello World');
++                return $resp;
++            },
++        },
++        
++        '/' => {
++            'POST' => sub {
++                my $rqst = shift;
++                
++                my $body = $rqst->content();
++                
++                my $resp = HTTP::Response->new(200);
++                $resp->content($body);
++                
++                return $resp
++            },
++        },
++    }
++}
++
++
++
++sub can_fork {
++    $Config{d_fork} || (($^O eq 'MSWin32' || $^O eq 'NetWare')
++    and $Config{useithreads}
++    and $Config{ccflags} =~ /-DPERL_IMPLICIT_SYS/);
++}
++
++
++
++# run the mini HTTP dispatcher that can handle various routes / methods
++sub accept_requests{
++    my $http_daemon = shift;
++    while (my $conn = $http_daemon->accept) {
++        while (my $rqst = $conn->get_request) {
++            if (my $resp = dispatch_request($rqst)) {
++                $conn->send_response($resp);
++            }
++        }
++        $conn->close;
++        undef($conn);
++        $http_daemon->close;
++        exit 1;
++    }
++}
++
++
++
++sub dispatch_request{
++    my $rqst = shift
++        or return;
++    my $path = $rqst->uri->path
++        or return;
++    my $meth = $rqst->method
++        or return;
++    my $code =  router_table()->{$path}{$meth}
++        or return HTTP::Response->new(RC_NOT_FOUND);
++    my $resp = $code->($rqst);
++    return $resp;
++}
++
++
++
++sub http_test_request {
++    my $test = shift;
++    my $http_client = HTTP::Tiny->new(
++        timeout => 5,
++        proxy => undef,
++        http_proxy => undef,
++        https_proxy => undef,
++    );
++    my $resp;
++    eval {
++        local $SIG{ALRM} = sub { die "Timeout\n" };
++        alarm 2;
++        $resp = $http_client->request(
++            $test->{method} || "GET",
++            $BASE_URL . ($test->{path} || ""),
++            {
++                headers => $test->{headers},
++                content => $test->{body}
++            },
++        );
++    };
++    my $err = $@;
++    alarm 0;
++    diag $err if $err;
++
++    return $resp
++}
++
++
++
++sub patch_http_tiny {
++    
++    # we need to patch write_content_body
++    # this is part of HTTP::Tiny internal module HTTP::Tiny::Handle
++    #
++    # the below code is from the original HTTP::Tiny module, where just two lines
++    # have been commented out
++    
++    no strict 'refs';
++    
++    *HTTP::Tiny::Handle::write_content_body = sub {
++        @_ == 2 || die(q/Usage: $handle->write_content_body(request)/ . "\n");
++        my ($self, $request) = @_;
++        
++        my ($len, $content_length) = (0, $request->{headers}{'content-length'});
++        while () {
++            my $data = $request->{cb}->();
++            
++            defined $data && length $data
++                or last;
++            
++            if ( $] ge '5.008' ) {
++                utf8::downgrade($data, 1)
++                    or die(qq/Wide character in write_content()\n/);
++            }
++            
++            $len += $self->write($data);
++        }
++        
++#       this should not be checked during our tests, we want to forge bad requests
++#       
++#       $len == $content_length
++#           or die(qq/Content-Length mismatch (got: $len expected: $content_length)\n/);
++        
++        return $len;
++    };
++}
diff -Nru libhttp-daemon-perl-6.12/debian/patches/series libhttp-daemon-perl-6.12/debian/patches/series
--- libhttp-daemon-perl-6.12/debian/patches/series	1970-01-01 01:00:00.000000000 +0100
+++ libhttp-daemon-perl-6.12/debian/patches/series	2022-07-26 20:08:59.000000000 +0200
@@ -0,0 +1,4 @@
+CVE-2022-31081-testcase.patch
+CVE-2022-31081-rename.patch
+CVE-2022-31081-1.patch
+CVE-2022-31081-2.patch

--- End Message ---
--- Begin Message ---
Package: release.debian.org
Version: 11.5

Hi,

The updates referred to in each of these bugs were included in today's
11.5 point release.

Regards,

Adam

--- End Message ---

Reply to: