From af0bca74e49811646bc0ecb21473893cc95e0352 Mon Sep 17 00:00:00 2001 From: Bananymous Date: Sun, 10 Aug 2025 18:19:31 +0300 Subject: [PATCH] Kernel/LibC: Implement {get,set,init}groups This allows dropping /etc/group parsing from the kernel :D --- kernel/include/kernel/Credentials.h | 6 +- kernel/include/kernel/Process.h | 3 + kernel/kernel/Credentials.cpp | 157 +----------------- kernel/kernel/Process.cpp | 28 +++- userspace/libraries/LibC/grp.cpp | 104 ++++++++++++ userspace/libraries/LibC/include/grp.h | 5 +- .../libraries/LibC/include/sys/syscall.h | 2 + userspace/libraries/LibC/unistd.cpp | 3 +- userspace/programs/init/main.cpp | 3 + 9 files changed, 150 insertions(+), 161 deletions(-) diff --git a/kernel/include/kernel/Credentials.h b/kernel/include/kernel/Credentials.h index 83dfba4a..af0bab9e 100644 --- a/kernel/include/kernel/Credentials.h +++ b/kernel/include/kernel/Credentials.h @@ -35,10 +35,8 @@ namespace Kernel bool has_egid(gid_t) const; - BAN::ErrorOr initialize_supplementary_groups(); - - private: - BAN::ErrorOr find_username() const; + BAN::Span groups() const { return m_supplementary.span(); } + BAN::ErrorOr set_groups(BAN::Span groups); private: uid_t m_ruid, m_euid, m_suid; diff --git a/kernel/include/kernel/Process.h b/kernel/include/kernel/Process.h index b71a92bf..8a0819f1 100644 --- a/kernel/include/kernel/Process.h +++ b/kernel/include/kernel/Process.h @@ -95,6 +95,9 @@ namespace Kernel BAN::ErrorOr sys_getppid() const { return m_parent; } BAN::ErrorOr sys_getpid() const { return pid(); } + BAN::ErrorOr sys_getgroups(gid_t groups[], size_t count); + BAN::ErrorOr sys_setgroups(const gid_t groups[], size_t count); + BAN::ErrorOr open_inode(VirtualFileSystem::File&&, int flags); BAN::ErrorOr create_file_or_dir(int fd, const char* path, mode_t mode) const; diff --git a/kernel/kernel/Credentials.cpp b/kernel/kernel/Credentials.cpp index b97e3807..895aaa4f 100644 --- a/kernel/kernel/Credentials.cpp +++ b/kernel/kernel/Credentials.cpp @@ -1,157 +1,9 @@ #include #include -#include -#include - namespace Kernel { - static id_t parse_id(BAN::StringView line) - { - id_t id = 0; - for (char c : line) - { - if (!isdigit(c)) - return -1; - id = (id * 10) + (c - '0'); - } - return id; - }; - - BAN::ErrorOr Credentials::find_username() const - { - auto inode = TRY(VirtualFileSystem::get().file_from_absolute_path(*this, "/etc/passwd"_sv, O_RDONLY)).inode; - - BAN::String line; - off_t offset = 0; - uint8_t buffer[128]; - while (offset < inode->size()) - { - size_t nread = TRY(inode->read(offset, { buffer, sizeof(buffer) })); - - bool line_done = false; - for (size_t i = 0; i < nread; i++) - { - if (buffer[i] == '\n') - { - TRY(line.append({ (const char*)buffer, i })); - line_done = true; - offset += i + 1; - break; - } - } - if (!line_done) - { - offset += nread; - TRY(line.append({ (const char*)buffer, nread })); - continue; - } - - auto parts = TRY(line.sv().split(':', true)); - if (parts.size() == 7 && m_euid == parse_id(parts[2])) - { - BAN::String result; - TRY(result.append(parts[0])); - return result; - } - - line.clear(); - } - - auto parts = TRY(line.sv().split(':', true)); - if (parts.size() == 7 && m_euid == parse_id(parts[2])) - { - BAN::String result; - TRY(result.append(parts[0])); - return result; - } - - return BAN::Error::from_errno(EINVAL); - } - - BAN::ErrorOr Credentials::initialize_supplementary_groups() - { - m_supplementary.clear(); - - auto username = TRY(find_username()); - - auto file_or_error = VirtualFileSystem::get().file_from_absolute_path(*this, "/etc/group", O_RDONLY); - if (file_or_error.is_error()) - { - if (file_or_error.error().get_error_code() == ENOENT) - return {}; - return file_or_error.error(); - } - - auto inode = file_or_error.value().inode; - - BAN::String line; - off_t offset = 0; - uint8_t buffer[128]; - while (offset < inode->size()) - { - size_t nread = TRY(inode->read(offset, { buffer, sizeof(buffer) })); - - bool line_done = false; - for (size_t i = 0; i < nread; i++) - { - if (buffer[i] == '\n') - { - TRY(line.append({ (const char*)buffer, i })); - line_done = true; - offset += i + 1; - break; - } - } - if (!line_done) - { - offset += nread; - TRY(line.append({ (const char*)buffer, nread })); - continue; - } - - auto parts = TRY(line.sv().split(':', true)); - if (parts.size() != 4) - { - line.clear(); - continue; - } - - auto users = TRY(parts[3].split(',')); - for (auto user : users) - { - if (user != username) - continue; - if (gid_t gid = parse_id(parts[2]); gid != -1) - { - TRY(m_supplementary.push_back(gid)); - break; - } - } - - line.clear(); - } - - auto parts = TRY(line.sv().split(':', true)); - if (parts.size() == 4) - { - auto users = TRY(parts[3].split(',')); - for (auto user : users) - { - if (user != username) - continue; - if (gid_t gid = parse_id(parts[2]); gid != -1) - { - TRY(m_supplementary.push_back(gid)); - break; - } - } - } - - return {}; - } - bool Credentials::has_egid(gid_t gid) const { if (m_egid == gid) @@ -162,4 +14,13 @@ namespace Kernel return false; } + BAN::ErrorOr Credentials::set_groups(BAN::Span groups) + { + m_supplementary.clear(); + TRY(m_supplementary.resize(groups.size())); + for (size_t i = 0; i < groups.size(); i++) + m_supplementary[i] = groups[i]; + return {}; + } + } diff --git a/kernel/kernel/Process.cpp b/kernel/kernel/Process.cpp index 6e360abb..68e9fb39 100644 --- a/kernel/kernel/Process.cpp +++ b/kernel/kernel/Process.cpp @@ -101,7 +101,6 @@ namespace Kernel BAN::ErrorOr Process::create_userspace(const Credentials& credentials, BAN::StringView path, BAN::Span arguments) { auto* process = create_process(credentials, 0); - TRY(process->m_credentials.initialize_supplementary_groups()); process->m_working_directory = VirtualFileSystem::get().root_file(); process->m_page_table = BAN::UniqPtr::adopt(MUST(PageTable::create_userspace())); @@ -2919,7 +2918,6 @@ namespace Kernel m_credentials.set_euid(uid); m_credentials.set_ruid(uid); m_credentials.set_suid(uid); - TRY(m_credentials.initialize_supplementary_groups()); return 0; } @@ -2928,7 +2926,6 @@ namespace Kernel if (uid == m_credentials.ruid() || uid == m_credentials.suid()) { m_credentials.set_euid(uid); - TRY(m_credentials.initialize_supplementary_groups()); return 0; } @@ -2989,7 +2986,6 @@ namespace Kernel if (uid == m_credentials.ruid() || uid == m_credentials.suid() || m_credentials.is_superuser()) { m_credentials.set_euid(uid); - TRY(m_credentials.initialize_supplementary_groups()); return 0; } @@ -3056,8 +3052,6 @@ namespace Kernel if (euid != -1) m_credentials.set_euid(euid); - TRY(m_credentials.initialize_supplementary_groups()); - return 0; } @@ -3205,6 +3199,28 @@ namespace Kernel return BAN::Error::from_errno(error); } + BAN::ErrorOr Process::sys_getgroups(gid_t groups[], size_t count) + { + LockGuard _(m_process_lock); + TRY(validate_pointer_access(groups, count * sizeof(gid_t), true)); + auto current = m_credentials.groups(); + if (current.size() > count) + return BAN::Error::from_errno(EINVAL); + for (size_t i = 0; i < current.size(); i++) + groups[i] = current[i]; + return current.size(); + } + + BAN::ErrorOr Process::sys_setgroups(const gid_t groups[], size_t count) + { + LockGuard _(m_process_lock); + TRY(validate_pointer_access(groups, count * sizeof(gid_t), true)); + if (!m_credentials.is_superuser()) + return BAN::Error::from_errno(EPERM); + TRY(m_credentials.set_groups({ groups, count })); + return 0; + } + BAN::ErrorOr Process::absolute_path_of(BAN::StringView path) const { LockGuard _(m_process_lock); diff --git a/userspace/libraries/LibC/grp.cpp b/userspace/libraries/LibC/grp.cpp index b31b633e..9898b533 100644 --- a/userspace/libraries/LibC/grp.cpp +++ b/userspace/libraries/LibC/grp.cpp @@ -4,6 +4,7 @@ #include #include #include +#include static FILE* s_grent_fp = nullptr; static group s_grent_struct; @@ -211,3 +212,106 @@ int getgrnam_r(const char* name, struct group* grp, char* buffer, size_t bufsize fclose(fp); return ret; } + +static gid_t* get_user_groups(const char* user) +{ + FILE* fp = fopen("/etc/group", "r"); + if (fp == nullptr) + return nullptr; + + const long initial_len = sysconf(_SC_GETGR_R_SIZE_MAX); + + size_t buffer_len = (initial_len == -1) ? 512 : initial_len; + char* buffer = static_cast(malloc(buffer_len)); + if (buffer == nullptr) + { + fclose(fp); + return nullptr; + } + + size_t group_count = 0; + gid_t* groups = static_cast(malloc(sizeof(gid_t))); + if (groups == nullptr) + { + free(buffer); + fclose(fp); + return nullptr; + } + + for (;;) + { + struct group result; + struct group* resultp; + + int error = getgrent_impl(fp, &result, buffer, buffer_len, &resultp); + if (error == ERANGE) + { + const size_t new_buffer_len = buffer_len * 2; + char* new_buffer = static_cast(realloc(buffer, new_buffer_len)); + if (new_buffer == nullptr) + { + error = ENOMEM; + break; + } + buffer = new_buffer; + buffer_len = new_buffer_len; + continue; + } + + if (error != 0) + { + free(buffer); + free(groups); + fclose(fp); + return nullptr; + } + + if (resultp == nullptr) + break; + + bool contains = false; + for (size_t i = 0; result.gr_mem[i] && !contains; i++) + contains = (strcmp(result.gr_mem[i], user) == 0); + if (!contains) + continue; + + gid_t* new_groups = static_cast(realloc(groups, group_count * sizeof(gid_t))); + if (new_groups == nullptr) + { + free(buffer); + free(groups); + fclose(fp); + return nullptr; + } + + groups = new_groups; + groups[group_count++] = result.gr_gid; + } + + groups[group_count] = -1; + + free(buffer); + fclose(fp); + return groups; +} + +int initgroups(const char* user, gid_t group) +{ + gid_t* groups = get_user_groups(user); + if (groups == nullptr) + return -1; + + size_t group_count = 0; + while (groups[group_count] != -1) + group_count++; + groups[group_count] = group; + + int result = setgroups(group_count + 1, groups); + free(groups); + return result; +} + +int setgroups(size_t size, const gid_t list[]) +{ + return syscall(SYS_SETGROUPS, list, size); +} diff --git a/userspace/libraries/LibC/include/grp.h b/userspace/libraries/LibC/include/grp.h index 7d8448fb..74125053 100644 --- a/userspace/libraries/LibC/include/grp.h +++ b/userspace/libraries/LibC/include/grp.h @@ -14,7 +14,7 @@ __BEGIN_DECLS struct group { char* gr_name; /* The name of the group. */ - char* gr_passwd;/* The password of the group */ + char* gr_passwd; /* The password of the group */ gid_t gr_gid; /* Numerical group ID. */ char** gr_mem; /* Pointer to a null-terminated array of character pointers to member names. */ }; @@ -27,6 +27,9 @@ struct group* getgrnam(const char* name); int getgrnam_r(const char* name, struct group* grp, char* buffer, size_t bufsize, struct group** result); void setgrent(void); +int initgroups(const char* user, gid_t group); +int setgroups(size_t size, const gid_t list[]); + __END_DECLS #endif diff --git a/userspace/libraries/LibC/include/sys/syscall.h b/userspace/libraries/LibC/include/sys/syscall.h index 2f67d999..b045c2e8 100644 --- a/userspace/libraries/LibC/include/sys/syscall.h +++ b/userspace/libraries/LibC/include/sys/syscall.h @@ -111,6 +111,8 @@ __BEGIN_DECLS O(SYS_FLOCK, flock) \ O(SYS_GET_NPROCESSOR, get_nprocessor) \ O(SYS_FUTEX, futex) \ + O(SYS_GETGROUPS, getgroups) \ + O(SYS_SETGROUPS, setgroups) \ enum Syscall { diff --git a/userspace/libraries/LibC/unistd.cpp b/userspace/libraries/LibC/unistd.cpp index 0025343e..a484bec8 100644 --- a/userspace/libraries/LibC/unistd.cpp +++ b/userspace/libraries/LibC/unistd.cpp @@ -673,8 +673,7 @@ error: int getgroups(int gidsetsize, gid_t grouplist[]) { - dwarnln("FIXME: getgroups({}, {})", gidsetsize, grouplist); - return 0; + return syscall(SYS_GETGROUPS, grouplist, gidsetsize); } pid_t getppid(void) diff --git a/userspace/programs/init/main.cpp b/userspace/programs/init/main.cpp index c9d4c81e..6f11d424 100644 --- a/userspace/programs/init/main.cpp +++ b/userspace/programs/init/main.cpp @@ -3,6 +3,7 @@ #include #include +#include #include #include #include @@ -94,6 +95,8 @@ int main(int argc, char** argv) printf("Welcome back %s!\n", pwd->pw_name); + if (initgroups(name_buffer, pwd->pw_gid) == -1) + perror("initgroups"); if (setgid(pwd->pw_gid) == -1) perror("setgid"); if (setuid(pwd->pw_uid) == -1)