summaryrefslogtreecommitdiff
path: root/Kernel/Process.cpp
diff options
context:
space:
mode:
authorNico Weber <thakis@chromium.org>2020-06-17 08:58:00 -0400
committerAndreas Kling <kling@serenityos.org>2020-06-18 23:19:16 +0200
commita38754d9f2e1fc5955de02eb75cd1d4cf853a83a (patch)
tree2241b5afd86bb7a2051f3171ed4ceb1a24909526 /Kernel/Process.cpp
parent0001bbf1824c17d048f0ec8dde75eccf91a86f26 (diff)
downloadserenity-a38754d9f2e1fc5955de02eb75cd1d4cf853a83a.zip
Kernel+LibC: Implement seteuid() and friends!
Add seteuid()/setegid() under _POSIX_SAVED_IDS semantics, which also requires adding suid and sgid to Process, and changing setuid()/setgid() to honor these semantics. The exact semantics aren't specified by POSIX and differ between different Unix implementations. This patch makes serenity follow FreeBSD. The 2002 USENIX paper "Setuid Demystified" explains the differences well. In addition to seteuid() and setegid() this also adds setreuid()/setregid() and setresuid()/setresgid(), and the accessors getresuid()/getresgid(). Also reorder uid/euid functions so that they are the same order everywhere (namely, the order that geteuid()/getuid() already have).
Diffstat (limited to 'Kernel/Process.cpp')
-rw-r--r--Kernel/Process.cpp160
1 files changed, 154 insertions, 6 deletions
diff --git a/Kernel/Process.cpp b/Kernel/Process.cpp
index 420dfcd160..c0a308df4c 100644
--- a/Kernel/Process.cpp
+++ b/Kernel/Process.cpp
@@ -986,9 +986,9 @@ int Process::do_exec(NonnullRefPtr<FileDescription> main_program_description, Ve
if (!(main_program_description->custody()->mount_flags() & MS_NOSUID)) {
if (main_program_metadata.is_setuid())
- m_euid = main_program_metadata.uid;
+ m_euid = m_suid = main_program_metadata.uid;
if (main_program_metadata.is_setgid())
- m_egid = main_program_metadata.gid;
+ m_egid = m_sgid = main_program_metadata.gid;
}
Thread::current->set_default_signal_dispositions();
@@ -1386,10 +1386,12 @@ Process* Process::create_kernel_process(Thread*& first_thread, String&& name, vo
Process::Process(Thread*& first_thread, const String& name, uid_t uid, gid_t gid, pid_t ppid, RingLevel ring, RefPtr<Custody> cwd, RefPtr<Custody> executable, TTY* tty, Process* fork_parent)
: m_name(move(name))
, m_pid(allocate_pid())
- , m_uid(uid)
- , m_gid(gid)
, m_euid(uid)
, m_egid(gid)
+ , m_uid(uid)
+ , m_gid(gid)
+ , m_suid(uid)
+ , m_sgid(gid)
, m_ring(ring)
, m_executable(move(executable))
, m_cwd(move(cwd))
@@ -2125,23 +2127,147 @@ int Process::sys$killpg(int pgrp, int signum)
return do_killpg(pgrp, signum);
}
+int Process::sys$seteuid(uid_t euid)
+{
+ REQUIRE_PROMISE(id);
+
+ // This has FreeBSD semantics.
+ // Linux and Solaris also allow m_euid.
+ if (euid != m_uid && euid != m_suid && !is_superuser())
+ return -EPERM;
+
+ m_euid = euid;
+ return 0;
+}
+
+int Process::sys$setegid(gid_t egid)
+{
+ REQUIRE_PROMISE(id);
+
+ // This has FreeBSD semantics.
+ // Linux and Solaris also allow m_egid.
+ if (egid != m_gid && egid != m_sgid && !is_superuser())
+ return -EPERM;
+
+ m_egid = egid;
+ return 0;
+}
+
int Process::sys$setuid(uid_t uid)
{
REQUIRE_PROMISE(id);
- if (uid != m_uid && !is_superuser())
+
+ // Linux and Solaris require real or saved.
+ // FreeBSD requires real or effective.
+ if (uid != m_uid && uid != m_euid && !is_superuser())
return -EPERM;
+
+ // Solaris and Linux only set uid and suid if is_superuser(),
+ // FreeBSD always sets all 3.
m_uid = uid;
m_euid = uid;
+ m_suid = uid;
return 0;
}
int Process::sys$setgid(gid_t gid)
{
REQUIRE_PROMISE(id);
- if (gid != m_gid && !is_superuser())
+
+ // Linux and Solaris require real or saved.
+ // FreeBSD requires real or effective.
+ if (gid != m_gid && gid != m_egid && !is_superuser())
return -EPERM;
+
+ // Solaris and Linux only set uid and suid if is_superuser(),
+ // FreeBSD always sets all 3.
m_gid = gid;
m_egid = gid;
+ m_sgid = gid;
+ return 0;
+}
+
+int Process::sys$setreuid(uid_t ruid, uid_t euid)
+{
+ REQUIRE_PROMISE(id);
+
+ // This has FreeBSD semantics.
+ // Linux and Solaris also allow id == m_suid.
+ auto ok = [this](uid_t id) { return id == (uid_t)-1 || id == m_uid || id == m_euid; };
+ if ((!ok(ruid) || !ok(euid)) && !is_superuser())
+ return -EPERM;
+
+ if (ruid != (uid_t)-1)
+ m_uid = ruid;
+ if (euid != (uid_t)-1)
+ m_euid = euid;
+
+ if (ruid != (uid_t)-1 || m_euid != m_uid)
+ m_suid = m_euid;
+
+ return 0;
+}
+
+int Process::sys$setregid(gid_t rgid, gid_t egid)
+{
+ REQUIRE_PROMISE(id);
+
+ // This has FreeBSD semantics.
+ // Linux and Solaris also allow id == m_sgid.
+ auto ok = [this](gid_t id) { return id == (gid_t)-1 || id == m_gid || id == m_egid; };
+ if ((!ok(rgid) || !ok(egid)) && !is_superuser())
+ return -EPERM;
+
+ if (rgid != (gid_t)-1)
+ m_gid = rgid;
+ if (egid != (gid_t)-1)
+ m_egid = egid;
+
+ if (rgid != (gid_t)-1 || m_egid != m_gid)
+ m_sgid = m_egid;
+
+ return 0;
+}
+
+int Process::sys$setresuid(uid_t ruid, uid_t euid, uid_t suid)
+{
+ REQUIRE_PROMISE(id);
+
+ if (ruid == (uid_t)-1)
+ ruid = m_uid;
+ if (euid == (uid_t)-1)
+ euid = m_euid;
+ if (suid == (uid_t)-1)
+ suid = m_suid;
+
+ auto ok = [this](uid_t id) { return id == m_uid || id == m_euid || id == m_suid; };
+ if ((!ok(ruid) || !ok(euid) || !ok(suid)) && !is_superuser())
+ return -EPERM;
+
+ m_uid = ruid;
+ m_euid = euid;
+ m_suid = suid;
+ return 0;
+}
+
+int Process::sys$setresgid(gid_t rgid, gid_t egid, gid_t sgid)
+{
+ REQUIRE_PROMISE(id);
+
+ if (rgid == (gid_t)-1)
+ rgid = m_gid;
+ if (egid == (gid_t)-1)
+ egid = m_egid;
+ if (sgid == (gid_t)-1)
+ sgid = m_sgid;
+
+ auto ok = [this](gid_t id) { return id == m_gid || id == m_egid || id == m_sgid; };
+ if ((!ok(rgid) || !ok(egid) || !ok(sgid)) && !is_superuser())
+ return -EPERM;
+
+ m_gid = rgid;
+ m_egid = egid;
+ m_sgid = sgid;
return 0;
}
@@ -2384,6 +2510,28 @@ pid_t Process::sys$getppid()
return m_ppid;
}
+int Process::sys$getresuid(uid_t* ruid, uid_t* euid, uid_t* suid)
+{
+ REQUIRE_PROMISE(stdio);
+ if (!validate_write_typed(ruid) || !validate_write_typed(euid) || !validate_write_typed(suid))
+ return -EFAULT;
+ copy_to_user(ruid, &m_uid);
+ copy_to_user(euid, &m_euid);
+ copy_to_user(suid, &m_suid);
+ return 0;
+}
+
+int Process::sys$getresgid(gid_t* rgid, gid_t* egid, gid_t* sgid)
+{
+ REQUIRE_PROMISE(stdio);
+ if (!validate_write_typed(rgid) || !validate_write_typed(egid) || !validate_write_typed(sgid))
+ return -EFAULT;
+ copy_to_user(rgid, &m_gid);
+ copy_to_user(egid, &m_egid);
+ copy_to_user(sgid, &m_sgid);
+ return 0;
+}
+
mode_t Process::sys$umask(mode_t mask)
{
REQUIRE_PROMISE(stdio);