Collect file name validation patterns
authorJacob Bachmeyer <jcb@gnu.org>
Sun, 9 Oct 2022 03:08:42 +0000 (22:08 -0500)
committerJacob Bachmeyer <jcb@gnu.org>
Sun, 9 Oct 2022 03:08:42 +0000 (22:08 -0500)
This results in some small semantic changes:  the new patterns are
stricter in handling symlink arguments and slightly looser for the
directory line.  However, this improves consistency, since the old
patterns could imply directories that could not be directly named.

gatekeeper.pl

index 5d18b4837fb778ce032c93245b442dbc45764de9..bef37b688756d0ac2c4aedc0bf68782af060ee48 100755 (executable)
@@ -439,6 +439,27 @@ if (IN_TEST_MODE) {
 openlog(SYSLOG_APP_IDENT, 'pid', SYSLOG_FACILITY);
 ftp_syslog('info', "Beginning upload processing run.");
 
+#
+# -- Filename validation patterns
+#
+
+# Directives use POSIX-style filenames, regardless of what platform we are
+# actually running on, even though this tool expects to be run on the GNU
+# system, which uses POSIX filename syntax.
+require File::Spec::Unix;      # ensure that File::Spec::Unix is loaded
+# note that loading File::Spec earlier almost certainly made this a no-op
+
+# regex matching an acceptable filename in "this" directory
+#  must begin with alphanumeric, underscore, or plus sign
+#  and contain only those characters, plus hyphen, dot, and tilde
+#  note that an acceptable filename may not begin with dot, so ".." is out
+my $RE_filename_here = qr/[[:alnum:]_+][-.[:alnum:]_+~]*/;
+# regex matching an acceptable relative filename
+#  all components must be acceptable filenames
+#  empty components are not allowed
+#  a trailing slash is not allowed
+my $RE_filename_relative = qr[$RE_filename_here(?:/$RE_filename_here)*];
+
 #
 # -- Configuration sanity check
 #
@@ -835,7 +856,7 @@ sub scan_incoming {
   while (my $tainted_ent = readdir (INCOMING)) {
     # don't look at files with a leading dot or dash, but allow those chars
     # subsequently.  Omit files containing any other weird characters.
-    next unless $tainted_ent =~ /^([\w_+][-.\w_+~]*)$/;
+    next unless $tainted_ent =~ /^($RE_filename_here)$/;
     my $ent = $1;
 
     # Don't look at files with really long names, either.
@@ -1022,7 +1043,7 @@ sub parse_directory_line {
   # Can't let it start with  - . /  or contain strange characters.
   # This disallows .. as a file name component since no component
   # can start with a . at all.
-  $tainted_val =~ m,^(\w[-.\w]*(/\w[-.\w]*)*)$,
+  $tainted_val =~ m,^($RE_filename_relative)$,
     or fatal("invalid directory $tainted_val\n$directive_file_contents",
             1,$directive_file_contents);
   my $val = $1;  # so far so good
@@ -1191,7 +1212,7 @@ sub read_directive_file {
       parse_directory_line($tainted_val, $directive_file_contents,0);
     } elsif ($tainted_cmd =~ /^Filename:?$/i) {
       # We use the same filename restrictions as scan_incoming
-      $tainted_val =~ /^([\w_+][-.\w_+~]*)$/
+      $tainted_val =~ /^($RE_filename_here)$/
        or fatal("invalid filename $tainted_val",1,$directive_file_contents);
       my $val = $1;  # so far so good
 
@@ -1217,7 +1238,7 @@ sub read_directive_file {
 
       $info{"version"} = $val; #ok.
     } elsif ($tainted_cmd =~ /^symlink:?$/i) {
-      $tainted_val =~ /^([\w_+][-.\w_+\/]*)\s+([\w_+][-.\w_+\/]*)$/
+      $tainted_val =~ /^($RE_filename_relative)\s+($RE_filename_relative)$/
        or fatal("invalid parameters for symlink command: $tainted_val",
                 1,$directive_file_contents);
       my ($target,$link) = ($1,$2);  # so far so good
@@ -1226,7 +1247,7 @@ sub read_directive_file {
        if ($target =~ /\.\./ || $link =~ /\.\./);
       $info{"symlink-$target"} = {"link" => $link, "order" => $cnt++}; #ok.
     } elsif ($tainted_cmd =~ /^rmsymlink:?$/i) {
-      $tainted_val =~ /^([\w_+][-.\w_+\/]*)$/
+      $tainted_val =~ /^($RE_filename_relative)$/
        or fatal("invalid parameters for rmsymlink command: $tainted_val",
                 1,$directive_file_contents);
       my $val = $1;  # so far so good
@@ -1235,7 +1256,7 @@ sub read_directive_file {
        if ($val =~ /\.\./);
       $info{"rmsymlink-$1"} = {"order" => $cnt++}; #ok.
     } elsif ($tainted_cmd =~ /^archive:?$/i) {
-      $tainted_val =~ /^([\w_+][-.\w_+\/]*)$/
+      $tainted_val =~ /^($RE_filename_relative)$/
        or fatal("invalid parameters for archive command: $tainted_val",
                 1,$directive_file_contents);
       my $val = $1;  # so far so good