summaryrefslogtreecommitdiff
path: root/proc
diff options
context:
space:
mode:
authorSergey Bugaev <bugaevc@gmail.com>2021-05-29 16:36:53 +0300
committerSamuel Thibault <samuel.thibault@ens-lyon.org>2022-08-10 22:14:43 +0200
commit3c9f1482b6890cfed89880e728ec6114e37c33d4 (patch)
treee2123a981944ae638387df90e0c2696e8ac899a9 /proc
parent29e3f1804c28cdcef6d130cf4c426fd40d10197b (diff)
proc: Use UIDs for evaluating permissions
The previous scheme was to check that PROC1 has the UID that is designated as PROC2's "owner". This is extremely problematic for several reasons, including: * The UIDs and the owner of a process are set separately and can easily get out of sync. While S_proc_setowner () checks that the new owner is among the UIDs that the process has, this invariant is violated after the process is given a new set of UIDs with S_proc_reauthenticate (). In particular, this happens during execution of a SUID binary: the process is reauthenticated to a different set of UIDs first, and its owner is udpated later, if at all. * In the Hurd, a process can have multiple UIDs. Having to designate just one of them as the owner means the other UIDs get ignored for the purpose of permission checking. One particularly problematic case is a SUID process that temporarily lowers its effective UID: glibc sets the first effective UID as the process owner, giving just about anyone access to the task. Resolve this by ignoring the owner for the purpose of permission checking, and relying solely on the authenticated UIDs. Roughly speaking, PROC1 can get complete access to PROC2 if the UIDs PROC1 has form a superset of the UIDs that PROC2 has; in other words, the access is only allowed when it would not result in PROC1 getting access to UIDs it doesn't have already. Of course, root is still allowed to access any process. In particular, this means that: * a process can access another process if they have the same auth; * a process that has "more auth" can access one that has "less auth", but not the other way around; * a SUID-root process that has lowered its effective UIDs can only be accessed by root. Another important point here is that the UIDs in question are both effective and available UIDs that a process has. Normally, available UIDs are ignored for the purpose of permission checking (and that is their whole point). However, POSIX description of kill(2) has the following clause: > For a process to have permission to send a signal to a process designated by > pid, unless the sending process has appropriate privileges, the real or > effective user ID of the sending process shall match the real or saved > set-user-ID of the receiving process. Which I read as saying that the real (i.e. available) UID(s) of PROC1 should be used for evaluating whether kill(2) can succeed, not only its effective UID(s).
Diffstat (limited to 'proc')
-rw-r--r--proc/info.c40
1 files changed, 31 insertions, 9 deletions
diff --git a/proc/info.c b/proc/info.c
index edade95b..ab74fb39 100644
--- a/proc/info.c
+++ b/proc/info.c
@@ -1,5 +1,6 @@
/* Process information queries
- Copyright (C) 1992,93,94,95,96,99,2000,01,02 Free Software Foundation, Inc.
+ Copyright (C) 1992,93,94,95,96,99,2000,01,02,21
+ Free Software Foundation, Inc.
This file is part of the GNU Hurd.
@@ -34,17 +35,38 @@
#include "proc.h"
#include "process_S.h"
-
-/* Returns true if PROC1 has `owner' privileges over PROC2 (and can thus get
- its task port &c). If PROC2 has an owner, then PROC1 must have that uid;
- otherwise, both must be in the same login collection. */
+/* Returns true if PROC1 has `owner' privileges over PROC2 (and can thus get its
+ task port &c). In the usual case, this checks that PROC1 already has all
+ UID's that PROC2 has, meaning PROC1 would not gain access to any new UID's
+ this way. The reason the check is performed using PROC1's both effective and
+ available UID's and not just its effective UID's is that POSIX requires
+ kill(2) to work when "the real or effective user ID of the sending process
+ shall match the real or saved set-user-ID of the receiving process". */
int
check_owner (struct proc *proc1, struct proc *proc2)
{
- return
- proc2->p_noowner
- ? check_uid (proc1, 0) || proc1->p_login == proc2->p_login
- : check_uid (proc1, proc2->p_owner);
+ /* Anyone can access themselves. */
+ if (proc1 == proc2)
+ return 1;
+
+ /* If PROC1 is unowned, it cannot access anyone else. */
+ if (!proc1->p_id || !proc1->p_id->i_nuids)
+ return 0;
+
+ /* Root can always access anyone. */
+ if (check_uid (proc1, 0))
+ return 1;
+
+ /* Nobody (except root) can access an unowned process. */
+ if (!proc2->p_id || !proc2->p_id->i_nuids)
+ return 0;
+
+ /* Verify that PROC1 has all UIDs that PROC2 has. */
+ for (size_t i = 0; i < proc2->p_id->i_nuids; i++)
+ if (!check_uid (proc1, proc2->p_id->i_uids[i]))
+ return 0;
+
+ return 1;
}