Browse Source

Kernel: Start adding various file system permission checks.

Fail with EACCES in various situations. Fix userland bugs that were exposed.
Andreas Kling 6 years ago
parent
commit
f5f136931a

+ 3 - 2
Kernel/Ext2FileSystem.cpp

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

+ 24 - 0
Kernel/InodeMetadata.h

@@ -4,6 +4,8 @@
 #include "UnixTypes.h"
 #include <AK/HashTable.h>
 
+class Process;
+
 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_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 {
     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
     {
         if (uid == u)

+ 16 - 0
Kernel/Process.h

@@ -115,6 +115,7 @@ public:
     State state() const { return m_state; }
     uid_t uid() const { return m_uid; }
     gid_t gid() const { return m_gid; }
+    const HashTable<gid_t>& gids() const { return m_gids; }
     uid_t euid() const { return m_euid; }
     gid_t egid() const { return m_egid; }
     pid_t ppid() const { return m_ppid; }
@@ -537,3 +538,18 @@ inline void Process::for_each_living(Callback callback)
         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());
+}

+ 48 - 1
Kernel/VirtualFileSystem.cpp

@@ -8,6 +8,7 @@
 #include <AK/ktime.h>
 #include "CharacterDevice.h"
 #include <LibC/errno_numbers.h>
+#include <Kernel/Process.h>
 
 //#define VFS_DEBUG
 
@@ -145,6 +146,20 @@ RetainPtr<FileDescriptor> VFS::open(const String& path, int& error, int options,
     if (!inode)
         return nullptr;
     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()) {
         auto it = m_devices.find(encoded_device(metadata.major_device, metadata.minor_device));
         if (it == m_devices.end()) {
@@ -181,6 +196,11 @@ RetainPtr<FileDescriptor> VFS::create(const String& path, int& error, int option
     if (error != -ENOENT) {
         return nullptr;
     }
+    if (!parent_inode->metadata().may_write(*current)) {
+        error = -EACCES;
+        return nullptr;
+    }
+
     FileSystemPath p(path);
     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);
@@ -208,6 +228,11 @@ bool VFS::mkdir(const String& path, mode_t mode, Inode& base, int& error)
     if (error != -ENOENT) {
         return false;
     }
+    if (!parent_inode->metadata().may_write(*current)) {
+        error = -EACCES;
+        return false;
+    }
+
     FileSystemPath p(path);
     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);
@@ -225,7 +250,15 @@ bool VFS::chmod(const String& path, mode_t mode, Inode& base, int& error)
     if (!inode)
         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.
     mode = (inode->mode() & ~04777) | (mode & 04777);
@@ -299,6 +332,11 @@ bool VFS::unlink(const String& path, Inode& base, int& error)
         return false;
     }
 
+    if (!parent_inode->metadata().may_write(*current)) {
+        error = -EACCES;
+        return false;
+    }
+
     if (!parent_inode->remove_child(FileSystemPath(path).basename(), error))
         return false;
 
@@ -328,6 +366,11 @@ bool VFS::rmdir(const String& path, Inode& base, int& error)
         return false;
     }
 
+    if (!parent_inode->metadata().may_write(*current)) {
+        error = -EACCES;
+        return false;
+    }
+
     if (inode->directory_entry_count() != 2) {
         error = -ENOTEMPTY;
         return false;
@@ -455,6 +498,10 @@ InodeIdentifier VFS::resolve_path(const String& path, InodeIdentifier base, int&
             error = -ENOTDIR;
             return { };
         }
+        if (!metadata.may_execute(*current)) {
+            error = -EACCES;
+            return { };
+        }
         auto parent = crumb_id;
         crumb_id = crumb_inode->lookup(part);
         if (!crumb_id.is_valid()) {

+ 2 - 2
Kernel/sync.sh

@@ -16,7 +16,7 @@ mkdir -vp mnt/tmp
 chmod 1777 mnt/tmp
 mkdir -vp mnt/dev
 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/tty1 c 4 1
 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/keyboard c 85 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/1 mnt/dev/stdout
 ln -s /proc/self/fd/2 mnt/dev/stderr

+ 1 - 1
LibGUI/GEventLoop.cpp

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

+ 2 - 2
WindowServer/WSMessageLoop.cpp

@@ -39,13 +39,13 @@ int WSMessageLoop::exec()
     m_keyboard_fd = open("/dev/keyboard", 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);
     ASSERT(m_server_fd >= 0);
     sockaddr_un address;
     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));
     ASSERT(rc == 0);
     rc = listen(m_server_fd, 5);