Kernel/LibC: Implement {get,set,init}groups

This allows dropping /etc/group parsing from the kernel :D
This commit is contained in:
2025-08-10 18:19:31 +03:00
parent f41e254e35
commit af0bca74e4
9 changed files with 150 additions and 161 deletions

View File

@@ -35,10 +35,8 @@ namespace Kernel
bool has_egid(gid_t) const;
BAN::ErrorOr<void> initialize_supplementary_groups();
private:
BAN::ErrorOr<BAN::String> find_username() const;
BAN::Span<const gid_t> groups() const { return m_supplementary.span(); }
BAN::ErrorOr<void> set_groups(BAN::Span<const gid_t> groups);
private:
uid_t m_ruid, m_euid, m_suid;

View File

@@ -95,6 +95,9 @@ namespace Kernel
BAN::ErrorOr<long> sys_getppid() const { return m_parent; }
BAN::ErrorOr<long> sys_getpid() const { return pid(); }
BAN::ErrorOr<long> sys_getgroups(gid_t groups[], size_t count);
BAN::ErrorOr<long> sys_setgroups(const gid_t groups[], size_t count);
BAN::ErrorOr<long> open_inode(VirtualFileSystem::File&&, int flags);
BAN::ErrorOr<void> create_file_or_dir(int fd, const char* path, mode_t mode) const;

View File

@@ -1,157 +1,9 @@
#include <kernel/Credentials.h>
#include <kernel/FS/VirtualFileSystem.h>
#include <ctype.h>
#include <fcntl.h>
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<BAN::String> 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<void> 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<void> Credentials::set_groups(BAN::Span<const gid_t> 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 {};
}
}

View File

@@ -101,7 +101,6 @@ namespace Kernel
BAN::ErrorOr<Process*> Process::create_userspace(const Credentials& credentials, BAN::StringView path, BAN::Span<BAN::StringView> 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<PageTable>::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<long> 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<long> 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<BAN::String> Process::absolute_path_of(BAN::StringView path) const
{
LockGuard _(m_process_lock);