Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 20 May 2015 19:16:18 +0000 (UTC)
From:      Xin LI <delphij@FreeBSD.org>
To:        ports-committers@freebsd.org, svn-ports-all@freebsd.org, svn-ports-branches@freebsd.org
Subject:   svn commit: r386882 - in branches/2015Q2/ftp/proftpd: . files
Message-ID:  <201505201916.t4KJGIhe048429@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: delphij
Date: Wed May 20 19:16:18 2015
New Revision: 386882
URL: https://svnweb.freebsd.org/changeset/ports/386882

Log:
  MFH: r386881
  
  Fix proftpd unauthenticated copying of files via SITE CPFR/CPTO
  vulnerability.
  
  Security:	CVE-2015-3306
  Approved by:	ports-secteam (self)

Added:
  branches/2015Q2/ftp/proftpd/files/patch-CVE-2015-3306
     - copied unchanged from r386881, head/ftp/proftpd/files/patch-CVE-2015-3306
Modified:
  branches/2015Q2/ftp/proftpd/Makefile
Directory Properties:
  branches/2015Q2/   (props changed)

Modified: branches/2015Q2/ftp/proftpd/Makefile
==============================================================================
--- branches/2015Q2/ftp/proftpd/Makefile	Wed May 20 19:15:24 2015	(r386881)
+++ branches/2015Q2/ftp/proftpd/Makefile	Wed May 20 19:16:18 2015	(r386882)
@@ -5,7 +5,7 @@ PORTNAME?=	proftpd
 .if !defined(DISTVERSION)
 PORTVERSION?=	${PROFTPD_VERSION}
 .endif
-PORTREVISION?=	6
+PORTREVISION?=	7
 CATEGORIES?=	ftp
 MASTER_SITES=	ftp://ftp.proftpd.org/distrib/source/ \
 		https://github.com/downloads/proftpd/proftpd.github.com/ \

Copied: branches/2015Q2/ftp/proftpd/files/patch-CVE-2015-3306 (from r386881, head/ftp/proftpd/files/patch-CVE-2015-3306)
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ branches/2015Q2/ftp/proftpd/files/patch-CVE-2015-3306	Wed May 20 19:16:18 2015	(r386882, copy of r386881, head/ftp/proftpd/files/patch-CVE-2015-3306)
@@ -0,0 +1,610 @@
+Index: contrib/mod_copy.c
+===================================================================
+--- contrib/mod_copy.c
++++ contrib/mod_copy.c
+@@ -2,7 +2,7 @@
+  * ProFTPD: mod_copy -- a module supporting copying of files on the server
+  *                      without transferring the data to the client and back
+  *
+- * Copyright (c) 2009-2012 TJ Saunders
++ * Copyright (c) 2009-2015 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
+@@ -25,13 +25,11 @@
+  *
+  * This is mod_copy, contrib software for proftpd 1.3.x and above.
+  * For more information contact TJ Saunders <tj@castaglia.org>.
+- *
+- * $Id: mod_copy.c,v 1.8 2012/12/27 22:31:29 castaglia Exp $
+  */
+ 
+ #include "conf.h"
+ 
+-#define MOD_COPY_VERSION	"mod_copy/0.4"
++#define MOD_COPY_VERSION	"mod_copy/0.5"
+ 
+ /* Make sure the version of proftpd is as necessary. */
+ #if PROFTPD_VERSION_NUMBER < 0x0001030401
+@@ -40,6 +38,8 @@
+ 
+ extern pr_response_t *resp_list, *resp_err_list;
+ 
++static int copy_engine = TRUE;
++
+ static const char *trace_channel = "copy";
+ 
+ /* These are copied largely from src/mkhome.c */
+@@ -165,7 +165,7 @@ static int copy_symlink(pool *p, const c
+       src_path, strerror(xerrno));
+ 
+     errno = xerrno;
+-    return -1;
++    return -1; 
+   }
+   link_path[len] = '\0';
+ 
+@@ -471,10 +471,37 @@ static int copy_paths(pool *p, const cha
+   return 0;
+ }
+ 
++/* Configuration handlers
++ */
++
++/* usage: CopyEngine on|off */
++MODRET set_copyengine(cmd_rec *cmd) {
++  int engine = -1;
++  config_rec *c;
++
++  CHECK_ARGS(cmd, 1);
++  CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
++
++  engine = get_boolean(cmd, 1);
++  if (engine == -1) {
++    CONF_ERROR(cmd, "expected Boolean parameter");
++  }
++
++  c = add_config_param(cmd->argv[0], 1, NULL);
++  c->argv[0] = palloc(c->pool, sizeof(int));
++  *((int *) c->argv[0]) = engine;
++
++  return PR_HANDLED(cmd);
++}
++
+ /* Command handlers
+  */
+ 
+ MODRET copy_copy(cmd_rec *cmd) {
++  if (copy_engine == FALSE) {
++    return PR_DECLINED(cmd);
++  }
++
+   if (cmd->argc < 2) {
+     return PR_DECLINED(cmd);
+   }
+@@ -539,12 +566,26 @@ MODRET copy_cpfr(cmd_rec *cmd) {
+   register unsigned int i;
+   int res;
+   char *path = "";
++  unsigned char *authenticated = NULL;
++
++  if (copy_engine == FALSE) {
++    return PR_DECLINED(cmd);
++  }
+ 
+   if (cmd->argc < 3 ||
+       strncasecmp(cmd->argv[1], "CPFR", 5) != 0) {
+     return PR_DECLINED(cmd);
+   }
+ 
++  authenticated = get_param_ptr(cmd->server->conf, "authenticated", FALSE);
++  if (authenticated == NULL ||
++      *authenticated == FALSE) {
++    pr_response_add_err(R_530, _("Please login with USER and PASS"));
++  
++    errno = EPERM;
++    return PR_ERROR(cmd);
++  }
++
+   CHECK_CMD_MIN_ARGS(cmd, 3);
+ 
+   /* Construct the target file name by concatenating all the parameters after
+@@ -594,12 +635,26 @@ MODRET copy_cpfr(cmd_rec *cmd) {
+ MODRET copy_cpto(cmd_rec *cmd) {
+   register unsigned int i;
+   char *from, *to = "";
++  unsigned char *authenticated = NULL;
++
++  if (copy_engine == FALSE) {
++    return PR_DECLINED(cmd);
++  }
+ 
+   if (cmd->argc < 3 ||
+       strncasecmp(cmd->argv[1], "CPTO", 5) != 0) {
+     return PR_DECLINED(cmd);
+   }
+ 
++  authenticated = get_param_ptr(cmd->server->conf, "authenticated", FALSE);
++  if (authenticated == NULL ||
++      *authenticated == FALSE) {
++    pr_response_add_err(R_530, _("Please login with USER and PASS"));
++
++    errno = EPERM;
++    return PR_ERROR(cmd);
++  }
++
+   CHECK_CMD_MIN_ARGS(cmd, 3);
+ 
+   from = pr_table_get(session.notes, "mod_copy.cpfr-path", NULL);
+@@ -632,6 +687,10 @@ MODRET copy_cpto(cmd_rec *cmd) {
+ }
+ 
+ MODRET copy_log_site(cmd_rec *cmd) {
++  if (copy_engine == FALSE) {
++    return PR_DECLINED(cmd);
++  }
++
+   if (cmd->argc < 3 ||
+       strncasecmp(cmd->argv[1], "CPTO", 5) != 0) {
+     return PR_DECLINED(cmd);
+@@ -643,23 +702,58 @@ MODRET copy_log_site(cmd_rec *cmd) {
+   return PR_DECLINED(cmd);
+ }
+ 
++MODRET copy_post_pass(cmd_rec *cmd) {
++  config_rec *c;
++
++  if (copy_engine == FALSE) {
++    return PR_DECLINED(cmd);
++  }
++
++  /* The CopyEngine directive may have been changed for this user by
++   * e.g. mod_ifsession, thus we check again.
++   */
++  c = find_config(main_server->conf, CONF_PARAM, "CopyEngine", FALSE);
++  if (c != NULL) {
++    copy_engine = *((int *) c->argv[0]);
++  }
++
++  return PR_DECLINED(cmd);
++}
++
+ /* Initialization functions
+  */
+ 
+ static int copy_sess_init(void) {
++  config_rec *c;
++
++  c = find_config(main_server->conf, CONF_PARAM, "CopyEngine", FALSE);
++  if (c != NULL) {
++    copy_engine = *((int *) c->argv[0]);
++  }
++
++  if (copy_engine == FALSE) {
++    return 0;
++  }
++
+   /* Advertise support for the SITE command */
+   pr_feat_add("SITE COPY");
+-
+   return 0;
+ }
+ 
+ /* Module API tables
+  */
+ 
++static conftable copy_conftab[] = {
++  { "CopyEngine",	set_copyengine,		NULL },
++
++  { NULL }
++};
++
+ static cmdtable copy_cmdtab[] = {
+   { CMD, 	C_SITE, G_WRITE,	copy_copy,	FALSE,	FALSE, CL_MISC },
+   { CMD, 	C_SITE, G_DIRS,		copy_cpfr,	FALSE,	FALSE, CL_MISC },
+   { CMD, 	C_SITE, G_WRITE,	copy_cpto,	FALSE,	FALSE, CL_MISC },
++  { POST_CMD,	C_PASS,	G_NONE,		copy_post_pass, FALSE,	FALSE },
+   { LOG_CMD, 	C_SITE, G_NONE,		copy_log_site,	FALSE,	FALSE },
+   { LOG_CMD_ERR, C_SITE, G_NONE,	copy_log_site,	FALSE,	FALSE },
+ 
+@@ -676,7 +770,7 @@ module copy_module = {
+   "copy",
+ 
+   /* Module configuration handler table */
+-  NULL,
++  copy_conftab,
+ 
+   /* Module command handler table */
+   copy_cmdtab,
+Index: doc/contrib/mod_copy.html
+===================================================================
+--- doc/contrib/mod_copy.html
++++ doc/contrib/mod_copy.html
+@@ -1,5 +1,5 @@
+-<!-- $Id: mod_copy.html,v 1.1 2010/03/10 19:20:43 castaglia Exp $ -->
+-<!-- $Source: /cvsroot/proftp/proftpd/doc/contrib/mod_copy.html,v $ -->
++<!-- $Id: mod_copy.html,v 1.1 2010-03-10 19:20:43 castaglia Exp $ -->
++<!-- $Source: /home/proftpd-core/backup/proftp-cvsroot/proftpd/doc/contrib/mod_copy.html,v $ -->
+ 
+ <html>
+ <head>
+@@ -27,22 +27,40 @@ ProFTPD 1.3.<i>x</i>, and is not compile
+ instructions are discussed <a href="#Installation">here</a>.
+ 
+ <p>
+-The most current version of <code>mod_copy</code> can be found at:
+-<pre>
+-  <a href="http://www.castaglia.org/proftpd/">http://www.castaglia.org/proftpd/</a>;
+-</pre>
++The most current version of <code>mod_copy</code> is distributed with the
++ProFTPD source code.
+ 
+ <h2>Author</h2>
+ <p>
+ Please contact TJ Saunders &lt;tj <i>at</i> castaglia.org&gt; with any
+ questions, concerns, or suggestions regarding this module.
+ 
++<h2>Directives</h2>
++<ul>
++  <li><a href="#CopyEngine">CopyEngine</a>
++</ul>
++
+ <h2><code>SITE</code> Commands</h2>
+ <ul>
+   <li><a href="#SITE_CPFR">SITE CPFR</a>
+   <li><a href="#SITE_CPTO">SITE CPTO</a>
+ </ul>
+ 
++<p>
++<hr>
++<h2><a name="CopyEngine">CopyEngine</a></h2>
++<strong>Syntax:</strong> CopyEngine <em>on|off</em><br>
++<strong>Default:</strong> CopyEngine on<br>
++<strong>Context:</strong> server config, <code>&lt;VirtualHost&gt;</code>, <code>&lt;Global&gt;</code><br>
++<strong>Module:</strong> mod_radius<br>
++<strong>Compatibility:</strong> 1.3.6rc1 and later
++
++<p>
++The <code>CopyEngine</code> directive enables or disables the module's
++handling of <code>SITE COPY</code> <i>et al</i> commands.  If it is set to
++<em>off</em> this module ignores these commands.
++
++<p>
+ <hr>
+ <h2><a name="SITE_CPFR">SITE CPFR</a></h2>
+ This <code>SITE</code> command specifies the source file/directory to use
+@@ -118,13 +136,8 @@ your existing server:
+ <p>
+ <hr><br>
+ 
+-Author: <i>$Author: castaglia $</i><br>
+-Last Updated: <i>$Date: 2010/03/10 19:20:43 $</i><br>
+-
+-<br><hr>
+-
+ <font size=2><b><i>
+-&copy; Copyright 2009-2010 TJ Saunders<br>
++&copy; Copyright 2009-2015 TJ Saunders<br>
+  All Rights Reserved<br>
+ </i></b></font>
+ 
+Index: tests/t/lib/ProFTPD/Tests/Modules/mod_copy.pm
+===================================================================
+--- tests/t/lib/ProFTPD/Tests/Modules/mod_copy.pm
++++ tests/t/lib/ProFTPD/Tests/Modules/mod_copy.pm
+@@ -21,6 +21,11 @@ my $TESTS = {
+     test_class => [qw(forking)],
+   },
+ 
++  copy_file_no_login => {
++    order => ++$order,
++    test_class => [qw(bug forking)],
++  },
++
+   copy_dir => {
+     order => ++$order,
+     test_class => [qw(forking)],
+@@ -86,6 +91,11 @@ my $TESTS = {
+     test_class => [qw(forking)],
+   },
+ 
++  copy_cpfr_cpto_no_login => {
++    order => ++$order,
++    test_class => [qw(bug forking)],
++  },
++
+   copy_cpto_no_cpfr => {
+     order => ++$order,
+     test_class => [qw(forking)],
+@@ -263,6 +273,137 @@ sub copy_file {
+   unlink($log_file);
+ }
+ 
++sub copy_file_no_login {
++  my $self = shift;
++  my $tmpdir = $self->{tmpdir};
++
++  my $config_file = "$tmpdir/copy.conf";
++  my $pid_file = File::Spec->rel2abs("$tmpdir/copy.pid");
++  my $scoreboard_file = File::Spec->rel2abs("$tmpdir/copy.scoreboard");
++
++  my $log_file = File::Spec->rel2abs('tests.log');
++
++  my $auth_user_file = File::Spec->rel2abs("$tmpdir/copy.passwd");
++  my $auth_group_file = File::Spec->rel2abs("$tmpdir/copy.group");
++
++  my $user = 'proftpd';
++  my $passwd = 'test';
++  my $group = 'ftpd';
++  my $home_dir = File::Spec->rel2abs($tmpdir);
++  my $uid = 500;
++  my $gid = 500;
++
++  # 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)) {
++      die("Can't set perms on $home_dir to 0755: $!");
++    }
++
++    unless (chown($uid, $gid, $home_dir)) {
++      die("Can't set owner of $home_dir to $uid/$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 $src_file = File::Spec->rel2abs("$home_dir/foo.txt");
++  if (open(my $fh, "> $src_file")) {
++    print $fh "Hello, World!\n";
++
++    unless (close($fh)) {
++      die("Can't write $src_file: $!");
++    }
++
++  } else {
++    die("Can't open $src_file: $!");
++  }
++
++  my $dst_file = File::Spec->rel2abs("$home_dir/bar.txt");
++
++  my $config = {
++    PidFile => $pid_file,
++    ScoreboardFile => $scoreboard_file,
++    SystemLog => $log_file,
++
++    AuthUserFile => $auth_user_file,
++    AuthGroupFile => $auth_group_file,
++
++    IfModules => {
++      'mod_delay.c' => {
++        DelayEngine => 'off',
++      },
++    },
++  };
++
++  my ($port, $config_user, $config_group) = config_write($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: $!");
++  }
++
++  my $ex;
++
++  # Fork child
++  $self->handle_sigchld();
++  defined(my $pid = fork()) or die("Can't fork: $!");
++  if ($pid) {
++    eval {
++      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
++
++      eval { $client->site('COPY', 'foo.txt', 'bar.txt') };
++      unless ($@) { 
++        die("SITE COPY succeeded unexpectedly");
++      }
++
++      my $resp_code = $client->response_code();
++      my $resp_msg = $client->response_msg();
++
++      my $expected;
++      $expected = 530;
++      $self->assert($expected == $resp_code,
++        test_msg("Expected response code $expected, got $resp_code"));
++
++      $expected = "Please login with USER and PASS";
++      $self->assert($expected eq $resp_msg,
++        test_msg("Expected response message '$expected', got '$resp_msg'"));
++    };
++
++    if ($@) {
++      $ex = $@;
++    }
++
++    $wfh->print("done\n");
++    $wfh->flush();
++
++  } else {
++    eval { server_wait($config_file, $rfh) };
++    if ($@) {
++      warn($@);
++      exit 1;
++    }
++
++    exit 0;
++  }
++
++  # Stop server
++  server_stop($pid_file);
++
++  $self->assert_child_ok($pid);
++
++  if ($ex) {
++    die($ex);
++  }
++
++  unlink($log_file);
++}
++
+ sub copy_dir {
+   my $self = shift;
+   my $tmpdir = $self->{tmpdir};
+@@ -2578,6 +2719,153 @@ sub copy_cpfr_cpto {
+     };
+ 
+     if ($@) {
++      $ex = $@;
++    }
++
++    $wfh->print("done\n");
++    $wfh->flush();
++
++  } else {
++    eval { server_wait($config_file, $rfh) };
++    if ($@) {
++      warn($@);
++      exit 1;
++    }
++
++    exit 0;
++  }
++
++  # Stop server
++  server_stop($pid_file);
++
++  $self->assert_child_ok($pid);
++
++  if ($ex) {
++    die($ex);
++  }
++
++  unlink($log_file);
++}
++
++sub copy_cpfr_cpto_no_login {
++  my $self = shift;
++  my $tmpdir = $self->{tmpdir};
++
++  my $config_file = "$tmpdir/copy.conf";
++  my $pid_file = File::Spec->rel2abs("$tmpdir/copy.pid");
++  my $scoreboard_file = File::Spec->rel2abs("$tmpdir/copy.scoreboard");
++
++  my $log_file = File::Spec->rel2abs('tests.log');
++
++  my $auth_user_file = File::Spec->rel2abs("$tmpdir/copy.passwd");
++  my $auth_group_file = File::Spec->rel2abs("$tmpdir/copy.group");
++
++  my $user = 'proftpd';
++  my $passwd = 'test';
++  my $group = 'ftpd';
++  my $home_dir = File::Spec->rel2abs($tmpdir);
++  my $uid = 500;
++  my $gid = 500;
++
++  # 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)) {
++      die("Can't set perms on $home_dir to 0755: $!");
++    }
++
++    unless (chown($uid, $gid, $home_dir)) {
++      die("Can't set owner of $home_dir to $uid/$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 $src_file = File::Spec->rel2abs("$home_dir/foo.txt");
++  if (open(my $fh, "> $src_file")) {
++    print $fh "Hello, World!\n";
++
++    unless (close($fh)) {
++      die("Can't write $src_file: $!");
++    }
++
++  } else {
++    die("Can't open $src_file: $!");
++  }
++
++  my $dst_file = File::Spec->rel2abs("$home_dir/bar.txt");
++
++  my $config = {
++    PidFile => $pid_file,
++    ScoreboardFile => $scoreboard_file,
++    SystemLog => $log_file,
++
++    AuthUserFile => $auth_user_file,
++    AuthGroupFile => $auth_group_file,
++
++    IfModules => {
++      'mod_delay.c' => {
++        DelayEngine => 'off',
++      },
++    },
++  };
++
++  my ($port, $config_user, $config_group) = config_write($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: $!");
++  }
++
++  my $ex;
++
++  # Fork child
++  $self->handle_sigchld();
++  defined(my $pid = fork()) or die("Can't fork: $!");
++  if ($pid) {
++    eval {
++      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
++ 
++      eval { $client->site('CPFR', 'foo.txt') };
++      unless ($@) {
++        die("SITE CPFR succeeded unexpectedly");
++      }
++
++      my $resp_code = $client->response_code();
++      my $resp_msg = $client->response_msg();
++
++      my $expected;
++      $expected = 530;
++      $self->assert($expected == $resp_code,
++        test_msg("Expected response code $expected, got $resp_code"));
++
++      $expected = "Please login with USER and PASS";
++      $self->assert($expected eq $resp_msg,
++        test_msg("Expected response message '$expected', got '$resp_msg'"));
++
++      eval { $client->site('CPTO', 'bar.txt') };
++      unless ($@) {
++        die("SITE CPTO succeeded unexpectedly");
++      }
++
++      $resp_code = $client->response_code();
++      $resp_msg = $client->response_msg();
++
++      $expected = 530;
++      $self->assert($expected == $resp_code,
++        test_msg("Expected response code $expected, got $resp_code"));
++
++      $expected = "Please login with USER and PASS";
++      $self->assert($expected eq $resp_msg,
++        test_msg("Expected response message '$expected', got '$resp_msg'"));
++    };
++
++    if ($@) {
+       $ex = $@;
+     }
+ 



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201505201916.t4KJGIhe048429>