File CVE-2024-48651.patch of Package proftpd.38039

Index: NEWS
===================================================================
--- NEWS.orig	2023-12-20 04:36:38.000000000 +0000
+++ NEWS	2025-03-25 08:50:02.491459567 +0000
@@ -35,6 +35,8 @@
   incomplete/missing library linker flags.
 - Issue 1597 - <Class> section is allowed to be in <Global>, but From directive
   is not.
+- Issue 1830 - Supplemental group inheritance grants unintended access to
+  GID 0 due to lack of supplemental groups from mod_sql.
 - Issue 1617 - ExtendedLog SSH, SFTP classes not working as expected.
 - Issue 1646 - mod_sftp does not handle multiple concurrent open file
   handles/transfers well for logging.
Index: contrib/mod_sftp/auth.c
===================================================================
--- contrib/mod_sftp/auth.c.orig	2023-12-20 04:36:38.000000000 +0000
+++ contrib/mod_sftp/auth.c	2025-03-25 08:50:02.491839129 +0000
@@ -388,8 +388,20 @@ static int setup_env(pool *p, const char
       session.groups == NULL) {
     res = pr_auth_getgroups(p, pw->pw_name, &session.gids, &session.groups);
     if (res < 1) {
+      /* If no supplemental groups are provided, default to using the process
+       * primary GID as the supplemental group.  This prevents access
+       * regressions as seen in Issue #1830.
+       */
       (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION,
-        "no supplemental groups found for user '%s'", pw->pw_name);
+        "no supplemental groups found for user '%s', "
+        "using primary group %s (GID %lu)", pw->pw_name, session.group,
+        (unsigned long) session.login_gid);
+
+      session.gids = make_array(p, 2, sizeof(gid_t));
+      session.groups = make_array(p, 2, sizeof(char *));
+
+      *((gid_t *) push_array(session.gids)) = session.login_gid;
+      *((char **) push_array(session.groups)) = pstrdup(p, session.group);
     }
   }
 
Index: modules/mod_auth.c
===================================================================
--- modules/mod_auth.c.orig	2023-12-20 04:36:38.000000000 +0000
+++ modules/mod_auth.c	2025-03-25 08:51:18.331990828 +0000
@@ -2,7 +2,7 @@
  * ProFTPD - FTP server daemon
  * Copyright (c) 1997, 1998 Public Flood Software
  * Copyright (c) 1999, 2000 MacGyver aka Habeeb J. Dihu <macgyver@tos.net>
- * Copyright (c) 2001-2022 The ProFTPD Project team
+ * Copyright (c) 2001-2024 The ProFTPD Project team
  *
  * 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
@@ -1113,8 +1113,8 @@ static int setup_env(pool *p, cmd_rec *c
     session.groups = NULL;
   }
 
-  if (!session.gids &&
-      !session.groups) {
+  if (session.gids == NULL &&
+      session.groups == NULL) {
     /* Get the supplemental groups.  Note that we only look up the
      * supplemental group credentials if we have not cached the group
      * credentials before, in session.gids and session.groups.  
@@ -1124,8 +1124,19 @@ static int setup_env(pool *p, cmd_rec *c
      */
      res = pr_auth_getgroups(p, pw->pw_name, &session.gids, &session.groups);
      if (res < 1) {
-       pr_log_debug(DEBUG5, "no supplemental groups found for user '%s'",
-         pw->pw_name);
+       /* If no supplemental groups are provided, default to using the process
+        * primary GID as the supplemental group.  This prevents access
+        * regressions as seen in Issue #1830.
+        */
+       pr_log_debug(DEBUG5, "no supplemental groups found for user '%s', "
+         "using primary group %s (GID %lu)", pw->pw_name, session.group,
+         (unsigned long) session.login_gid);
+
+       session.gids = make_array(p, 2, sizeof(gid_t));
+       session.groups = make_array(p, 2, sizeof(char *));
+
+       *((gid_t *) push_array(session.gids)) = session.login_gid;
+       *((char **) push_array(session.groups)) = pstrdup(p, session.group);
      }
   }
 
Index: src/auth.c
===================================================================
--- src/auth.c.orig	2023-12-20 04:36:38.000000000 +0000
+++ src/auth.c	2025-03-25 08:50:02.492609113 +0000
@@ -2,7 +2,7 @@
  * ProFTPD - FTP server daemon
  * Copyright (c) 1997, 1998 Public Flood Software
  * Copyright (c) 1999, 2000 MacGyver aka Habeeb J. Dihu <macgyver@tos.net>
- * Copyright (c) 2001-2022 The ProFTPD Project team
+ * Copyright (c) 2001-2024 The ProFTPD Project team
  *
  * 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
@@ -1471,12 +1471,12 @@ int pr_auth_getgroups(pool *p, const cha
   }
 
   /* Allocate memory for the array_headers of GIDs and group names. */
-  if (group_ids) {
-    *group_ids = make_array(permanent_pool, 2, sizeof(gid_t));
+  if (group_ids != NULL) {
+    *group_ids = make_array(p, 2, sizeof(gid_t));
   }
 
-  if (group_names) {
-    *group_names = make_array(permanent_pool, 2, sizeof(char *));
+  if (group_names != NULL) {
+    *group_names = make_array(p, 2, sizeof(char *));
   }
 
   cmd = make_cmd(p, 3, name, group_ids ? *group_ids : NULL,
@@ -1495,7 +1495,7 @@ int pr_auth_getgroups(pool *p, const cha
      * for the benefit of auth_getgroup() implementors.
      */
 
-    if (group_ids) {
+    if (group_ids != NULL) {
       register unsigned int i;
       char *strgids = "";
       gid_t *gids = (*group_ids)->elts;
@@ -1511,7 +1511,7 @@ int pr_auth_getgroups(pool *p, const cha
         *strgids ? strgids : "(None; corrupted group file?)");
     }
 
-    if (group_names) {
+    if (group_names != NULL) {
       register unsigned int i;
       char *strgroups = ""; 
       char **groups = (*group_names)->elts;
@@ -1527,7 +1527,7 @@ int pr_auth_getgroups(pool *p, const cha
     }
   }
 
-  if (cmd->tmp_pool) {
+  if (cmd->tmp_pool != NULL) {
     destroy_pool(cmd->tmp_pool);
     cmd->tmp_pool = NULL;
   }
Index: tests/t/lib/ProFTPD/Tests/Modules/mod_sql_sqlite.pm
===================================================================
--- tests/t/lib/ProFTPD/Tests/Modules/mod_sql_sqlite.pm.orig	2023-12-20 04:36:38.000000000 +0000
+++ tests/t/lib/ProFTPD/Tests/Modules/mod_sql_sqlite.pm	2025-03-25 08:50:02.493253031 +0000
@@ -467,6 +467,11 @@ my $TESTS = {
     order => ++$order,
     test_class => [qw(forking bug mod_tls)],
   },
+
+  sql_user_info_no_suppl_groups_issue1830 => {
+    order => ++$order,
+    test_class => [qw(forking bug rootprivs)],
+  },
 };
 
 sub new {
@@ -15763,5 +15768,174 @@ EOC
 
   test_cleanup($setup->{log_file}, $ex);
 }
+
+sub sql_user_info_no_suppl_groups_issue1830 {
+  my $self = shift;
+  my $tmpdir = $self->{tmpdir};
+  my $setup = test_setup($tmpdir, 'sqlite');
+
+  my $db_file = File::Spec->rel2abs("$tmpdir/proftpd.db");
+
+  # Build up sqlite3 command to create users, groups tables and populate them
+  my $db_script = File::Spec->rel2abs("$tmpdir/proftpd.sql");
+
+  if (open(my $fh, "> $db_script")) {
+    print $fh <<EOS;
+CREATE TABLE users (
+  userid TEXT,
+  passwd TEXT,
+  uid INTEGER,
+  gid INTEGER,
+  homedir TEXT,
+  shell TEXT
+);
+INSERT INTO users (userid, passwd, uid, gid, homedir, shell) VALUES ('$setup->{user}', '$setup->{passwd}', $setup->{uid}, $setup->{gid}, '$setup->{home_dir}', '/bin/bash');
+
+CREATE TABLE groups (
+  groupname TEXT,
+  gid INTEGER,
+  members TEXT
+);
+INSERT INTO groups (groupname, gid, members) VALUES ('$setup->{group}', $setup->{gid}, '$setup->{user}');
+EOS
+
+    unless (close($fh)) {
+      die("Can't write $db_script: $!");
+    }
+
+  } else {
+    die("Can't open $db_script: $!");
+  }
+
+  my $cmd = "sqlite3 $db_file < $db_script";
+  build_db($cmd, $db_script);
+
+  # Make sure that, if we're running as root, the database file has
+  # the permissions/privs set for use by proftpd
+  if ($< == 0) {
+    unless (chmod(0666, $db_file)) {
+      die("Can't set perms on $db_file to 0666: $!");
+    }
+  }
+
+  my $config = {
+    PidFile => $setup->{pid_file},
+    ScoreboardFile => $setup->{scoreboard_file},
+    SystemLog => $setup->{log_file},
+    TraceLog => $setup->{log_file},
+    Trace => 'auth:20 sql:20',
+
+    # Required for logging the expected message
+    DebugLevel => 5,
+
+    IfModules => {
+      'mod_delay.c' => {
+        DelayEngine => 'off',
+      },
+
+      'mod_sql.c' => {
+        AuthOrder => 'mod_sql.c',
+
+        SQLAuthenticate => 'users',
+        SQLAuthTypes => 'plaintext',
+        SQLBackend => 'sqlite3',
+        SQLConnectInfo => $db_file,
+        SQLLogFile => $setup->{log_file},
+
+        # Set these, so that our lower UID/GID will be used
+        SQLMinUserUID => 100,
+        SQLMinUserGID => 100,
+      },
+    },
+  };
+
+  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: $!");
+  }
+
+  my $ex;
+
+  # Fork child
+  $self->handle_sigchld();
+  defined(my $pid = fork()) or die("Can't fork: $!");
+  if ($pid) {
+    eval {
+      sleep(2);
+
+      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
+      $client->login($setup->{user}, $setup->{passwd});
+
+      my $resp_msgs = $client->response_msgs();
+      my $nmsgs = scalar(@$resp_msgs);
+
+      my $expected = 1;
+      $self->assert($expected == $nmsgs,
+        test_msg("Expected $expected, got $nmsgs"));
+
+      $expected = "User $setup->{user} logged in";
+      $self->assert($expected eq $resp_msgs->[0],
+        test_msg("Expected response '$expected', got '$resp_msgs->[0]'"));
+
+      $client->quit();
+    };
+    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);
+
+  eval {
+    if (open(my $fh, "< $setup->{log_file}")) {
+      my $ok = 0;
+
+      while (my $line = <$fh>) {
+        chomp($line);
+
+        if ($ENV{TEST_VERBOSE}) {
+          print STDERR "# $line\n";
+        }
+
+        if ($line =~ /no supplemental groups found for user '$setup->{user}', using primary group/) {
+          $ok = 1;
+          last;
+        }
+      }
+
+      close($fh);
+
+      $self->assert($ok, test_msg("Did not see expected log message"));
+
+    } else {
+      die("Can't read $setup->{log_file}: $!");
+    }
+  };
+  if ($@) {
+    $ex = $@ unless $ex;
+  }
+
+  test_cleanup($setup->{log_file}, $ex);
+}
 
 1;
openSUSE Build Service is sponsored by