1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-26 01:35:08 +00:00

Kernel: Start adding various file system permission checks.

Fail with EACCES in various situations. Fix userland bugs that were exposed.
This commit is contained in:
Andreas Kling 2019-02-21 15:45:31 +01:00
parent 43075e5878
commit f5f136931a
7 changed files with 96 additions and 8 deletions

View file

@ -9,6 +9,7 @@
#include <AK/kstdio.h> #include <AK/kstdio.h>
#include <AK/BufferStream.h> #include <AK/BufferStream.h>
#include <LibC/errno_numbers.h> #include <LibC/errno_numbers.h>
#include <Kernel/Process.h>
//#define EXT2_DEBUG //#define EXT2_DEBUG
@ -1183,13 +1184,13 @@ RetainPtr<Inode> Ext2FS::create_inode(InodeIdentifier parent_id, const String& n
ext2_inode e2inode; ext2_inode e2inode;
memset(&e2inode, 0, sizeof(ext2_inode)); memset(&e2inode, 0, sizeof(ext2_inode));
e2inode.i_mode = mode; e2inode.i_mode = mode;
e2inode.i_uid = 0; e2inode.i_uid = current->euid();
e2inode.i_gid = current->egid();
e2inode.i_size = size; e2inode.i_size = size;
e2inode.i_atime = timestamp; e2inode.i_atime = timestamp;
e2inode.i_ctime = timestamp; e2inode.i_ctime = timestamp;
e2inode.i_mtime = timestamp; e2inode.i_mtime = timestamp;
e2inode.i_dtime = 0; e2inode.i_dtime = 0;
e2inode.i_gid = 0;
e2inode.i_links_count = initial_links_count; e2inode.i_links_count = initial_links_count;
success = write_block_list_for_inode(inode_id, e2inode, blocks); success = write_block_list_for_inode(inode_id, e2inode, blocks);

View file

@ -4,6 +4,8 @@
#include "UnixTypes.h" #include "UnixTypes.h"
#include <AK/HashTable.h> #include <AK/HashTable.h>
class Process;
inline bool is_directory(mode_t mode) { return (mode & 0170000) == 0040000; } inline bool is_directory(mode_t mode) { return (mode & 0170000) == 0040000; }
inline bool is_character_device(mode_t mode) { return (mode & 0170000) == 0020000; } inline bool is_character_device(mode_t mode) { return (mode & 0170000) == 0020000; }
inline bool is_block_device(mode_t mode) { return (mode & 0170000) == 0060000; } inline bool is_block_device(mode_t mode) { return (mode & 0170000) == 0060000; }
@ -18,6 +20,28 @@ inline bool is_setgid(mode_t mode) { return mode & 02000; }
struct InodeMetadata { struct InodeMetadata {
bool is_valid() const { return inode.is_valid(); } bool is_valid() const { return inode.is_valid(); }
bool may_read(Process&) const;
bool may_write(Process&) const;
bool may_execute(Process&) const;
bool may_read(uid_t u, const HashTable<gid_t>& g) const
{
if (uid == u)
return mode & 0400;
if (g.contains(gid))
return mode & 0040;
return mode & 0004;
}
bool may_write(uid_t u, const HashTable<gid_t>& g) const
{
if (uid == u)
return mode & 0200;
if (g.contains(gid))
return mode & 0020;
return mode & 0002;
}
bool may_execute(uid_t u, const HashTable<gid_t>& g) const bool may_execute(uid_t u, const HashTable<gid_t>& g) const
{ {
if (uid == u) if (uid == u)

View file

@ -115,6 +115,7 @@ public:
State state() const { return m_state; } State state() const { return m_state; }
uid_t uid() const { return m_uid; } uid_t uid() const { return m_uid; }
gid_t gid() const { return m_gid; } gid_t gid() const { return m_gid; }
const HashTable<gid_t>& gids() const { return m_gids; }
uid_t euid() const { return m_euid; } uid_t euid() const { return m_euid; }
gid_t egid() const { return m_egid; } gid_t egid() const { return m_egid; }
pid_t ppid() const { return m_ppid; } pid_t ppid() const { return m_ppid; }
@ -537,3 +538,18 @@ inline void Process::for_each_living(Callback callback)
process = next_process; process = next_process;
} }
} }
inline bool InodeMetadata::may_read(Process& process) const
{
return may_read(process.euid(), process.gids());
}
inline bool InodeMetadata::may_write(Process& process) const
{
return may_write(process.euid(), process.gids());
}
inline bool InodeMetadata::may_execute(Process& process) const
{
return may_execute(process.euid(), process.gids());
}

View file

@ -8,6 +8,7 @@
#include <AK/ktime.h> #include <AK/ktime.h>
#include "CharacterDevice.h" #include "CharacterDevice.h"
#include <LibC/errno_numbers.h> #include <LibC/errno_numbers.h>
#include <Kernel/Process.h>
//#define VFS_DEBUG //#define VFS_DEBUG
@ -145,6 +146,20 @@ RetainPtr<FileDescriptor> VFS::open(const String& path, int& error, int options,
if (!inode) if (!inode)
return nullptr; return nullptr;
auto metadata = inode->metadata(); auto metadata = inode->metadata();
// NOTE: Read permission is a bit weird, since O_RDONLY == 0,
// so we check if (NOT write_only OR read_and_write)
if (!(options & O_WRONLY) || (options & O_RDWR)) {
if (!metadata.may_read(*current)) {
error = -EACCES;
return nullptr;
}
}
if ((options & O_WRONLY) || (options & O_RDWR)) {
if (!metadata.may_write(*current)) {
error = -EACCES;
return nullptr;
}
}
if (!(options & O_DONT_OPEN_DEVICE) && metadata.is_device()) { if (!(options & O_DONT_OPEN_DEVICE) && metadata.is_device()) {
auto it = m_devices.find(encoded_device(metadata.major_device, metadata.minor_device)); auto it = m_devices.find(encoded_device(metadata.major_device, metadata.minor_device));
if (it == m_devices.end()) { if (it == m_devices.end()) {
@ -181,6 +196,11 @@ RetainPtr<FileDescriptor> VFS::create(const String& path, int& error, int option
if (error != -ENOENT) { if (error != -ENOENT) {
return nullptr; return nullptr;
} }
if (!parent_inode->metadata().may_write(*current)) {
error = -EACCES;
return nullptr;
}
FileSystemPath p(path); FileSystemPath p(path);
dbgprintf("VFS::create_file: '%s' in %u:%u\n", p.basename().characters(), parent_inode->fsid(), parent_inode->index()); dbgprintf("VFS::create_file: '%s' in %u:%u\n", p.basename().characters(), parent_inode->fsid(), parent_inode->index());
auto new_file = parent_inode->fs().create_inode(parent_inode->identifier(), p.basename(), mode, 0, error); auto new_file = parent_inode->fs().create_inode(parent_inode->identifier(), p.basename(), mode, 0, error);
@ -208,6 +228,11 @@ bool VFS::mkdir(const String& path, mode_t mode, Inode& base, int& error)
if (error != -ENOENT) { if (error != -ENOENT) {
return false; return false;
} }
if (!parent_inode->metadata().may_write(*current)) {
error = -EACCES;
return false;
}
FileSystemPath p(path); FileSystemPath p(path);
dbgprintf("VFS::mkdir: '%s' in %u:%u\n", p.basename().characters(), parent_inode->fsid(), parent_inode->index()); dbgprintf("VFS::mkdir: '%s' in %u:%u\n", p.basename().characters(), parent_inode->fsid(), parent_inode->index());
auto new_dir = parent_inode->fs().create_directory(parent_inode->identifier(), p.basename(), mode, error); auto new_dir = parent_inode->fs().create_directory(parent_inode->identifier(), p.basename(), mode, error);
@ -225,7 +250,15 @@ bool VFS::chmod(const String& path, mode_t mode, Inode& base, int& error)
if (!inode) if (!inode)
return false; return false;
// FIXME: Permission checks. if (inode->fs().is_readonly()) {
error = -EROFS;
return false;
}
if (current->euid() != inode->metadata().uid) {
error = -EPERM;
return false;
}
// Only change the permission bits. // Only change the permission bits.
mode = (inode->mode() & ~04777) | (mode & 04777); mode = (inode->mode() & ~04777) | (mode & 04777);
@ -299,6 +332,11 @@ bool VFS::unlink(const String& path, Inode& base, int& error)
return false; return false;
} }
if (!parent_inode->metadata().may_write(*current)) {
error = -EACCES;
return false;
}
if (!parent_inode->remove_child(FileSystemPath(path).basename(), error)) if (!parent_inode->remove_child(FileSystemPath(path).basename(), error))
return false; return false;
@ -328,6 +366,11 @@ bool VFS::rmdir(const String& path, Inode& base, int& error)
return false; return false;
} }
if (!parent_inode->metadata().may_write(*current)) {
error = -EACCES;
return false;
}
if (inode->directory_entry_count() != 2) { if (inode->directory_entry_count() != 2) {
error = -ENOTEMPTY; error = -ENOTEMPTY;
return false; return false;
@ -455,6 +498,10 @@ InodeIdentifier VFS::resolve_path(const String& path, InodeIdentifier base, int&
error = -ENOTDIR; error = -ENOTDIR;
return { }; return { };
} }
if (!metadata.may_execute(*current)) {
error = -EACCES;
return { };
}
auto parent = crumb_id; auto parent = crumb_id;
crumb_id = crumb_inode->lookup(part); crumb_id = crumb_inode->lookup(part);
if (!crumb_id.is_valid()) { if (!crumb_id.is_valid()) {

View file

@ -16,7 +16,7 @@ mkdir -vp mnt/tmp
chmod 1777 mnt/tmp chmod 1777 mnt/tmp
mkdir -vp mnt/dev mkdir -vp mnt/dev
mkdir -vp mnt/dev/pts mkdir -vp mnt/dev/pts
mknod mnt/dev/bxvga b 82 413 mknod -m 666 mnt/dev/bxvga b 82 413
mknod mnt/dev/tty0 c 4 0 mknod mnt/dev/tty0 c 4 0
mknod mnt/dev/tty1 c 4 1 mknod mnt/dev/tty1 c 4 1
mknod mnt/dev/tty2 c 4 2 mknod mnt/dev/tty2 c 4 2
@ -27,7 +27,7 @@ mknod mnt/dev/zero c 1 5
mknod mnt/dev/full c 1 7 mknod mnt/dev/full c 1 7
mknod mnt/dev/keyboard c 85 1 mknod mnt/dev/keyboard c 85 1
mknod mnt/dev/psaux c 10 1 mknod mnt/dev/psaux c 10 1
mknod mnt/dev/ptmx c 5 2 mknod -m 666 mnt/dev/ptmx c 5 2
ln -s /proc/self/fd/0 mnt/dev/stdin ln -s /proc/self/fd/0 mnt/dev/stdin
ln -s /proc/self/fd/1 mnt/dev/stdout ln -s /proc/self/fd/1 mnt/dev/stdout
ln -s /proc/self/fd/2 mnt/dev/stderr ln -s /proc/self/fd/2 mnt/dev/stderr

View file

@ -38,7 +38,7 @@ GEventLoop::GEventLoop()
sockaddr_un address; sockaddr_un address;
address.sun_family = AF_LOCAL; address.sun_family = AF_LOCAL;
strcpy(address.sun_path, "/wsportal"); strcpy(address.sun_path, "/tmp/wsportal");
int retries = 1000; int retries = 1000;
int rc = 0; int rc = 0;

View file

@ -39,13 +39,13 @@ int WSMessageLoop::exec()
m_keyboard_fd = open("/dev/keyboard", O_RDONLY | O_NONBLOCK | O_CLOEXEC); m_keyboard_fd = open("/dev/keyboard", O_RDONLY | O_NONBLOCK | O_CLOEXEC);
m_mouse_fd = open("/dev/psaux", O_RDONLY | O_NONBLOCK | O_CLOEXEC); m_mouse_fd = open("/dev/psaux", O_RDONLY | O_NONBLOCK | O_CLOEXEC);
unlink("/wsportal"); unlink("/tmp/wsportal");
m_server_fd = socket(AF_LOCAL, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0); m_server_fd = socket(AF_LOCAL, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
ASSERT(m_server_fd >= 0); ASSERT(m_server_fd >= 0);
sockaddr_un address; sockaddr_un address;
address.sun_family = AF_LOCAL; address.sun_family = AF_LOCAL;
strcpy(address.sun_path, "/wsportal"); strcpy(address.sun_path, "/tmp/wsportal");
int rc = bind(m_server_fd, (const sockaddr*)&address, sizeof(address)); int rc = bind(m_server_fd, (const sockaddr*)&address, sizeof(address));
ASSERT(rc == 0); ASSERT(rc == 0);
rc = listen(m_server_fd, 5); rc = listen(m_server_fd, 5);