From 812e027e1d5a4f83394069edd67bdf8404ffa2bb Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 30 Oct 2017 12:39:59 +0100 Subject: [PATCH] Add option allowed-uris This allows network access in restricted eval mode. --- doc/manual/command-ref/conf-file.xml | 18 +++++++++++++++++- doc/manual/release-notes/rl-1.12.xml | 8 ++++++++ src/libexpr/eval.cc | 20 ++++++++++++++++++++ src/libexpr/eval.hh | 2 ++ src/libexpr/primops.cc | 3 +-- src/libexpr/primops/fetchgit.cc | 7 ++++--- src/libstore/globals.hh | 4 +++- tests/restricted.sh | 12 ++++++++++++ 8 files changed, 67 insertions(+), 7 deletions(-) diff --git a/doc/manual/command-ref/conf-file.xml b/doc/manual/command-ref/conf-file.xml index 6b90083f..fb4d8cef 100644 --- a/doc/manual/command-ref/conf-file.xml +++ b/doc/manual/command-ref/conf-file.xml @@ -563,7 +563,8 @@ password my-password If set to true, the Nix evaluator will not allow access to any files outside of the Nix search path (as set via the NIX_PATH environment variable or the - option). The default is + option), or to URIs outside of + . The default is false. @@ -571,6 +572,21 @@ password my-password + allowed-uris + + + + A list of URI prefixes to which access is allowed in + restricted evaluation mode. For example, when set to + https://github.com/NixOS, builtin functions + such as fetchGit are allowed to access + https://github.com/NixOS/patchelf.git. + + + + + + pre-build-hook diff --git a/doc/manual/release-notes/rl-1.12.xml b/doc/manual/release-notes/rl-1.12.xml index 609dcef6..7c9a8b75 100644 --- a/doc/manual/release-notes/rl-1.12.xml +++ b/doc/manual/release-notes/rl-1.12.xml @@ -418,6 +418,14 @@ configureFlags = "--prefix=${placeholder "out"} --includedir=${placeholder "dev" through the MELPA package repository. + + In restricted evaluation mode + (), builtin functions that + download from the network (such as fetchGit) + are permitted to fetch underneath the list of URI prefixes + specified in the option . + + This release has contributions from TBD. diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 548537b7..63de2d60 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -355,6 +355,26 @@ Path EvalState::checkSourcePath(const Path & path_) } +void EvalState::checkURI(const std::string & uri) +{ + if (!restricted) return; + + /* 'uri' should be equal to a prefix, or in a subdirectory of a + prefix. Thus, the prefix https://github.co does not permit + access to https://github.com. Note: this allows 'http://' and + 'https://' as prefixes for any http/https URI. */ + for (auto & prefix : settings.allowedUris.get()) + if (uri == prefix || + (uri.size() > prefix.size() + && prefix.size() > 0 + && hasPrefix(uri, prefix) + && (prefix[prefix.size() - 1] == '/' || uri[prefix.size()] == '/'))) + return; + + throw RestrictedPathError("access to URI '%s' is forbidden in restricted mode", uri); +} + + void EvalState::addConstant(const string & name, Value & v) { Value * v2 = allocValue(); diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index 04a36b14..f0ab1435 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -110,6 +110,8 @@ public: Path checkSourcePath(const Path & path); + void checkURI(const std::string & uri); + /* Parse a Nix expression from the specified file. */ Expr * parseExprFromFile(const Path & path); Expr * parseExprFromFile(const Path & path, StaticEnv & staticEnv); diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 22925ba4..cd0dfbc0 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -1937,8 +1937,7 @@ void fetch(EvalState & state, const Pos & pos, Value * * args, Value & v, } else url = state.forceStringNoCtx(*args[0], pos); - if (state.restricted) - throw Error(format("'%1%' is not allowed in restricted mode") % who); + state.checkURI(url); Path res = getDownloader()->downloadCached(state.store, url, unpack, name, expectedHash); mkString(v, res, PathSet({res})); diff --git a/src/libexpr/primops/fetchgit.cc b/src/libexpr/primops/fetchgit.cc index 38bffd8d..81b64190 100644 --- a/src/libexpr/primops/fetchgit.cc +++ b/src/libexpr/primops/fetchgit.cc @@ -113,9 +113,6 @@ GitInfo exportGit(ref store, const std::string & uri, static void prim_fetchGit(EvalState & state, const Pos & pos, Value * * args, Value & v) { - // FIXME: cut&paste from fetch(). - if (state.restricted) throw Error("'fetchGit' is not allowed in restricted mode"); - std::string url; std::string ref = "master"; std::string rev; @@ -150,6 +147,10 @@ static void prim_fetchGit(EvalState & state, const Pos & pos, Value * * args, Va } else url = state.forceStringNoCtx(*args[0], pos); + // FIXME: git externals probably can be used to bypass the URI + // whitelist. Ah well. + state.checkURI(url); + auto gitInfo = exportGit(state.store, url, ref, rev, name); state.mkAttrs(v, 8); diff --git a/src/libstore/globals.hh b/src/libstore/globals.hh index 538273b5..a4aa842d 100644 --- a/src/libstore/globals.hh +++ b/src/libstore/globals.hh @@ -225,7 +225,7 @@ public: Setting restrictEval{this, false, "restrict-eval", "Whether to restrict file system access to paths in $NIX_PATH, " - "and to disallow fetching files from the network."}; + "and network access to the URI prefixes listed in 'allowed-uris'."}; Setting buildRepeat{this, 0, "repeat", "The number of times to repeat a build in order to verify determinism.", @@ -353,6 +353,8 @@ public: Setting maxFree{this, std::numeric_limits::max(), "max-free", "Stop deleting garbage when free disk space is above the specified amount."}; + Setting allowedUris{this, {}, "allowed-uris", + "Prefixes of URIs that builtin functions such as fetchurl and fetchGit are allowed to fetch."}; }; diff --git a/tests/restricted.sh b/tests/restricted.sh index 19096a9f..a297847c 100644 --- a/tests/restricted.sh +++ b/tests/restricted.sh @@ -16,3 +16,15 @@ nix-instantiate --option restrict-eval true --eval -E 'builtins.readDir ../src/b (! nix-instantiate --option restrict-eval true --eval -E 'let __nixPath = [ { prefix = "foo"; path = ./.; } ]; in ') nix-instantiate --option restrict-eval true --eval -E 'let __nixPath = [ { prefix = "foo"; path = ./.; } ]; in ' -I src=. +p=$(nix eval --raw "(builtins.fetchurl file://$(pwd)/restricted.sh)" --restrict-eval --allowed-uris "file://$(pwd)") +cmp $p restricted.sh + +(! nix eval --raw "(builtins.fetchurl file://$(pwd)/restricted.sh)" --restrict-eval) + +(! nix eval --raw "(builtins.fetchurl file://$(pwd)/restricted.sh)" --restrict-eval --allowed-uris "file://$(pwd)/restricted.sh/") + +nix eval --raw "(builtins.fetchurl file://$(pwd)/restricted.sh)" --restrict-eval --allowed-uris "file://$(pwd)/restricted.sh" + +(! nix eval --raw "(builtins.fetchurl https://github.com/NixOS/patchelf/archive/master.tar.gz)" --restrict-eval) +(! nix eval --raw "(builtins.fetchTarball https://github.com/NixOS/patchelf/archive/master.tar.gz)" --restrict-eval) +(! nix eval --raw "(fetchGit git://github.com/NixOS/patchelf.git)" --restrict-eval)