#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"
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 */
}
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);
}
*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;
r = -ESRCH;
else if (streq(error_id, "io.systemd.UserDatabase.ServiceNotAvailable"))
r = -EHOSTDOWN;
+ else if (streq(error_id, "io.systemd.UserDatabase.EnumerationNotSupported"))
+ r = -EOPNOTSUPP;
else if (streq(error_id, VARLINK_ERROR_TIMEOUT))
r = -ETIMEDOUT;
else
if (r < 0)
return log_debug_errno(r, "Failed to invoke varlink method: %m");
- r = set_ensure_allocated(&iterator->links, &link_hash_ops);
- if (r < 0)
- return log_debug_errno(r, "Failed to allocate set: %m");
-
- r = set_put(iterator->links, vl);
+ r = set_ensure_consume(&iterator->links, &link_hash_ops, TAKE_PTR(vl));
if (r < 0)
return log_debug_errno(r, "Failed to add varlink connection to set: %m");
-
- TAKE_PTR(vl);
return r;
}
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, ret);
+ r = nss_user_record_by_name(name, !FLAGS_SET(flags, USERDB_AVOID_SHADOW), ret);
if (r >= 0)
return r;
}
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, ret);
+ r = nss_user_record_by_uid(uid, !FLAGS_SET(flags, USERDB_AVOID_SHADOW), ret);
if (r >= 0)
return r;
}
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;
}
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 = nss_group_record_by_name(name, ret);
+ 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;
}
}
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 = nss_group_record_by_gid(gid, ret);
+ 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;
}
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;
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)
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, &gr);
+ (void) nss_group_record_by_name(name, false, &gr);
if (gr) {
iterator->members_of_group = strv_copy(gr->members);
if (!iterator->members_of_group)
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;
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);
}