Kernel: Allow fchmod() and fchown() on pre-bind() local sockets

In order to ensure a specific owner and mode when the local socket
filesystem endpoint is instantiated, we need to be able to call
fchmod() and fchown() on a socket fd between socket() and bind().

This is because until we call bind(), there is no filesystem inode
for the socket yet.
This commit is contained in:
Andreas Kling 2020-01-03 20:14:56 +01:00
parent 4abbedb6e4
commit d84299c7be
8 changed files with 57 additions and 13 deletions

View file

@ -57,6 +57,8 @@ public:
virtual String absolute_path(const FileDescription&) const = 0;
virtual KResult truncate(off_t) { return KResult(-EINVAL); }
virtual KResult chown(uid_t, gid_t) { return KResult(-EBADF); }
virtual KResult chmod(mode_t) { return KResult(-EBADF); }
virtual const char* class_name() const = 0;

View file

@ -64,13 +64,6 @@ KResult FileDescription::fstat(stat& buffer)
return metadata().stat(buffer);
}
KResult FileDescription::fchmod(mode_t mode)
{
if (!m_inode)
return KResult(-EBADF);
return VFS::the().chmod(*m_inode, mode);
}
off_t FileDescription::seek(off_t offset, int whence)
{
if (!m_file->is_seekable())
@ -282,9 +275,12 @@ void FileDescription::set_file_flags(u32 flags)
m_file_flags = flags;
}
KResult FileDescription::chmod(mode_t mode)
{
return m_file->chmod(mode);
}
KResult FileDescription::chown(uid_t uid, gid_t gid)
{
if (!m_inode)
return KResult(-EINVAL);
return VFS::the().chown(*m_inode, uid, gid);
return m_file->chown(uid, gid);
}

View file

@ -52,7 +52,7 @@ public:
ssize_t write(const u8* data, ssize_t);
KResult fstat(stat&);
KResult fchmod(mode_t);
KResult chmod(mode_t);
bool can_read() const;
bool can_write() const;

View file

@ -52,3 +52,13 @@ KResult InodeFile::truncate(off_t size)
{
return m_inode->truncate(size);
}
KResult InodeFile::chown(uid_t uid, gid_t gid)
{
return VFS::the().chown(*m_inode, uid, gid);
}
KResult InodeFile::chmod(mode_t mode)
{
return VFS::the().chmod(*m_inode, mode);
}

View file

@ -26,6 +26,8 @@ public:
virtual String absolute_path(const FileDescription&) const override;
virtual KResult truncate(off_t) override;
virtual KResult chown(uid_t, gid_t) override;
virtual KResult chmod(mode_t) override;
virtual const char* class_name() const override { return "InodeFile"; }

View file

@ -34,6 +34,10 @@ LocalSocket::LocalSocket(int type)
LOCKER(all_sockets().lock());
all_sockets().resource().append(this);
m_prebind_uid = current->process().uid();
m_prebind_gid = current->process().gid();
m_prebind_mode = 0666;
#ifdef DEBUG_LOCAL_SOCKET
kprintf("%s(%u) LocalSocket{%p} created with type=%u\n", current->process().name().characters(), current->pid(), this, type);
#endif
@ -76,7 +80,9 @@ KResult LocalSocket::bind(const sockaddr* address, socklen_t address_size)
kprintf("%s(%u) LocalSocket{%p} bind(%s)\n", current->process().name().characters(), current->pid(), this, safe_address);
#endif
auto result = VFS::the().open(safe_address, O_CREAT | O_EXCL, S_IFSOCK | 0666, current->process().current_directory());
mode_t mode = S_IFSOCK | (m_prebind_mode & ~current->process().umask());
UidAndGid owner { m_prebind_uid, m_prebind_gid };
auto result = VFS::the().open( safe_address, O_CREAT | O_EXCL, mode, current->process().current_directory(), owner);
if (result.is_error()) {
if (result.error() == -EEXIST)
return KResult(-EADDRINUSE);
@ -334,3 +340,25 @@ KResult LocalSocket::getsockopt(FileDescription& description, int level, int opt
return Socket::getsockopt(description, level, option, value, value_size);
}
}
KResult LocalSocket::chmod(mode_t mode)
{
if (m_file)
return m_file->chmod(mode);
m_prebind_mode = mode & 04777;
return KSuccess;
}
KResult LocalSocket::chown(uid_t uid, gid_t gid)
{
if (m_file)
return m_file->chown(uid, gid);
if (!current->process().is_superuser() && (current->process().euid() != uid || !current->process().in_group(gid)))
return KResult(-EPERM);
m_prebind_uid = uid;
m_prebind_gid = gid;
return KSuccess;
}

View file

@ -30,6 +30,8 @@ public:
virtual ssize_t sendto(FileDescription&, const void*, size_t, int, const sockaddr*, socklen_t) override;
virtual ssize_t recvfrom(FileDescription&, void*, size_t, int flags, sockaddr*, socklen_t*) override;
virtual KResult getsockopt(FileDescription&, int level, int option, void*, socklen_t*) override;
virtual KResult chown(uid_t, gid_t) override;
virtual KResult chmod(mode_t) override;
private:
explicit LocalSocket(int type);
@ -43,6 +45,10 @@ private:
// An open socket file on the filesystem.
RefPtr<FileDescription> m_file;
uid_t m_prebind_uid { 0 };
uid_t m_prebind_gid { 0 };
mode_t m_prebind_mode { 0 };
// A single LocalSocket is shared between two file descriptions
// on the connect side and the accept side; so we need to store
// an additional role for the connect side and differentiate

View file

@ -2524,7 +2524,7 @@ int Process::sys$fchmod(int fd, mode_t mode)
auto* description = file_description(fd);
if (!description)
return -EBADF;
return description->fchmod(mode);
return description->chmod(mode);
}
int Process::sys$fchown(int fd, uid_t uid, gid_t gid)