Просмотр исходного кода

Kernel: Strip SUID+SGID bits from file when written to or chowned

Fixes #1624.
Andreas Kling 5 лет назад
Родитель
Сommit
53d0ca2ad8

+ 4 - 0
Kernel/FileSystem/Ext2FileSystem.cpp

@@ -760,6 +760,10 @@ ssize_t Ext2FSInode::write_bytes(off_t offset, ssize_t count, const u8* data, Fi
     Locker inode_locker(m_lock);
     Locker fs_locker(fs().m_lock);
 
+    auto result = prepare_to_write_data();
+    if (result.is_error())
+        return result;
+
     if (is_symlink()) {
         ASSERT(offset == 0);
         if (max((size_t)(offset + count), (size_t)m_raw_inode.i_size) < max_inline_symlink_length) {

+ 15 - 0
Kernel/FileSystem/Inode.cpp

@@ -217,4 +217,19 @@ void Inode::set_metadata_dirty(bool metadata_dirty)
     }
 }
 
+KResult Inode::prepare_to_write_data()
+{
+    // FIXME: It's a poor design that filesystems are expected to call this before writing out data.
+    //        We should funnel everything through an interface at the VFS layer so this can happen from a single place.
+    LOCKER(m_lock);
+    if (fs().is_readonly())
+        return KResult(-EROFS);
+    auto metadata = this->metadata();
+    if (metadata.is_setuid() || metadata.is_setgid()) {
+        dbg() << "Inode::prepare_to_write_data(): Stripping SUID/SGID bits from " << identifier();
+        return chmod(metadata.mode & ~(04000 | 02000));
+    }
+    return KSuccess;
+}
+
 }

+ 1 - 0
Kernel/FileSystem/Inode.h

@@ -119,6 +119,7 @@ protected:
     void set_metadata_dirty(bool);
     void inode_contents_changed(off_t, ssize_t, const u8*);
     void inode_size_changed(size_t old_size, size_t new_size);
+    KResult prepare_to_write_data();
 
     mutable Lock m_lock { "Inode" };
 

+ 4 - 0
Kernel/FileSystem/ProcFS.cpp

@@ -1435,6 +1435,10 @@ void ProcFSInode::flush_metadata()
 
 ssize_t ProcFSInode::write_bytes(off_t offset, ssize_t size, const u8* buffer, FileDescription*)
 {
+    auto result = prepare_to_write_data();
+    if (result.is_error())
+        return result;
+
     auto* directory_entry = fs().get_directory_entry(identifier());
 
     Function<ssize_t(InodeIdentifier, const ByteBuffer&)> callback_tmp;

+ 4 - 0
Kernel/FileSystem/TmpFS.cpp

@@ -207,6 +207,10 @@ ssize_t TmpFSInode::write_bytes(off_t offset, ssize_t size, const u8* buffer, Fi
     ASSERT(!is_directory());
     ASSERT(offset >= 0);
 
+    auto result = prepare_to_write_data();
+    if (result.is_error())
+        return result;
+
     off_t old_size = m_metadata.size;
     off_t new_size = m_metadata.size;
     if ((offset + size) > new_size)

+ 8 - 0
Kernel/FileSystem/VirtualFileSystem.cpp

@@ -511,6 +511,14 @@ KResult VFS::chown(Inode& inode, uid_t a_uid, gid_t a_gid)
     }
 
     dbg() << "VFS::chown(): inode " << inode.identifier() << " <- uid:" << new_uid << " gid:" << new_gid;
+
+    if (metadata.is_setuid() || metadata.is_setgid()) {
+        dbg() << "VFS::chown(): Stripping SUID/SGID bits from " << inode.identifier();
+        auto result = inode.chmod(metadata.mode & ~(04000 | 02000));
+        if (result.is_error())
+            return result;
+    }
+
     return inode.chown(new_uid, new_gid);
 }