Kernel/LibC: Implement {get,set,init}groups
This allows dropping /etc/group parsing from the kernel :D
This commit is contained in:
parent
f41e254e35
commit
af0bca74e4
|
@ -35,10 +35,8 @@ namespace Kernel
|
||||||
|
|
||||||
bool has_egid(gid_t) const;
|
bool has_egid(gid_t) const;
|
||||||
|
|
||||||
BAN::ErrorOr<void> initialize_supplementary_groups();
|
BAN::Span<const gid_t> groups() const { return m_supplementary.span(); }
|
||||||
|
BAN::ErrorOr<void> set_groups(BAN::Span<const gid_t> groups);
|
||||||
private:
|
|
||||||
BAN::ErrorOr<BAN::String> find_username() const;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
uid_t m_ruid, m_euid, m_suid;
|
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_getppid() const { return m_parent; }
|
||||||
BAN::ErrorOr<long> sys_getpid() const { return pid(); }
|
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<long> open_inode(VirtualFileSystem::File&&, int flags);
|
||||||
|
|
||||||
BAN::ErrorOr<void> create_file_or_dir(int fd, const char* path, mode_t mode) const;
|
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/Credentials.h>
|
||||||
#include <kernel/FS/VirtualFileSystem.h>
|
#include <kernel/FS/VirtualFileSystem.h>
|
||||||
|
|
||||||
#include <ctype.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
|
|
||||||
namespace Kernel
|
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
|
bool Credentials::has_egid(gid_t gid) const
|
||||||
{
|
{
|
||||||
if (m_egid == gid)
|
if (m_egid == gid)
|
||||||
|
@ -162,4 +14,13 @@ namespace Kernel
|
||||||
return false;
|
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)
|
BAN::ErrorOr<Process*> Process::create_userspace(const Credentials& credentials, BAN::StringView path, BAN::Span<BAN::StringView> arguments)
|
||||||
{
|
{
|
||||||
auto* process = create_process(credentials, 0);
|
auto* process = create_process(credentials, 0);
|
||||||
TRY(process->m_credentials.initialize_supplementary_groups());
|
|
||||||
|
|
||||||
process->m_working_directory = VirtualFileSystem::get().root_file();
|
process->m_working_directory = VirtualFileSystem::get().root_file();
|
||||||
process->m_page_table = BAN::UniqPtr<PageTable>::adopt(MUST(PageTable::create_userspace()));
|
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_euid(uid);
|
||||||
m_credentials.set_ruid(uid);
|
m_credentials.set_ruid(uid);
|
||||||
m_credentials.set_suid(uid);
|
m_credentials.set_suid(uid);
|
||||||
TRY(m_credentials.initialize_supplementary_groups());
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2928,7 +2926,6 @@ namespace Kernel
|
||||||
if (uid == m_credentials.ruid() || uid == m_credentials.suid())
|
if (uid == m_credentials.ruid() || uid == m_credentials.suid())
|
||||||
{
|
{
|
||||||
m_credentials.set_euid(uid);
|
m_credentials.set_euid(uid);
|
||||||
TRY(m_credentials.initialize_supplementary_groups());
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2989,7 +2986,6 @@ namespace Kernel
|
||||||
if (uid == m_credentials.ruid() || uid == m_credentials.suid() || m_credentials.is_superuser())
|
if (uid == m_credentials.ruid() || uid == m_credentials.suid() || m_credentials.is_superuser())
|
||||||
{
|
{
|
||||||
m_credentials.set_euid(uid);
|
m_credentials.set_euid(uid);
|
||||||
TRY(m_credentials.initialize_supplementary_groups());
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3056,8 +3052,6 @@ namespace Kernel
|
||||||
if (euid != -1)
|
if (euid != -1)
|
||||||
m_credentials.set_euid(euid);
|
m_credentials.set_euid(euid);
|
||||||
|
|
||||||
TRY(m_credentials.initialize_supplementary_groups());
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3205,6 +3199,28 @@ namespace Kernel
|
||||||
return BAN::Error::from_errno(error);
|
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
|
BAN::ErrorOr<BAN::String> Process::absolute_path_of(BAN::StringView path) const
|
||||||
{
|
{
|
||||||
LockGuard _(m_process_lock);
|
LockGuard _(m_process_lock);
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#include <sys/syscall.h>
|
||||||
|
|
||||||
static FILE* s_grent_fp = nullptr;
|
static FILE* s_grent_fp = nullptr;
|
||||||
static group s_grent_struct;
|
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);
|
fclose(fp);
|
||||||
return ret;
|
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<char*>(malloc(buffer_len));
|
||||||
|
if (buffer == nullptr)
|
||||||
|
{
|
||||||
|
fclose(fp);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t group_count = 0;
|
||||||
|
gid_t* groups = static_cast<gid_t*>(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<char*>(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<gid_t*>(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);
|
||||||
|
}
|
||||||
|
|
|
@ -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);
|
int getgrnam_r(const char* name, struct group* grp, char* buffer, size_t bufsize, struct group** result);
|
||||||
void setgrent(void);
|
void setgrent(void);
|
||||||
|
|
||||||
|
int initgroups(const char* user, gid_t group);
|
||||||
|
int setgroups(size_t size, const gid_t list[]);
|
||||||
|
|
||||||
__END_DECLS
|
__END_DECLS
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -111,6 +111,8 @@ __BEGIN_DECLS
|
||||||
O(SYS_FLOCK, flock) \
|
O(SYS_FLOCK, flock) \
|
||||||
O(SYS_GET_NPROCESSOR, get_nprocessor) \
|
O(SYS_GET_NPROCESSOR, get_nprocessor) \
|
||||||
O(SYS_FUTEX, futex) \
|
O(SYS_FUTEX, futex) \
|
||||||
|
O(SYS_GETGROUPS, getgroups) \
|
||||||
|
O(SYS_SETGROUPS, setgroups) \
|
||||||
|
|
||||||
enum Syscall
|
enum Syscall
|
||||||
{
|
{
|
||||||
|
|
|
@ -673,8 +673,7 @@ error:
|
||||||
|
|
||||||
int getgroups(int gidsetsize, gid_t grouplist[])
|
int getgroups(int gidsetsize, gid_t grouplist[])
|
||||||
{
|
{
|
||||||
dwarnln("FIXME: getgroups({}, {})", gidsetsize, grouplist);
|
return syscall(SYS_GETGROUPS, grouplist, gidsetsize);
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pid_t getppid(void)
|
pid_t getppid(void)
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
#include <BAN/Vector.h>
|
#include <BAN/Vector.h>
|
||||||
|
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
#include <grp.h>
|
||||||
#include <pwd.h>
|
#include <pwd.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
@ -94,6 +95,8 @@ int main(int argc, char** argv)
|
||||||
|
|
||||||
printf("Welcome back %s!\n", pwd->pw_name);
|
printf("Welcome back %s!\n", pwd->pw_name);
|
||||||
|
|
||||||
|
if (initgroups(name_buffer, pwd->pw_gid) == -1)
|
||||||
|
perror("initgroups");
|
||||||
if (setgid(pwd->pw_gid) == -1)
|
if (setgid(pwd->pw_gid) == -1)
|
||||||
perror("setgid");
|
perror("setgid");
|
||||||
if (setuid(pwd->pw_uid) == -1)
|
if (setuid(pwd->pw_uid) == -1)
|
||||||
|
|
Loading…
Reference in New Issue