aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/libexpr/eval.cc65
-rw-r--r--src/libexpr/eval.hh8
-rw-r--r--src/libexpr/primops.cc44
-rw-r--r--src/libexpr/primops/fetchGit.cc15
-rw-r--r--src/libexpr/primops/fetchMercurial.cc6
-rw-r--r--src/libstore/globals.hh3
-rw-r--r--src/libutil/util.cc6
-rw-r--r--src/libutil/util.hh6
-rwxr-xr-xsrc/nix-build/nix-build.cc4
-rw-r--r--src/nix-instantiate/nix-instantiate.cc2
10 files changed, 110 insertions, 49 deletions
diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc
index 087a95dde..f8685e010 100644
--- a/src/libexpr/eval.cc
+++ b/src/libexpr/eval.cc
@@ -300,16 +300,25 @@ EvalState::EvalState(const Strings & _searchPath, ref<Store> store)
{
countCalls = getEnv("NIX_COUNT_CALLS", "0") != "0";
- restricted = settings.restrictEval;
-
assert(gcInitialised);
/* Initialise the Nix expression search path. */
- Strings paths = parseNixPath(getEnv("NIX_PATH", ""));
- for (auto & i : _searchPath) addToSearchPath(i);
- for (auto & i : paths) addToSearchPath(i);
+ if (!settings.pureEval) {
+ Strings paths = parseNixPath(getEnv("NIX_PATH", ""));
+ for (auto & i : _searchPath) addToSearchPath(i);
+ for (auto & i : paths) addToSearchPath(i);
+ }
addToSearchPath("nix=" + settings.nixDataDir + "/nix/corepkgs");
+ if (settings.restrictEval || settings.pureEval) {
+ allowedPaths = PathSet();
+ for (auto & i : searchPath) {
+ auto r = resolveSearchPathElem(i);
+ if (!r.first) continue;
+ allowedPaths->insert(r.second);
+ }
+ }
+
clearValue(vEmptySet);
vEmptySet.type = tAttrs;
vEmptySet.attrs = allocBindings(0);
@@ -326,38 +335,39 @@ EvalState::~EvalState()
Path EvalState::checkSourcePath(const Path & path_)
{
- if (!restricted) return path_;
+ if (!allowedPaths) return path_;
+
+ auto doThrow = [&]() [[noreturn]] {
+ throw RestrictedPathError("access to path '%1%' is forbidden in restricted mode", path_);
+ };
+
+ bool found = false;
+
+ for (auto & i : *allowedPaths) {
+ if (isDirOrInDir(path_, i)) {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) doThrow();
/* Resolve symlinks. */
debug(format("checking access to '%s'") % path_);
Path path = canonPath(path_, true);
- for (auto & i : searchPath) {
- auto r = resolveSearchPathElem(i);
- if (!r.first) continue;
- if (path == r.second || isInDir(path, r.second))
+ for (auto & i : *allowedPaths) {
+ if (isDirOrInDir(path, i))
return path;
}
- /* To support import-from-derivation, allow access to anything in
- the store. FIXME: only allow access to paths that have been
- constructed by this evaluation. */
- if (store->isInStore(path)) return path;
-
-#if 0
- /* Hack to support the chroot dependencies of corepkgs (see
- corepkgs/config.nix.in). */
- if (path == settings.nixPrefix && isStorePath(settings.nixPrefix))
- return path;
-#endif
-
- throw RestrictedPathError(format("access to path '%1%' is forbidden in restricted mode") % path_);
+ doThrow();
}
void EvalState::checkURI(const std::string & uri)
{
- if (!restricted) return;
+ if (!settings.restrictEval) return;
/* 'uri' should be equal to a prefix, or in a subdirectory of a
prefix. Thus, the prefix https://github.co does not permit
@@ -396,7 +406,7 @@ void EvalState::addConstant(const string & name, Value & v)
}
-void EvalState::addPrimOp(const string & name,
+Value * EvalState::addPrimOp(const string & name,
unsigned int arity, PrimOpFun primOp)
{
Value * v = allocValue();
@@ -407,6 +417,7 @@ void EvalState::addPrimOp(const string & name,
staticBaseEnv.vars[symbols.create(name)] = baseEnvDispl;
baseEnv.values[baseEnvDispl++] = v;
baseEnv.values[0]->attrs->push_back(Attr(sym, v));
+ return v;
}
@@ -659,8 +670,10 @@ Value * ExprPath::maybeThunk(EvalState & state, Env & env)
}
-void EvalState::evalFile(const Path & path, Value & v)
+void EvalState::evalFile(const Path & path_, Value & v)
{
+ auto path = checkSourcePath(path_);
+
FileEvalCache::iterator i;
if ((i = fileEvalCache.find(path)) != fileEvalCache.end()) {
v = i->second;
diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh
index cc971ae80..9e3d30d95 100644
--- a/src/libexpr/eval.hh
+++ b/src/libexpr/eval.hh
@@ -76,9 +76,9 @@ public:
already exist there. */
RepairFlag repair;
- /* If set, don't allow access to files outside of the Nix search
- path or to environment variables. */
- bool restricted;
+ /* The allowed filesystem paths in restricted or pure evaluation
+ mode. */
+ std::experimental::optional<PathSet> allowedPaths;
Value vEmptySet;
@@ -212,7 +212,7 @@ private:
void addConstant(const string & name, Value & v);
- void addPrimOp(const string & name,
+ Value * addPrimOp(const string & name,
unsigned int arity, PrimOpFun primOp);
public:
diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc
index 98fe2199e..0ec035b86 100644
--- a/src/libexpr/primops.cc
+++ b/src/libexpr/primops.cc
@@ -439,7 +439,7 @@ static void prim_tryEval(EvalState & state, const Pos & pos, Value * * args, Val
static void prim_getEnv(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
string name = state.forceStringNoCtx(*args[0], pos);
- mkString(v, state.restricted ? "" : getEnv(name));
+ mkString(v, settings.restrictEval || settings.pureEval ? "" : getEnv(name));
}
@@ -1929,7 +1929,14 @@ void fetch(EvalState & state, const Pos & pos, Value * * args, Value & v,
state.checkURI(url);
+ if (settings.pureEval && !expectedHash)
+ throw Error("in pure evaluation mode, '%s' requires a 'sha256' argument", who);
+
Path res = getDownloader()->downloadCached(state.store, url, unpack, name, expectedHash);
+
+ if (state.allowedPaths)
+ state.allowedPaths->insert(res);
+
mkString(v, res, PathSet({res}));
}
@@ -1981,11 +1988,28 @@ void EvalState::createBaseEnv()
mkNull(v);
addConstant("null", v);
- mkInt(v, time(0));
- addConstant("__currentTime", v);
+ auto vThrow = addPrimOp("throw", 1, prim_throw);
- mkString(v, settings.thisSystem);
- addConstant("__currentSystem", v);
+ auto addPurityError = [&](const std::string & name) {
+ Value * v2 = allocValue();
+ mkString(*v2, fmt("'%s' is not allowed in pure evaluation mode", name));
+ mkApp(v, *vThrow, *v2);
+ addConstant(name, v);
+ };
+
+ if (settings.pureEval)
+ addPurityError("__currentTime");
+ else {
+ mkInt(v, time(0));
+ addConstant("__currentTime", v);
+ }
+
+ if (settings.pureEval)
+ addPurityError("__currentSystem");
+ else {
+ mkString(v, settings.thisSystem);
+ addConstant("__currentSystem", v);
+ }
mkString(v, nixVersion);
addConstant("__nixVersion", v);
@@ -2001,10 +2025,10 @@ void EvalState::createBaseEnv()
addConstant("__langVersion", v);
// Miscellaneous
- addPrimOp("scopedImport", 2, prim_scopedImport);
+ auto vScopedImport = addPrimOp("scopedImport", 2, prim_scopedImport);
Value * v2 = allocValue();
mkAttrs(*v2, 0);
- mkApp(v, *baseEnv.values[baseEnvDispl - 1], *v2);
+ mkApp(v, *vScopedImport, *v2);
forceValue(v);
addConstant("import", v);
if (settings.enableNativeCode) {
@@ -2020,7 +2044,6 @@ void EvalState::createBaseEnv()
addPrimOp("__isBool", 1, prim_isBool);
addPrimOp("__genericClosure", 1, prim_genericClosure);
addPrimOp("abort", 1, prim_abort);
- addPrimOp("throw", 1, prim_throw);
addPrimOp("__addErrorContext", 2, prim_addErrorContext);
addPrimOp("__tryEval", 1, prim_tryEval);
addPrimOp("__getEnv", 1, prim_getEnv);
@@ -2035,7 +2058,10 @@ void EvalState::createBaseEnv()
// Paths
addPrimOp("__toPath", 1, prim_toPath);
- addPrimOp("__storePath", 1, prim_storePath);
+ if (settings.pureEval)
+ addPurityError("__storePath");
+ else
+ addPrimOp("__storePath", 1, prim_storePath);
addPrimOp("__pathExists", 1, prim_pathExists);
addPrimOp("baseNameOf", 1, prim_baseNameOf);
addPrimOp("dirOf", 1, prim_dirOf);
diff --git a/src/libexpr/primops/fetchGit.cc b/src/libexpr/primops/fetchGit.cc
index fb664cffb..2e3e2634d 100644
--- a/src/libexpr/primops/fetchGit.cc
+++ b/src/libexpr/primops/fetchGit.cc
@@ -22,10 +22,15 @@ struct GitInfo
uint64_t revCount = 0;
};
+std::regex revRegex("^[0-9a-fA-F]{40}$");
+
GitInfo exportGit(ref<Store> store, const std::string & uri,
std::experimental::optional<std::string> ref, std::string rev,
const std::string & name)
{
+ if (settings.pureEval && rev == "")
+ throw Error("in pure evaluation mode, 'fetchGit' requires a Git revision");
+
if (!ref && rev == "" && hasPrefix(uri, "/") && pathExists(uri + "/.git")) {
bool clean = true;
@@ -76,11 +81,8 @@ GitInfo exportGit(ref<Store> store, const std::string & uri,
if (!ref) ref = "master"s;
- if (rev != "") {
- std::regex revRegex("^[0-9a-fA-F]{40}$");
- if (!std::regex_match(rev, revRegex))
- throw Error("invalid Git revision '%s'", rev);
- }
+ if (rev != "" && !std::regex_match(rev, revRegex))
+ throw Error("invalid Git revision '%s'", rev);
Path cacheDir = getCacheDir() + "/nix/git";
@@ -231,6 +233,9 @@ static void prim_fetchGit(EvalState & state, const Pos & pos, Value * * args, Va
mkString(*state.allocAttr(v, state.symbols.create("shortRev")), gitInfo.shortRev);
mkInt(*state.allocAttr(v, state.symbols.create("revCount")), gitInfo.revCount);
v.attrs->sort();
+
+ if (state.allowedPaths)
+ state.allowedPaths->insert(gitInfo.storePath);
}
static RegisterPrimOp r("fetchGit", 1, prim_fetchGit);
diff --git a/src/libexpr/primops/fetchMercurial.cc b/src/libexpr/primops/fetchMercurial.cc
index a317476c5..5517d83df 100644
--- a/src/libexpr/primops/fetchMercurial.cc
+++ b/src/libexpr/primops/fetchMercurial.cc
@@ -27,6 +27,9 @@ std::regex commitHashRegex("^[0-9a-fA-F]{40}$");
HgInfo exportMercurial(ref<Store> store, const std::string & uri,
std::string rev, const std::string & name)
{
+ if (settings.pureEval && rev == "")
+ throw Error("in pure evaluation mode, 'fetchMercurial' requires a Mercurial revision");
+
if (rev == "" && hasPrefix(uri, "/") && pathExists(uri + "/.hg")) {
bool clean = runProgram("hg", true, { "status", "-R", uri, "--modified", "--added", "--removed" }) == "";
@@ -196,6 +199,9 @@ static void prim_fetchMercurial(EvalState & state, const Pos & pos, Value * * ar
mkString(*state.allocAttr(v, state.symbols.create("shortRev")), std::string(hgInfo.rev, 0, 12));
mkInt(*state.allocAttr(v, state.symbols.create("revCount")), hgInfo.revCount);
v.attrs->sort();
+
+ if (state.allowedPaths)
+ state.allowedPaths->insert(hgInfo.storePath);
}
static RegisterPrimOp r("fetchMercurial", 1, prim_fetchMercurial);
diff --git a/src/libstore/globals.hh b/src/libstore/globals.hh
index af72f7b1e..81bb24a4e 100644
--- a/src/libstore/globals.hh
+++ b/src/libstore/globals.hh
@@ -232,6 +232,9 @@ public:
"Whether to restrict file system access to paths in $NIX_PATH, "
"and network access to the URI prefixes listed in 'allowed-uris'."};
+ Setting<bool> pureEval{this, false, "pure-eval",
+ "Whether to restrict file system and network access to files specified by cryptographic hash."};
+
Setting<size_t> buildRepeat{this, 0, "repeat",
"The number of times to repeat a build in order to verify determinism.",
{"build-repeat"}};
diff --git a/src/libutil/util.cc b/src/libutil/util.cc
index 197df0c44..272997397 100644
--- a/src/libutil/util.cc
+++ b/src/libutil/util.cc
@@ -192,6 +192,12 @@ bool isInDir(const Path & path, const Path & dir)
}
+bool isDirOrInDir(const Path & path, const Path & dir)
+{
+ return path == dir or isInDir(path, dir);
+}
+
+
struct stat lstat(const Path & path)
{
struct stat st;
diff --git a/src/libutil/util.hh b/src/libutil/util.hh
index a3494e09b..75eb97515 100644
--- a/src/libutil/util.hh
+++ b/src/libutil/util.hh
@@ -53,10 +53,12 @@ Path dirOf(const Path & path);
following the final `/'. */
string baseNameOf(const Path & path);
-/* Check whether a given path is a descendant of the given
- directory. */
+/* Check whether 'path' is a descendant of 'dir'. */
bool isInDir(const Path & path, const Path & dir);
+/* Check whether 'path' is equal to 'dir' or a descendant of 'dir'. */
+bool isDirOrInDir(const Path & path, const Path & dir);
+
/* Get status of `path'. */
struct stat lstat(const Path & path);
diff --git a/src/nix-build/nix-build.cc b/src/nix-build/nix-build.cc
index 58366daa6..1b2494275 100755
--- a/src/nix-build/nix-build.cc
+++ b/src/nix-build/nix-build.cc
@@ -279,8 +279,8 @@ void mainWrapped(int argc, char * * argv)
else
/* If we're in a #! script, interpret filenames
relative to the script. */
- exprs.push_back(state.parseExprFromFile(resolveExprPath(lookupFileArg(state,
- inShebang && !packages ? absPath(i, absPath(dirOf(script))) : i))));
+ exprs.push_back(state.parseExprFromFile(resolveExprPath(state.checkSourcePath(lookupFileArg(state,
+ inShebang && !packages ? absPath(i, absPath(dirOf(script))) : i)))));
}
/* Evaluate them into derivations. */
diff --git a/src/nix-instantiate/nix-instantiate.cc b/src/nix-instantiate/nix-instantiate.cc
index 55ac007e8..e05040a42 100644
--- a/src/nix-instantiate/nix-instantiate.cc
+++ b/src/nix-instantiate/nix-instantiate.cc
@@ -182,7 +182,7 @@ int main(int argc, char * * argv)
for (auto & i : files) {
Expr * e = fromArgs
? state.parseExprFromString(i, absPath("."))
- : state.parseExprFromFile(resolveExprPath(lookupFileArg(state, i)));
+ : state.parseExprFromFile(resolveExprPath(state.checkSourcePath(lookupFileArg(state, i))));
processExpr(state, attrPaths, parseOnly, strict, autoArgs,
evalOnly, outputKind, xmlOutputSourceLocation, e);
}