Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,8 @@ test: $(BIN)
# --- Basic sanity tests ---
$(call run_test, ./nsjail -q -Mo --chroot / --user 99999 --group 99999 -- /bin/true, 0)
$(call run_test, ./nsjail -q -Mo --chroot / --user 99999 --group 99999 -- /bin/false, 1)
$(call run_test, ./nsjail -q -Mo --chroot / --user 99999 --group 99999 -- /bin/true < /tmp, 255)
$(call run_test, ./nsjail -q -Mo --chroot / --user 99999 --group 99999 --pass_fd 0 -- /bin/true < /tmp, 0)
$(call run_test, ./nsjail --config tests/seccomp.cfg -q -t 2 -- /bin/bash -c 'strace -o /dev/null /bin/true || exit 77', 77)
$(call run_test, ./nsjail --config tests/basic.cfg -q -t 2 -- /bin/bash -c 'strace -o /dev/null /bin/true && exit 77', 77)
$(call run_test, ./nsjail --config tests/pasta-nat.cfg -q -t 3 -- /bin/bash -c 'sleep 0.2; ping -W 1 -c 1 8.8.8.8 && exit 77', 77)
Expand Down
9 changes: 6 additions & 3 deletions cmdline.cc
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ static const struct custom_option custom_opts[] = {
{ { "silent", no_argument, nullptr, 0x0502 }, "Redirect child process' fd:0/1/2 to /dev/null" },
{ { "stderr_to_null", no_argument, nullptr, 0x0503 }, "Redirect child process' fd:2 (STDERR_FILENO) to /dev/null" },
{ { "skip_setsid", no_argument, nullptr, 0x0504 }, "Don't call setsid(), allows for terminal signal handling in the sandboxed process. Dangerous" },
{ { "pass_fd", required_argument, nullptr, 0x0505 }, "Don't close this FD before executing the child process (can be specified multiple times), by default: 0/1/2 are kept open" },
{ { "pass_fd", required_argument, nullptr, 0x0505 }, "Don't close this FD before executing the child process (can be specified multiple times), by default: 0/1/2 are kept open, but directory stdio fds are rejected unless explicitly passed" },
{ { "disable_no_new_privs", no_argument, nullptr, 0x0507 }, "Don't set the prctl(NO_NEW_PRIVS, 1) (DANGEROUS)" },
{ { "rlimit_as", required_argument, nullptr, 0x0201 }, "RLIMIT_AS in MB, 'max' or 'hard' for the current hard limit, 'def' or 'soft' for the current soft limit, 'inf' for RLIM64_INFINITY (default: 4096)" },
{ { "rlimit_core", required_argument, nullptr, 0x0202 }, "RLIMIT_CORE in MB, 'max' or 'hard' for the current hard limit, 'def' or 'soft' for the current soft limit, 'inf' for RLIM64_INFINITY (default: 0)" },
Expand Down Expand Up @@ -687,8 +687,11 @@ std::unique_ptr<nsj_t> parseArgs(int argc, char* argv[]) {
case 0x0504:
nsj->njc.set_skip_setsid(true);
break;
case 0x0505:
nsj->openfds.push_back((int)strtol(optarg, NULL, 0));
case 0x0505: {
int fd = (int)strtol(optarg, NULL, 0);
nsj->openfds.push_back(fd);
nsj->passfds.push_back(fd);
}
break;
case 0x0507:
nsj->njc.set_disable_no_new_privs(true);
Expand Down
1 change: 1 addition & 0 deletions config.cc
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ static bool parseInternal(nsj_t* nsj, const nsjail::NsJailConfig& njc) {
}
for (ssize_t i = 0; i < njc.pass_fd_size(); i++) {
nsj->openfds.push_back(njc.pass_fd(i));
nsj->passfds.push_back(njc.pass_fd(i));
}
for (ssize_t i = 0; i < njc.uidmap_size(); i++) {
if (!user::parseId(nsj, njc.uidmap(i).inside_id(), njc.uidmap(i).outside_id(),
Expand Down
30 changes: 30 additions & 0 deletions contain.cc
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
#include <sys/personality.h>
#include <sys/prctl.h>
#include <sys/resource.h>
#include <sys/stat.h>
#include <unistd.h>

#include <algorithm>
Expand Down Expand Up @@ -225,6 +226,34 @@ static bool containPassFd(nsj_t* nsj, int fd) {
return (std::find(nsj->openfds.begin(), nsj->openfds.end(), fd) != nsj->openfds.end());
}

static bool containExplicitPassFd(nsj_t* nsj, int fd) {
return (std::find(nsj->passfds.begin(), nsj->passfds.end(), fd) != nsj->passfds.end());
}

static bool containValidateDefaultStdioFds(nsj_t* nsj) {
for (int fd = STDIN_FILENO; fd <= STDERR_FILENO; fd++) {
if (containExplicitPassFd(nsj, fd)) {
continue;
}

struct stat st;
if (TEMP_FAILURE_RETRY(fstat(fd, &st)) == -1) {
if (errno == EBADF) {
continue;
}
PLOG_E("fstat(fd=%d)", fd);
return false;
}
if (S_ISDIR(st.st_mode)) {
LOG_E("Default stdio fd=%d is a directory. Refusing to pass a directory fd "
"into the sandbox; use --pass_fd=%d to pass it explicitly",
fd, fd);
return false;
}
}
return true;
}

static bool containMakeFdCOE(int fd, bool pass_fd) {
int flags = TEMP_FAILURE_RETRY(fcntl(fd, F_GETFD, 0));
if (flags == -1) {
Expand Down Expand Up @@ -321,6 +350,7 @@ static bool containMakeFdsCOEProc(nsj_t* nsj) {
}

static bool containMakeFdsCOE(nsj_t* nsj) {
RETURN_ON_FAILURE(containValidateDefaultStdioFds(nsj));
if (containMakeFdsCOECloseRange(nsj)) {
return true;
}
Expand Down
1 change: 1 addition & 0 deletions nsjail.h
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ struct nsj_t {
std::vector<idmap_t> uids;
std::vector<idmap_t> gids;
std::vector<int> openfds;
std::vector<int> passfds;

std::vector<pipemap_t> pipes;
int exit_status;
Expand Down