diff options
| author | Gyorgy Sarvari <skandigraun@gmail.com> | 2025-12-16 20:40:05 +0100 |
|---|---|---|
| committer | Gyorgy Sarvari <skandigraun@gmail.com> | 2025-12-17 15:38:00 +0100 |
| commit | 8611f92c20ba3aab57f3586a6a2e3b9cd1fe00c7 (patch) | |
| tree | eeffcc9ca1c2a5d68eacedf4589d71719dec04d1 | |
| parent | 5c3e0fc51613e6de2bf5bfeb7a83fb93dde3afde (diff) | |
| download | meta-openembedded-8611f92c20ba3aab57f3586a6a2e3b9cd1fe00c7.tar.gz | |
proftpd: patch CVE-2024-48651
Details: https://nvd.nist.gov/vuln/detail/CVE-2024-48651
Backport the patch mentioned in the NVD report.
Signed-off-by: Gyorgy Sarvari <skandigraun@gmail.com>
| -rw-r--r-- | meta-networking/recipes-daemons/proftpd/files/CVE-2024-48651.patch | 321 | ||||
| -rw-r--r-- | meta-networking/recipes-daemons/proftpd/proftpd_1.3.7c.bb | 3 |
2 files changed, 323 insertions, 1 deletions
diff --git a/meta-networking/recipes-daemons/proftpd/files/CVE-2024-48651.patch b/meta-networking/recipes-daemons/proftpd/files/CVE-2024-48651.patch new file mode 100644 index 0000000000..db525c5418 --- /dev/null +++ b/meta-networking/recipes-daemons/proftpd/files/CVE-2024-48651.patch | |||
| @@ -0,0 +1,321 @@ | |||
| 1 | From 1df9a7b29aaedfc563ba908b52ca2414caddf25f Mon Sep 17 00:00:00 2001 | ||
| 2 | From: TJ Saunders <tj@castaglia.org> | ||
| 3 | Date: Wed, 13 Nov 2024 06:33:35 -0800 | ||
| 4 | Subject: [PATCH] Issue #1830: When no supplemental groups are provided by the | ||
| 5 | underlying authentication providers, fall back to using the primary | ||
| 6 | group/GID. (#1835) | ||
| 7 | |||
| 8 | This prevents surprise due to inheritance of the parent processes' supplemental group membership, which might inadvertently provided undesired access. | ||
| 9 | |||
| 10 | CVE: CVE-2024-48651 | ||
| 11 | Upstream-Status: Backport [https://github.com/proftpd/proftpd/commit/cec01cc0a2523453e5da5a486bc6d977c3768db1] | ||
| 12 | Signed-off-by: Gyorgy Sarvari <skandigraun@gmail.com> | ||
| 13 | --- | ||
| 14 | contrib/mod_sftp/auth.c | 14 +- | ||
| 15 | modules/mod_auth.c | 19 +- | ||
| 16 | src/auth.c | 14 +- | ||
| 17 | .../ProFTPD/Tests/Modules/mod_sql_sqlite.pm | 175 ++++++++++++++++++ | ||
| 18 | 4 files changed, 210 insertions(+), 12 deletions(-) | ||
| 19 | |||
| 20 | diff --git a/contrib/mod_sftp/auth.c b/contrib/mod_sftp/auth.c | ||
| 21 | index ede821daa..2854a03cd 100644 | ||
| 22 | --- a/contrib/mod_sftp/auth.c | ||
| 23 | +++ b/contrib/mod_sftp/auth.c | ||
| 24 | @@ -382,8 +382,20 @@ static int setup_env(pool *p, const char *user) { | ||
| 25 | session.groups == NULL) { | ||
| 26 | res = pr_auth_getgroups(p, pw->pw_name, &session.gids, &session.groups); | ||
| 27 | if (res < 1) { | ||
| 28 | + /* If no supplemental groups are provided, default to using the process | ||
| 29 | + * primary GID as the supplemental group. This prevents access | ||
| 30 | + * regressions as seen in Issue #1830. | ||
| 31 | + */ | ||
| 32 | (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, | ||
| 33 | - "no supplemental groups found for user '%s'", pw->pw_name); | ||
| 34 | + "no supplemental groups found for user '%s', " | ||
| 35 | + "using primary group %s (GID %lu)", pw->pw_name, session.group, | ||
| 36 | + (unsigned long) session.login_gid); | ||
| 37 | + | ||
| 38 | + session.gids = make_array(p, 2, sizeof(gid_t)); | ||
| 39 | + session.groups = make_array(p, 2, sizeof(char *)); | ||
| 40 | + | ||
| 41 | + *((gid_t *) push_array(session.gids)) = session.login_gid; | ||
| 42 | + *((char **) push_array(session.groups)) = pstrdup(p, session.group); | ||
| 43 | } | ||
| 44 | } | ||
| 45 | |||
| 46 | diff --git a/modules/mod_auth.c b/modules/mod_auth.c | ||
| 47 | index e47ed148d..a1b71c0f7 100644 | ||
| 48 | --- a/modules/mod_auth.c | ||
| 49 | +++ b/modules/mod_auth.c | ||
| 50 | @@ -1111,8 +1111,8 @@ static int setup_env(pool *p, cmd_rec *cmd, const char *user, char *pass) { | ||
| 51 | session.groups = NULL; | ||
| 52 | } | ||
| 53 | |||
| 54 | - if (!session.gids && | ||
| 55 | - !session.groups) { | ||
| 56 | + if (session.gids == NULL && | ||
| 57 | + session.groups == NULL) { | ||
| 58 | /* Get the supplemental groups. Note that we only look up the | ||
| 59 | * supplemental group credentials if we have not cached the group | ||
| 60 | * credentials before, in session.gids and session.groups. | ||
| 61 | @@ -1122,8 +1122,19 @@ static int setup_env(pool *p, cmd_rec *cmd, const char *user, char *pass) { | ||
| 62 | */ | ||
| 63 | res = pr_auth_getgroups(p, pw->pw_name, &session.gids, &session.groups); | ||
| 64 | if (res < 1) { | ||
| 65 | - pr_log_debug(DEBUG5, "no supplemental groups found for user '%s'", | ||
| 66 | - pw->pw_name); | ||
| 67 | + /* If no supplemental groups are provided, default to using the process | ||
| 68 | + * primary GID as the supplemental group. This prevents access | ||
| 69 | + * regressions as seen in Issue #1830. | ||
| 70 | + */ | ||
| 71 | + pr_log_debug(DEBUG5, "no supplemental groups found for user '%s', " | ||
| 72 | + "using primary group %s (GID %lu)", pw->pw_name, session.group, | ||
| 73 | + (unsigned long) session.login_gid); | ||
| 74 | + | ||
| 75 | + session.gids = make_array(p, 2, sizeof(gid_t)); | ||
| 76 | + session.groups = make_array(p, 2, sizeof(char *)); | ||
| 77 | + | ||
| 78 | + *((gid_t *) push_array(session.gids)) = session.login_gid; | ||
| 79 | + *((char **) push_array(session.groups)) = pstrdup(p, session.group); | ||
| 80 | } | ||
| 81 | } | ||
| 82 | |||
| 83 | diff --git a/src/auth.c b/src/auth.c | ||
| 84 | index 494a479c0..a6fe9f1c2 100644 | ||
| 85 | --- a/src/auth.c | ||
| 86 | +++ b/src/auth.c | ||
| 87 | @@ -1512,12 +1512,12 @@ int pr_auth_getgroups(pool *p, const char *name, array_header **group_ids, | ||
| 88 | } | ||
| 89 | |||
| 90 | /* Allocate memory for the array_headers of GIDs and group names. */ | ||
| 91 | - if (group_ids) { | ||
| 92 | - *group_ids = make_array(permanent_pool, 2, sizeof(gid_t)); | ||
| 93 | + if (group_ids != NULL) { | ||
| 94 | + *group_ids = make_array(p, 2, sizeof(gid_t)); | ||
| 95 | } | ||
| 96 | |||
| 97 | - if (group_names) { | ||
| 98 | - *group_names = make_array(permanent_pool, 2, sizeof(char *)); | ||
| 99 | + if (group_names != NULL) { | ||
| 100 | + *group_names = make_array(p, 2, sizeof(char *)); | ||
| 101 | } | ||
| 102 | |||
| 103 | cmd = make_cmd(p, 3, name, group_ids ? *group_ids : NULL, | ||
| 104 | @@ -1536,7 +1536,7 @@ int pr_auth_getgroups(pool *p, const char *name, array_header **group_ids, | ||
| 105 | * for the benefit of auth_getgroup() implementors. | ||
| 106 | */ | ||
| 107 | |||
| 108 | - if (group_ids) { | ||
| 109 | + if (group_ids != NULL) { | ||
| 110 | register unsigned int i; | ||
| 111 | char *strgids = ""; | ||
| 112 | gid_t *gids = (*group_ids)->elts; | ||
| 113 | @@ -1552,7 +1552,7 @@ int pr_auth_getgroups(pool *p, const char *name, array_header **group_ids, | ||
| 114 | *strgids ? strgids : "(None; corrupted group file?)"); | ||
| 115 | } | ||
| 116 | |||
| 117 | - if (group_names) { | ||
| 118 | + if (group_names != NULL) { | ||
| 119 | register unsigned int i; | ||
| 120 | char *strgroups = ""; | ||
| 121 | char **groups = (*group_names)->elts; | ||
| 122 | @@ -1568,7 +1568,7 @@ int pr_auth_getgroups(pool *p, const char *name, array_header **group_ids, | ||
| 123 | } | ||
| 124 | } | ||
| 125 | |||
| 126 | - if (cmd->tmp_pool) { | ||
| 127 | + if (cmd->tmp_pool != NULL) { | ||
| 128 | destroy_pool(cmd->tmp_pool); | ||
| 129 | cmd->tmp_pool = NULL; | ||
| 130 | } | ||
| 131 | diff --git a/tests/t/lib/ProFTPD/Tests/Modules/mod_sql_sqlite.pm b/tests/t/lib/ProFTPD/Tests/Modules/mod_sql_sqlite.pm | ||
| 132 | index 4abb6eb59..f1ffeef34 100644 | ||
| 133 | --- a/tests/t/lib/ProFTPD/Tests/Modules/mod_sql_sqlite.pm | ||
| 134 | +++ b/tests/t/lib/ProFTPD/Tests/Modules/mod_sql_sqlite.pm | ||
| 135 | @@ -467,6 +467,11 @@ my $TESTS = { | ||
| 136 | order => ++$order, | ||
| 137 | test_class => [qw(forking bug mod_tls)], | ||
| 138 | }, | ||
| 139 | + | ||
| 140 | + sql_user_info_no_suppl_groups_issue1830 => { | ||
| 141 | + order => ++$order, | ||
| 142 | + test_class => [qw(forking bug rootprivs)], | ||
| 143 | + }, | ||
| 144 | }; | ||
| 145 | |||
| 146 | sub new { | ||
| 147 | @@ -15732,4 +15737,174 @@ EOC | ||
| 148 | test_cleanup($setup->{log_file}, $ex); | ||
| 149 | } | ||
| 150 | |||
| 151 | +sub sql_user_info_no_suppl_groups_issue1830 { | ||
| 152 | + my $self = shift; | ||
| 153 | + my $tmpdir = $self->{tmpdir}; | ||
| 154 | + my $setup = test_setup($tmpdir, 'sqlite'); | ||
| 155 | + | ||
| 156 | + my $db_file = File::Spec->rel2abs("$tmpdir/proftpd.db"); | ||
| 157 | + | ||
| 158 | + # Build up sqlite3 command to create users, groups tables and populate them | ||
| 159 | + my $db_script = File::Spec->rel2abs("$tmpdir/proftpd.sql"); | ||
| 160 | + | ||
| 161 | + if (open(my $fh, "> $db_script")) { | ||
| 162 | + print $fh <<EOS; | ||
| 163 | +CREATE TABLE users ( | ||
| 164 | + userid TEXT, | ||
| 165 | + passwd TEXT, | ||
| 166 | + uid INTEGER, | ||
| 167 | + gid INTEGER, | ||
| 168 | + homedir TEXT, | ||
| 169 | + shell TEXT | ||
| 170 | +); | ||
| 171 | +INSERT INTO users (userid, passwd, uid, gid, homedir, shell) VALUES ('$setup->{user}', '$setup->{passwd}', $setup->{uid}, $setup->{gid}, '$setup->{home_dir}', '/bin/bash'); | ||
| 172 | + | ||
| 173 | +CREATE TABLE groups ( | ||
| 174 | + groupname TEXT, | ||
| 175 | + gid INTEGER, | ||
| 176 | + members TEXT | ||
| 177 | +); | ||
| 178 | +INSERT INTO groups (groupname, gid, members) VALUES ('$setup->{group}', $setup->{gid}, '$setup->{user}'); | ||
| 179 | +EOS | ||
| 180 | + | ||
| 181 | + unless (close($fh)) { | ||
| 182 | + die("Can't write $db_script: $!"); | ||
| 183 | + } | ||
| 184 | + | ||
| 185 | + } else { | ||
| 186 | + die("Can't open $db_script: $!"); | ||
| 187 | + } | ||
| 188 | + | ||
| 189 | + my $cmd = "sqlite3 $db_file < $db_script"; | ||
| 190 | + build_db($cmd, $db_script); | ||
| 191 | + | ||
| 192 | + # Make sure that, if we're running as root, the database file has | ||
| 193 | + # the permissions/privs set for use by proftpd | ||
| 194 | + if ($< == 0) { | ||
| 195 | + unless (chmod(0666, $db_file)) { | ||
| 196 | + die("Can't set perms on $db_file to 0666: $!"); | ||
| 197 | + } | ||
| 198 | + } | ||
| 199 | + | ||
| 200 | + my $config = { | ||
| 201 | + PidFile => $setup->{pid_file}, | ||
| 202 | + ScoreboardFile => $setup->{scoreboard_file}, | ||
| 203 | + SystemLog => $setup->{log_file}, | ||
| 204 | + TraceLog => $setup->{log_file}, | ||
| 205 | + Trace => 'auth:20 sql:20', | ||
| 206 | + | ||
| 207 | + # Required for logging the expected message | ||
| 208 | + DebugLevel => 5, | ||
| 209 | + | ||
| 210 | + IfModules => { | ||
| 211 | + 'mod_delay.c' => { | ||
| 212 | + DelayEngine => 'off', | ||
| 213 | + }, | ||
| 214 | + | ||
| 215 | + 'mod_sql.c' => { | ||
| 216 | + AuthOrder => 'mod_sql.c', | ||
| 217 | + | ||
| 218 | + SQLAuthenticate => 'users', | ||
| 219 | + SQLAuthTypes => 'plaintext', | ||
| 220 | + SQLBackend => 'sqlite3', | ||
| 221 | + SQLConnectInfo => $db_file, | ||
| 222 | + SQLLogFile => $setup->{log_file}, | ||
| 223 | + | ||
| 224 | + # Set these, so that our lower UID/GID will be used | ||
| 225 | + SQLMinUserUID => 100, | ||
| 226 | + SQLMinUserGID => 100, | ||
| 227 | + }, | ||
| 228 | + }, | ||
| 229 | + }; | ||
| 230 | + | ||
| 231 | + my ($port, $config_user, $config_group) = config_write($setup->{config_file}, | ||
| 232 | + $config); | ||
| 233 | + | ||
| 234 | + # Open pipes, for use between the parent and child processes. Specifically, | ||
| 235 | + # the child will indicate when it's done with its test by writing a message | ||
| 236 | + # to the parent. | ||
| 237 | + my ($rfh, $wfh); | ||
| 238 | + unless (pipe($rfh, $wfh)) { | ||
| 239 | + die("Can't open pipe: $!"); | ||
| 240 | + } | ||
| 241 | + | ||
| 242 | + my $ex; | ||
| 243 | + | ||
| 244 | + # Fork child | ||
| 245 | + $self->handle_sigchld(); | ||
| 246 | + defined(my $pid = fork()) or die("Can't fork: $!"); | ||
| 247 | + if ($pid) { | ||
| 248 | + eval { | ||
| 249 | + sleep(2); | ||
| 250 | + | ||
| 251 | + my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); | ||
| 252 | + $client->login($setup->{user}, $setup->{passwd}); | ||
| 253 | + | ||
| 254 | + my $resp_msgs = $client->response_msgs(); | ||
| 255 | + my $nmsgs = scalar(@$resp_msgs); | ||
| 256 | + | ||
| 257 | + my $expected = 1; | ||
| 258 | + $self->assert($expected == $nmsgs, | ||
| 259 | + test_msg("Expected $expected, got $nmsgs")); | ||
| 260 | + | ||
| 261 | + $expected = "User $setup->{user} logged in"; | ||
| 262 | + $self->assert($expected eq $resp_msgs->[0], | ||
| 263 | + test_msg("Expected response '$expected', got '$resp_msgs->[0]'")); | ||
| 264 | + | ||
| 265 | + $client->quit(); | ||
| 266 | + }; | ||
| 267 | + if ($@) { | ||
| 268 | + $ex = $@; | ||
| 269 | + } | ||
| 270 | + | ||
| 271 | + $wfh->print("done\n"); | ||
| 272 | + $wfh->flush(); | ||
| 273 | + | ||
| 274 | + } else { | ||
| 275 | + eval { server_wait($setup->{config_file}, $rfh) }; | ||
| 276 | + if ($@) { | ||
| 277 | + warn($@); | ||
| 278 | + exit 1; | ||
| 279 | + } | ||
| 280 | + | ||
| 281 | + exit 0; | ||
| 282 | + } | ||
| 283 | + | ||
| 284 | + # Stop server | ||
| 285 | + server_stop($setup->{pid_file}); | ||
| 286 | + $self->assert_child_ok($pid); | ||
| 287 | + | ||
| 288 | + eval { | ||
| 289 | + if (open(my $fh, "< $setup->{log_file}")) { | ||
| 290 | + my $ok = 0; | ||
| 291 | + | ||
| 292 | + while (my $line = <$fh>) { | ||
| 293 | + chomp($line); | ||
| 294 | + | ||
| 295 | + if ($ENV{TEST_VERBOSE}) { | ||
| 296 | + print STDERR "# $line\n"; | ||
| 297 | + } | ||
| 298 | + | ||
| 299 | + if ($line =~ /no supplemental groups found for user '$setup->{user}', using primary group/) { | ||
| 300 | + $ok = 1; | ||
| 301 | + last; | ||
| 302 | + } | ||
| 303 | + } | ||
| 304 | + | ||
| 305 | + close($fh); | ||
| 306 | + | ||
| 307 | + $self->assert($ok, test_msg("Did not see expected log message")); | ||
| 308 | + | ||
| 309 | + } else { | ||
| 310 | + die("Can't read $setup->{log_file}: $!"); | ||
| 311 | + } | ||
| 312 | + }; | ||
| 313 | + if ($@) { | ||
| 314 | + $ex = $@ unless $ex; | ||
| 315 | + } | ||
| 316 | + | ||
| 317 | + test_cleanup($setup->{log_file}, $ex); | ||
| 318 | +} | ||
| 319 | + | ||
| 320 | + | ||
| 321 | 1; | ||
diff --git a/meta-networking/recipes-daemons/proftpd/proftpd_1.3.7c.bb b/meta-networking/recipes-daemons/proftpd/proftpd_1.3.7c.bb index c8097a14b0..345c714a52 100644 --- a/meta-networking/recipes-daemons/proftpd/proftpd_1.3.7c.bb +++ b/meta-networking/recipes-daemons/proftpd/proftpd_1.3.7c.bb | |||
| @@ -12,12 +12,13 @@ SRC_URI = "git://github.com/proftpd/proftpd.git;branch=${BRANCH};protocol=https | |||
| 12 | file://proftpd-basic.init \ | 12 | file://proftpd-basic.init \ |
| 13 | file://default \ | 13 | file://default \ |
| 14 | file://close-RequireValidShell-check.patch \ | 14 | file://close-RequireValidShell-check.patch \ |
| 15 | file://contrib.patch \ | 15 | file://contrib.patch \ |
| 16 | file://build_fixup.patch \ | 16 | file://build_fixup.patch \ |
| 17 | file://proftpd.service \ | 17 | file://proftpd.service \ |
| 18 | file://CVE-2023-51713.patch \ | 18 | file://CVE-2023-51713.patch \ |
| 19 | file://CVE-2024-57392.patch \ | 19 | file://CVE-2024-57392.patch \ |
| 20 | file://CVE-2023-48795.patch \ | 20 | file://CVE-2023-48795.patch \ |
| 21 | file://CVE-2024-48651.patch \ | ||
| 21 | " | 22 | " |
| 22 | 23 | ||
| 23 | S = "${WORKDIR}/git" | 24 | S = "${WORKDIR}/git" |
