From 03101b5186a43b893165f44726f4865702005d8e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= <zbyszek@in.waw.pl>
Date: Fri, 7 Oct 2022 17:34:53 +0200
Subject: [PATCH] shared/condition: avoid nss lookup in PID1

PID 1 is not allowed to do nss lookups because this may take a long time or
even deadlock.

While at it, the comparisons are reordered to do the "easy" comparisons which
only require a string comparison first. Delay parsing of the UID until it is
really necessary. The result is the same, because we know that "root" and
"nobody" parse as valid.

(cherry picked from commit 734f96b8490a2c48712ff6754a84fcaeac3d53c1)
(cherry picked from commit 5da595db39e8c6b229dfe388130683ff9a32eda5)
(cherry picked from commit 4ddeea92faf69291449af95dc9ba6440ad06ec1b)

Conflict:adapt context
Reference:https://github.com/systemd/systemd/commit/03101b5186a43b893165f44726f4865702005d8e
---
 src/shared/condition.c | 23 ++++++++++++++---------
 1 file changed, 14 insertions(+), 9 deletions(-)

diff --git a/src/shared/condition.c b/src/shared/condition.c
index 8d460f9..b07b71b 100644
--- a/src/shared/condition.c
+++ b/src/shared/condition.c
@@ -314,31 +314,36 @@ static int condition_test_cpus(Condition *c) {
 static int condition_test_user(Condition *c) {
         uid_t id;
         int r;
-        _cleanup_free_ char *username = NULL;
-        const char *u;
 
         assert(c);
         assert(c->parameter);
         assert(c->type == CONDITION_USER);
 
+        /* Do the quick&easy comparisons first, and only parse the UID later. */
+        if (streq(c->parameter, "root"))
+                return getuid() == 0 || geteuid() == 0;
+        if (streq(c->parameter, NOBODY_USER_NAME))
+                return getuid() == UID_NOBODY || geteuid() == UID_NOBODY;
+        if (streq(c->parameter, "@system"))
+                return uid_is_system(getuid()) || uid_is_system(geteuid());
+
         r = parse_uid(c->parameter, &id);
         if (r >= 0)
                 return id == getuid() || id == geteuid();
 
-        if (streq("@system", c->parameter))
-                return uid_is_system(getuid()) || uid_is_system(geteuid());
+        if (getpid_cached() == 1)  /* We already checked for "root" above, and we know that
+                                    * PID 1 is running as root, hence we know it cannot match. */
+                return false;
 
-        username = getusername_malloc();
+        /* getusername_malloc() may do an nss lookup, which is not allowed in PID 1. */
+        _cleanup_free_ char *username = getusername_malloc();
         if (!username)
                 return -ENOMEM;
 
         if (streq(username, c->parameter))
                 return 1;
 
-        if (getpid_cached() == 1)
-                return streq(c->parameter, "root");
-
-        u = c->parameter;
+        const char *u = c->parameter;
         r = get_user_creds(&u, &id, NULL, NULL, NULL, USER_CREDS_ALLOW_MISSING);
         if (r < 0)
                 return 0;
-- 
2.27.0

