From 1fb36bcbf7c214aa146875420de12f5407107672 Mon Sep 17 00:00:00 2001
From: TJ Saunders <tj@castaglia.org>
Date: Fri, 1 Sep 2017 23:08:41 -0700
Subject: [PATCH] Bug#4314: When retrieving the appropriate <Anonymous> section
 for a client, the configured User/Group retrieved were from the parent
 section, not the retrieved <Anonymous> section.  This appeared to cause
 regressions when "AuthAliasOnly on" was used.  Let's hope that _this_ change
 does not itself cause regressions.

The whole AuthAliasOnly is so buggy/fragile it should be removed entirely.
---
 modules/mod_auth.c                            |   5 +
 src/auth.c                                    |   8 ++
 .../lib/ProFTPD/Tests/Config/AuthAliasOnly.pm | 113 ++++++++++++++++++
 3 files changed, 126 insertions(+)

--- proftpd-dfsg.orig/modules/mod_auth.c
+++ proftpd-dfsg/modules/mod_auth.c
@@ -1039,6 +1039,11 @@
   origuser = user;
   c = pr_auth_get_anon_config(p, &user, &ourname, &anonname);
   if (c != NULL) {
+    pr_trace_msg("auth", 13,
+      "found <Anonymous> config: login user = %s, config user = %s, "
+      "anon name = %s", user != NULL ? user : "(null)",
+      ourname != NULL ? ourname : "(null)",
+      anonname != NULL ? anonname : "(null)");
     session.anon_config = c;
   }
 
--- proftpd-dfsg.orig/src/auth.c
+++ proftpd-dfsg/src/auth.c
@@ -1784,6 +1784,14 @@
     }
   }
 
+  if (anon_config != NULL) {
+    config_user_name = get_param_ptr(anon_config->subset, "UserName", FALSE);
+    if (config_user_name != NULL &&
+        real_user != NULL) {
+      *real_user = config_user_name;
+    }
+  }
+
   return anon_config;
 }
 
--- proftpd-dfsg.orig/tests/t/lib/ProFTPD/Tests/Config/AuthAliasOnly.pm
+++ proftpd-dfsg/tests/t/lib/ProFTPD/Tests/Config/AuthAliasOnly.pm
@@ -40,6 +40,11 @@
     test_class => [qw(bug forking rootprivs)],
   },
 
+  authaliasonly_on_anon_bug4314 => {
+    order => ++$order,
+    test_class => [qw(bug forking rootprivs)],
+  },
+
 };
 
 sub new {
@@ -626,6 +631,114 @@
     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 authaliasonly_on_anon_bug4314 {
+  my $self = shift;
+  my $tmpdir = $self->{tmpdir};
+  my $setup = test_setup($tmpdir, 'config');
+
+  my ($config_user, $config_group) = config_get_identity();
+
+  my $config = {
+    PidFile => $setup->{pid_file},
+    ScoreboardFile => $setup->{scoreboard_file},
+    SystemLog => $setup->{log_file},
+    TraceLog => $setup->{log_file},
+    Trace => 'auth:20',
+
+    User => $config_user,
+    Group => $config_group,
+
+    AuthUserFile => $setup->{auth_user_file},
+    AuthGroupFile => $setup->{auth_group_file},
+    AuthOrder => 'mod_auth_file.c',
+
+    Anonymous => {
+      $setup->{home_dir} => {
+        User => $setup->{user},
+        Group => $setup->{group},
+        RequireValidShell => 'off',
+        UserAlias => "anonymous $setup->{user}",
+        AuthAliasOnly => 'on',
+        AnonRequirePassword => 'off',
+      },
+    },
+
+    IfModules => {
+      'mod_delay.c' => {
+        DelayEngine => 'off',
+      },
+    },
+  };
+
+  my $port;
+  ($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: $!");
+  }
+
+  my $ex;
+
+  # Fork child
+  $self->handle_sigchld();
+  defined(my $pid = fork()) or die("Can't fork: $!");
+  if ($pid) {
+    eval {
+      sleep(1);
+
+      # First, try logging in as user 'anonymous', i.e. the alias.
+      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port, 0, 1);
+      my ($resp_code, $resp_msg) = $client->user("anonymous");
+
+      my $expected = 331;
+      $self->assert($expected == $resp_code,
+        "Expected response code $expected, got $resp_code");
+
+      $expected = 'Anonymous login ok, send your complete email address as your password';
+      $self->assert($expected eq $resp_msg,
+        "Expected response message '$expected', got '$resp_msg'");
+
+      ($resp_code, $resp_msg) = $client->pass('ftp@nospam.org');
+
+      $expected = 230;
+      $self->assert($expected == $resp_code,
+        "Expected response code $expected, got $resp_code");
+
+      $expected = 'Anonymous access granted, restrictions apply';
+      $self->assert($expected eq $resp_msg,
+        "Expected response message '$expected', got '$resp_msg'");
+
+      $client->quit();
+    };
+    if ($@) {
+      $ex = $@;
+    }
 
     $wfh->print("done\n");
     $wfh->flush();
