fusefs: use effective gid, not real gid, for FUSE operations

This is the gid used for stuff like setting the group of a newly created
file.

Reported by:	pjdfstest
Sponsored by:	The FreeBSD Foundation
This commit is contained in:
Alan Somers 2019-05-04 02:11:28 +00:00
parent 72f03b7ccd
commit 61b0a927cb
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/projects/fuse2/; revision=347077
4 changed files with 47 additions and 5 deletions

View File

@ -191,6 +191,7 @@ fuse_interrupt_send(struct fuse_ticket *otick, int err)
struct fuse_data *data = otick->tk_data;
struct fuse_ticket *tick, *xtick;
struct ucred reused_creds;
gid_t reused_groups[1];
if (otick->irq_unique == 0) {
/*
@ -233,7 +234,8 @@ fuse_interrupt_send(struct fuse_ticket *otick, int err)
*/
ftick_hdr = fticket_in_header(otick);
reused_creds.cr_uid = ftick_hdr->uid;
reused_creds.cr_rgid = ftick_hdr->gid;
reused_groups[0] = ftick_hdr->gid;
reused_creds.cr_groups = reused_groups;
fdisp_init(&fdi, sizeof(*fii));
fdisp_make_pid(&fdi, FUSE_INTERRUPT, data, ftick_hdr->nodeid,
ftick_hdr->pid, &reused_creds);
@ -878,7 +880,7 @@ fuse_setup_ihead(struct fuse_in_header *ihead, struct fuse_ticket *ftick,
ihead->pid = pid;
ihead->uid = cred->cr_uid;
ihead->gid = cred->cr_rgid;
ihead->gid = cred->cr_groups[0];
}
/*

View File

@ -98,6 +98,31 @@ TEST_F(AllowOther, allowed)
ASSERT_EQ(0, WEXITSTATUS(status));
}
/* Check that fusefs uses the correct credentials for FUSE operations */
TEST_F(AllowOther, creds)
{
int status;
uid_t uid;
gid_t gid;
get_unprivileged_id(&uid, &gid);
fork(true, &status, [=] {
EXPECT_CALL(*m_mock, process( ResultOf([=](auto in) {
return (in->header.opcode == FUSE_LOOKUP &&
in->header.uid == uid &&
in->header.gid == gid);
}, Eq(true)),
_)
).Times(1)
.WillOnce(Invoke(ReturnErrno(ENOENT)));
}, []() {
eaccess(FULLPATH, F_OK);
return 0;
}
);
ASSERT_EQ(0, WEXITSTATUS(status));
}
/*
* A variation of the Open.multiple_creds test showing how the bug can lead to a
* privilege elevation. The first process is privileged and opens a file only

View File

@ -35,6 +35,7 @@ extern "C" {
#include <sys/sysctl.h>
#include <sys/wait.h>
#include <grp.h>
#include <pwd.h>
#include <semaphore.h>
#include <unistd.h>
@ -317,10 +318,11 @@ void FuseTest::expect_write(uint64_t ino, uint64_t offset, uint64_t isize,
})));
}
static void
get_unprivileged_uid(uid_t *uid)
void
get_unprivileged_id(uid_t *uid, gid_t *gid)
{
struct passwd *pw;
struct group *gr;
/*
* First try "tests", Kyua's default unprivileged user. XXX after
@ -333,7 +335,12 @@ get_unprivileged_uid(uid_t *uid)
}
if (pw == NULL)
GTEST_SKIP() << "Test requires an unprivileged user";
/* Use group "nobody", which is Kyua's default unprivileged group */
gr = getgrnam("nobody");
if (gr == NULL)
GTEST_SKIP() << "Test requires an unprivileged group";
*uid = pw->pw_uid;
*gid = gr->gr_gid;
}
void
@ -346,9 +353,10 @@ FuseTest::fork(bool drop_privs, int *child_status,
int mflags = MAP_ANON | MAP_SHARED;
pid_t child;
uid_t uid;
gid_t gid;
if (drop_privs) {
get_unprivileged_uid(&uid);
get_unprivileged_id(&uid, &gid);
if (IsSkipped())
return;
}
@ -367,6 +375,11 @@ FuseTest::fork(bool drop_privs, int *child_status,
goto out;
}
if (drop_privs && 0 != setegid(gid)) {
perror("setegid");
err = 1;
goto out;
}
if (drop_privs && 0 != setreuid(-1, uid)) {
perror("setreuid");
err = 1;

View File

@ -37,6 +37,8 @@
#define FUSE_WRITE_CACHE 1
#endif
void get_unprivileged_id(uid_t *uid, gid_t *gid);
class FuseTest : public ::testing::Test {
protected:
uint32_t m_maxreadahead;