diff --git a/kernel/include/kernel/Credentials.h b/kernel/include/kernel/Credentials.h index 984579da2c..09083753a2 100644 --- a/kernel/include/kernel/Credentials.h +++ b/kernel/include/kernel/Credentials.h @@ -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; diff --git a/kernel/include/kernel/Process.h b/kernel/include/kernel/Process.h index 07e41f3468..7f2ac097d8 100644 --- a/kernel/include/kernel/Process.h +++ b/kernel/include/kernel/Process.h @@ -58,6 +58,13 @@ namespace Kernel BAN::ErrorOr setenvp(char** envp); + BAN::ErrorOr set_uid(uid_t); + BAN::ErrorOr set_gid(gid_t); + BAN::ErrorOr set_euid(uid_t); + BAN::ErrorOr set_egid(gid_t); + BAN::ErrorOr set_reuid(uid_t, uid_t); + BAN::ErrorOr set_regid(gid_t, gid_t); + BAN::ErrorOr open(BAN::StringView, int); BAN::ErrorOr openat(int, BAN::StringView, int); BAN::ErrorOr close(int fd); diff --git a/kernel/kernel/FS/Inode.cpp b/kernel/kernel/FS/Inode.cpp index 5d56e2897c..67b2c30fe9 100644 --- a/kernel/kernel/FS/Inode.cpp +++ b/kernel/kernel/FS/Inode.cpp @@ -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 diff --git a/kernel/kernel/Process.cpp b/kernel/kernel/Process.cpp index 632e74fd43..1ab68699ed 100644 --- a/kernel/kernel/Process.cpp +++ b/kernel/kernel/Process.cpp @@ -785,6 +785,187 @@ namespace Kernel strcpy(buffer + 5, m_tty->name().data()); } + BAN::ErrorOr 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 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 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 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 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 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 Process::absolute_path_of(BAN::StringView path) const { if (path.empty()) diff --git a/kernel/kernel/Syscall.cpp b/kernel/kernel/Syscall.cpp index ae52935a39..aa30f4a633 100644 --- a/kernel/kernel/Syscall.cpp +++ b/kernel/kernel/Syscall.cpp @@ -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; diff --git a/libc/include/sys/syscall.h b/libc/include/sys/syscall.h index 78af2442b6..311616d256 100644 --- a/libc/include/sys/syscall.h +++ b/libc/include/sys/syscall.h @@ -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 diff --git a/libc/unistd.cpp b/libc/unistd.cpp index a86489f2cd..b5474a2dba 100644 --- a/libc/unistd.cpp +++ b/libc/unistd.cpp @@ -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); +}