]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
userdb: replace recursion lock 16112/head
authorLennart Poettering <lennart@poettering.net>
Thu, 4 Jun 2020 09:46:36 +0000 (11:46 +0200)
committerZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
Tue, 23 Jun 2020 15:24:24 +0000 (17:24 +0200)
Previously we'd used the existance of a specific AF_UNIX socket in the
abstract namespace as lock for disabling lookup recursions. (for
breaking out of the loop: userdb synthesized from nss → nss synthesized
from userdb → userdb synthesized from nss → …)

I did it like that because it promised to work the same both in static
and in dynmically linked environments and is accessible easily from any
programming language.

However, it has a weakness regarding reuse attacks: the socket is
securely hashed (siphash) from the thread ID in combination with the
AT_RANDOM secret. Thus it should not be guessable from an attacker in
advance. That's only true if a thread takes the lock only once and
keeps it forever. However, if a thread takes and releases it multiple
times an attacker might monitor that and quickly take the lock
after the first iteration for follow-up iterations.

It's not a big issue given that userdb (as the primary user for this)
never released the lock and we never made the concept a public
interface, and it was only included in one release so far, but it's
something that deserves fixing. (moreover it's a local DoS only, only
permitting to disable native userdb lookups)

With this rework the libnss_systemd.so.2 module will now export two
additional symbols. These symbols are not used by glibc, but can be used
by arbitrary programs: one can be used to disable nss-systemd, the other
to check if it is currently disabled.

The lock is per-thread. It's slightly less pretty, since it requires
people to manually link against C code via dlopen()/dlsym(), but it
should work safely without the aforementioned weakness.

meson.build
src/nss-systemd/nss-systemd.c
src/nss-systemd/nss-systemd.h [new file with mode: 0644]
src/nss-systemd/nss-systemd.sym
src/nss-systemd/userdb-glue.c
src/shared/userdb.c
src/shared/userdb.h
src/userdb/userwork.c

index 1df2ee86481026f97bc73d160005e9792a51ff52..55cb0e6054264691cc225269f0355228e730d0f3 100644 (file)
@@ -1656,7 +1656,7 @@ test_dlopen = executable(
         build_by_default : want_tests != 'false')
 
 foreach tuple : [['myhostname', 'ENABLE_NSS_MYHOSTNAME'],
-                 ['systemd',    'ENABLE_NSS_SYSTEMD', 'src/nss-systemd/userdb-glue.c src/nss-systemd/userdb-glue.h'],
+                 ['systemd',    'ENABLE_NSS_SYSTEMD', 'src/nss-systemd/userdb-glue.c src/nss-systemd/userdb-glue.h src/nss-systemd/nss-systemd.h'],
                  ['mymachines', 'ENABLE_NSS_MYMACHINES'],
                  ['resolve',    'ENABLE_NSS_RESOLVE']]
 
index e11f917c1981197b78f9aeb0be57cac2e3f4ba69..5dc5aacdff200a37fdb2829e4c0814c2472323b4 100644 (file)
@@ -8,6 +8,7 @@
 #include "fd-util.h"
 #include "group-record-nss.h"
 #include "macro.h"
+#include "nss-systemd.h"
 #include "nss-util.h"
 #include "pthread-util.h"
 #include "signal-util.h"
@@ -299,7 +300,7 @@ enum nss_status _nss_systemd_setpwent(int stayopen) {
         PROTECT_ERRNO;
         BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
 
-        if (userdb_nss_compat_is_enabled() <= 0)
+        if (_nss_systemd_is_blocked())
                 return NSS_STATUS_NOTFOUND;
 
         _cleanup_(pthread_mutex_unlock_assertp) pthread_mutex_t *_l = NULL;
@@ -323,7 +324,7 @@ enum nss_status _nss_systemd_setgrent(int stayopen) {
         PROTECT_ERRNO;
         BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
 
-        if (userdb_nss_compat_is_enabled() <= 0)
+        if (_nss_systemd_is_blocked())
                 return NSS_STATUS_NOTFOUND;
 
         _cleanup_(pthread_mutex_unlock_assertp) pthread_mutex_t *_l = NULL;
@@ -353,13 +354,7 @@ enum nss_status _nss_systemd_getpwent_r(
         assert(result);
         assert(errnop);
 
-        r = userdb_nss_compat_is_enabled();
-        if (r < 0) {
-                UNPROTECT_ERRNO;
-                *errnop = -r;
-                return NSS_STATUS_UNAVAIL;
-        }
-        if (!r)
+        if (_nss_systemd_is_blocked())
                 return NSS_STATUS_NOTFOUND;
 
         _cleanup_(pthread_mutex_unlock_assertp) pthread_mutex_t *_l = NULL;
@@ -406,14 +401,8 @@ enum nss_status _nss_systemd_getgrent_r(
         assert(result);
         assert(errnop);
 
-        r = userdb_nss_compat_is_enabled();
-        if (r < 0) {
-                UNPROTECT_ERRNO;
-                *errnop = -r;
-                return NSS_STATUS_UNAVAIL;
-        }
-        if (!r)
-                return NSS_STATUS_UNAVAIL;
+        if (_nss_systemd_is_blocked())
+                return NSS_STATUS_NOTFOUND;
 
         _cleanup_(pthread_mutex_unlock_assertp) pthread_mutex_t *_l = NULL;
 
@@ -459,7 +448,7 @@ enum nss_status _nss_systemd_getgrent_r(
         }
 
         if (getgrent_data.by_membership) {
-                _cleanup_close_ int lock_fd = -1;
+                _cleanup_(_nss_systemd_unblockp) bool blocked = false;
 
                 for (;;) {
                         _cleanup_free_ char *user_name = NULL, *group_name = NULL;
@@ -479,13 +468,15 @@ enum nss_status _nss_systemd_getgrent_r(
                                 continue;
 
                         /* We are about to recursively call into NSS, let's make sure we disable recursion into our own code. */
-                        if (lock_fd < 0) {
-                                lock_fd = userdb_nss_compat_disable();
-                                if (lock_fd < 0 && lock_fd != -EBUSY) {
+                        if (!blocked) {
+                                r = _nss_systemd_block(true);
+                                if (r < 0) {
                                         UNPROTECT_ERRNO;
-                                        *errnop = -lock_fd;
+                                        *errnop = -r;
                                         return NSS_STATUS_UNAVAIL;
                                 }
+
+                                blocked = true;
                         }
 
                         r = nss_group_record_by_name(group_name, false, &gr);
@@ -549,13 +540,7 @@ enum nss_status _nss_systemd_initgroups_dyn(
         if (STR_IN_SET(user_name, root_passwd.pw_name, nobody_passwd.pw_name))
                 return NSS_STATUS_NOTFOUND;
 
-        r = userdb_nss_compat_is_enabled();
-        if (r < 0) {
-                UNPROTECT_ERRNO;
-                *errnop = -r;
-                return NSS_STATUS_UNAVAIL;
-        }
-        if (!r)
+        if (_nss_systemd_is_blocked())
                 return NSS_STATUS_NOTFOUND;
 
         r = membershipdb_by_user(user_name, nss_glue_userdb_flags(), &iterator);
@@ -627,3 +612,29 @@ enum nss_status _nss_systemd_initgroups_dyn(
 
         return any ? NSS_STATUS_SUCCESS : NSS_STATUS_NOTFOUND;
 }
+
+static thread_local unsigned _blocked = 0;
+
+_public_ int _nss_systemd_block(bool b) {
+
+        /* This blocks recursively: it's blocked for as many times this function is called with `true` until
+         * it is called an equal time with `false`. */
+
+        if (b) {
+                if (_blocked >= UINT_MAX)
+                        return -EOVERFLOW;
+
+                _blocked++;
+        } else {
+                if (_blocked <= 0)
+                        return -EOVERFLOW;
+
+                _blocked--;
+        }
+
+        return b; /* Return what is passed in, i.e. the new state from the PoV of the caller */
+}
+
+_public_ bool _nss_systemd_is_blocked(void) {
+        return _blocked > 0;
+}
diff --git a/src/nss-systemd/nss-systemd.h b/src/nss-systemd/nss-systemd.h
new file mode 100644 (file)
index 0000000..ffa75c1
--- /dev/null
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include <stdbool.h>
+
+int _nss_systemd_block(bool b);
+bool _nss_systemd_is_blocked(void);
+
+/* For use with the _cleanup_() macro */
+static inline void _nss_systemd_unblockp(bool *b) {
+        if (*b)
+                assert_se(_nss_systemd_block(false) >= 0);
+}
index 77e1fbe93f227e6e878c7847bb9f773b9dfa7769..f86d7643d1a412dc316c1f5633187c4b8c55a760 100644 (file)
@@ -20,5 +20,9 @@ global:
         _nss_systemd_setgrent;
         _nss_systemd_getgrent_r;
         _nss_systemd_initgroups_dyn;
+
+        /* These two are not used by glibc, but can be used by apps to explicitly disable nss-systemd for the calling thread. */
+        _nss_systemd_block;
+        _nss_systemd_is_blocked;
 local: *;
 };
index da1248a132a8c19ce4a89c218276513dd94e7bd4..8e5b3eba6c08076eb533aff60bb25fdb9efad976 100644 (file)
@@ -3,6 +3,7 @@
 #include "env-util.h"
 #include "fd-util.h"
 #include "group-record-nss.h"
+#include "nss-systemd.h"
 #include "strv.h"
 #include "user-record.h"
 #include "userdb-glue.h"
@@ -74,12 +75,7 @@ enum nss_status userdb_getpwnam(
         assert(pwd);
         assert(errnop);
 
-        r = userdb_nss_compat_is_enabled();
-        if (r < 0) {
-                *errnop = -r;
-                return NSS_STATUS_UNAVAIL;
-        }
-        if (!r)
+        if (_nss_systemd_is_blocked())
                 return NSS_STATUS_NOTFOUND;
 
         r = userdb_by_name(name, nss_glue_userdb_flags(), &hr);
@@ -112,12 +108,7 @@ enum nss_status userdb_getpwuid(
         assert(pwd);
         assert(errnop);
 
-        r = userdb_nss_compat_is_enabled();
-        if (r < 0) {
-                *errnop = -r;
-                return NSS_STATUS_UNAVAIL;
-        }
-        if (!r)
+        if (_nss_systemd_is_blocked())
                 return NSS_STATUS_NOTFOUND;
 
         r = userdb_by_uid(uid, nss_glue_userdb_flags(), &hr);
@@ -214,12 +205,7 @@ enum nss_status userdb_getgrnam(
         assert(gr);
         assert(errnop);
 
-        r = userdb_nss_compat_is_enabled();
-        if (r < 0) {
-                *errnop = -r;
-                return NSS_STATUS_UNAVAIL;
-        }
-        if (!r)
+        if (_nss_systemd_is_blocked())
                 return NSS_STATUS_NOTFOUND;
 
         r = groupdb_by_name(name, nss_glue_userdb_flags(), &g);
@@ -235,7 +221,7 @@ enum nss_status userdb_getgrnam(
         }
 
         if (!g) {
-                _cleanup_close_ int lock_fd = -1;
+                _cleanup_(_nss_systemd_unblockp) bool blocked = false;
 
                 if (strv_isempty(members))
                         return NSS_STATUS_NOTFOUND;
@@ -245,11 +231,13 @@ enum nss_status userdb_getgrnam(
                  * acquire it, so that we can extend it (that's because glibc's group merging feature will
                  * merge groups only if both GID and name match and thus we need to have both first). It
                  * sucks behaving recursively likely this, but it's apparently what everybody does. We break
-                 * the recursion for ourselves via the userdb_nss_compat_disable() lock. */
+                 * the recursion for ourselves via the _nss_systemd_block_nss() lock. */
+
+                r = _nss_systemd_block(true);
+                if (r < 0)
+                        return r;
 
-                lock_fd = userdb_nss_compat_disable();
-                if (lock_fd < 0 && lock_fd != -EBUSY)
-                        return lock_fd;
+                blocked = true;
 
                 r = nss_group_record_by_name(name, false, &g);
                 if (r == -ESRCH)
@@ -285,12 +273,7 @@ enum nss_status userdb_getgrgid(
         assert(gr);
         assert(errnop);
 
-        r = userdb_nss_compat_is_enabled();
-        if (r < 0) {
-                *errnop = -r;
-                return NSS_STATUS_UNAVAIL;
-        }
-        if (!r)
+        if (_nss_systemd_is_blocked())
                 return NSS_STATUS_NOTFOUND;
 
         r = groupdb_by_gid(gid, nss_glue_userdb_flags(), &g);
@@ -300,20 +283,21 @@ enum nss_status userdb_getgrgid(
         }
 
         if (!g) {
-                _cleanup_close_ int lock_fd = -1;
+                _cleanup_(_nss_systemd_unblockp) bool blocked = false;
 
                 /* So, quite possibly we have to extend an existing group record with additional members. But
                  * to do this we need to know the group name first. The group didn't exist via non-NSS
                  * queries though, hence let's try to acquire it here recursively via NSS. */
 
-                lock_fd = userdb_nss_compat_disable();
-                if (lock_fd < 0 && lock_fd != -EBUSY)
-                        return lock_fd;
+                r = _nss_systemd_block(true);
+                if (r < 0)
+                        return r;
+
+                blocked = true;
 
                 r = nss_group_record_by_gid(gid, false, &g);
                 if (r == -ESRCH)
                         return NSS_STATUS_NOTFOUND;
-
                 if (r < 0) {
                         *errnop = -r;
                         return NSS_STATUS_UNAVAIL;
index c3a6e02e5a1e1b5e31d24b7edda95cdf0e5cf7fa..6c955ed523a818128c59094e5054d3dcbd3cf8f9 100644 (file)
@@ -3,6 +3,7 @@
 #include <sys/auxv.h>
 
 #include "dirent-util.h"
+#include "dlfcn-util.h"
 #include "errno-util.h"
 #include "fd-util.h"
 #include "group-record-nss.h"
@@ -32,8 +33,8 @@ struct UserDBIterator {
         bool nss_iterating:1;
         bool synthesize_root:1;
         bool synthesize_nobody:1;
+        bool nss_systemd_blocked:1;
         int error;
-        int nss_lock;
         unsigned n_found;
         sd_event *event;
         UserRecord *found_user;                   /* when .what == LOOKUP_USER */
@@ -85,7 +86,9 @@ UserDBIterator* userdb_iterator_free(UserDBIterator *iterator) {
         }
 
         sd_event_unref(iterator->event);
-        safe_close(iterator->nss_lock);
+
+        if (iterator->nss_systemd_blocked)
+                assert_se(userdb_block_nss_systemd(false) >= 0);
 
         return mfree(iterator);
 }
@@ -102,12 +105,27 @@ static UserDBIterator* userdb_iterator_new(LookupWhat what) {
 
         *i = (UserDBIterator) {
                 .what = what,
-                .nss_lock = -1,
         };
 
         return i;
 }
 
+static int userdb_iterator_block_nss_systemd(UserDBIterator *iterator) {
+        int r;
+
+        assert(iterator);
+
+        if (iterator->nss_systemd_blocked)
+                return 0;
+
+        r = userdb_block_nss_systemd(true);
+        if (r < 0)
+                return r;
+
+        iterator->nss_systemd_blocked = true;
+        return 1;
+}
+
 struct user_group_data {
         JsonVariant *record;
         bool incomplete;
@@ -606,13 +624,11 @@ int userdb_by_name(const char *name, UserDBFlags flags, UserRecord **ret) {
                         return r;
         }
 
-        if (!FLAGS_SET(flags, USERDB_AVOID_NSS) && !(iterator && iterator->nss_covered)) {
-                /* Make sure the NSS lookup doesn't recurse back to us. (EBUSY is fine here, it just means we
-                 * already took the lock from our thread, which is totally OK.) */
-                r = userdb_nss_compat_disable();
-                if (r >= 0 || r == -EBUSY) {
-                        iterator->nss_lock = r;
+        if (!FLAGS_SET(flags, USERDB_AVOID_NSS) && !iterator->nss_covered) {
+                /* Make sure the NSS lookup doesn't recurse back to us. */
 
+                r = userdb_iterator_block_nss_systemd(iterator);
+                if (r >= 0) {
                         /* Client-side NSS fallback */
                         r = nss_user_record_by_name(name, !FLAGS_SET(flags, USERDB_AVOID_SHADOW), ret);
                         if (r >= 0)
@@ -655,11 +671,9 @@ int userdb_by_uid(uid_t uid, UserDBFlags flags, UserRecord **ret) {
                         return r;
         }
 
-        if (!FLAGS_SET(flags, USERDB_AVOID_NSS) && !(iterator && iterator->nss_covered)) {
-                r = userdb_nss_compat_disable();
-                if (r >= 0 || r == -EBUSY) {
-                        iterator->nss_lock = r;
-
+        if (!FLAGS_SET(flags, USERDB_AVOID_NSS) && !iterator->nss_covered) {
+                r = userdb_iterator_block_nss_systemd(iterator);
+                if (r >= 0) {
                         /* Client-side NSS fallback */
                         r = nss_user_record_by_uid(uid, !FLAGS_SET(flags, USERDB_AVOID_SHADOW), ret);
                         if (r >= 0)
@@ -693,9 +707,9 @@ int userdb_all(UserDBFlags flags, UserDBIterator **ret) {
         r = userdb_start_query(iterator, "io.systemd.UserDatabase.GetUserRecord", true, NULL, flags);
 
         if (!FLAGS_SET(flags, USERDB_AVOID_NSS) && (r < 0 || !iterator->nss_covered)) {
-                iterator->nss_lock = userdb_nss_compat_disable();
-                if (iterator->nss_lock < 0 && iterator->nss_lock != -EBUSY)
-                        return iterator->nss_lock;
+                r = userdb_iterator_block_nss_systemd(iterator);
+                if (r < 0)
+                        return r;
 
                 setpwent();
                 iterator->nss_iterating = true;
@@ -815,10 +829,8 @@ int groupdb_by_name(const char *name, UserDBFlags flags, GroupRecord **ret) {
         }
 
         if (!FLAGS_SET(flags, USERDB_AVOID_NSS) && !(iterator && iterator->nss_covered)) {
-                r = userdb_nss_compat_disable();
-                if (r >= 0 || r == -EBUSY) {
-                        iterator->nss_lock = r;
-
+                r = userdb_iterator_block_nss_systemd(iterator);
+                if (r >= 0) {
                         r = nss_group_record_by_name(name, !FLAGS_SET(flags, USERDB_AVOID_SHADOW), ret);
                         if (r >= 0)
                                 return r;
@@ -861,10 +873,8 @@ int groupdb_by_gid(gid_t gid, UserDBFlags flags, GroupRecord **ret) {
         }
 
         if (!FLAGS_SET(flags, USERDB_AVOID_NSS) && !(iterator && iterator->nss_covered)) {
-                r = userdb_nss_compat_disable();
-                if (r >= 0 || r == -EBUSY) {
-                        iterator->nss_lock = r;
-
+                r = userdb_iterator_block_nss_systemd(iterator);
+                if (r >= 0) {
                         r = nss_group_record_by_gid(gid, !FLAGS_SET(flags, USERDB_AVOID_SHADOW), ret);
                         if (r >= 0)
                                 return r;
@@ -897,9 +907,9 @@ int groupdb_all(UserDBFlags flags, UserDBIterator **ret) {
         r = userdb_start_query(iterator, "io.systemd.UserDatabase.GetGroupRecord", true, NULL, flags);
 
         if (!FLAGS_SET(flags, USERDB_AVOID_NSS) && (r < 0 || !iterator->nss_covered)) {
-                iterator->nss_lock = userdb_nss_compat_disable();
-                if (iterator->nss_lock < 0 && iterator->nss_lock != -EBUSY)
-                        return iterator->nss_lock;
+                r = userdb_iterator_block_nss_systemd(iterator);
+                if (r < 0)
+                        return r;
 
                 setgrent();
                 iterator->nss_iterating = true;
@@ -998,9 +1008,9 @@ int membershipdb_by_user(const char *name, UserDBFlags flags, UserDBIterator **r
         if ((r >= 0 && iterator->nss_covered) || FLAGS_SET(flags, USERDB_AVOID_NSS))
                 goto finish;
 
-        iterator->nss_lock = userdb_nss_compat_disable();
-        if (iterator->nss_lock < 0 && iterator->nss_lock != -EBUSY)
-                return iterator->nss_lock;
+        r = userdb_iterator_block_nss_systemd(iterator);
+        if (r < 0)
+                return r;
 
         iterator->filter_user_name = strdup(name);
         if (!iterator->filter_user_name)
@@ -1041,9 +1051,9 @@ int membershipdb_by_group(const char *name, UserDBFlags flags, UserDBIterator **
         if ((r >= 0 && iterator->nss_covered) || FLAGS_SET(flags, USERDB_AVOID_NSS))
                 goto finish;
 
-        iterator->nss_lock = userdb_nss_compat_disable();
-        if (iterator->nss_lock < 0 && iterator->nss_lock != -EBUSY)
-                return iterator->nss_lock;
+        r = userdb_iterator_block_nss_systemd(iterator);
+        if (r < 0)
+                return r;
 
         /* We ignore all errors here, since the group might be defined by a userdb native service, and we queried them already above. */
         (void) nss_group_record_by_name(name, false, &gr);
@@ -1082,9 +1092,9 @@ int membershipdb_all(UserDBFlags flags, UserDBIterator **ret) {
         if ((r >= 0 && iterator->nss_covered) || FLAGS_SET(flags, USERDB_AVOID_NSS))
                 goto finish;
 
-        iterator->nss_lock = userdb_nss_compat_disable();
-        if (iterator->nss_lock < 0 && iterator->nss_lock != -EBUSY)
-                return iterator->nss_lock;
+        r = userdb_iterator_block_nss_systemd(iterator);
+        if (r < 0)
+                return r;
 
         setgrent();
         iterator->nss_iterating = true;
@@ -1221,115 +1231,24 @@ int membershipdb_by_group_strv(const char *name, UserDBFlags flags, char ***ret)
         return 0;
 }
 
-static int userdb_thread_sockaddr(struct sockaddr_un *ret_sa, socklen_t *ret_salen) {
-        static const uint8_t
-                k1[16] = { 0x35, 0xc1, 0x1f, 0x41, 0x59, 0xc6, 0xa0, 0xf9, 0x33, 0x4b, 0x17, 0x3d, 0xb9, 0xf6, 0x14, 0xd9 },
-                k2[16] = { 0x6a, 0x11, 0x4c, 0x37, 0xe5, 0xa3, 0x8c, 0xa6, 0x93, 0x55, 0x64, 0x8c, 0x93, 0xee, 0xa1, 0x7b };
-
-        struct siphash sh;
-        uint64_t x, y;
-        pid_t tid;
-        void *p;
-
-        assert(ret_sa);
-        assert(ret_salen);
-
-        /* This calculates an AF_UNIX socket address in the abstract namespace whose existence works as an
-         * indicator whether to emulate NSS records for complex user records that are also available via the
-         * varlink protocol. The name of the socket is picked in a way so that:
-         *
-         *     → it is per-thread (by hashing from the TID)
-         *
-         *     → is not guessable for foreign processes (by hashing from the — hopefully secret — AT_RANDOM
-         *       value every process gets passed from the kernel
-         *
-         * By using a socket the NSS emulation can be nicely turned off for limited amounts of time only,
-         * simply controlled by the lifetime of the fd itself. By using an AF_UNIX socket in the abstract
-         * namespace the lock is automatically cleaned up when the process dies abnormally.
-         *
-         */
-
-        p = ULONG_TO_PTR(getauxval(AT_RANDOM));
-        if (!p)
-                return -EIO;
-
-        tid = gettid();
-
-        siphash24_init(&sh, k1);
-        siphash24_compress(p, 16, &sh);
-        siphash24_compress(&tid, sizeof(tid), &sh);
-        x = siphash24_finalize(&sh);
-
-        siphash24_init(&sh, k2);
-        siphash24_compress(p, 16, &sh);
-        siphash24_compress(&tid, sizeof(tid), &sh);
-        y = siphash24_finalize(&sh);
-
-        *ret_sa = (struct sockaddr_un) {
-                .sun_family = AF_UNIX,
-        };
-
-        sprintf(ret_sa->sun_path + 1, "userdb-%016" PRIx64 "%016" PRIx64, x, y);
-        *ret_salen = offsetof(struct sockaddr_un, sun_path) + 1 + 7 + 32;
-
-        return 0;
-}
-
-int userdb_nss_compat_is_enabled(void) {
-        _cleanup_close_ int fd = -1;
-        union sockaddr_union sa;
-        socklen_t salen;
-        int r;
-
-        /* Tests whether the NSS compatibility logic is currently turned on for the invoking thread. Returns
-         * true if NSS compatibility is turned on, i.e. whether NSS records shall be synthesized from complex
-         * user records. */
-
-        r = userdb_thread_sockaddr(&sa.un, &salen);
-        if (r < 0)
-                return r;
-
-        fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0);
-        if (fd < 0)
-                return -errno;
+int userdb_block_nss_systemd(int b) {
+        _cleanup_(dlclosep) void *dl = NULL;
+        int (*call)(bool b);
 
-        /* Try to connect(). This doesn't do anything really, except that it checks whether the socket
-         * address is bound at all. */
-        if (connect(fd, &sa.sa, salen) < 0) {
-                if (errno == ECONNREFUSED) /* the socket is not bound, hence NSS emulation shall be done */
-                        return true;
+        /* Note that we might be called from libnss_systemd.so.2 itself, but that should be fine, really. */
 
-                return -errno;
+        dl = dlopen(ROOTLIBDIR "libnss_systemd.so.2", RTLD_LAZY|RTLD_NODELETE);
+        if (!dl) {
+                /* If the file isn't installed, don't complain loudly */
+                log_debug("Failed to dlopen(libnss_systemd.so.2), ignoring: %s", dlerror());
+                return 0;
         }
 
-        return false;
-}
-
-int userdb_nss_compat_disable(void) {
-        _cleanup_close_ int fd = -1;
-        union sockaddr_union sa;
-        socklen_t salen;
-        int r;
-
-        /* Turn off the NSS compatibility logic for the invoking thread. By default NSS records are
-         * synthesized for all complex user records looked up via NSS. If this call is invoked this is
-         * disabled for the invoking thread, but only for it. A caller that natively supports the varlink
-         * user record protocol may use that to turn off the compatibility for NSS lookups. */
-
-        r = userdb_thread_sockaddr(&sa.un, &salen);
-        if (r < 0)
-                return r;
-
-        fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
-        if (fd < 0)
-                return -errno;
-
-        if (bind(fd, &sa.sa, salen) < 0) {
-                if (errno == EADDRINUSE) /* lock already taken, convert this into a recognizable error */
-                        return -EBUSY;
-
-                return -errno;
-        }
+        call = (int (*)(bool b)) dlsym(dl, "_nss_systemd_block");
+        if (!call)
+                /* If the file is is installed but lacks the symbol we expect, things are weird, let's complain */
+                return log_debug_errno(SYNTHETIC_ERRNO(ELIBBAD),
+                                       "Unable to find symbol _nss_systemd_block in libnss_systemd.so.2: %s", dlerror());
 
-        return TAKE_FD(fd);
+        return call(b);
 }
index 8af31aa86c62a810b05ee7ee772baa2690de6ec9..2464f54c3e22e1dd2d79639d9d28c58e35d89293 100644 (file)
@@ -38,5 +38,4 @@ int membershipdb_all(UserDBFlags flags, UserDBIterator **ret);
 int membershipdb_iterator_get(UserDBIterator *iterator, char **user, char **group);
 int membershipdb_by_group_strv(const char *name, UserDBFlags flags, char ***ret);
 
-int userdb_nss_compat_is_enabled(void);
-int userdb_nss_compat_disable(void);
+int userdb_block_nss_systemd(int b);
index ac04af0ca8d216e674cfaf437ccc8bed81cd42ff..357b5407e7e0b103e34bae2e78a08e1d34029147 100644 (file)
@@ -659,7 +659,6 @@ static int process_connection(VarlinkServer *server, int fd) {
 static int run(int argc, char *argv[]) {
         usec_t start_time, listen_idle_usec, last_busy_usec = USEC_INFINITY;
         _cleanup_(varlink_server_unrefp) VarlinkServer *server = NULL;
-        _cleanup_close_ int lock = -1;
         unsigned n_iterations = 0;
         int m, listen_fd, r;
 
@@ -696,8 +695,8 @@ static int run(int argc, char *argv[]) {
                 return log_error_errno(r, "Failed to parse USERDB_FIXED_WORKER: %m");
         listen_idle_usec = r ? USEC_INFINITY : LISTEN_IDLE_USEC;
 
-        lock = userdb_nss_compat_disable();
-        if (lock < 0)
+        r = userdb_block_nss_systemd(true);
+        if (r < 0)
                 return log_error_errno(r, "Failed to disable userdb NSS compatibility: %m");
 
         start_time = now(CLOCK_MONOTONIC);