about summary refs log tree commit homepage
path: root/lib
diff options
context:
space:
mode:
authorEric Wong <e@80x24.org>2020-01-01 10:38:58 +0000
committerEric Wong <e@80x24.org>2020-01-01 19:11:07 +0000
commit8c21d7dc061b5e53eb096f5e7edcb1cd16c0f959 (patch)
tree21d53e5161667dd47ac2b9dcc000822ad3349061 /lib
parent7dddc72dd59b8947696cf0563034fda672b67905 (diff)
downloadpublic-inbox-8c21d7dc061b5e53eb096f5e7edcb1cd16c0f959.tar.gz
We can use Perl's sysopen function to pass O_NONBLOCK to open(2)
and avoid blocking on FIFOs.  This avoids a TOCTTOU race where
somebody can change a regular to FIFO in between the stat(2) and
open(2) syscalls.
Diffstat (limited to 'lib')
-rw-r--r--lib/PublicInbox/WwwStatic.pm17
1 files changed, 11 insertions, 6 deletions
diff --git a/lib/PublicInbox/WwwStatic.pm b/lib/PublicInbox/WwwStatic.pm
index 093a7920..ce4bfe9b 100644
--- a/lib/PublicInbox/WwwStatic.pm
+++ b/lib/PublicInbox/WwwStatic.pm
@@ -4,9 +4,10 @@
 package PublicInbox::WwwStatic;
 use strict;
 use parent qw(Exporter);
-use Fcntl qw(:seek);
+use Fcntl qw(SEEK_SET O_RDONLY O_NONBLOCK);
 use HTTP::Date qw(time2str);
 use HTTP::Status qw(status_message);
+use Errno qw(EACCES ENOTDIR ENOENT);
 our @EXPORT_OK = qw(@NO_CACHE r);
 
 our @NO_CACHE = ('Expires', 'Fri, 01 Jan 1980 00:00:00 GMT',
@@ -70,15 +71,19 @@ sub prepare_range {
 
 sub response {
         my ($env, $h, $path, $type) = @_;
-        return r(404) unless -f $path && -r _; # just in case it's a FIFO :P
 
-        my ($size, $in);
+        my $in;
         if ($env->{REQUEST_METHOD} eq 'HEAD') {
-                $size = -s _;
+                return r(404) unless -f $path && -r _; # in case it's a FIFO :P
         } else { # GET, callers should've already filtered out other methods
-                open $in, '<', $path or return r(403);
-                $size = -s $in;
+                if (!sysopen($in, $path, O_RDONLY|O_NONBLOCK)) {
+                        return r(404) if $! == ENOENT || $! == ENOTDIR;
+                        return r(403) if $! == EACCES;
+                        return r(500);
+                }
+                return r(404) unless -f $in;
         }
+        my $size = -s _; # bare "_" reuses "struct stat" from "-f" above
         my $mtime = time2str((stat(_))[9]);
 
         if (my $ims = $env->{HTTP_IF_MODIFIED_SINCE}) {