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
#
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.
# 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
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
$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
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
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