[Git][debian-proftpd-team/proftpd][master] Add patch for Issue #1886.

Hilmar Preuße (@hilmar) hille42 at debian.org
Sat Apr 19 09:48:09 BST 2025



Hilmar Preuße pushed to branch master at Debian ProFTPD Team / proftpd


Commits:
fca1c005 by Hilmar Preuße at 2025-04-19T10:47:45+02:00
Add patch for Issue #1886.

- - - - -


3 changed files:

- debian/changelog
- + debian/patches/536608f849dddd2b551e7c1e4fd930a50a527ffe.diff
- debian/patches/series


Changes:

=====================================
debian/changelog
=====================================
@@ -1,3 +1,10 @@
+proftpd-dfsg (1.3.9~dfsg-2) UNRELEASED; urgency=medium
+
+  *  Add patch for Issue #1886: Fix the handling of SCP target paths
+     that might contain spaces.
+
+ -- Hilmar Preuße <hille42 at debian.org>  Sat, 19 Apr 2025 10:46:47 +0200
+
 proftpd-dfsg (1.3.9~dfsg-1) experimental; urgency=medium
 
   * New upstream version.


=====================================
debian/patches/536608f849dddd2b551e7c1e4fd930a50a527ffe.diff
=====================================
@@ -0,0 +1,961 @@
+From 536608f849dddd2b551e7c1e4fd930a50a527ffe Mon Sep 17 00:00:00 2001
+From: TJ Saunders <tj at castaglia.org>
+Date: Sun, 23 Mar 2025 12:47:04 -0700
+Subject: [PATCH] Issue #1886: Fix the handling of SCP target paths that might
+ contain spaces.
+
+---
+ contrib/mod_sftp/scp.c                        | 229 +++++-----
+ tests/t/lib/ProFTPD/Tests/Modules/mod_sftp.pm | 399 +++++++++++++++---
+ 2 files changed, 475 insertions(+), 153 deletions(-)
+
+diff --git a/contrib/mod_sftp/scp.c b/contrib/mod_sftp/scp.c
+index 397cea41c..ea8154343 100644
+--- a/contrib/mod_sftp/scp.c
++++ b/contrib/mod_sftp/scp.c
+@@ -1,6 +1,6 @@
+ /*
+  * ProFTPD - mod_sftp SCP
+- * Copyright (c) 2008-2023 TJ Saunders
++ * Copyright (c) 2008-2025 TJ Saunders
+  *
+  * This program is free software; you can redistribute it and/or modify
+  * it under the terms of the GNU General Public License as published by
+@@ -2458,13 +2458,36 @@ int sftp_scp_handle_packet(pool *p, void *ssh2, uint32_t channel_id,
+   return 0;
+ }
+ 
++static char *scp_canonicalize_target(pool *p, char *target) {
++  size_t target_len;
++
++  target_len = strlen(target);
++
++  /* Remove any enclosing shell quotations, e.g. single and double quotation
++   * marks.  Some SCP clients (i.e. newer libssh2) will quote the paths,
++   * assuming that the handling server (us) uses a shell to handle the
++   * command.  Sigh.
++   */
++
++  if ((target[0] == '\'' &&
++       target[target_len-1] == '\'') ||
++      (target[0] == '"' &&
++       target[target_len-1] == '"')) {
++    target[target_len-1] = '\0';
++    return pstrdup(p, target + 1);
++  }
++
++  return target;
++}
++
+ int sftp_scp_set_params(pool *p, uint32_t channel_id, array_header *req) {
+   register unsigned int i;
+   int optc, use_glob = TRUE;
+-  char **reqargv;
++  char **reqargv, *target_path = NULL;
+   const char *opts = "dfprtv";
+   config_rec *c;
+   struct scp_paths *paths;
++  struct scp_path *sp;
+ 
+   if (!(sftp_services & SFTP_SERVICE_FL_SCP)) {
+     (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION,
+@@ -2487,13 +2510,13 @@ int sftp_scp_set_params(pool *p, uint32_t channel_id, array_header *req) {
+ 
+   reqargv = (char **) req->elts;
+   for (i = 0; i < req->nelts; i++) {
+-    if (reqargv[i]) {
++    if (reqargv[i] != NULL) {
+       pr_trace_msg(trace_channel, 5, "reqargv[%u] = '%s'", i, reqargv[i]);
+     }
+   }
+ 
+   c = find_config(main_server->conf, CONF_PARAM, "UseGlobbing", FALSE);
+-  if (c) {
++  if (c != NULL) {
+     use_glob = *((unsigned char *) c->argv[0]);
+   }
+ 
+@@ -2546,128 +2569,130 @@ int sftp_scp_set_params(pool *p, uint32_t channel_id, array_header *req) {
+     return -1;
+   }
+ 
+-  /* Make a copy of the remaining paths, for later handling. */
++  /* Make a copy of the remaining paths, for later handling.
++   *
++   * Normally there's only one path, but we might be handling a glob which
++   * matches multiple paths.
++   */
+   paths->paths = make_array(paths->pool, 1, sizeof(struct scp_path *));
+   paths->path_idx = 0;
+ 
++  /* First concatenate any remaining command-line arguments, using spaces,
++   * in order to handle paths that contain spaces; see Issue #1886.
++   */
+   for (i = optind; i < req->nelts; i++) {
+     pr_signals_handle();
+ 
+-    if (reqargv[i]) {
+-      struct scp_path *sp;
+-      size_t pathlen;
+-      char *glob_path;
++    if (reqargv[i] != NULL) {
++      size_t reqarg_len;
+ 
+-      if (use_glob &&
+-          (scp_opts & SFTP_SCP_OPT_ISSRC) &&
+-          strpbrk(reqargv[i], "{[*?") != NULL) {
+-        int res, xerrno;
+-        glob_t gl;
++      /* Note that we might be dealing with an empty-length argument, which
++       * we get from pr_str_get_word() parsing the client-provided string,
++       * which might have spaces.  Watch for them.
++       */
+ 
+-        /* Whee, glob characters.  Need to expand the pattern to the
+-         * list of matching files, just as the shell would do.
+-         */
++      reqarg_len = strlen(reqargv[i]);
++      if (reqarg_len == 0) {
++        continue;
++      }
+ 
+-        memset(&gl, 0, sizeof(gl));
++      if (target_path != NULL) {
++        target_path = pstrcat(p, target_path, " ", reqargv[i], NULL);
+ 
+-        glob_path = pstrdup(paths->pool, reqargv[i]);
+-        pathlen = strlen(glob_path);
++      } else {
++        target_path = pstrdup(p, reqargv[i]);
++      }
++    }
++  }
+ 
+-        /* Remove any enclosing shell quotations, e.g. single and double
+-         * quotation marks.  Some SCP clients (i.e. newer libssh2) will
+-         * quote the paths, assuming that the handling server (us) uses
+-         * a shell to handle the command.  Sigh.
+-         */
+-        if ((glob_path[0] == '\'' &&
+-             glob_path[pathlen-1] == '\'') ||
+-            (glob_path[0] == '"' &&
+-             glob_path[pathlen-1] == '"')) {
+-          glob_path[pathlen-1] = '\0';
+-          glob_path = (glob_path + 1);
+-        }
++  target_path = scp_canonicalize_target(p, target_path);
+ 
+-        res = pr_fs_glob(glob_path, GLOB_NOSORT|GLOB_BRACE, NULL, &gl);
+-        switch (res) {
+-          case 0: {
+-            register unsigned int j;
++  if (use_glob == TRUE &&
++      (scp_opts & SFTP_SCP_OPT_ISSRC) &&
++      strpbrk(target_path, "{[*?") != NULL) {
++    int res, xerrno;
++    char *glob_path;
++    size_t path_len;
++    glob_t gl;
+ 
+-            for (j = 0; j < gl.gl_pathc; j++) {
+-              pr_signals_handle();
++    /* Whee, glob characters.  Need to expand the pattern to the
++     * list of matching files, just as the shell would do.
++     */
+ 
+-              sp = pcalloc(paths->pool, sizeof(struct scp_path));
+-              sp->path = pstrdup(paths->pool, gl.gl_pathv[j]);
+-              pathlen = strlen(sp->path);
++    memset(&gl, 0, sizeof(gl));
+ 
+-              /* Trim any trailing path separators.  It's important. */
+-              while (pathlen > 1 &&
+-                     sp->path[pathlen-1] == '/') {
+-                pr_signals_handle();
+-                sp->path[--pathlen] = '\0';
+-              }
++    glob_path = pstrdup(paths->pool, target_path);
++    path_len = strlen(glob_path);
+ 
+-              sp->orig_path = pstrdup(paths->pool, sp->path);
++    res = pr_fs_glob(glob_path, GLOB_NOSORT|GLOB_BRACE, NULL, &gl);
++    switch (res) {
++      case 0: {
++        register unsigned int j;
+ 
+-              if (pathlen > 0) {
+-                *((struct scp_path **) push_array(paths->paths)) = sp;
+-              }
+-            }
++        for (j = 0; j < gl.gl_pathc; j++) {
++          pr_signals_handle();
+ 
+-            break;
++          sp = pcalloc(paths->pool, sizeof(struct scp_path));
++          sp->path = pstrdup(paths->pool, gl.gl_pathv[j]);
++          path_len = strlen(sp->path);
++
++          /* Trim any trailing path separators.  It's important. */
++          while (path_len > 1 &&
++                 sp->path[path_len-1] == '/') {
++            pr_signals_handle();
++            sp->path[--path_len] = '\0';
+           }
+ 
+-          case GLOB_NOSPACE:
+-            xerrno = errno;
+-            pr_trace_msg(trace_channel, 1, "error globbing '%s': Not "
+-              "enough memory (%s)", reqargv[i], strerror(xerrno));
+-            write_confirm(p, channel_id, 1, pstrcat(p, reqargv[i], ": ",
+-              strerror(xerrno), NULL));
+-            errno = xerrno;
+-            return 0;
+-
+-          case GLOB_NOMATCH:
+-            xerrno = ENOENT;
+-            pr_trace_msg(trace_channel, 1, "error globbing '%s': No "
+-              "matches found (%s)", reqargv[i], strerror(xerrno));
+-            write_confirm(p, channel_id, 1, pstrcat(p, reqargv[i], ": ",
+-              strerror(xerrno), NULL));
+-            errno = xerrno;
+-            return 0;
++          sp->orig_path = pstrdup(paths->pool, sp->path);
++
++          if (path_len > 0) {
++            *((struct scp_path **) push_array(paths->paths)) = sp;
++          }
+         }
+ 
+-        pr_fs_globfree(&gl);
++        break;
++      }
+ 
+-      } else {
+-        sp = pcalloc(paths->pool, sizeof(struct scp_path));
+-        sp->path = pstrdup(paths->pool, reqargv[i]);
+-        pathlen = strlen(sp->path);
+-
+-        /* Remove any enclosing shell quotations, e.g. single and double
+-         * quotation marks.  Some SCP clients (i.e. newer libssh2) will
+-         * quote the paths, assuming that the handling server (us) uses
+-         * a shell to handle the command.  Sigh.
+-         */
+-        if ((sp->path[0] == '\'' &&
+-             sp->path[pathlen-1] == '\'') ||
+-            (sp->path[0] == '"' &&
+-             sp->path[pathlen-1] == '"')) {
+-          sp->path[pathlen-1] = '\0';
+-          sp->path = (sp->path + 1);
+-          pathlen -= 2;
+-        }
++      case GLOB_NOSPACE:
++        xerrno = errno;
++        pr_trace_msg(trace_channel, 1, "error globbing '%s': Not "
++          "enough memory (%s)", reqargv[i], strerror(xerrno));
++        write_confirm(p, channel_id, 1, pstrcat(p, target_path, ": ",
++          strerror(xerrno), NULL));
++        errno = xerrno;
++        return 0;
+ 
+-        /* Trim any trailing path separators.  It's important. */
+-        while (pathlen > 1 &&
+-               sp->path[pathlen-1] == '/') {
+-          pr_signals_handle();
+-          sp->path[--pathlen] = '\0';
+-        }
++      case GLOB_NOMATCH:
++        xerrno = ENOENT;
++        pr_trace_msg(trace_channel, 1, "error globbing '%s': No "
++          "matches found (%s)", reqargv[i], strerror(xerrno));
++        write_confirm(p, channel_id, 1, pstrcat(p, target_path, ": ",
++          strerror(xerrno), NULL));
++        errno = xerrno;
++        return 0;
++    }
+ 
+-        sp->orig_path = pstrdup(paths->pool, sp->path);
++    pr_fs_globfree(&gl);
+ 
+-        if (pathlen > 0) {
+-          *((struct scp_path **) push_array(paths->paths)) = sp;
+-        }
+-      }
++  } else {
++    size_t path_len;
++
++    sp = pcalloc(paths->pool, sizeof(struct scp_path));
++    sp->path = pstrdup(paths->pool, target_path);
++
++    path_len = strlen(sp->path);
++
++    /* Trim any trailing path separators.  It's important. */
++    while (path_len > 1 &&
++           sp->path[path_len-1] == '/') {
++      pr_signals_handle();
++      sp->path[--path_len] = '\0';
++    }
++
++    sp->orig_path = pstrdup(paths->pool, sp->path);
++
++    if (path_len > 0) {
++      *((struct scp_path **) push_array(paths->paths)) = sp;
+     }
+   }
+ 
+@@ -2687,8 +2712,8 @@ int sftp_scp_set_params(pool *p, uint32_t channel_id, array_header *req) {
+     struct scp_path *sp;
+ 
+     sp = ((struct scp_path **) paths->paths->elts)[i];
+-    if (sp) {
+-      pr_trace_msg(trace_channel, 5, "scp_path[%u] = '%s'", i, sp->path);
++    if (sp != NULL) {
++      pr_trace_msg(trace_channel, 5, "scp path[%u] = '%s'", i, sp->path);
+     }
+   }
+ 
+diff --git a/tests/t/lib/ProFTPD/Tests/Modules/mod_sftp.pm b/tests/t/lib/ProFTPD/Tests/Modules/mod_sftp.pm
+index be37f1eb3..c445399ff 100644
+--- a/tests/t/lib/ProFTPD/Tests/Modules/mod_sftp.pm
++++ b/tests/t/lib/ProFTPD/Tests/Modules/mod_sftp.pm
+@@ -1676,6 +1676,11 @@ my $TESTS = {
+     test_class => [qw(forking scp ssh2)],
+   },
+ 
++  scp_upload_filename_with_spaces_issue1886 => {
++    order => ++$order,
++    test_class => [qw(bug forking scp ssh2)],
++  },
++
+   scp_upload_abs_symlink => {
+     order => ++$order,
+     test_class => [qw(forking scp ssh2)],
+@@ -1806,6 +1811,11 @@ my $TESTS = {
+     test_class => [qw(bug forking rootprivs scp ssh2)],
+   },
+ 
++  scp_download_filename_with_spaces_issue1886 => {
++    order => ++$order,
++    test_class => [qw(bug forking scp ssh2)],
++  },
++
+   scp_ext_download_bug3544 => {
+     order => ++$order,
+     test_class => [qw(bug forking scp ssh2)],
+@@ -56731,6 +56741,166 @@ sub scp_upload_largefile {
+   test_cleanup($setup->{log_file}, $ex);
+ }
+ 
++sub scp_upload_filename_with_spaces_issue1886 {
++  my $self = shift;
++  my $tmpdir = $self->{tmpdir};
++  my $setup = test_setup($tmpdir, 'scp');
++
++  my $src_file = File::Spec->rel2abs("$tmpdir/src.txt");
++  if (open(my $fh, "> $src_file")) {
++    print $fh "ABCDefgh" x 32;
++    unless (close($fh)) {
++      die("Can't write $src_file: $!");
++    }
++
++  } else {
++    die("Can't open $src_file: $!");
++  }
++
++  # Calculate the MD5 checksum of this file, for comparison with the
++  # downloaded file.
++  my $ctx = Digest::MD5->new();
++  my $expected_md5;
++
++  if (open(my $fh, "< $src_file")) {
++    binmode($fh);
++    $ctx->addfile($fh);
++    $expected_md5 = $ctx->hexdigest();
++    close($fh);
++
++  } else {
++    die("Can't read $src_file: $!");
++  }
++
++  my $dst_file = File::Spec->rel2abs("$tmpdir/test filename with spaces.txt");
++
++  my $rsa_host_key = File::Spec->rel2abs('t/etc/modules/mod_sftp/ssh_host_rsa_key');
++  my $dsa_host_key = File::Spec->rel2abs('t/etc/modules/mod_sftp/ssh_host_dsa_key');
++
++  my $config = {
++    PidFile => $setup->{pid_file},
++    ScoreboardFile => $setup->{scoreboard_file},
++    SystemLog => $setup->{log_file},
++    TraceLog => $setup->{log_file},
++    Trace => 'ssh2:10 scp:30',
++
++    AuthUserFile => $setup->{auth_user_file},
++    AuthGroupFile => $setup->{auth_group_file},
++    AuthOrder => 'mod_auth_file.c',
++
++    IfModules => {
++      'mod_delay.c' => {
++        DelayEngine => 'off',
++      },
++
++      'mod_sftp.c' => [
++        "SFTPEngine on",
++        "SFTPLog $setup->{log_file}",
++        "SFTPHostKey $rsa_host_key",
++        "SFTPHostKey $dsa_host_key",
++      ],
++    },
++  };
++
++  my ($port, $config_user, $config_group) = config_write($setup->{config_file},
++    $config);
++
++  # Open pipes, for use between the parent and child processes.  Specifically,
++  # the child will indicate when it's done with its test by writing a message
++  # to the parent.
++  my ($rfh, $wfh);
++  unless (pipe($rfh, $wfh)) {
++    die("Can't open pipe: $!");
++  }
++
++  require Net::SSH2;
++
++  my $ex;
++
++  # Ignore SIGPIPE
++  local $SIG{PIPE} = sub { };
++
++  # Fork child
++  $self->handle_sigchld();
++  defined(my $pid = fork()) or die("Can't fork: $!");
++  if ($pid) {
++    eval {
++      my $ssh2 = Net::SSH2->new();
++
++      sleep(1);
++
++      unless ($ssh2->connect('127.0.0.1', $port)) {
++        my ($err_code, $err_name, $err_str) = $ssh2->error();
++        die("Can't connect to SSH2 server: [$err_name] ($err_code) $err_str");
++      }
++
++      unless ($ssh2->auth_password($setup->{user}, $setup->{passwd})) {
++        my ($err_code, $err_name, $err_str) = $ssh2->error();
++        die("Can't login to SSH2 server: [$err_name] ($err_code) $err_str");
++      }
++
++      my $res = $ssh2->scp_put($src_file, 'test filename with spaces.txt');
++      unless ($res) {
++        my ($err_code, $err_name, $err_str) = $ssh2->error();
++        die("Can't upload to server: [$err_name] ($err_code) $err_str");
++      }
++
++      $ssh2->disconnect();
++
++      $self->assert(-f $dst_file,
++        test_msg("File '$dst_file' does not exist as expected"));
++    };
++    if ($@) {
++      $ex = $@;
++    }
++
++    $wfh->print("done\n");
++    $wfh->flush();
++
++  } else {
++    eval { server_wait($setup->{config_file}, $rfh) };
++    if ($@) {
++      warn($@);
++      exit 1;
++    }
++
++    exit 0;
++  }
++
++  # Stop server
++  server_stop($setup->{pid_file});
++  $self->assert_child_ok($pid);
++
++  if ($ex) {
++    test_cleanup($setup->{log_file}, $ex);
++  }
++
++  # Calculate the MD5 checksum of the uploaded file, for comparison with the
++  # file that was uploaded.
++  $ctx->reset();
++  my $md5;
++
++  eval {
++    if (open(my $fh, "< $dst_file")) {
++      binmode($fh);
++      $ctx->addfile($fh);
++      $md5 = $ctx->hexdigest();
++      close($fh);
++
++    } else {
++      die("Can't read $dst_file: $!");
++    }
++
++    $self->assert($expected_md5 eq $md5,
++      test_msg("Expected '$expected_md5', got '$md5'"));
++  };
++  if ($@) {
++    $ex = $@;
++  }
++
++  test_cleanup($setup->{log_file}, $ex);
++}
++
+ sub scp_upload_abs_symlink {
+   my $self = shift;
+   my $tmpdir = $self->{tmpdir};
+@@ -58019,7 +58189,8 @@ sub scp_ext_null_ptr_issue1043 {
+         '-oStrictHostKeyChecking=no',
+         '-oUserKnownHostsFile=/dev/null',
+         "$setup->{user}\@127.0.0.1",
+-        'scp'
++        'scp',
++        '-O'
+       );
+ 
+       my $scp_rh = IO::Handle->new();
+@@ -58305,6 +58476,7 @@ sub scp_ext_upload_recursive_dir_bug3447 {
+         'scp',
+         '-r',
+         '-v',
++        '-O',
+         '-oBatchMode=yes',
+         '-oCheckHostIP=no',
+         "-oPort=$port",
+@@ -58606,6 +58778,7 @@ sub scp_ext_upload_recursive_dir_bug3792 {
+         '-r',
+         '-v',
+         '-p',
++        '-O',
+         '-oBatchMode=yes',
+         '-oCheckHostIP=no',
+         "-oPort=$port",
+@@ -58922,6 +59095,7 @@ sub scp_ext_upload_recursive_dir_bug4004 {
+         '-r',
+         '-p',
+         '-v',
++        '-O',
+         '-oBatchMode=yes',
+         '-oCheckHostIP=no',
+         "-oPort=$port",
+@@ -59183,6 +59357,7 @@ sub scp_ext_upload_recursive_dirs_bug4257 {
+         '-r',
+         '-p',
+         '-v',
++        '-O',
+         '-oBatchMode=yes',
+         '-oCheckHostIP=no',
+         "-oPort=$port",
+@@ -59407,6 +59582,7 @@ sub scp_ext_upload_different_name_bug3425 {
+       my @cmd = (
+         'scp',
+         '-v',
++        '-O',
+         '-oBatchMode=yes',
+         '-oCheckHostIP=no',
+         "-oPort=$port",
+@@ -59607,6 +59783,7 @@ sub scp_ext_upload_recursive_empty_dir {
+         'scp',
+         '-r',
+         '-v',
++        '-O',
+         '-oBatchMode=yes',
+         '-oCheckHostIP=no',
+         "-oPort=$port",
+@@ -59830,6 +60007,7 @@ sub scp_ext_upload_shorter_file_bug4013 {
+       my @cmd = (
+         'scp',
+         '-v',
++        '-O',
+         '-oBatchMode=yes',
+         '-oCheckHostIP=no',
+         "-oPort=$port",
+@@ -59928,22 +60106,7 @@ sub scp_ext_upload_shorter_file_bug4013 {
+ sub scp_ext_upload_file_with_timestamp_bug4026 {
+   my $self = shift;
+   my $tmpdir = $self->{tmpdir};
+-
+-  my $config_file = "$tmpdir/sftp.conf";
+-  my $pid_file = File::Spec->rel2abs("$tmpdir/sftp.pid");
+-  my $scoreboard_file = File::Spec->rel2abs("$tmpdir/sftp.scoreboard");
+-
+-  my $log_file = test_get_logfile();
+-
+-  my $auth_user_file = File::Spec->rel2abs("$tmpdir/sftp.passwd");
+-  my $auth_group_file = File::Spec->rel2abs("$tmpdir/sftp.group");
+-
+-  my $user = 'proftpd';
+-  my $passwd = 'test';
+-  my $group = 'ftpd';
+-  my $home_dir = File::Spec->rel2abs($tmpdir);
+-  my $uid = 500;
+-  my $gid = 500;
++  my $setup = test_setup($tmpdir, 'scp');
+ 
+   my $sub_dir = File::Spec->rel2abs("$tmpdir/sub.d");
+   mkpath($sub_dir);
+@@ -59951,19 +60114,15 @@ sub scp_ext_upload_file_with_timestamp_bug4026 {
+   # Make sure that, if we're running as root, that the home directory has
+   # permissions/privs set for the account we create
+   if ($< == 0) {
+-    unless (chmod(0755, $home_dir, $sub_dir)) {
+-      die("Can't set perms on $home_dir to 0755: $!");
++    unless (chmod(0755, $sub_dir)) {
++      die("Can't set perms on $sub_dir to 0755: $!");
+     }
+ 
+-    unless (chown($uid, $gid, $home_dir, $sub_dir)) {
+-      die("Can't set owner of $home_dir to $uid/$gid: $!");
++    unless (chown($setup->{uid}, $setup->{gid}, $sub_dir)) {
++      die("Can't set owner of $sub_dir to $setup->{uid}/$setup->{gid}: $!");
+     }
+   }
+ 
+-  auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir,
+-    '/bin/bash');
+-  auth_group_write($auth_group_file, $group, $gid, $user);
+-
+   my $rsa_host_key = File::Spec->rel2abs('t/etc/modules/mod_sftp/ssh_host_rsa_key');
+   my $dsa_host_key = File::Spec->rel2abs('t/etc/modules/mod_sftp/ssh_host_dsa_key');
+ 
+@@ -59994,18 +60153,18 @@ sub scp_ext_upload_file_with_timestamp_bug4026 {
+   my $dst_file = File::Spec->rel2abs("$sub_dir/dst.txt");
+ 
+   my $config = {
+-    PidFile => $pid_file,
+-    ScoreboardFile => $scoreboard_file,
+-    SystemLog => $log_file,
+-    TraceLog => $log_file,
+-    Trace => 'DEFAULT:10 ssh2:20 sftp:20 scp:20',
++    PidFile => $setup->{pid_file},
++    ScoreboardFile => $setup->{scoreboard_file},
++    SystemLog => $setup->{log_file},
++    TraceLog => $setup->{log_file},
++    Trace => 'ssh2:20 sftp:20 scp:20',
+ 
+-    AuthUserFile => $auth_user_file,
+-    AuthGroupFile => $auth_group_file,
++    AuthUserFile => $setup->{auth_user_file},
++    AuthGroupFile => $setup->{auth_group_file},
+     AuthOrder => 'mod_auth_file.c',
+ 
+     AllowOverwrite => 'on',
+-    DefaultRoot => '~',
++#    DefaultRoot => '~',
+ 
+     IfModules => {
+       'mod_delay.c' => {
+@@ -60014,7 +60173,7 @@ sub scp_ext_upload_file_with_timestamp_bug4026 {
+ 
+       'mod_sftp.c' => [
+         "SFTPEngine on",
+-        "SFTPLog $log_file",
++        "SFTPLog $setup->{log_file}",
+         "SFTPHostKey $rsa_host_key",
+         "SFTPHostKey $dsa_host_key",
+         "SFTPAuthorizedUserKeys file:~/.authorized_keys",
+@@ -60022,7 +60181,8 @@ sub scp_ext_upload_file_with_timestamp_bug4026 {
+     },
+   };
+ 
+-  my ($port, $config_user, $config_group) = config_write($config_file, $config);
++  my ($port, $config_user, $config_group) = config_write($setup->{config_file},
++    $config);
+ 
+   # Open pipes, for use between the parent and child processes.  Specifically,
+   # the child will indicate when it's done with its test by writing a message
+@@ -60046,8 +60206,9 @@ sub scp_ext_upload_file_with_timestamp_bug4026 {
+     eval {
+       my @cmd = (
+         'scp',
+-        '-v',
++        '-vvv',
+         '-p',
++        '-O',
+         '-oBatchMode=yes',
+         '-oCheckHostIP=no',
+         "-oPort=$port",
+@@ -60056,7 +60217,7 @@ sub scp_ext_upload_file_with_timestamp_bug4026 {
+         '-oStrictHostKeyChecking=no',
+         '-oUserKnownHostsFile=/dev/null',
+         "$src_file",
+-        "$user\@127.0.0.1:sub.d/dst.txt",
++        "$setup->{user}\@127.0.0.1:sub.d/dst.txt",
+       );
+ 
+       my $scp_rh = IO::Handle->new();
+@@ -60102,6 +60263,10 @@ sub scp_ext_upload_file_with_timestamp_bug4026 {
+         $res = 1;
+       }
+ 
++if ($ENV{TEST_VERBOSE}) {
++  print STDERR "# Errstr:\n$errstr\n";
++}
++
+       unless ($res == 0) {
+         die("Can't upload $src_file to server: $errstr");
+       }
+@@ -60113,7 +60278,6 @@ sub scp_ext_upload_file_with_timestamp_bug4026 {
+       $self->assert($atime == 0, test_msg("Expected atime 0, got $atime"));
+       $self->assert($mtime == 0, test_msg("Expected mtime 0, got $mtime"));
+     };
+-
+     if ($@) {
+       $ex = $@;
+     }
+@@ -60122,7 +60286,7 @@ sub scp_ext_upload_file_with_timestamp_bug4026 {
+     $wfh->flush();
+ 
+   } else {
+-    eval { server_wait($config_file, $rfh) };
++    eval { server_wait($setup->{config_file}, $rfh) };
+     if ($@) {
+       warn($@);
+       exit 1;
+@@ -60132,18 +60296,10 @@ sub scp_ext_upload_file_with_timestamp_bug4026 {
+   }
+ 
+   # Stop server
+-  server_stop($pid_file);
+-
++  server_stop($setup->{pid_file});
+   $self->assert_child_ok($pid);
+ 
+-  if ($ex) {
+-    test_append_logfile($log_file, $ex);
+-    unlink($log_file);
+-
+-    die($ex);
+-  }
+-
+-  unlink($log_file);
++  test_cleanup($setup->{log_file}, $ex);
+ }
+ 
+ sub scp_download {
+@@ -60161,7 +60317,7 @@ sub scp_download {
+     ScoreboardFile => $setup->{scoreboard_file},
+     SystemLog => $setup->{log_file},
+     TraceLog => $setup->{log_file},
+-    Trace => 'DEFAULT:10 ssh2:20 sftp:20 scp:20',
++    Trace => 'ssh2:20 sftp:20 scp:20',
+ 
+     AuthUserFile => $setup->{auth_user_file},
+     AuthGroupFile => $setup->{auth_group_file},
+@@ -61488,6 +61644,139 @@ sub scp_download_rel_symlink_chrooted_bug4219 {
+   test_cleanup($setup->{log_file}, $ex);
+ }
+ 
++sub scp_download_filename_with_spaces_issue1886 {
++  my $self = shift;
++  my $tmpdir = $self->{tmpdir};
++  my $setup = test_setup($tmpdir, 'scp');
++
++  my $src_file = File::Spec->rel2abs("$tmpdir/src.txt");
++  if (open(my $fh, "> $src_file")) {
++    print $fh "ABCDefgh" x 32;
++    unless (close($fh)) {
++      die("Can't write $src_file: $!");
++    }
++
++  } else {
++    die("Can't open $src_file: $!");
++  }
++
++  # Calculate the MD5 checksum of this file, for comparison with the
++  # downloaded file.
++  my $ctx = Digest::MD5->new();
++  my $expected_md5;
++
++  if (open(my $fh, "< $src_file")) {
++    binmode($fh);
++    $ctx->addfile($fh);
++    $expected_md5 = $ctx->hexdigest();
++    close($fh);
++
++  } else {
++    die("Can't read $src_file: $!");
++  }
++
++  my $dst_file = File::Spec->rel2abs("$tmpdir/test filename with spaces.txt");
++
++  my $rsa_host_key = File::Spec->rel2abs('t/etc/modules/mod_sftp/ssh_host_rsa_key');
++  my $dsa_host_key = File::Spec->rel2abs('t/etc/modules/mod_sftp/ssh_host_dsa_key');
++
++  my $config = {
++    PidFile => $setup->{pid_file},
++    ScoreboardFile => $setup->{scoreboard_file},
++    SystemLog => $setup->{log_file},
++    TraceLog => $setup->{log_file},
++    Trace => 'ssh2:10 scp:20',
++
++    AuthUserFile => $setup->{auth_user_file},
++    AuthGroupFile => $setup->{auth_group_file},
++    AuthOrder => 'mod_auth_file.c',
++
++    IfModules => {
++      'mod_delay.c' => {
++        DelayEngine => 'off',
++      },
++
++      'mod_sftp.c' => [
++        "SFTPEngine on",
++        "SFTPLog $setup->{log_file}",
++        "SFTPHostKey $rsa_host_key",
++        "SFTPHostKey $dsa_host_key",
++      ],
++    },
++  };
++
++  my ($port, $config_user, $config_group) = config_write($setup->{config_file},
++    $config);
++
++  # Open pipes, for use between the parent and child processes.  Specifically,
++  # the child will indicate when it's done with its test by writing a message
++  # to the parent.
++  my ($rfh, $wfh);
++  unless (pipe($rfh, $wfh)) {
++    die("Can't open pipe: $!");
++  }
++
++  require Net::SSH2;
++
++  my $ex;
++
++  # Ignore SIGPIPE
++  local $SIG{PIPE} = sub { };
++
++  # Fork child
++  $self->handle_sigchld();
++  defined(my $pid = fork()) or die("Can't fork: $!");
++  if ($pid) {
++    eval {
++      my $ssh2 = Net::SSH2->new();
++
++      sleep(1);
++
++      unless ($ssh2->connect('127.0.0.1', $port)) {
++        my ($err_code, $err_name, $err_str) = $ssh2->error();
++        die("Can't connect to SSH2 server: [$err_name] ($err_code) $err_str");
++      }
++
++      unless ($ssh2->auth_password($setup->{user}, $setup->{passwd})) {
++        my ($err_code, $err_name, $err_str) = $ssh2->error();
++        die("Can't login to SSH2 server: [$err_name] ($err_code) $err_str");
++      }
++
++      my $res = $ssh2->scp_get('src.txt', $dst_file);
++      unless ($res) {
++        my ($err_code, $err_name, $err_str) = $ssh2->error();
++        die("Can't download $src_file from server: [$err_name] ($err_code) $err_str");
++      }
++
++      $ssh2->disconnect();
++
++      $self->assert(-f $dst_file,
++        test_msg("File $dst_file file does not exist"));
++    };
++    if ($@) {
++      $ex = $@;
++    }
++
++    $wfh->print("done\n");
++    $wfh->flush();
++
++  } else {
++    eval { server_wait($setup->{config_file}, $rfh) };
++    if ($@) {
++      warn($@);
++      exit 1;
++    }
++
++    exit 0;
++  }
++
++  # Stop server
++  server_stop($setup->{pid_file});
++  $self->assert_child_ok($pid);
++
++  test_cleanup($setup->{log_file}, $ex);
++}
++
+ sub scp_ext_download_bug3544 {
+   my $self = shift;
+   my $tmpdir = $self->{tmpdir};
+@@ -61545,7 +61834,7 @@ sub scp_ext_download_bug3544 {
+     ScoreboardFile => $scoreboard_file,
+     SystemLog => $log_file,
+     TraceLog => $log_file,
+-    Trace => 'DEFAULT:10 ssh2:20 sftp:20 scp:20',
++    Trace => 'ssh2:20 sftp:20 scp:20',
+ 
+     AuthUserFile => $auth_user_file,
+     AuthGroupFile => $auth_group_file,
+@@ -61562,6 +61851,7 @@ sub scp_ext_download_bug3544 {
+         "SFTPHostKey $rsa_host_key",
+         "SFTPHostKey $dsa_host_key",
+         "SFTPAuthorizedUserKeys file:~/.authorized_keys",
++'SFTPTrafficPolicy none',
+       ],
+     },
+   };
+@@ -61591,6 +61881,7 @@ sub scp_ext_download_bug3544 {
+       my @cmd = (
+         'scp',
+         '-vvv',
++        '-O',
+         '-oBatchMode=yes',
+         '-oCheckHostIP=no',
+         "-oPort=$port",
+@@ -61789,6 +62080,7 @@ sub scp_ext_download_bug3798 {
+       my @cmd = (
+         'scp',
+         '-vvv',
++        '-O',
+         '-oBatchMode=yes',
+         '-oCheckHostIP=no',
+         "-oPort=$port",
+@@ -61988,6 +62280,7 @@ sub scp_ext_download_glob_single_match_bug3904 {
+       my @cmd = (
+         'scp',
+         '-vvv',
++        '-O',
+         '-oBatchMode=yes',
+         '-oCheckHostIP=no',
+         "-oPort=$port",
+@@ -62216,6 +62509,7 @@ sub scp_ext_download_glob_multiple_matches_bug3904 {
+       my @cmd = (
+         'scp',
+         '-vvv',
++        '-O',
+         '-oBatchMode=yes',
+         '-oCheckHostIP=no',
+         "-oPort=$port",
+@@ -62506,6 +62800,7 @@ sub scp_ext_download_recursive_dir_bug3456 {
+         'scp',
+         '-r',
+         '-vvv',
++        '-O',
+         '-oBatchMode=yes',
+         '-oCheckHostIP=no',
+         "-oPort=$port",
+@@ -62733,6 +63028,7 @@ sub scp_ext_download_recursive_empty_dir {
+         'scp',
+         '-r',
+         '-v',
++        '-O',
+         '-oBatchMode=yes',
+         '-oCheckHostIP=no',
+         "-oPort=$port",
+@@ -62935,6 +63231,7 @@ sub scp_ext_download_glob_no_matches_bug3935 {
+       my @cmd = (
+         'scp',
+         '-vvv',
++        '-O',
+         '-oBatchMode=yes',
+         '-oCheckHostIP=no',
+         "-oPort=$port",


=====================================
debian/patches/series
=====================================
@@ -20,3 +20,4 @@ odbc
 #e7539bd772ca6e12d3e05fb56da274cf78ee1edf.diff
 #14c006b62c09d1efe302c57b2d183a489bcb22dc.diff
 #9b2b4a3e32d251798bf8fa841b124ab15ba58f11.diff
+536608f849dddd2b551e7c1e4fd930a50a527ffe.diff



View it on GitLab: https://salsa.debian.org/debian-proftpd-team/proftpd/-/commit/fca1c00522b6d3050716ebb9e9f67b57d8ef84b9

-- 
View it on GitLab: https://salsa.debian.org/debian-proftpd-team/proftpd/-/commit/fca1c00522b6d3050716ebb9e9f67b57d8ef84b9
You're receiving this email because of your account on salsa.debian.org.




More information about the Pkg-proftpd-maintainers mailing list