diff options
Diffstat (limited to 'webserver')
| -rwxr-xr-x | webserver | 20 | 
1 files changed, 12 insertions, 8 deletions
| @@ -37,7 +37,7 @@ use warnings;  # as "www-data:www-data"; bind(2)'ing to a privileged port such as 80 is  # not a problem since FD can be bound as root prior to the execve(2). -use Errno 'EINTR'; +use Errno qw/EINTR ENOENT/;  use Fcntl qw/O_NOFOLLOW O_RDONLY/;  use Socket qw/AF_UNIX AF_INET AF_INET6/; @@ -92,19 +92,23 @@ while (1) {      while (defined (my $h = $conn->getline())) { last if $h eq "\r\n" };      my ($status_line, $content_type, $content); -    if ($req =~ /\A\Q$ROOT\E\/([A-Za-z0-9_\-]+)\z/ and -f $1) { -        # only open files in the cwd, and refuse to follow symlinks -        if (sysopen(my $fh, $1, O_NOFOLLOW|O_RDONLY)) { +    if ($req =~ /\A\Q$ROOT\E\/([A-Za-z0-9_\-]+)\z/) { +        # only serve base64-encoded filenames (tokens) in the cwd +        # XXX stat(2) followed by open(2) is racy; open(2) would hang if +        # an attacker manages to replace a regular file with a FIFO +        # between the syscalls, leading to denial of service; however +        # there shouldn't be any risk of information disclosure as we're +        # not following symlinks (and the cwd is not owned by us) +        if (-f $1 and sysopen(my $fh, $1, O_NOFOLLOW|O_RDONLY)) {              ($status_line, $content_type) = ('200 OK', 'application/jose+json');              $content = do { local $/ = undef; $fh->getline() };              $fh->close() or die "close: $!"; -        } -        else { -            $status_line = '403 Forbidden'; +        } elsif ($! == ENOENT) { # errno from either stat(2) or open(2) +            $status_line = '404 Not Found';          }      } -    $conn->print( "HTTP/$proto ", ($status_line // '404 Not Found'), "\r\n" ); +    $conn->print( "HTTP/$proto ", ($status_line // '403 Forbidden'), "\r\n" );      $conn->print( "Content-Type: $content_type\r\n" ) if defined $content_type;      $conn->print( "Content-Length: ".length($content)."\r\n" ) if defined $content;      $conn->print( "Connection: close\r\n" ); | 
