Kernel/LibC: Implement {get,set,init}groups
This allows dropping /etc/group parsing from the kernel :D
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 {};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user