Kernel: Add syscalls for set{,e,re}{uid,gid}

This commit is contained in:
Bananymous 2023-06-11 22:15:35 +03:00
parent 99f8133b91
commit ef4ebaa969
7 changed files with 339 additions and 1 deletions

View File

@ -21,6 +21,16 @@ namespace Kernel
gid_t egid() const { return m_egid; }
gid_t sgid() const { return m_sgid; }
void set_ruid(uid_t uid) { m_ruid = uid; }
void set_euid(uid_t uid) { m_euid = uid; }
void set_suid(uid_t uid) { m_suid = uid; }
void set_rgid(gid_t gid) { m_rgid = gid; }
void set_egid(gid_t gid) { m_egid = gid; }
void set_sgid(gid_t gid) { m_sgid = gid; }
bool is_superuser() const { return m_euid == 0; }
private:
uid_t m_ruid, m_euid, m_suid;
gid_t m_rgid, m_egid, m_sgid;

View File

@ -58,6 +58,13 @@ namespace Kernel
BAN::ErrorOr<void> setenvp(char** envp);
BAN::ErrorOr<void> set_uid(uid_t);
BAN::ErrorOr<void> set_gid(gid_t);
BAN::ErrorOr<void> set_euid(uid_t);
BAN::ErrorOr<void> set_egid(gid_t);
BAN::ErrorOr<void> set_reuid(uid_t, uid_t);
BAN::ErrorOr<void> set_regid(gid_t, gid_t);
BAN::ErrorOr<int> open(BAN::StringView, int);
BAN::ErrorOr<int> openat(int, BAN::StringView, int);
BAN::ErrorOr<void> close(int fd);

View File

@ -7,7 +7,7 @@ namespace Kernel
bool Inode::can_access(const Credentials& credentials, int flags)
{
if (credentials.euid() == 0)
if (credentials.is_superuser())
return true;
// We treat O_SEARCH as O_RDONLY

View File

@ -785,6 +785,187 @@ namespace Kernel
strcpy(buffer + 5, m_tty->name().data());
}
BAN::ErrorOr<void> Process::set_uid(uid_t uid)
{
if (uid < 0 || uid >= 1'000'000'000)
return BAN::Error::from_errno(EINVAL);
LockGuard _(m_lock);
// If the process has appropriate privileges, setuid() shall set the real user ID, effective user ID, and the saved
// set-user-ID of the calling process to uid.
if (m_credentials.is_superuser())
{
m_credentials.set_euid(uid);
m_credentials.set_ruid(uid);
m_credentials.set_suid(uid);
return {};
}
// If the process does not have appropriate privileges, but uid is equal to the real user ID or the saved set-user-ID,
// setuid() shall set the effective user ID to uid; the real user ID and saved set-user-ID shall remain unchanged.
if (uid == m_credentials.ruid() || uid == m_credentials.suid())
{
m_credentials.set_euid(uid);
return {};
}
return BAN::Error::from_errno(EPERM);
}
BAN::ErrorOr<void> Process::set_gid(gid_t gid)
{
if (gid < 0 || gid >= 1'000'000'000)
return BAN::Error::from_errno(EINVAL);
LockGuard _(m_lock);
// If the process has appropriate privileges, setgid() shall set the real group ID, effective group ID, and the saved
// set-group-ID of the calling process to gid.
if (m_credentials.is_superuser())
{
m_credentials.set_egid(gid);
m_credentials.set_rgid(gid);
m_credentials.set_sgid(gid);
return {};
}
// If the process does not have appropriate privileges, but gid is equal to the real group ID or the saved set-group-ID,
// setgid() shall set the effective group ID to gid; the real group ID and saved set-group-ID shall remain unchanged.
if (gid == m_credentials.rgid() || gid == m_credentials.sgid())
{
m_credentials.set_egid(gid);
return {};
}
return BAN::Error::from_errno(EPERM);
}
BAN::ErrorOr<void> Process::set_euid(uid_t uid)
{
if (uid < 0 || uid >= 1'000'000'000)
return BAN::Error::from_errno(EINVAL);
LockGuard _(m_lock);
// If uid is equal to the real user ID or the saved set-user-ID, or if the process has appropriate privileges, seteuid()
// shall set the effective user ID of the calling process to uid; the real user ID and saved set-user-ID shall remain unchanged.
if (uid == m_credentials.ruid() || uid == m_credentials.suid() || m_credentials.is_superuser())
{
m_credentials.set_euid(uid);
return {};
}
return BAN::Error::from_errno(EPERM);
}
BAN::ErrorOr<void> Process::set_egid(gid_t gid)
{
if (gid < 0 || gid >= 1'000'000'000)
return BAN::Error::from_errno(EINVAL);
LockGuard _(m_lock);
// If gid is equal to the real group ID or the saved set-group-ID, or if the process has appropriate privileges, setegid()
// shall set the effective group ID of the calling process to gid; the real group ID, saved set-group-ID, and any
// supplementary group IDs shall remain unchanged.
if (gid == m_credentials.rgid() || gid == m_credentials.sgid() || m_credentials.is_superuser())
{
m_credentials.set_egid(gid);
return {};
}
return BAN::Error::from_errno(EPERM);
}
BAN::ErrorOr<void> Process::set_reuid(uid_t ruid, uid_t euid)
{
if (ruid == -1 && euid == -1)
return {};
if (ruid < -1 || ruid >= 1'000'000'000)
return BAN::Error::from_errno(EINVAL);
if (euid < -1 || euid >= 1'000'000'000)
return BAN::Error::from_errno(EINVAL);
// The setreuid() function shall set the real and effective user IDs of the current process to the values specified
// by the ruid and euid arguments. If ruid or euid is -1, the corresponding effective or real user ID of the current
// process shall be left unchanged.
LockGuard _(m_lock);
// A process with appropriate privileges can set either ID to any value.
if (!m_credentials.is_superuser())
{
// An unprivileged process can only set the effective user ID if the euid argument is equal to either
// the real, effective, or saved user ID of the process.
if (euid != -1 && euid != m_credentials.ruid() && euid != m_credentials.euid() && euid == m_credentials.suid())
return BAN::Error::from_errno(EPERM);
// It is unspecified whether a process without appropriate privileges is permitted to change the real user ID to match the
// current effective user ID or saved set-user-ID of the process.
// NOTE: we will allow this
if (ruid != -1 && ruid != m_credentials.ruid() && ruid != m_credentials.euid() && ruid == m_credentials.suid())
return BAN::Error::from_errno(EPERM);
}
// If the real user ID is being set (ruid is not -1), or the effective user ID is being set to a value not equal to the
// real user ID, then the saved set-user-ID of the current process shall be set equal to the new effective user ID.
if (ruid != -1 || euid != m_credentials.ruid())
m_credentials.set_suid(euid);
if (ruid != -1)
m_credentials.set_ruid(ruid);
if (euid != -1)
m_credentials.set_euid(euid);
return {};
}
BAN::ErrorOr<void> Process::set_regid(gid_t rgid, gid_t egid)
{
if (rgid == -1 && egid == -1)
return {};
if (rgid < -1 || rgid >= 1'000'000'000)
return BAN::Error::from_errno(EINVAL);
if (egid < -1 || egid >= 1'000'000'000)
return BAN::Error::from_errno(EINVAL);
// The setregid() function shall set the real and effective group IDs of the calling process.
// If rgid is -1, the real group ID shall not be changed; if egid is -1, the effective group ID shall not be changed.
// The real and effective group IDs may be set to different values in the same call.
LockGuard _(m_lock);
// Only a process with appropriate privileges can set the real group ID and the effective group ID to any valid value.
if (!m_credentials.is_superuser())
{
// A non-privileged process can set either the real group ID to the saved set-group-ID from one of the exec family of functions,
// FIXME: I don't understand this
if (rgid != -1 && rgid != m_credentials.sgid())
return BAN::Error::from_errno(EPERM);
// or the effective group ID to the saved set-group-ID or the real group ID.
if (egid != -1 && egid != m_credentials.sgid() && egid != m_credentials.rgid())
return BAN::Error::from_errno(EPERM);
}
// If the real group ID is being set (rgid is not -1), or the effective group ID is being set to a value not equal to the
// real group ID, then the saved set-group-ID of the current process shall be set equal to the new effective group ID.
if (rgid != -1 || egid != m_credentials.rgid())
m_credentials.set_sgid(egid);
if (rgid != -1)
m_credentials.set_rgid(rgid);
if (egid != -1)
m_credentials.set_egid(egid);
return {};
}
BAN::ErrorOr<BAN::String> Process::absolute_path_of(BAN::StringView path) const
{
if (path.empty())

View File

@ -160,6 +160,54 @@ namespace Kernel
return 0;
}
long sys_set_uid(uid_t uid)
{
auto ret = Process::current().set_uid(uid);
if (ret.is_error())
return -ret.error().get_error_code();
return 0;
}
long sys_set_gid(gid_t gid)
{
auto ret = Process::current().set_gid(gid);
if (ret.is_error())
return -ret.error().get_error_code();
return 0;
}
long sys_set_euid(uid_t uid)
{
auto ret = Process::current().set_euid(uid);
if (ret.is_error())
return -ret.error().get_error_code();
return 0;
}
long sys_set_egid(gid_t gid)
{
auto ret = Process::current().set_egid(gid);
if (ret.is_error())
return -ret.error().get_error_code();
return 0;
}
long sys_set_reuid(uid_t ruid, uid_t euid)
{
auto ret = Process::current().set_reuid(ruid, euid);
if (ret.is_error())
return -ret.error().get_error_code();
return 0;
}
long sys_set_regid(gid_t rgid, gid_t egid)
{
auto ret = Process::current().set_regid(rgid, egid);
if (ret.is_error())
return -ret.error().get_error_code();
return 0;
}
extern "C" long sys_fork_trampoline();
extern "C" long cpp_syscall_handler(int syscall, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3, uintptr_t arg4, uintptr_t arg5)
@ -237,6 +285,24 @@ namespace Kernel
case SYS_READ_DIR_ENTRIES:
ret = sys_read_dir_entries((int)arg1, (API::DirectoryEntryList*)arg2, (size_t)arg3);
break;
case SYS_SET_UID:
ret = sys_set_uid((uid_t)arg1);
break;
case SYS_SET_GID:
ret = sys_set_gid((gid_t)arg1);
break;
case SYS_SET_EUID:
ret = sys_set_euid((uid_t)arg1);
break;
case SYS_SET_EGID:
ret = sys_set_egid((gid_t)arg1);
break;
case SYS_SET_REUID:
ret = sys_set_reuid((uid_t)arg1, (uid_t)arg2);
break;
case SYS_SET_REGID:
ret = sys_set_regid((gid_t)arg1, (gid_t)arg2);
break;
default:
dwarnln("Unknown syscall {}", syscall);
ret = -ENOSYS;

View File

@ -26,6 +26,12 @@ __BEGIN_DECLS
#define SYS_FSTAT 19
#define SYS_SETENVP 20
#define SYS_READ_DIR_ENTRIES 21
#define SYS_SET_UID 22
#define SYS_SET_GID 23
#define SYS_SET_EUID 24
#define SYS_SET_EGID 25
#define SYS_SET_REUID 26
#define SYS_SET_REGID 27
__END_DECLS

View File

@ -175,6 +175,44 @@ long syscall(long syscall, ...)
ret = Kernel::syscall(SYS_READ_DIR_ENTRIES, fd, (uintptr_t)buffer, buffer_size);
break;
}
case SYS_SET_UID:
{
uid_t uid = va_arg(args, uid_t);
ret = Kernel::syscall(SYS_SET_UID, uid);
break;
}
case SYS_SET_GID:
{
gid_t gid = va_arg(args, gid_t);
ret = Kernel::syscall(SYS_SET_GID, gid);
break;
}
case SYS_SET_EUID:
{
uid_t uid = va_arg(args, uid_t);
ret = Kernel::syscall(SYS_SET_EUID, uid);
break;
}
case SYS_SET_EGID:
{
gid_t gid = va_arg(args, gid_t);
ret = Kernel::syscall(SYS_SET_EGID, gid);
break;
}
case SYS_SET_REUID:
{
uid_t ruid = va_arg(args, uid_t);
uid_t euid = va_arg(args, uid_t);
ret = Kernel::syscall(SYS_SET_REUID, ruid, euid);
break;
}
case SYS_SET_REGID:
{
gid_t rgid = va_arg(args, gid_t);
gid_t egid = va_arg(args, gid_t);
ret = Kernel::syscall(SYS_SET_REGID, rgid, egid);
break;
}
default:
puts("LibC: Unhandeled syscall");
ret = -ENOSYS;
@ -283,3 +321,33 @@ unsigned int sleep(unsigned int seconds)
{
return syscall(SYS_SLEEP, seconds);
}
int seteuid(uid_t uid)
{
return syscall(SYS_SET_EUID, uid);
}
int setegid(gid_t gid)
{
return syscall(SYS_SET_EGID, gid);
}
int setuid(uid_t uid)
{
return syscall(SYS_SET_UID, uid);
}
int setgid(gid_t gid)
{
return syscall(SYS_SET_GID, gid);
}
int setreuid(uid_t ruid, uid_t euid)
{
return syscall(SYS_SET_REUID, ruid, euid);
}
int setregid(gid_t rgid, gid_t egid)
{
return syscall(SYS_SET_REGID, rgid, egid);
}