diff --git a/AK/DoublyLinkedList.h b/AK/DoublyLinkedList.h index 31f506626f4171..60288ef26289be 100644 --- a/AK/DoublyLinkedList.h +++ b/AK/DoublyLinkedList.h @@ -8,6 +8,7 @@ template class DoublyLinkedList { private: struct Node { + explicit Node(const T& v) : value(v) { } explicit Node(T&& v) : value(move(v)) { } T value; Node* next { nullptr }; @@ -38,17 +39,13 @@ class DoublyLinkedList { void append(T&& value) { - auto* node = new Node(move(value)); - if (!m_head) { - ASSERT(!m_tail); - m_head = node; - m_tail = node; - return; - } - ASSERT(m_tail); - m_tail->next = node; - node->prev = m_tail; - m_tail = node; + append_node(new Node(move(value))); + + } + + void append(const T& value) + { + append_node(new Node(value)); } bool containsSlow(const T& value) const @@ -136,6 +133,20 @@ class DoublyLinkedList { private: friend class Iterator; + void append_node(Node* node) + { + if (!m_head) { + ASSERT(!m_tail); + m_head = node; + m_tail = node; + return; + } + ASSERT(m_tail); + m_tail->next = node; + node->prev = m_tail; + m_tail = node; + } + Node* head() { return m_head; } const Node* head() const { return m_head; } diff --git a/AK/HashTable.h b/AK/HashTable.h index b91020f54fc5c2..91df234be4f10f 100644 --- a/AK/HashTable.h +++ b/AK/HashTable.h @@ -48,6 +48,7 @@ class HashTable { unsigned size() const { return m_size; } unsigned capacity() const { return m_capacity; } + void set(const T&); void set(T&&); bool contains(const T&) const; void clear(); @@ -224,6 +225,7 @@ class HashTable { Bucket& lookup(const T&, unsigned* bucketIndex = nullptr); const Bucket& lookup(const T&, unsigned* bucketIndex = nullptr) const; void rehash(unsigned capacity); + void insert(const T&); void insert(T&&); Bucket* m_buckets { nullptr }; @@ -251,6 +253,26 @@ void HashTable::set(T&& value) m_size++; } +template +void HashTable::set(const T& value) +{ + if (!m_capacity) + rehash(1); + auto& bucket = lookup(value); + for (auto& e : bucket.chain) { + if (e == value) + return; + } + if (size() >= capacity()) { + rehash(size() + 1); + insert(value); + } else { + bucket.chain.append(value); + } + m_size++; +} + + template void HashTable::rehash(unsigned newCapacity) { @@ -291,6 +313,13 @@ void HashTable::insert(T&& value) bucket.chain.append(move(value)); } +template +void HashTable::insert(const T& value) +{ + auto& bucket = lookup(value); + bucket.chain.append(value); +} + template bool HashTable::contains(const T& value) const { diff --git a/Kernel/Process.cpp b/Kernel/Process.cpp index 972c5611dfd2fc..6137ad24412db6 100644 --- a/Kernel/Process.cpp +++ b/Kernel/Process.cpp @@ -21,6 +21,7 @@ //#define FORK_DEBUG //#define SCHEDULER_DEBUG #define COOL_GLOBALS +#define MAX_PROCESS_GIDS 32 #ifdef COOL_GLOBALS struct CoolGlobals { @@ -304,7 +305,7 @@ int Process::exec(const String& path, Vector&& arguments, Vector return error; } - if (!handle->metadata().mayExecute(m_euid, m_egid)) + if (!handle->metadata().mayExecute(m_euid, m_gids)) return -EACCES; auto elfData = handle->readEntireFile(); @@ -573,6 +574,8 @@ Process::Process(String&& name, uid_t uid, gid_t gid, pid_t ppid, RingLevel ring , m_tty(tty) , m_ppid(ppid) { + m_gids.set(m_gid); + if (fork_parent) { m_sid = fork_parent->m_sid; m_pgid = fork_parent->m_pgid; @@ -1566,3 +1569,33 @@ int Process::sys$sigaction(int signum, const Unix::sigaction* act, Unix::sigacti action.handler_or_sigaction = LinearAddress((dword)act->sa_sigaction); return 0; } + +int Process::sys$getgroups(int count, gid_t* gids) +{ + if (count < 0) + return -EINVAL; + ASSERT(m_gids.size() < MAX_PROCESS_GIDS); + if (!count) + return m_gids.size(); + if (count != m_gids.size()) + return -EINVAL; + VALIDATE_USER_WRITE(gids, sizeof(gid_t) * count); + size_t i = 0; + for (auto gid : m_gids) + gids[i++] = gid; + return 0; +} + +int Process::sys$setgroups(size_t count, const gid_t* gids) +{ + if (!is_root()) + return -EPERM; + if (count >= MAX_PROCESS_GIDS) + return -EINVAL; + VALIDATE_USER_READ(gids, sizeof(gid_t) * count); + m_gids.clear(); + m_gids.set(m_gid); + for (size_t i = 0; i < count; ++i) + m_gids.set(gids[i]); + return 0; +} diff --git a/Kernel/Process.h b/Kernel/Process.h index 64deb9a44a81d8..1af04888d382c7 100644 --- a/Kernel/Process.h +++ b/Kernel/Process.h @@ -139,6 +139,8 @@ class Process : public InlineLinkedListNode { int sys$dup(int oldfd); int sys$dup2(int oldfd, int newfd); int sys$sigaction(int signum, const Unix::sigaction* act, Unix::sigaction* old_act); + int sys$getgroups(int size, gid_t*); + int sys$setgroups(size_t, const gid_t*); static void initialize(); @@ -177,6 +179,8 @@ class Process : public InlineLinkedListNode { Process* fork(RegisterDump&); int exec(const String& path, Vector&& arguments, Vector&& environment); + bool is_root() const { return m_euid == 0; } + private: friend class MemoryManager; friend bool scheduleNewProcess(); @@ -242,6 +246,7 @@ class Process : public InlineLinkedListNode { Vector m_arguments; Vector m_initialEnvironment; + HashTable m_gids; }; class ProcessInspectionScope { diff --git a/Kernel/Syscall.cpp b/Kernel/Syscall.cpp index aa6d84cd1a3c07..0a6acf80220e31 100644 --- a/Kernel/Syscall.cpp +++ b/Kernel/Syscall.cpp @@ -152,6 +152,10 @@ static DWORD handle(RegisterDump& regs, DWORD function, DWORD arg1, DWORD arg2, return current->sys$sigaction((int)arg1, (const Unix::sigaction*)arg2, (Unix::sigaction*)arg3); case Syscall::SC_umask: return current->sys$umask((mode_t)arg1); + case Syscall::SC_getgroups: + return current->sys$getgroups((int)arg1, (gid_t*)arg2); + case Syscall::SC_setgroups: + return current->sys$setgroups((size_t)arg1, (const gid_t*)arg2); default: kprintf("<%u> int0x80: Unknown function %x requested {%x, %x, %x}\n", current->pid(), function, arg1, arg2, arg3); break; diff --git a/Kernel/Syscall.h b/Kernel/Syscall.h index 53b3ed848b8f55..d60ea052e3ff06 100644 --- a/Kernel/Syscall.h +++ b/Kernel/Syscall.h @@ -52,6 +52,8 @@ __ENUMERATE_SYSCALL(sigaction) \ __ENUMERATE_SYSCALL(getppid) \ __ENUMERATE_SYSCALL(umask) \ + __ENUMERATE_SYSCALL(getgroups) \ + __ENUMERATE_SYSCALL(setgroups) \ #define DO_SYSCALL_A0(function) Syscall::invoke((dword)(function)) diff --git a/LibC/grp.cpp b/LibC/grp.cpp index d12cd699bc0a58..405744968ecc2f 100644 --- a/LibC/grp.cpp +++ b/LibC/grp.cpp @@ -118,4 +118,26 @@ struct group* getgrent() return __grdb_entry; } +int initgroups(const char* user, gid_t extra_gid) +{ + size_t count = 0; + gid_t gids[32]; + bool extra_gid_added = false; + setgrent(); + while (auto* gr = getgrent()) { + for (auto* mem = gr->gr_mem; *mem; ++mem) { + if (!strcmp(*mem, user)) { + gids[count++] = gr->gr_gid; + if (gr->gr_gid == extra_gid) + extra_gid_added = true; + break; + } + } + } + endgrent(); + if (!extra_gid_added) + gids[count++] = extra_gid; + return setgroups(count, gids); +} + } diff --git a/LibC/grp.h b/LibC/grp.h index e29d4b954549d5..176907baa4d53c 100644 --- a/LibC/grp.h +++ b/LibC/grp.h @@ -18,4 +18,6 @@ void endgrent(); struct group* getgrnam(const char* name); struct group* getgrgid(gid_t); +int initgroups(const char* user, gid_t); + __END_DECLS diff --git a/LibC/unistd.cpp b/LibC/unistd.cpp index 1a5d8b0e9511c0..83443f38e77195 100644 --- a/LibC/unistd.cpp +++ b/LibC/unistd.cpp @@ -3,6 +3,9 @@ #include #include #include +#include +#include +#include #include extern "C" { @@ -218,4 +221,16 @@ int dup2(int old_fd, int new_fd) __RETURN_WITH_ERRNO(rc, rc, -1); } +int setgroups(size_t size, const gid_t* list) +{ + int rc = Syscall::invoke(Syscall::SC_getgroups, (dword)size, (dword)list); + __RETURN_WITH_ERRNO(rc, rc, -1); +} + +int getgroups(int size, gid_t list[]) +{ + int rc = Syscall::invoke(Syscall::SC_getgroups, (dword)size, (dword)list); + __RETURN_WITH_ERRNO(rc, rc, -1); +} + } diff --git a/LibC/unistd.h b/LibC/unistd.h index d16663140c85ee..81e9146ae88ee8 100644 --- a/LibC/unistd.h +++ b/LibC/unistd.h @@ -21,6 +21,8 @@ gid_t getegid(); uid_t getuid(); gid_t getgid(); pid_t getpid(); +int getgroups(int size, gid_t list[]); +int setgroups(size_t, const gid_t*); pid_t tcgetpgrp(int fd); int tcsetpgrp(int fd, pid_t pgid); int open(const char* path, int options, ...); diff --git a/Userland/id.cpp b/Userland/id.cpp index e82cfcf2c41d4e..9c7334a2a17b66 100644 --- a/Userland/id.cpp +++ b/Userland/id.cpp @@ -1,7 +1,8 @@ -#include -#include -#include -#include +#include +#include +#include +#include +#include int main(int c, char** v) { @@ -11,7 +12,28 @@ int main(int c, char** v) struct passwd* pw = getpwuid(uid); struct group* gr = getgrgid(gid); - printf("uid=%u(%s), gid=%u(%s)\n", uid, pw ? pw->pw_name : "n/a", gid, gr ? gr->gr_name : "n/a", getpid()); + printf("uid=%u(%s), gid=%u(%s)", uid, pw ? pw->pw_name : "n/a", gid, gr ? gr->gr_name : "n/a", getpid()); + + int extra_gid_count = getgroups(0, nullptr); + if (extra_gid_count) { + auto* extra_gids = (gid_t*)alloca(extra_gid_count * sizeof(gid_t)); + int rc = getgroups(extra_gid_count, extra_gids); + if (rc < 0) { + perror("\ngetgroups"); + return 1; + } + printf(","); + for (int g = 0; g < extra_gid_count; ++g) { + auto* gr = getgrgid(extra_gids[g]); + if (gr) + printf("%u(%s)", extra_gids[g], gr->gr_name); + else + printf("%u", extra_gids[g]); + if (g != extra_gid_count - 1) + printf(","); + } + } + printf("\n"); return 0; } diff --git a/VirtualFileSystem/InodeMetadata.h b/VirtualFileSystem/InodeMetadata.h index 9d31145b544a28..28a51841a1dbe4 100644 --- a/VirtualFileSystem/InodeMetadata.h +++ b/VirtualFileSystem/InodeMetadata.h @@ -2,6 +2,7 @@ #include "InodeIdentifier.h" #include "UnixTypes.h" +#include inline bool isDirectory(Unix::mode_t mode) { return (mode & 0170000) == 0040000; } inline bool isCharacterDevice(Unix::mode_t mode) { return (mode & 0170000) == 0020000; } @@ -17,11 +18,11 @@ inline bool isSetGID(Unix::mode_t mode) { return mode & 02000; } struct InodeMetadata { bool isValid() const { return inode.isValid(); } - bool mayExecute(uid_t u, gid_t g) const + bool mayExecute(uid_t u, const HashTable& g) const { if (uid == u) return mode & 0100; - if (gid == g) + if (g.contains(gid)) return mode & 0010; return mode & 0001; }