From d84299c7be8ba887288e63f5bd9447131c66c882 Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Fri, 3 Jan 2020 20:14:56 +0100 Subject: [PATCH] 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. --- Kernel/FileSystem/File.h | 2 ++ Kernel/FileSystem/FileDescription.cpp | 16 ++++++-------- Kernel/FileSystem/FileDescription.h | 2 +- Kernel/FileSystem/InodeFile.cpp | 10 +++++++++ Kernel/FileSystem/InodeFile.h | 2 ++ Kernel/Net/LocalSocket.cpp | 30 ++++++++++++++++++++++++++- Kernel/Net/LocalSocket.h | 6 ++++++ Kernel/Process.cpp | 2 +- 8 files changed, 57 insertions(+), 13 deletions(-) diff --git a/Kernel/FileSystem/File.h b/Kernel/FileSystem/File.h index f21ffc2d605..43d10e60be4 100644 --- a/Kernel/FileSystem/File.h +++ b/Kernel/FileSystem/File.h @@ -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; diff --git a/Kernel/FileSystem/FileDescription.cpp b/Kernel/FileSystem/FileDescription.cpp index 067941d09d5..0a4514db484 100644 --- a/Kernel/FileSystem/FileDescription.cpp +++ b/Kernel/FileSystem/FileDescription.cpp @@ -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); } diff --git a/Kernel/FileSystem/FileDescription.h b/Kernel/FileSystem/FileDescription.h index df3ab854295..8d7f5b499a3 100644 --- a/Kernel/FileSystem/FileDescription.h +++ b/Kernel/FileSystem/FileDescription.h @@ -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; diff --git a/Kernel/FileSystem/InodeFile.cpp b/Kernel/FileSystem/InodeFile.cpp index ea7a2347b6e..f3449498a89 100644 --- a/Kernel/FileSystem/InodeFile.cpp +++ b/Kernel/FileSystem/InodeFile.cpp @@ -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); +} diff --git a/Kernel/FileSystem/InodeFile.h b/Kernel/FileSystem/InodeFile.h index c7def9be459..46a732d5fab 100644 --- a/Kernel/FileSystem/InodeFile.h +++ b/Kernel/FileSystem/InodeFile.h @@ -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"; } diff --git a/Kernel/Net/LocalSocket.cpp b/Kernel/Net/LocalSocket.cpp index 605dc5a660b..01782d67626 100644 --- a/Kernel/Net/LocalSocket.cpp +++ b/Kernel/Net/LocalSocket.cpp @@ -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; +} diff --git a/Kernel/Net/LocalSocket.h b/Kernel/Net/LocalSocket.h index 2d3566eb4c5..ba292c72866 100644 --- a/Kernel/Net/LocalSocket.h +++ b/Kernel/Net/LocalSocket.h @@ -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 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 diff --git a/Kernel/Process.cpp b/Kernel/Process.cpp index 80cd27a9455..62d5cccf0b4 100644 --- a/Kernel/Process.cpp +++ b/Kernel/Process.cpp @@ -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)