preloadNSS: load NSS before threads are started

preloadNSS is not thread-safe, this commit moves it before we start the
first thread.

Signed-off-by: Arthur Gautier <baloo@superbaloo.net>
This commit is contained in:
Arthur Gautier 2021-09-08 18:11:43 +00:00
parent 0b42a0f781
commit 3b72741f23
2 changed files with 30 additions and 34 deletions

View file

@ -15,6 +15,9 @@
#include <sys/stat.h> #include <sys/stat.h>
#include <unistd.h> #include <unistd.h>
#include <signal.h> #include <signal.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <openssl/crypto.h> #include <openssl/crypto.h>
@ -110,6 +113,31 @@ static void opensslLockCallback(int mode, int type, const char * file, int line)
} }
#endif #endif
static std::once_flag dns_resolve_flag;
static void preloadNSS() {
/* builtin:fetchurl can trigger a DNS lookup, which with glibc can trigger a dynamic library load of
one of the glibc NSS libraries in a sandboxed child, which will fail unless the library's already
been loaded in the parent. So we force a lookup of an invalid domain to force the NSS machinery to
load its lookup libraries in the parent before any child gets a chance to. */
std::call_once(dns_resolve_flag, []() {
struct addrinfo *res = NULL;
/* nss will only force the "local" (not through nscd) dns resolution if its on the LOCALDOMAIN.
We need the resolution to be done locally, as nscd socket will not be accessible in the
sandbox. */
char * previous_env = getenv("LOCALDOMAIN");
setenv("LOCALDOMAIN", "invalid", 1);
if (getaddrinfo("this.pre-initializes.the.dns.resolvers.invalid.", "http", NULL, &res) == 0) {
if (res) freeaddrinfo(res);
}
if (previous_env) {
setenv("LOCALDOMAIN", previous_env, 1);
} else {
unsetenv("LOCALDOMAIN");
}
});
}
static void sigHandler(int signo) { } static void sigHandler(int signo) { }
@ -176,6 +204,8 @@ void initNix()
if (hasPrefix(getEnv("TMPDIR").value_or("/tmp"), "/var/folders/")) if (hasPrefix(getEnv("TMPDIR").value_or("/tmp"), "/var/folders/"))
unsetenv("TMPDIR"); unsetenv("TMPDIR");
#endif #endif
preloadNSS();
} }

View file

@ -17,10 +17,7 @@
#include <regex> #include <regex>
#include <queue> #include <queue>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h> #include <sys/un.h>
#include <netdb.h>
#include <fcntl.h> #include <fcntl.h>
#include <termios.h> #include <termios.h>
#include <unistd.h> #include <unistd.h>
@ -34,7 +31,6 @@
/* Includes required for chroot support. */ /* Includes required for chroot support. */
#if __linux__ #if __linux__
#include <sys/socket.h>
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include <net/if.h> #include <net/if.h>
#include <netinet/ip.h> #include <netinet/ip.h>
@ -344,33 +340,6 @@ int childEntry(void * arg)
} }
static std::once_flag dns_resolve_flag;
static void preloadNSS() {
/* builtin:fetchurl can trigger a DNS lookup, which with glibc can trigger a dynamic library load of
one of the glibc NSS libraries in a sandboxed child, which will fail unless the library's already
been loaded in the parent. So we force a lookup of an invalid domain to force the NSS machinery to
load its lookup libraries in the parent before any child gets a chance to. */
std::call_once(dns_resolve_flag, []() {
struct addrinfo *res = NULL;
/* nss will only force the "local" (not through nscd) dns resolution if its on the LOCALDOMAIN.
We need the resolution to be done locally, as nscd socket will not be accessible in the
sandbox. */
char * previous_env = getenv("LOCALDOMAIN");
setenv("LOCALDOMAIN", "invalid", 1);
if (getaddrinfo("this.pre-initializes.the.dns.resolvers.invalid.", "http", NULL, &res) == 0) {
if (res) freeaddrinfo(res);
}
if (previous_env) {
setenv("LOCALDOMAIN", previous_env, 1);
} else {
unsetenv("LOCALDOMAIN");
}
});
}
static void linkOrCopy(const Path & from, const Path & to) static void linkOrCopy(const Path & from, const Path & to)
{ {
if (link(from.c_str(), to.c_str()) == -1) { if (link(from.c_str(), to.c_str()) == -1) {
@ -399,9 +368,6 @@ void LocalDerivationGoal::startBuilder()
settings.thisSystem, settings.thisSystem,
concatStringsSep<StringSet>(", ", worker.store.systemFeatures)); concatStringsSep<StringSet>(", ", worker.store.systemFeatures));
if (drv->isBuiltin())
preloadNSS();
#if __APPLE__ #if __APPLE__
additionalSandboxProfile = parsedDrv->getStringAttr("__sandboxProfile").value_or(""); additionalSandboxProfile = parsedDrv->getStringAttr("__sandboxProfile").value_or("");
#endif #endif