bunpen: avoid binding /proc entries; these especially confuse bwrap apps like geary
This commit is contained in:
@@ -67,12 +67,19 @@ export fn namespace_restrict(what: *resources) void = {
|
|||||||
|
|
||||||
let pwd = strings::dup(os::getcwd()); // dup because API uses a static buffer
|
let pwd = strings::dup(os::getcwd()); // dup because API uses a static buffer
|
||||||
defer(free(pwd));
|
defer(free(pwd));
|
||||||
isolate_paths(what.paths);
|
isolate_paths(what);
|
||||||
// try to change to the old working directory;
|
// try to change to the old working directory;
|
||||||
// this can fail if it's not within the sandbox.
|
// this can fail if it's not within the sandbox.
|
||||||
errors::ext::swallow("namespace: restore $PWD", os::chdir(pwd));
|
errors::ext::swallow("namespace: restore $PWD", os::chdir(pwd));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// everything inside this struct is borrowed
|
||||||
|
type ns_ctx = struct {
|
||||||
|
what: *resources,
|
||||||
|
old_fs: *fs::fs,
|
||||||
|
new_fs: *fs::fs,
|
||||||
|
};
|
||||||
|
|
||||||
// fork and:
|
// fork and:
|
||||||
// - in the child: continue execution as normal
|
// - in the child: continue execution as normal
|
||||||
// - in the parent: wait for the child, then propagate its exit status
|
// - in the parent: wait for the child, then propagate its exit status
|
||||||
@@ -94,7 +101,7 @@ fn fork_and_propagate() (void | os::exec::error) = {
|
|||||||
// i don't know if this really matters anywhere (maybe `/` and `/proc`?),
|
// i don't know if this really matters anywhere (maybe `/` and `/proc`?),
|
||||||
// `sanebox` behavior is to gather all paths, expand their symlinks,
|
// `sanebox` behavior is to gather all paths, expand their symlinks,
|
||||||
// and then only bind-mount the top-most path in the case of overlap.
|
// and then only bind-mount the top-most path in the case of overlap.
|
||||||
fn isolate_paths(paths: []path::buffer) void = {
|
fn isolate_paths(what: *resources) void = {
|
||||||
// allow new mounts to propagate from the parent namespace into the child
|
// allow new mounts to propagate from the parent namespace into the child
|
||||||
// namespace, but not vice versa:
|
// namespace, but not vice versa:
|
||||||
errors::ext::check("[namespace] reconfigure / as MS_SLAVE", rt::ext::mount("/", "/", "", rt::ext::mount_flag::SLAVE | rt::ext::mount_flag::REC, null));
|
errors::ext::check("[namespace] reconfigure / as MS_SLAVE", rt::ext::mount("/", "/", "", rt::ext::mount_flag::SLAVE | rt::ext::mount_flag::REC, null));
|
||||||
@@ -156,10 +163,16 @@ fn isolate_paths(paths: []path::buffer) void = {
|
|||||||
let new_fs = os::dirfdopen(new_fd);
|
let new_fs = os::dirfdopen(new_fd);
|
||||||
defer(free(new_fs));
|
defer(free(new_fs));
|
||||||
|
|
||||||
for (let path .. paths) {
|
let ctx = ns_ctx {
|
||||||
|
what = what,
|
||||||
|
old_fs = old_fs,
|
||||||
|
new_fs = new_fs,
|
||||||
|
};
|
||||||
|
|
||||||
|
for (let path .. what.paths) {
|
||||||
errors::ext::swallow(
|
errors::ext::swallow(
|
||||||
"[namespace] unable to bind {}",
|
"[namespace] unable to bind {}",
|
||||||
bind_leaf(old_fs, new_fs, &path),
|
bind_leaf(&ctx, &path),
|
||||||
path::string(&path),
|
path::string(&path),
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -192,7 +205,7 @@ fn isolate_paths(paths: []path::buffer) void = {
|
|||||||
// - [x] path is too long => does not create the leaf *nor any ancestors*.
|
// - [x] path is too long => does not create the leaf *nor any ancestors*.
|
||||||
// - [x] canonical path points outside the fs (e.g. `..`, or `../new/proc`).
|
// - [x] canonical path points outside the fs (e.g. `..`, or `../new/proc`).
|
||||||
// does not create the leaf *nor any of its ancestors* at/after the `..`.
|
// does not create the leaf *nor any of its ancestors* at/after the `..`.
|
||||||
fn bind_leaf(old_fs: *fs::fs, new_fs: *fs::fs, user_path: *path::buffer) (void | path::error) = {
|
fn bind_leaf(ctx: *ns_ctx, user_path: *path::buffer) (void | path::error) = {
|
||||||
let path_str = path::string(user_path);
|
let path_str = path::string(user_path);
|
||||||
log::printfln("[namespace] permit path: {}", path_str);
|
log::printfln("[namespace] permit path: {}", path_str);
|
||||||
|
|
||||||
@@ -204,35 +217,43 @@ fn bind_leaf(old_fs: *fs::fs, new_fs: *fs::fs, user_path: *path::buffer) (void |
|
|||||||
log::printfln("[namespace] not binding external path {} (of {})", cur_strpath, path_str);
|
log::printfln("[namespace] not binding external path {} (of {})", cur_strpath, path_str);
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
if (path::abs(comp)) {
|
if (path::abs(comp)) {
|
||||||
// dirfd doesn't do well will absolute paths.
|
// dirfd doesn't do well will absolute paths.
|
||||||
comp = strings::sub(comp, 1, strings::end);
|
comp = strings::sub(comp, 1, strings::end);
|
||||||
};
|
};
|
||||||
cur_strpath = path::push(&cur_path, comp)?;
|
cur_strpath = path::push(&cur_path, comp)?;
|
||||||
|
|
||||||
|
if (cur_strpath == "proc" && !ctx.what.pid) {
|
||||||
|
// if we're inside a PID space, don't bind-mount /proc entries from the
|
||||||
|
// outer /proc mount as it confuses things like bwrap.
|
||||||
|
log::printfln("[namespace] not binding proc path {}", path_str);
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
// hmm, should we swallow this, or raise?
|
// hmm, should we swallow this, or raise?
|
||||||
// seems unlikely we'll fail to bind one part of the path, but then
|
// seems unlikely we'll fail to bind one part of the path, but then
|
||||||
// successfully bind the *next* part.
|
// successfully bind the *next* part.
|
||||||
errors::ext::swallow(
|
errors::ext::swallow(
|
||||||
"[namespace] unable to copy intermediate path {} of {}",
|
"[namespace] unable to copy intermediate path {} of {}",
|
||||||
bind_component(old_fs, new_fs, cur_strpath, path::iterrem(&it)),
|
bind_component(ctx, cur_strpath, path::iterrem(&it)),
|
||||||
cur_strpath, path_str
|
cur_strpath, path_str
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
fn bind_component(old_fs: *fs::fs, new_fs: *fs::fs, strpath: str, remaining: str) (void | fs::error | path::error | rt::errno) = {
|
fn bind_component(ctx: *ns_ctx, strpath: str, remaining: str) (void | fs::error | path::error | rt::errno) = {
|
||||||
let new_exists = match (fs::stat(new_fs, strpath)) {
|
let new_exists = match (fs::stat(ctx.new_fs, strpath)) {
|
||||||
case let e: fs::error => yield false; // hasn't been bound yet
|
case let e: fs::error => yield false; // hasn't been bound yet
|
||||||
case let other: fs::filestat => yield true; // already created
|
case let other: fs::filestat => yield true; // already created
|
||||||
};
|
};
|
||||||
let st = fs::stat(old_fs, strpath)?;
|
let st = fs::stat(ctx.old_fs, strpath)?;
|
||||||
|
|
||||||
if (fs::islink(st.mode)) {
|
if (fs::islink(st.mode)) {
|
||||||
if (new_exists) return; // we already made the link
|
if (new_exists) return; // we already made the link
|
||||||
let linktext = fs::readlink(old_fs, strpath)?;
|
let linktext = fs::readlink(ctx.old_fs, strpath)?;
|
||||||
log::printfln("[namespace/bind] ln new/{} -> {}", strpath, linktext);
|
log::printfln("[namespace/bind] ln new/{} -> {}", strpath, linktext);
|
||||||
fs::symlink(new_fs, linktext, strpath)?;
|
fs::symlink(ctx.new_fs, linktext, strpath)?;
|
||||||
|
|
||||||
// bind the real path (or, the "more real" path, in case there are
|
// bind the real path (or, the "more real" path, in case there are
|
||||||
// multiple layers of symlink).
|
// multiple layers of symlink).
|
||||||
@@ -245,14 +266,14 @@ fn bind_component(old_fs: *fs::fs, new_fs: *fs::fs, strpath: str, remaining: str
|
|||||||
// foo/bar/baz/fnord with (fnord -> target, remaining="") => `foo/bar/baz/target`
|
// foo/bar/baz/fnord with (fnord -> target, remaining="") => `foo/bar/baz/target`
|
||||||
yield path::init(strpath, "..", linktext, remaining)?;
|
yield path::init(strpath, "..", linktext, remaining)?;
|
||||||
};
|
};
|
||||||
return bind_leaf(old_fs, new_fs, &target_path);
|
return bind_leaf(ctx, &target_path);
|
||||||
} else if (fs::isdir(st.mode)) {
|
} else if (fs::isdir(st.mode)) {
|
||||||
// don't recreate the directory if it exists, but DO try to bind-mount it.
|
// don't recreate the directory if it exists, but DO try to bind-mount it.
|
||||||
// we could have mounted something below it, and then discovered the need
|
// we could have mounted something below it, and then discovered the need
|
||||||
// to mount more.
|
// to mount more.
|
||||||
if (!new_exists) {
|
if (!new_exists) {
|
||||||
log::printfln("[namespace/bind] mkdir new/{}", strpath);
|
log::printfln("[namespace/bind] mkdir new/{}", strpath);
|
||||||
fs::mkdir(new_fs, strpath, st.mode)?;
|
fs::mkdir(ctx.new_fs, strpath, st.mode)?;
|
||||||
};
|
};
|
||||||
} else { // file-like
|
} else { // file-like
|
||||||
if (new_exists) return; // we already bound the file
|
if (new_exists) return; // we already bound the file
|
||||||
@@ -263,7 +284,7 @@ fn bind_component(old_fs: *fs::fs, new_fs: *fs::fs, strpath: str, remaining: str
|
|||||||
|
|
||||||
// TODO: tune options (optional parameter; default is fs::flag::TRUNC)
|
// TODO: tune options (optional parameter; default is fs::flag::TRUNC)
|
||||||
log::printfln("[namespace/bind] touch new/{}", strpath);
|
log::printfln("[namespace/bind] touch new/{}", strpath);
|
||||||
fs::create(new_fs, strpath, st.mode)?;
|
fs::create(ctx.new_fs, strpath, st.mode)?;
|
||||||
};
|
};
|
||||||
|
|
||||||
if (remaining != "")
|
if (remaining != "")
|
||||||
|
Reference in New Issue
Block a user