From 037b0a47b0d7df09d720dda6703135117e7e0472 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 4 Jun 2020 11:46:36 +0200 Subject: [PATCH] userdb: replace recursion lock MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit 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 | 2 +- src/nss-systemd/nss-systemd.c | 69 ++++++----- src/nss-systemd/nss-systemd.h | 13 ++ src/nss-systemd/nss-systemd.sym | 4 + src/nss-systemd/userdb-glue.c | 52 +++----- src/shared/userdb.c | 205 ++++++++++---------------------- src/shared/userdb.h | 3 +- src/userdb/userwork.c | 5 +- 8 files changed, 141 insertions(+), 212 deletions(-) create mode 100644 src/nss-systemd/nss-systemd.h diff --git a/meson.build b/meson.build index 1df2ee86481..55cb0e60542 100644 --- a/meson.build +++ b/meson.build @@ -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']] diff --git a/src/nss-systemd/nss-systemd.c b/src/nss-systemd/nss-systemd.c index e11f917c198..5dc5aacdff2 100644 --- a/src/nss-systemd/nss-systemd.c +++ b/src/nss-systemd/nss-systemd.c @@ -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 index 00000000000..ffa75c12c4b --- /dev/null +++ b/src/nss-systemd/nss-systemd.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +#pragma once + +#include + +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); +} diff --git a/src/nss-systemd/nss-systemd.sym b/src/nss-systemd/nss-systemd.sym index 77e1fbe93f2..f86d7643d1a 100644 --- a/src/nss-systemd/nss-systemd.sym +++ b/src/nss-systemd/nss-systemd.sym @@ -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: *; }; diff --git a/src/nss-systemd/userdb-glue.c b/src/nss-systemd/userdb-glue.c index da1248a132a..8e5b3eba6c0 100644 --- a/src/nss-systemd/userdb-glue.c +++ b/src/nss-systemd/userdb-glue.c @@ -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; diff --git a/src/shared/userdb.c b/src/shared/userdb.c index c3a6e02e5a1..6c955ed523a 100644 --- a/src/shared/userdb.c +++ b/src/shared/userdb.c @@ -3,6 +3,7 @@ #include #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); } diff --git a/src/shared/userdb.h b/src/shared/userdb.h index 8af31aa86c6..2464f54c3e2 100644 --- a/src/shared/userdb.h +++ b/src/shared/userdb.h @@ -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); diff --git a/src/userdb/userwork.c b/src/userdb/userwork.c index ac04af0ca8d..357b5407e7e 100644 --- a/src/userdb/userwork.c +++ b/src/userdb/userwork.c @@ -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); -- 2.39.2