-Wall -Wextra -Werror \
-Ikafel/include
-LDFLAGS += -Wl,-z,now -Wl,-z,relro -pie -Wl,-z,noexecstack -lpthread
+LDFLAGS += -Wl,-z,now -Wl,-z,relro -pie -Wl,-z,noexecstack -lpthread -lcap
BIN = nsjail
LIBS = kafel/libkafel.a
-SRCS = nsjail.c cmdline.c config.c contain.c log.c cgroup.c mount.c net.c pid.c sandbox.c subproc.c user.c util.c uts.c cpu.c
+SRCS = nsjail.c caps.c cmdline.c config.c contain.c log.c cgroup.c mount.c net.c pid.c sandbox.c subproc.c user.c util.c uts.c cpu.c
OBJS = $(SRCS:.c=.o)
ifdef DEBUG
# DO NOT DELETE THIS LINE -- make depend depends on it.
-nsjail.o: nsjail.h common.h cmdline.h log.h net.h subproc.h util.h
-cmdline.o: cmdline.h common.h config.h log.h mount.h util.h user.h
+nsjail.o: nsjail.h common.h caps.h cmdline.h log.h net.h subproc.h util.h
+caps.o: caps.h common.h log.h
+cmdline.o: cmdline.h common.h caps.h config.h log.h mount.h util.h user.h
config.o: common.h config.h log.h mount.h user.h util.h
-contain.o: contain.h common.h cgroup.h cpu.h log.h mount.h net.h pid.h user.h
-contain.o: util.h uts.h
+contain.o: contain.h common.h caps.h cgroup.h cpu.h log.h mount.h net.h pid.h
+contain.o: user.h util.h uts.h
log.o: log.h common.h
cgroup.o: cgroup.h common.h log.h util.h
mount.o: mount.h common.h log.h subproc.h util.h
--- /dev/null
+/*
+
+ nsjail - capability-related operations
+ -----------------------------------------
+
+ Copyright 2014 Google Inc. All Rights Reserved.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+*/
+
+#include "caps.h"
+
+#include <sys/capability.h>
+#include <string.h>
+#include <sys/prctl.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "log.h"
+
+#define VALSTR_STRUCT(x) { x, #x }
+
+/* *INDENT-OFF* */
+static struct {
+ const int val;
+ const char* const name;
+} const capNames[] = {
+ VALSTR_STRUCT(CAP_CHOWN),
+ VALSTR_STRUCT(CAP_DAC_OVERRIDE),
+ VALSTR_STRUCT(CAP_DAC_READ_SEARCH),
+ VALSTR_STRUCT(CAP_FOWNER),
+ VALSTR_STRUCT(CAP_FSETID),
+ VALSTR_STRUCT(CAP_KILL),
+ VALSTR_STRUCT(CAP_SETGID),
+ VALSTR_STRUCT(CAP_SETUID),
+ VALSTR_STRUCT(CAP_SETPCAP),
+ VALSTR_STRUCT(CAP_LINUX_IMMUTABLE),
+ VALSTR_STRUCT(CAP_NET_BIND_SERVICE),
+ VALSTR_STRUCT(CAP_NET_BROADCAST),
+ VALSTR_STRUCT(CAP_NET_ADMIN),
+ VALSTR_STRUCT(CAP_NET_RAW),
+ VALSTR_STRUCT(CAP_IPC_LOCK),
+ VALSTR_STRUCT(CAP_IPC_OWNER),
+ VALSTR_STRUCT(CAP_SYS_MODULE),
+ VALSTR_STRUCT(CAP_SYS_RAWIO),
+ VALSTR_STRUCT(CAP_SYS_CHROOT),
+ VALSTR_STRUCT(CAP_SYS_PTRACE),
+ VALSTR_STRUCT(CAP_SYS_PACCT),
+ VALSTR_STRUCT(CAP_SYS_ADMIN),
+ VALSTR_STRUCT(CAP_SYS_BOOT),
+ VALSTR_STRUCT(CAP_SYS_NICE),
+ VALSTR_STRUCT(CAP_SYS_RESOURCE),
+ VALSTR_STRUCT(CAP_SYS_TIME),
+ VALSTR_STRUCT(CAP_SYS_TTY_CONFIG),
+ VALSTR_STRUCT(CAP_MKNOD),
+ VALSTR_STRUCT(CAP_LEASE),
+ VALSTR_STRUCT(CAP_AUDIT_WRITE),
+ VALSTR_STRUCT(CAP_AUDIT_CONTROL),
+ VALSTR_STRUCT(CAP_SETFCAP),
+ VALSTR_STRUCT(CAP_MAC_OVERRIDE),
+ VALSTR_STRUCT(CAP_MAC_ADMIN),
+ VALSTR_STRUCT(CAP_SYSLOG),
+ VALSTR_STRUCT(CAP_WAKE_ALARM),
+ VALSTR_STRUCT(CAP_BLOCK_SUSPEND),
+ VALSTR_STRUCT(CAP_AUDIT_READ),
+};
+/* *INDENT-ON* */
+
+int capsNameToVal(const char *name)
+{
+ for (size_t i = 0; i < ARRAYSIZE(capNames); i++) {
+ if (strcmp(name, capNames[i].name) == 0) {
+ return capNames[i].val;
+ }
+ }
+ LOG_W("Uknown capability: '%s'", name);
+ return -1;
+}
+
+static const char *capsValToStr(int val)
+{
+ static __thread char capsStr[1024];
+ for (size_t i = 0; i < ARRAYSIZE(capNames); i++) {
+ if (val == capNames[i].val) {
+ snprintf(capsStr, sizeof(capsStr), "%s", capNames[i].name);
+ return capsStr;
+ }
+ }
+
+ snprintf(capsStr, sizeof(capsStr), "CAP_UNKNOWN(%d)", val);
+ return capsStr;
+}
+
+bool capsInitInternal(struct nsjconf_t * nsjconf, bool is_global)
+{
+ cap_t cap_orig = cap_get_pid(getpid());
+ if (cap_orig == NULL) {
+ PLOG_W("capget(PID=%d)", (int)getpid());
+ return false;
+ }
+
+ cap_t cap_new = cap_dup(cap_orig);
+ if (cap_new == NULL) {
+ PLOG_W("cap_dup()");
+ cap_free(cap_orig);
+ return false;
+ }
+
+ struct capslistt *l = is_global ? &nsjconf->global_caps : &nsjconf->local_caps;
+ if (is_global || nsjconf->keep_caps == false) {
+ if (cap_clear_flag(cap_new, CAP_INHERITABLE) == -1) {
+ PLOG_W("cap_clear_flag(CAP_INHERITABLE)");
+ cap_free(cap_orig);
+ cap_free(cap_new);
+ return false;
+ }
+ if (is_global == false) {
+ if (cap_clear_flag(cap_new, CAP_PERMITTED) == -1) {
+ PLOG_W("cap_clear_flag(CAP_PERMITTED)");
+ cap_free(cap_orig);
+ cap_free(cap_new);
+ return false;
+ }
+ if (cap_clear_flag(cap_new, CAP_EFFECTIVE) == -1) {
+ PLOG_W("cap_clear_flag(CAP_EFFECTIVE)");
+ cap_free(cap_orig);
+ cap_free(cap_new);
+ return false;
+ }
+ }
+
+ struct ints_t *p;
+ TAILQ_FOREACH(p, l, pointers) {
+ cap_flag_value_t v;
+ if (cap_get_flag(cap_orig, p->val, CAP_PERMITTED, &v) == -1) {
+ PLOG_W("cap_get_flag(cap_orig, CAP_PERMITTED, %s)",
+ capsValToStr(p->val));
+ cap_free(cap_orig);
+ cap_free(cap_new);
+ return false;
+ }
+ if (v != CAP_SET) {
+ LOG_W("Capability %s is not permitted in the %s namespace",
+ capsValToStr(p->val), is_global ? "global" : "local");
+ cap_free(cap_orig);
+ cap_free(cap_new);
+ return false;
+ }
+ if (cap_set_flag(cap_new, CAP_INHERITABLE, 1, &p->val, CAP_SET) == -1) {
+ PLOG_W("cap_set_flag(cap_new, CAP_INHERITABLE, %s)",
+ capsValToStr(p->val));
+ cap_free(cap_orig);
+ cap_free(cap_new);
+ return false;
+ }
+ if (is_global == false) {
+ if (cap_set_flag(cap_new, CAP_PERMITTED, 1, &p->val, CAP_SET) == -1) {
+ PLOG_W("cap_set_flag(cap_new, CAP_PERMITTED, %s)",
+ capsValToStr(p->val));
+ cap_free(cap_orig);
+ cap_free(cap_new);
+ return false;
+ }
+ if (cap_set_flag(cap_new, CAP_EFFECTIVE, 1, &p->val, CAP_SET) == -1) {
+ PLOG_W("cap_set_flag(cap_new, CAP_EFFECTIVE, %s)",
+ capsValToStr(p->val));
+ cap_free(cap_orig);
+ cap_free(cap_new);
+ return false;
+ }
+ }
+ }
+ }
+
+ if (is_global == false || nsjconf->keep_caps == true) {
+ for (size_t i = 0; i < ARRAYSIZE(capNames); i++) {
+ cap_flag_value_t v;
+ if (cap_get_flag(cap_orig, capNames[i].val, CAP_PERMITTED, &v) == -1) {
+ PLOG_W("cap_get_flag(cap_orig, CAP_PERMITTED, %s)",
+ capNames[i].name);
+ cap_free(cap_orig);
+ cap_free(cap_new);
+ return false;
+ }
+ if (cap_set_flag(cap_new, CAP_INHERITABLE, 1, &capNames[i].val, v) == -1) {
+ PLOG_W("cap_set_flag(cap_new, CAP_INHERITABLE, %s)",
+ capNames[i].name);
+ cap_free(cap_orig);
+ cap_free(cap_new);
+ return false;
+ }
+ }
+ }
+
+ if (cap_set_proc(cap_new) == -1) {
+ PLOG_W("cap_set_proc()");
+ cap_free(cap_orig);
+ cap_free(cap_new);
+ return false;
+ }
+#if defined(PR_CAP_AMBIENT)
+ if (is_global == false && nsjconf->keep_caps == true) {
+ for (unsigned long i = 0; i < CAP_LAST_CAP; i++) {
+ if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, i, 0UL, 0UL) == -1) {
+ PLOG_W("prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, %s)",
+ capsValToStr(i));
+ }
+ }
+ } else {
+ struct ints_t *p;
+ TAILQ_FOREACH(p, l, pointers) {
+ if (prctl
+ (PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, (unsigned long)p->val, 0UL,
+ 0UL) == -1) {
+ PLOG_W("prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, %s)",
+ capsValToStr(p->val));
+ }
+ }
+ }
+#endif /* defined(PR_CAP_AMBIENT) */
+
+ cap_free(cap_orig);
+ cap_free(cap_new);
+ return true;
+}
+
+bool capsInitGlobalNs(struct nsjconf_t * nsjconf)
+{
+ return capsInitInternal(nsjconf, true /* global */ );
+}
+
+bool capsInitLocalNs(struct nsjconf_t * nsjconf)
+{
+ return capsInitInternal(nsjconf, false /* local */ );
+}
--- /dev/null
+/*
+
+ nsjail - capability-related operations
+ -----------------------------------------
+
+ Copyright 2017 Google Inc. All Rights Reserved.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+*/
+
+#ifndef NS_CAPS_H
+#define NS_CAPS_H
+
+#include "common.h"
+
+int capsNameToVal(const char *name);
+bool capsInitGlobalNs(struct nsjconf_t *nsjconf);
+bool capsInitLocalNs(struct nsjconf_t *nsjconf);
+
+#endif /* NS_CAPS_H */
#include "common.h"
-bool cgroupInitNsFromParent(struct nsjconf_t * nsjconf, pid_t pid);
+bool cgroupInitNsFromParent(struct nsjconf_t *nsjconf, pid_t pid);
bool cgroupInitNs(void);
void cgroupFinishFromParent(struct nsjconf_t *nsjconf, pid_t pid);
#include <sys/types.h>
#include <unistd.h>
+#include "caps.h"
#include "config.h"
#include "log.h"
#include "mount.h"
{{"skip_setsid", no_argument, NULL, 0x0504}, "Don't call setsid(), allows for terminal signal handling in the sandboxed process"},
{{"pass_fd", required_argument, NULL, 0x0505}, "Don't close this FD before executing child (can be specified multiple times), by default: 0/1/2 are kept open"},
{{"disable_no_new_privs", no_argument, NULL, 0x0507}, "Don't set the prctl(NO_NEW_PRIVS, 1) (DANGEROUS)"},
+ {{"global_cap", required_argument, NULL, 0x0509}, "Retain this capability in global namespace (e.g. CAP_PTRACE). Can be specified multiple times"},
+ {{"local_cap", required_argument, NULL, 0x050A}, "Retain this capability in local namespace (e.g. CAP_PTRACE). Can be specified multiple times)"},
{{"rlimit_as", required_argument, NULL, 0x0201}, "RLIMIT_AS in MB, 'max' for RLIM_INFINITY, 'def' for the current value (default: 512)"},
{{"rlimit_core", required_argument, NULL, 0x0202}, "RLIMIT_CORE in MB, 'max' for RLIM_INFINITY, 'def' for the current value (default: 0)"},
{{"rlimit_cpu", required_argument, NULL, 0x0203}, "RLIMIT_CPU, 'max' for RLIM_INFINITY, 'def' for the current value (default: 600)"},
TAILQ_INIT(&nsjconf->envs);
TAILQ_INIT(&nsjconf->uids);
TAILQ_INIT(&nsjconf->gids);
+ TAILQ_INIT(&nsjconf->global_caps);
+ TAILQ_INIT(&nsjconf->local_caps);
static char cmdlineTmpfsSz[PATH_MAX] = "size=4194304";
- struct fds_t *f;
- f = utilMalloc(sizeof(struct fds_t));
- f->fd = STDIN_FILENO;
+ struct ints_t *f;
+ f = utilMalloc(sizeof(struct ints_t));
+ f->val = STDIN_FILENO;
TAILQ_INSERT_HEAD(&nsjconf->open_fds, f, pointers);
- f = utilMalloc(sizeof(struct fds_t));
- f->fd = STDOUT_FILENO;
+ f = utilMalloc(sizeof(struct ints_t));
+ f->val = STDOUT_FILENO;
TAILQ_INSERT_HEAD(&nsjconf->open_fds, f, pointers);
- f = utilMalloc(sizeof(struct fds_t));
- f->fd = STDERR_FILENO;
+ f = utilMalloc(sizeof(struct ints_t));
+ f->val = STDERR_FILENO;
TAILQ_INSERT_HEAD(&nsjconf->open_fds, f, pointers);
// Generate options array for getopt_long.
nsjconf->skip_setsid = true;
break;
case 0x0505:{
- struct fds_t *f;
- f = utilMalloc(sizeof(struct fds_t));
- f->fd = (int)strtol(optarg, NULL, 0);
+ struct ints_t *f;
+ f = utilMalloc(sizeof(struct ints_t));
+ f->val = (int)strtol(optarg, NULL, 0);
TAILQ_INSERT_HEAD(&nsjconf->open_fds, f, pointers);
} break;
case 0x0507:
case 0x0508:
nsjconf->max_cpus = strtoul(optarg, NULL, 0);
break;
+ case 0x509:{
+ struct ints_t *f = utilMalloc(sizeof(struct ints_t));
+ f->val = capsNameToVal(optarg);
+ if (f->val == -1) {
+ return false;
+ }
+ TAILQ_INSERT_HEAD(&nsjconf->global_caps, f, pointers);
+ }
+ break;
+ case 0x50A:{
+ struct ints_t *f = utilMalloc(sizeof(struct ints_t));
+ f->val = capsNameToVal(optarg);
+ if (f->val == -1) {
+ return false;
+ }
+ TAILQ_INSERT_HEAD(&nsjconf->local_caps, f, pointers);
+ }
+ break;
case 0x0601:
nsjconf->is_root_rw = true;
break;
TAILQ_ENTRY(idmap_t) pointers;
};
-struct fds_t {
- int fd;
- TAILQ_ENTRY(fds_t) pointers;
+struct ints_t {
+ int val;
+ TAILQ_ENTRY(ints_t) pointers;
};
enum ns_mode_t {
TAILQ_HEAD(envlist, charptr_t) envs;
TAILQ_HEAD(pidslist, pids_t) pids;
TAILQ_HEAD(mountptslist, mounts_t) mountpts;
- TAILQ_HEAD(fdslistt, fds_t) open_fds;
+ TAILQ_HEAD(fdslistt, ints_t) open_fds;
+ TAILQ_HEAD(capslistt, ints_t) global_caps, local_caps;
};
#endif /* NS_COMMON_H */
nsjconf->skip_setsid = njc->skip_setsid;
for (size_t i = 0; i < njc->n_pass_fd; i++) {
- struct fds_t *f = utilMalloc(sizeof(struct fds_t));
- f->fd = njc->pass_fd[i];
+ struct ints_t *f = utilMalloc(sizeof(struct ints_t));
+ f->val = njc->pass_fd[i];
TAILQ_INSERT_HEAD(&nsjconf->open_fds, f, pointers);
}
#include <sys/time.h>
#include <unistd.h>
+#include "caps.h"
#include "cgroup.h"
#include "cpu.h"
#include "log.h"
PLOG_W("prctl(PR_SET_NO_NEW_PRIVS, 1)");
}
}
-
- struct __user_cap_header_struct cap_hdr = {
- .version = _LINUX_CAPABILITY_VERSION_3,
- .pid = 0,
- };
- struct __user_cap_data_struct cap_data[_LINUX_CAPABILITY_U32S_3];
- if (syscall(__NR_capget, &cap_hdr, &cap_data) == -1) {
- PLOG_E("capget()");
+ if (capsInitLocalNs(nsjconf) == false) {
return false;
}
- if (nsjconf->keep_caps == true) {
- for (size_t i = 0; i < _LINUX_CAPABILITY_U32S_3; i++) {
- cap_data[i].inheritable = cap_data[i].permitted;
- cap_data[i].effective = cap_data[i].permitted;
- }
- if (syscall(__NR_capset, &cap_hdr, &cap_data) == -1) {
- PLOG_E("capset()");
- return false;
- }
-#if defined(PR_CAP_AMBIENT)
- for (unsigned long i = 0; i < 128UL; i++) {
- /*
- * Number of capabilities varies between kernels, so
- * wait for the first one which returns EINVAL
- */
- if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, i, 0UL, 0UL, 0UL) == -1
- && errno != EINVAL) {
- PLOG_W("prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, %lu)", i);
- }
- }
-#endif /* defined(PR_CAP_AMBIENT) */
- } else {
- for (unsigned long i = 0; i < 128UL; i++) {
- /*
- * Number of capabilities varies between kernels, so
- * wait for the first one which returns EINVAL
- */
- if (prctl(PR_CAPBSET_DROP, i, 0UL, 0UL, 0UL) == -1 && errno != EINVAL) {
- PLOG_W("prctl(PR_CAPBSET_DROP, %lu", i);
- }
- }
- if (prctl(PR_SET_KEEPCAPS, 0, 0, 0, 0) == -1) {
- PLOG_E("prctl(PR_SET_KEEPCAPS, 0)");
- return false;
- }
-
- memset(&cap_data, '\0', sizeof(cap_data));
- if (syscall(__NR_capset, &cap_hdr, &cap_data) == -1) {
- PLOG_E("capset()");
- return false;
- }
- }
return true;
}
static bool containPassFd(struct nsjconf_t *nsjconf, int fd)
{
- struct fds_t *p;
+ struct ints_t *p;
TAILQ_FOREACH(p, &nsjconf->open_fds, pointers) {
- if (p->fd == fd) {
+ if (p->val == fd) {
return true;
}
}
#include <sys/time.h>
#include <unistd.h>
+#include "caps.h"
#include "cmdline.h"
#include "log.h"
#include "net.h"
{
struct nsjconf_t nsjconf;
if (!cmdlineParse(argc, argv, &nsjconf)) {
+ LOG_E("Couldn't parse cmdline options");
exit(1);
}
if (nsjconf.clone_newuser == false && geteuid() != 0) {
if (nsjailSetTimer(&nsjconf) == false) {
exit(1);
}
+ if (capsInitGlobalNs(&nsjconf) == false) {
+ LOG_E("Couldn't initialize global capabilities");
+ exit(1);
+ }
if (nsjconf.mode == MODE_LISTEN_TCP) {
nsjailListenMode(&nsjconf);