]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
nsresourced: use a hashed rather than a mangled name as fallback
authorLennart Poettering <lennart@poettering.net>
Tue, 26 Aug 2025 07:00:06 +0000 (09:00 +0200)
committerZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
Tue, 26 Aug 2025 13:30:39 +0000 (15:30 +0200)
If we are asked to come up with our own name for the namespace to
allocate (because client enabled "mangle"), then we so far created a
randomized name if shortening what was proposed didn't work. This broke
polkit authorization however, because when polkit is in the mix, we
process method calls twice, submitting the polkit request on the first
and then assuming a response is known on the second invocation. But if
we generate a randomized name for the two checks we'll not be ablet to
match up the requests because it's going to be different. Let's fix that
by not using a randomized name, but one hashed from the socket
connection we are processing mixed with the client provided name. This
will ensure that for the same method call we'll generate the same name,
but different calls (i.e. calls with different names on the same socket,
or with any name on any socket) we'll end up with different names,
minimizing chance of collision.

This ensures PK starts to work with nsresourced userns registration when
a bad or no name is specified, which previously would end up in a PK
query loop.

src/nsresourced/nsresourcework.c

index a648a72a07d96bd4c8d5e9a5276fb4ef64081b9a..0fc8d1cb0105816c46a170bd68fcbe0097415377 100644 (file)
@@ -630,14 +630,35 @@ static int test_userns_api_support(sd_varlink *link) {
         return 0;
 }
 
-static char* random_name(void) {
-        char *s = NULL;
+static char* hash_name(sd_varlink *link, const char *name) {
+        int r;
+
+        assert(link);
+        assert(name);
+
+        /* Make up a hashed name for this userns. We take the passed name, and hash it together with the
+         * connection cookie. This should make collisions unlikely but generation still deterministic (this
+         * matters because on polkit requests we might be called twice, and should generate the same string
+         * each time, to ensure the Polkit query looks the same) */
+
+        uint64_t cookie = 0;
+        r = socket_get_cookie(sd_varlink_get_fd(link), &cookie);
+        if (r < 0)
+                log_debug_errno(r, "Failed to determine connection cookie, ignoring: %m");
+
+        struct siphash h;
+        static sd_id128_t key = SD_ID128_MAKE(ed,3a,bb,01,3a,14,4b,b3,8a,63,a4,ad,ba,2d,c9,0a);
+        siphash24_init(&h, key.bytes);
+        siphash24_compress_typesafe(cookie, &h);
+        siphash24_compress_string(name, &h);
+
+        /* Make sure the hashed name fits into utmpx even if prefixed with "ns-", the peer's UID, "-", and
+         * suffixed by "-65535". */
 
-        /* Make up a random name for this userns. Make sure the random name fits into utmpx even if prefixed
-         * with "ns-", the peer's UID, "-", and suffixed by "-65535". */
         assert_cc(STRLEN("ns-65535-") + 16 + STRLEN("-65535") < sizeof_field(struct utmpx, ut_user));
 
-        if (asprintf(&s, "%016" PRIx64, random_u64()) < 0)
+        char *s = NULL;
+        if (asprintf(&s, "%016" PRIx64, siphash24_finalize(&h)) < 0)
                 return NULL;
 
         return s;
@@ -690,8 +711,8 @@ static int validate_name(sd_varlink *link, const char *name, bool mangle, char *
                         if (!userns_name_is_valid(un)) {
                                 free(un);
 
-                                /* if not, make up a random name */
-                                un = random_name();
+                                /* if not, make up a hashed name */
+                                un = hash_name(link, name);
                                 if (!un)
                                         return -ENOMEM;
                         }
@@ -716,7 +737,7 @@ static int validate_name(sd_varlink *link, const char *name, bool mangle, char *
                                 free_and_replace(un, c);
                         else  {
                                 free(c);
-                                c = random_name();
+                                c = hash_name(link, name);
                                 if (!c)
                                         return -ENOMEM;