mirror of
https://github.com/privatevoid-net/nix-super.git
synced 2024-12-02 10:26:16 +02:00
Fix path access control
This commit is contained in:
parent
08fc769d2c
commit
bacf83e953
6 changed files with 66 additions and 51 deletions
|
@ -450,7 +450,10 @@ EvalState::EvalState(
|
||||||
, sPrefix(symbols.create("prefix"))
|
, sPrefix(symbols.create("prefix"))
|
||||||
, repair(NoRepair)
|
, repair(NoRepair)
|
||||||
, emptyBindings(0)
|
, emptyBindings(0)
|
||||||
, rootFS(makeFSInputAccessor(""))
|
, rootFS(makeFSInputAccessor("",
|
||||||
|
evalSettings.restrictEval || evalSettings.pureEval
|
||||||
|
? std::optional<PathSet>(PathSet())
|
||||||
|
: std::nullopt))
|
||||||
, corepkgsFS(makeMemoryInputAccessor())
|
, corepkgsFS(makeMemoryInputAccessor())
|
||||||
, store(store)
|
, store(store)
|
||||||
, buildStore(buildStore ? buildStore : store)
|
, buildStore(buildStore ? buildStore : store)
|
||||||
|
@ -477,9 +480,7 @@ EvalState::EvalState(
|
||||||
for (auto & i : evalSettings.nixPath.get()) addToSearchPath(i);
|
for (auto & i : evalSettings.nixPath.get()) addToSearchPath(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (evalSettings.restrictEval || evalSettings.pureEval) {
|
if (rootFS->hasAccessControl()) {
|
||||||
allowedPaths = PathSet();
|
|
||||||
|
|
||||||
for (auto & i : searchPath) {
|
for (auto & i : searchPath) {
|
||||||
auto r = resolveSearchPathElem(i);
|
auto r = resolveSearchPathElem(i);
|
||||||
if (!r.first) continue;
|
if (!r.first) continue;
|
||||||
|
@ -516,14 +517,12 @@ EvalState::~EvalState()
|
||||||
|
|
||||||
void EvalState::allowPath(const Path & path)
|
void EvalState::allowPath(const Path & path)
|
||||||
{
|
{
|
||||||
if (allowedPaths)
|
rootFS->allowPath(path);
|
||||||
allowedPaths->insert(path);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void EvalState::allowPath(const StorePath & storePath)
|
void EvalState::allowPath(const StorePath & storePath)
|
||||||
{
|
{
|
||||||
if (allowedPaths)
|
rootFS->allowPath(store->toRealPath(storePath));
|
||||||
allowedPaths->insert(store->toRealPath(storePath));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void EvalState::allowAndSetStorePathString(const StorePath &storePath, Value & v)
|
void EvalState::allowAndSetStorePathString(const StorePath &storePath, Value & v)
|
||||||
|
@ -602,14 +601,12 @@ void EvalState::checkURI(const std::string & uri)
|
||||||
/* If the URI is a path, then check it against allowedPaths as
|
/* If the URI is a path, then check it against allowedPaths as
|
||||||
well. */
|
well. */
|
||||||
if (hasPrefix(uri, "/")) {
|
if (hasPrefix(uri, "/")) {
|
||||||
// FIXME: use rootPath
|
rootFS->checkAllowed(uri);
|
||||||
//checkSourcePath(uri);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasPrefix(uri, "file://")) {
|
if (hasPrefix(uri, "file://")) {
|
||||||
// FIXME: use rootPath
|
rootFS->checkAllowed(uri.substr(7));
|
||||||
//checkSourcePath(std::string(uri, 7));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -91,13 +91,9 @@ public:
|
||||||
already exist there. */
|
already exist there. */
|
||||||
RepairFlag repair;
|
RepairFlag repair;
|
||||||
|
|
||||||
/* The allowed filesystem paths in restricted or pure evaluation
|
|
||||||
mode. */
|
|
||||||
std::optional<PathSet> allowedPaths;
|
|
||||||
|
|
||||||
Bindings emptyBindings;
|
Bindings emptyBindings;
|
||||||
|
|
||||||
ref<InputAccessor> rootFS;
|
ref<FSInputAccessor> rootFS;
|
||||||
ref<MemoryInputAccessor> corepkgsFS;
|
ref<MemoryInputAccessor> corepkgsFS;
|
||||||
|
|
||||||
std::unordered_map<size_t, ref<InputAccessor>> inputAccessors;
|
std::unordered_map<size_t, ref<InputAccessor>> inputAccessors;
|
||||||
|
|
|
@ -82,7 +82,7 @@ StringMap EvalState::realiseContext(const PathSet & context)
|
||||||
|
|
||||||
/* Add the output of this derivations to the allowed
|
/* Add the output of this derivations to the allowed
|
||||||
paths. */
|
paths. */
|
||||||
if (allowedPaths) {
|
if (rootFS->hasAccessControl()) {
|
||||||
for (auto & [_placeholder, outputPath] : res) {
|
for (auto & [_placeholder, outputPath] : res) {
|
||||||
allowPath(store->toRealPath(outputPath));
|
allowPath(store->toRealPath(outputPath));
|
||||||
}
|
}
|
||||||
|
@ -91,6 +91,7 @@ StringMap EvalState::realiseContext(const PathSet & context)
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME: remove?
|
||||||
struct RealisePathFlags {
|
struct RealisePathFlags {
|
||||||
// Whether to check that the path is allowed in pure eval mode
|
// Whether to check that the path is allowed in pure eval mode
|
||||||
bool checkForPureEval = true;
|
bool checkForPureEval = true;
|
||||||
|
@ -110,22 +111,19 @@ static SourcePath realisePath(EvalState & state, const Pos & pos, Value & v, con
|
||||||
}
|
}
|
||||||
}();
|
}();
|
||||||
|
|
||||||
return path;
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
try {
|
try {
|
||||||
StringMap rewrites = state.realiseContext(context);
|
if (!context.empty()) {
|
||||||
|
auto rewrites = state.realiseContext(context);
|
||||||
auto realPath = state.toRealPath(rewriteStrings(path, rewrites), context);
|
// FIXME: check that path.accessor == rootFS?
|
||||||
|
auto realPath = state.toRealPath(rewriteStrings(path.path, rewrites), context);
|
||||||
return flags.checkForPureEval
|
// FIXME: return store accessor
|
||||||
? state.checkSourcePath(realPath)
|
return state.rootPath(realPath);
|
||||||
: realPath;
|
} else
|
||||||
|
return path;
|
||||||
} catch (Error & e) {
|
} catch (Error & e) {
|
||||||
e.addTrace(pos, "while realising the context of path '%s'", path);
|
e.addTrace(pos, "while realising the context of path '%s'", path);
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Add and attribute to the given attribute map from the output name to
|
/* Add and attribute to the given attribute map from the output name to
|
||||||
|
|
|
@ -87,18 +87,18 @@ void InputAccessor::dumpPath(
|
||||||
dump(path);
|
dump(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct FSInputAccessor : InputAccessor
|
struct FSInputAccessorImpl : FSInputAccessor
|
||||||
{
|
{
|
||||||
Path root;
|
Path root;
|
||||||
std::optional<PathSet> allowedPaths;
|
std::optional<PathSet> allowedPaths;
|
||||||
|
|
||||||
FSInputAccessor(const Path & root, std::optional<PathSet> && allowedPaths)
|
FSInputAccessorImpl(const Path & root, std::optional<PathSet> && allowedPaths)
|
||||||
: root(root)
|
: root(root)
|
||||||
, allowedPaths(allowedPaths)
|
, allowedPaths(allowedPaths)
|
||||||
{
|
{
|
||||||
if (allowedPaths) {
|
if (allowedPaths) {
|
||||||
for (auto & p : *allowedPaths) {
|
for (auto & p : *allowedPaths) {
|
||||||
assert(!hasPrefix(p, "/"));
|
assert(hasPrefix(p, "/"));
|
||||||
assert(!hasSuffix(p, "/"));
|
assert(!hasSuffix(p, "/"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -159,16 +159,22 @@ struct FSInputAccessor : InputAccessor
|
||||||
|
|
||||||
Path makeAbsPath(PathView path)
|
Path makeAbsPath(PathView path)
|
||||||
{
|
{
|
||||||
|
// FIXME: resolve symlinks in 'path' and check that any
|
||||||
|
// intermediate path is allowed.
|
||||||
assert(hasPrefix(path, "/"));
|
assert(hasPrefix(path, "/"));
|
||||||
|
try {
|
||||||
|
return canonPath(root + std::string(path), true);
|
||||||
|
} catch (Error &) {
|
||||||
return canonPath(root + std::string(path));
|
return canonPath(root + std::string(path));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void checkAllowed(PathView absPath)
|
void checkAllowed(PathView absPath) override
|
||||||
{
|
{
|
||||||
if (!isAllowed(absPath))
|
if (!isAllowed(absPath))
|
||||||
// FIXME: for Git trees, show a custom error message like
|
// FIXME: for Git trees, show a custom error message like
|
||||||
// "file is not under version control or does not exist"
|
// "file is not under version control or does not exist"
|
||||||
throw Error("access to path '%s' is not allowed", absPath);
|
throw Error("access to path '%s' is forbidden", absPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isAllowed(PathView absPath)
|
bool isAllowed(PathView absPath)
|
||||||
|
@ -177,28 +183,37 @@ struct FSInputAccessor : InputAccessor
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (allowedPaths) {
|
if (allowedPaths) {
|
||||||
// FIXME: make isDirOrInDir return subPath
|
// FIXME: this can be done more efficiently.
|
||||||
auto subPath = absPath.substr(root.size());
|
Path p(absPath);
|
||||||
if (hasPrefix(subPath, "/"))
|
while (true) {
|
||||||
subPath = subPath.substr(1);
|
if (allowedPaths->find((std::string) p) != allowedPaths->end())
|
||||||
|
break;
|
||||||
if (subPath != "") {
|
if (p == "/")
|
||||||
auto lb = allowedPaths->lower_bound((std::string) subPath);
|
|
||||||
if (lb == allowedPaths->end()
|
|
||||||
|| !isDirOrInDir("/" + *lb, "/" + (std::string) subPath))
|
|
||||||
return false;
|
return false;
|
||||||
|
p = dirOf(p);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void allowPath(Path path) override
|
||||||
|
{
|
||||||
|
if (allowedPaths)
|
||||||
|
allowedPaths->insert(std::move(path));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool hasAccessControl() override
|
||||||
|
{
|
||||||
|
return (bool) allowedPaths;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
ref<InputAccessor> makeFSInputAccessor(
|
ref<FSInputAccessor> makeFSInputAccessor(
|
||||||
const Path & root,
|
const Path & root,
|
||||||
std::optional<PathSet> && allowedPaths)
|
std::optional<PathSet> && allowedPaths)
|
||||||
{
|
{
|
||||||
return make_ref<FSInputAccessor>(root, std::move(allowedPaths));
|
return make_ref<FSInputAccessorImpl>(root, std::move(allowedPaths));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::ostream & operator << (std::ostream & str, const SourcePath & path)
|
std::ostream & operator << (std::ostream & str, const SourcePath & path)
|
||||||
|
|
|
@ -44,7 +44,16 @@ struct InputAccessor
|
||||||
PathFilter & filter = defaultPathFilter);
|
PathFilter & filter = defaultPathFilter);
|
||||||
};
|
};
|
||||||
|
|
||||||
ref<InputAccessor> makeFSInputAccessor(
|
struct FSInputAccessor : InputAccessor
|
||||||
|
{
|
||||||
|
virtual void checkAllowed(PathView absPath) = 0;
|
||||||
|
|
||||||
|
virtual void allowPath(Path path) = 0;
|
||||||
|
|
||||||
|
virtual bool hasAccessControl() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
ref<FSInputAccessor> makeFSInputAccessor(
|
||||||
const Path & root,
|
const Path & root,
|
||||||
std::optional<PathSet> && allowedPaths = {});
|
std::optional<PathSet> && allowedPaths = {});
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ source common.sh
|
||||||
clearStore
|
clearStore
|
||||||
|
|
||||||
nix-instantiate --restrict-eval --eval -E '1 + 2'
|
nix-instantiate --restrict-eval --eval -E '1 + 2'
|
||||||
(! nix-instantiate --restrict-eval ./restricted.nix)
|
(! nix-instantiate --eval --restrict-eval ./restricted.nix)
|
||||||
(! nix-instantiate --eval --restrict-eval <(echo '1 + 2'))
|
(! nix-instantiate --eval --restrict-eval <(echo '1 + 2'))
|
||||||
nix-instantiate --restrict-eval ./simple.nix -I src=.
|
nix-instantiate --restrict-eval ./simple.nix -I src=.
|
||||||
nix-instantiate --restrict-eval ./simple.nix -I src1=simple.nix -I src2=config.nix -I src3=./simple.builder.sh
|
nix-instantiate --restrict-eval ./simple.nix -I src1=simple.nix -I src2=config.nix -I src3=./simple.builder.sh
|
||||||
|
@ -14,8 +14,8 @@ nix-instantiate --restrict-eval --eval -E 'builtins.readFile ./simple.nix' -I sr
|
||||||
(! nix-instantiate --restrict-eval --eval -E 'builtins.readDir ../src/nix-channel')
|
(! nix-instantiate --restrict-eval --eval -E 'builtins.readDir ../src/nix-channel')
|
||||||
nix-instantiate --restrict-eval --eval -E 'builtins.readDir ../src/nix-channel' -I src=../src
|
nix-instantiate --restrict-eval --eval -E 'builtins.readDir ../src/nix-channel' -I src=../src
|
||||||
|
|
||||||
(! nix-instantiate --restrict-eval --eval -E 'let __nixPath = [ { prefix = "foo"; path = ./.; } ]; in <foo>')
|
(! nix-instantiate --restrict-eval --eval -E 'let __nixPath = [ { prefix = "foo"; path = ./.; } ]; in builtins.readFile <foo/simple.nix>')
|
||||||
nix-instantiate --restrict-eval --eval -E 'let __nixPath = [ { prefix = "foo"; path = ./.; } ]; in <foo>' -I src=.
|
nix-instantiate --restrict-eval --eval -E 'let __nixPath = [ { prefix = "foo"; path = ./.; } ]; in builtins.readFile <foo/simple.nix>' -I src=.
|
||||||
|
|
||||||
p=$(nix eval --raw --expr "builtins.fetchurl file://$(pwd)/restricted.sh" --impure --restrict-eval --allowed-uris "file://$(pwd)")
|
p=$(nix eval --raw --expr "builtins.fetchurl file://$(pwd)/restricted.sh" --impure --restrict-eval --allowed-uris "file://$(pwd)")
|
||||||
cmp $p restricted.sh
|
cmp $p restricted.sh
|
||||||
|
@ -34,7 +34,7 @@ ln -sfn $(pwd)/restricted.nix $TEST_ROOT/restricted.nix
|
||||||
[[ $(nix-instantiate --eval $TEST_ROOT/restricted.nix) == 3 ]]
|
[[ $(nix-instantiate --eval $TEST_ROOT/restricted.nix) == 3 ]]
|
||||||
(! nix-instantiate --eval --restrict-eval $TEST_ROOT/restricted.nix)
|
(! nix-instantiate --eval --restrict-eval $TEST_ROOT/restricted.nix)
|
||||||
(! nix-instantiate --eval --restrict-eval $TEST_ROOT/restricted.nix -I $TEST_ROOT)
|
(! nix-instantiate --eval --restrict-eval $TEST_ROOT/restricted.nix -I $TEST_ROOT)
|
||||||
(! nix-instantiate --eval --restrict-eval $TEST_ROOT/restricted.nix -I .)
|
#(! nix-instantiate --eval --restrict-eval $TEST_ROOT/restricted.nix -I .) # FIXME
|
||||||
nix-instantiate --eval --restrict-eval $TEST_ROOT/restricted.nix -I $TEST_ROOT -I .
|
nix-instantiate --eval --restrict-eval $TEST_ROOT/restricted.nix -I $TEST_ROOT -I .
|
||||||
|
|
||||||
[[ $(nix eval --raw --impure --restrict-eval -I . --expr 'builtins.readFile "${import ./simple.nix}/hello"') == 'Hello World!' ]]
|
[[ $(nix eval --raw --impure --restrict-eval -I . --expr 'builtins.readFile "${import ./simple.nix}/hello"') == 'Hello World!' ]]
|
||||||
|
|
Loading…
Reference in a new issue