From 3d75d87bd3affc59e5d725f218b7cd4b3b38180b Mon Sep 17 00:00:00 2001 From: Arthur Gautier Date: Tue, 7 Sep 2021 01:49:37 +0000 Subject: [PATCH 1/2] preloadNSS: fixup nss_dns load Before this commit, the dns lookup in preloadNSS would still go through nscd. This did not have the effect of loading the nss_dns.so as expected (nss_dns.so being out of reach from within the sandbox). Should LOCALDOMAIN environment variable be defined, nss will completely avoid nscd and will do its dns resolution on its own. By temporarly setting LOCALDOMAIN variable before calling in NSS, we can force NSS to load the shared libraries as expected. Signed-off-by: Arthur Gautier --- src/libstore/build.cc | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/libstore/build.cc b/src/libstore/build.cc index bf8914cc0..8e40d51a3 100644 --- a/src/libstore/build.cc +++ b/src/libstore/build.cc @@ -1896,9 +1896,19 @@ static void preloadNSS() { std::call_once(dns_resolve_flag, []() { struct addrinfo *res = NULL; - if (getaddrinfo("this.pre-initializes.the.dns.resolvers.invalid.", "http", NULL, &res) != 0) { + /* 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"); + } }); } From a67dcdd991e804d3555ef42e960db2b4c490dd4b Mon Sep 17 00:00:00 2001 From: Arthur Gautier Date: Wed, 8 Sep 2021 18:15:36 +0000 Subject: [PATCH 2/2] 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 --- src/libmain/shared.cc | 30 ++++++++++++++++++++++++++++++ src/libstore/build.cc | 31 ------------------------------- 2 files changed, 30 insertions(+), 31 deletions(-) diff --git a/src/libmain/shared.cc b/src/libmain/shared.cc index a14f9006a..055d8fb5b 100644 --- a/src/libmain/shared.cc +++ b/src/libmain/shared.cc @@ -14,6 +14,9 @@ #include #include #include +#include +#include +#include #include @@ -95,6 +98,31 @@ static void opensslLockCallback(int mode, int type, const char * file, int line) } #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) { } @@ -158,6 +186,8 @@ void initNix() if (hasPrefix(getEnv("TMPDIR"), "/var/folders/")) unsetenv("TMPDIR"); #endif + + preloadNSS(); } diff --git a/src/libstore/build.cc b/src/libstore/build.cc index 8e40d51a3..69e349225 100644 --- a/src/libstore/build.cc +++ b/src/libstore/build.cc @@ -34,7 +34,6 @@ #include #include #include -#include #include #include #include @@ -45,7 +44,6 @@ /* Includes required for chroot support. */ #if __linux__ -#include #include #include #include @@ -1886,32 +1884,6 @@ PathSet DerivationGoal::exportReferences(PathSet storePaths) return paths; } -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"); - } - }); -} - void DerivationGoal::startBuilder() { /* Right platform? */ @@ -1923,9 +1895,6 @@ void DerivationGoal::startBuilder() settings.thisSystem, concatStringsSep(", ", settings.systemFeatures)); - if (drv->isBuiltin()) - preloadNSS(); - #if __APPLE__ additionalSandboxProfile = parsedDrv->getStringAttr("__sandboxProfile").value_or(""); #endif