From 7ea554a3cfabfd04581efc044d9c158a8169f4c1 Mon Sep 17 00:00:00 2001 From: Jonathan Calmels Date: Fri, 8 Jun 2018 17:42:32 -0700 Subject: [PATCH] Rework capabilities to support more unprivileged use-cases --- src/cli/configure.c | 24 +++++++++----------- src/cli/info.c | 23 ++++++-------------- src/cli/list.c | 23 ++++++-------------- src/nvc.c | 11 ++++++++-- src/nvc_internal.h | 53 +++++++++++++++++++++++++-------------------- src/nvc_ldcache.c | 8 +++---- src/utils.c | 27 ++++++++++++----------- src/utils.h | 2 +- 8 files changed, 81 insertions(+), 90 deletions(-) diff --git a/src/cli/configure.c b/src/cli/configure.c index 75242bf..d64f950 100644 --- a/src/cli/configure.c +++ b/src/cli/configure.c @@ -173,20 +173,16 @@ configure_command(const struct context *ctx) struct error err = {0}; int rv = EXIT_FAILURE; - if (geteuid() != 0) { - warnx("requires root privileges"); - return (rv); - } - if (perm_set_capabilities(&err, CAP_PERMITTED, permitted_caps, nitems(permitted_caps)) < 0 || - perm_set_capabilities(&err, CAP_INHERITABLE, inherited_caps, nitems(inherited_caps)) < 0 || - perm_drop_bounds(&err) < 0) { + if (perm_set_capabilities(&err, CAP_PERMITTED, pcaps, nitems(pcaps)) < 0 || + perm_set_capabilities(&err, CAP_INHERITABLE, NULL, 0) < 0 || + perm_set_bounds(&err, bcaps, nitems(bcaps)) < 0) { warnx("permission error: %s", err.msg); return (rv); } /* Initialize the library and container contexts. */ - int c = ctx->load_kmods ? CAPS_INIT_KMODS : CAPS_INIT; - if (perm_set_capabilities(&err, CAP_EFFECTIVE, effective_caps[c], effective_caps_size(c)) < 0) { + int c = ctx->load_kmods ? NVC_INIT_KMODS : NVC_INIT; + if (perm_set_capabilities(&err, CAP_EFFECTIVE, ecaps[c], ecaps_size(c)) < 0) { warnx("permission error: %s", err.msg); goto fail; } @@ -204,7 +200,7 @@ configure_command(const struct context *ctx) warnx("initialization error: %s", nvc_error(nvc)); goto fail; } - if (perm_set_capabilities(&err, CAP_EFFECTIVE, effective_caps[CAPS_CONTAINER], effective_caps_size(CAPS_CONTAINER)) < 0) { + if (perm_set_capabilities(&err, CAP_EFFECTIVE, ecaps[NVC_CONTAINER], ecaps_size(NVC_CONTAINER)) < 0) { warnx("permission error: %s", err.msg); goto fail; } @@ -215,7 +211,7 @@ configure_command(const struct context *ctx) } /* Query the driver and device information. */ - if (perm_set_capabilities(&err, CAP_EFFECTIVE, effective_caps[CAPS_INFO], effective_caps_size(CAPS_INFO)) < 0) { + if (perm_set_capabilities(&err, CAP_EFFECTIVE, ecaps[NVC_INFO], ecaps_size(NVC_INFO)) < 0) { warnx("permission error: %s", err.msg); goto fail; } @@ -263,7 +259,7 @@ configure_command(const struct context *ctx) } /* Mount the driver and visible devices. */ - if (perm_set_capabilities(&err, CAP_EFFECTIVE, effective_caps[CAPS_MOUNT], effective_caps_size(CAPS_MOUNT)) < 0) { + if (perm_set_capabilities(&err, CAP_EFFECTIVE, ecaps[NVC_MOUNT], ecaps_size(NVC_MOUNT)) < 0) { warnx("permission error: %s", err.msg); goto fail; } @@ -279,7 +275,7 @@ configure_command(const struct context *ctx) } /* Update the container ldcache. */ - if (perm_set_capabilities(&err, CAP_EFFECTIVE, effective_caps[CAPS_LDCACHE], effective_caps_size(CAPS_LDCACHE)) < 0) { + if (perm_set_capabilities(&err, CAP_EFFECTIVE, ecaps[NVC_LDCACHE], ecaps_size(NVC_LDCACHE)) < 0) { warnx("permission error: %s", err.msg); goto fail; } @@ -288,7 +284,7 @@ configure_command(const struct context *ctx) goto fail; } - if (perm_set_capabilities(&err, CAP_EFFECTIVE, effective_caps[CAPS_SHUTDOWN], effective_caps_size(CAPS_SHUTDOWN)) < 0) { + if (perm_set_capabilities(&err, CAP_EFFECTIVE, ecaps[NVC_SHUTDOWN], ecaps_size(NVC_SHUTDOWN)) < 0) { warnx("permission error: %s", err.msg); goto fail; } diff --git a/src/cli/info.c b/src/cli/info.c index b73e073..35d3b6c 100644 --- a/src/cli/info.c +++ b/src/cli/info.c @@ -53,27 +53,18 @@ info_command(const struct context *ctx) int rv = EXIT_FAILURE; run_as_root = (geteuid() == 0); - if (!run_as_root && (ctx->load_kmods || ctx->root != NULL)) { - warnx("requires root privileges"); - return (rv); - } if (run_as_root) { - if (perm_set_capabilities(&err, CAP_PERMITTED, permitted_caps, nitems(permitted_caps)) < 0 || - perm_set_capabilities(&err, CAP_INHERITABLE, inherited_caps, nitems(inherited_caps)) < 0 || - perm_drop_bounds(&err) < 0) { - warnx("permission error: %s", err.msg); - return (rv); - } - } else { - if (perm_set_capabilities(&err, CAP_PERMITTED, NULL, 0) < 0) { + if (perm_set_capabilities(&err, CAP_PERMITTED, pcaps, nitems(pcaps)) < 0 || + perm_set_capabilities(&err, CAP_INHERITABLE, NULL, 0) < 0 || + perm_set_bounds(&err, bcaps, nitems(bcaps)) < 0) { warnx("permission error: %s", err.msg); return (rv); } } /* Initialize the library context. */ - int c = ctx->load_kmods ? CAPS_INIT_KMODS : CAPS_INIT; - if (run_as_root && perm_set_capabilities(&err, CAP_EFFECTIVE, effective_caps[c], effective_caps_size(c)) < 0) { + int c = ctx->load_kmods ? NVC_INIT_KMODS : NVC_INIT; + if (run_as_root && perm_set_capabilities(&err, CAP_EFFECTIVE, ecaps[c], ecaps_size(c)) < 0) { warnx("permission error: %s", err.msg); goto fail; } @@ -92,7 +83,7 @@ info_command(const struct context *ctx) } /* Query the driver and device information. */ - if (run_as_root && perm_set_capabilities(&err, CAP_EFFECTIVE, effective_caps[CAPS_INFO], effective_caps_size(CAPS_INFO)) < 0) { + if (run_as_root && perm_set_capabilities(&err, CAP_EFFECTIVE, ecaps[NVC_INFO], ecaps_size(NVC_INFO)) < 0) { warnx("permission error: %s", err.msg); goto fail; } @@ -116,7 +107,7 @@ info_command(const struct context *ctx) "Bus Location:", dev->gpus[i].busid, "Architecture:", dev->gpus[i].arch); } - if (run_as_root && perm_set_capabilities(&err, CAP_EFFECTIVE, effective_caps[CAPS_SHUTDOWN], effective_caps_size(CAPS_SHUTDOWN)) < 0) { + if (run_as_root && perm_set_capabilities(&err, CAP_EFFECTIVE, ecaps[NVC_SHUTDOWN], ecaps_size(NVC_SHUTDOWN)) < 0) { warnx("permission error: %s", err.msg); goto fail; } diff --git a/src/cli/list.c b/src/cli/list.c index c328c02..04872d2 100644 --- a/src/cli/list.c +++ b/src/cli/list.c @@ -84,27 +84,18 @@ list_command(const struct context *ctx) int rv = EXIT_FAILURE; run_as_root = (geteuid() == 0); - if (!run_as_root && (ctx->load_kmods || ctx->root != NULL)) { - warnx("requires root privileges"); - return (rv); - } if (run_as_root) { - if (perm_set_capabilities(&err, CAP_PERMITTED, permitted_caps, nitems(permitted_caps)) < 0 || - perm_set_capabilities(&err, CAP_INHERITABLE, inherited_caps, nitems(inherited_caps)) < 0 || - perm_drop_bounds(&err) < 0) { - warnx("permission error: %s", err.msg); - return (rv); - } - } else { - if (perm_set_capabilities(&err, CAP_PERMITTED, NULL, 0) < 0) { + if (perm_set_capabilities(&err, CAP_PERMITTED, pcaps, nitems(pcaps)) < 0 || + perm_set_capabilities(&err, CAP_INHERITABLE, NULL, 0) < 0 || + perm_set_bounds(&err, bcaps, nitems(bcaps)) < 0) { warnx("permission error: %s", err.msg); return (rv); } } /* Initialize the library context. */ - int c = ctx->load_kmods ? CAPS_INIT_KMODS : CAPS_INIT; - if (run_as_root && perm_set_capabilities(&err, CAP_EFFECTIVE, effective_caps[c], effective_caps_size(c)) < 0) { + int c = ctx->load_kmods ? NVC_INIT_KMODS : NVC_INIT; + if (run_as_root && perm_set_capabilities(&err, CAP_EFFECTIVE, ecaps[c], ecaps_size(c)) < 0) { warnx("permission error: %s", err.msg); goto fail; } @@ -123,7 +114,7 @@ list_command(const struct context *ctx) } /* Query the driver and device information. */ - if (run_as_root && perm_set_capabilities(&err, CAP_EFFECTIVE, effective_caps[CAPS_INFO], effective_caps_size(CAPS_INFO)) < 0) { + if (run_as_root && perm_set_capabilities(&err, CAP_EFFECTIVE, ecaps[NVC_INFO], ecaps_size(NVC_INFO)) < 0) { warnx("permission error: %s", err.msg); goto fail; } @@ -169,7 +160,7 @@ list_command(const struct context *ctx) printf("%s\n", drv->ipcs[i]); } - if (run_as_root && perm_set_capabilities(&err, CAP_EFFECTIVE, effective_caps[CAPS_SHUTDOWN], effective_caps_size(CAPS_SHUTDOWN)) < 0) { + if (run_as_root && perm_set_capabilities(&err, CAP_EFFECTIVE, ecaps[NVC_SHUTDOWN], ecaps_size(NVC_SHUTDOWN)) < 0) { warnx("permission error: %s", err.msg); goto fail; } diff --git a/src/nvc.c b/src/nvc.c index b54c8e9..0a5ec79 100644 --- a/src/nvc.c +++ b/src/nvc.c @@ -175,8 +175,15 @@ load_kernel_modules(struct error *err, const char *root) return (-1); } if (pid == 0) { - if (chroot(root) < 0 || chdir("/") < 0) { - log_errf("failed to change root directory: %s", strerror(errno)); + if (!str_equal(root, "/")) { + if (chroot(root) < 0 || chdir("/") < 0) { + log_errf("failed to change root directory: %s", strerror(errno)); + log_warn("skipping kernel modules load due to failure"); + _exit(EXIT_FAILURE); + } + } + if (perm_set_capabilities(NULL, CAP_INHERITABLE, &(cap_value_t){CAP_SYS_MODULE}, 1) < 0) { + log_warn("failed to set inheritable capabilities"); log_warn("skipping kernel modules load due to failure"); _exit(EXIT_FAILURE); } diff --git a/src/nvc_internal.h b/src/nvc_internal.h index 07b1684..40c6af8 100644 --- a/src/nvc_internal.h +++ b/src/nvc_internal.h @@ -54,18 +54,18 @@ struct nvc_container { }; enum { - CAPS_INIT, - CAPS_INIT_KMODS, - CAPS_SHUTDOWN, - CAPS_CONTAINER, - CAPS_INFO, - CAPS_MOUNT, - CAPS_LDCACHE, + NVC_INIT, + NVC_INIT_KMODS, + NVC_SHUTDOWN, + NVC_CONTAINER, + NVC_INFO, + NVC_MOUNT, + NVC_LDCACHE, }; -static const cap_value_t permitted_caps[] = { +static const cap_value_t pcaps[] = { CAP_CHOWN, /* kmods */ - CAP_DAC_OVERRIDE, /* rhel, cgroups */ + CAP_DAC_OVERRIDE, /* rhel userns, cgroups */ CAP_DAC_READ_SEARCH, /* userns */ CAP_FOWNER, /* kmods */ CAP_KILL, /* privsep */ @@ -78,30 +78,37 @@ static const cap_value_t permitted_caps[] = { CAP_SYS_PTRACE, /* procns */ }; -static const cap_value_t effective_caps[][nitems(permitted_caps) + 1] = { - [CAPS_INIT] = {CAP_KILL, CAP_SETGID, CAP_SETUID, CAP_SYS_CHROOT, -1}, - [CAPS_INIT_KMODS] = {CAP_KILL, CAP_CHOWN, CAP_FOWNER, CAP_MKNOD, CAP_SETGID, CAP_SETUID, CAP_SYS_CHROOT, -1}, - [CAPS_SHUTDOWN] = {CAP_KILL, -1}, - [CAPS_CONTAINER] = {CAP_KILL, CAP_DAC_READ_SEARCH, CAP_SYS_PTRACE, -1}, - [CAPS_INFO] = {CAP_KILL, -1}, - [CAPS_MOUNT] = {CAP_KILL, CAP_DAC_READ_SEARCH, CAP_SETGID, CAP_SETUID, CAP_SYS_ADMIN, - CAP_SYS_CHROOT, CAP_SYS_PTRACE, -1}, - [CAPS_LDCACHE] = {CAP_KILL, CAP_DAC_READ_SEARCH, CAP_SETGID, CAP_SETPCAP, CAP_SETUID, - CAP_SYS_ADMIN, CAP_SYS_CHROOT, CAP_SYS_PTRACE, -1}, +static const cap_value_t ecaps[][nitems(pcaps) + 1] = { + [NVC_INIT] = {CAP_KILL, CAP_SETUID, CAP_SETGID, CAP_SYS_CHROOT, -1}, + + [NVC_INIT_KMODS] = {CAP_KILL, CAP_SETUID, CAP_SETGID, CAP_SYS_CHROOT, + CAP_CHOWN, CAP_FOWNER, CAP_MKNOD, CAP_SETPCAP, -1}, + + [NVC_SHUTDOWN] = {CAP_KILL, -1}, + + [NVC_CONTAINER] = {CAP_KILL, CAP_DAC_READ_SEARCH, CAP_SYS_PTRACE, -1}, + + [NVC_INFO] = {CAP_KILL, -1}, + + [NVC_MOUNT] = {CAP_KILL, CAP_SETUID, CAP_SETGID, CAP_SYS_CHROOT, + CAP_SYS_ADMIN, CAP_DAC_READ_SEARCH, CAP_SYS_PTRACE, CAP_DAC_OVERRIDE, -1}, + + [NVC_LDCACHE] = {CAP_KILL, CAP_SETUID, CAP_SETGID, CAP_SYS_CHROOT, + CAP_SYS_ADMIN, CAP_DAC_READ_SEARCH, CAP_SYS_PTRACE, CAP_SETPCAP, -1}, }; -static const cap_value_t inherited_caps[] = { +static const cap_value_t bcaps[] = { CAP_DAC_OVERRIDE, CAP_SYS_MODULE, }; static inline size_t -effective_caps_size(int idx) +ecaps_size(int idx) { size_t i; - for (i = 0; i < nitems(*effective_caps); ++i) { - if (effective_caps[idx][i] == -1) + for (i = 0; i < nitems(*ecaps); ++i) { + if (ecaps[idx][i] == -1) break; } return (i); diff --git a/src/nvc_ldcache.c b/src/nvc_ldcache.c index 1ee7851..16a69bd 100644 --- a/src/nvc_ldcache.c +++ b/src/nvc_ldcache.c @@ -153,8 +153,6 @@ change_rootfs(struct error *err, const char *rootfs, bool mount_proc, bool *drop static int adjust_capabilities(struct error *err, uid_t uid, bool host_ldconfig) { - cap_value_t cap = CAP_DAC_OVERRIDE; - /* * Drop all the inheritable capabilities and the ambient capabilities consequently. * Don't bother with the other capabilities, execve will take care of it. @@ -168,13 +166,13 @@ adjust_capabilities(struct error *err, uid_t uid, bool host_ldconfig) * If allowed, set the CAP_DAC_OVERRIDE capability because some distributions rely on it * (e.g. https://bugzilla.redhat.com/show_bug.cgi?id=517575). */ - if (perm_set_capabilities(err, CAP_INHERITABLE, &cap, 1) < 0) { + if (perm_set_capabilities(err, CAP_INHERITABLE, &(cap_value_t){CAP_DAC_OVERRIDE}, 1) < 0) { if (err->code != EPERM) return (-1); if (perm_set_capabilities(err, CAP_INHERITABLE, NULL, 0) < 0) return (-1); log_warn("could not set inheritable capabilities, containers may require additional tuning"); - } else if (uid != 0 && perm_set_capabilities(err, CAP_AMBIENT, &cap, 1) < 0) { + } else if (uid != 0 && perm_set_capabilities(err, CAP_AMBIENT, &(cap_value_t){CAP_DAC_OVERRIDE}, 1) < 0) { if (err->code != EPERM) return (-1); log_warn("could not set ambient capabilities, containers may require additional tuning"); @@ -182,7 +180,7 @@ adjust_capabilities(struct error *err, uid_t uid, bool host_ldconfig) } /* Drop all the bounding set */ - if (perm_drop_bounds(err) < 0) + if (perm_set_bounds(err, NULL, 0) < 0) return (-1); return (0); diff --git a/src/utils.c b/src/utils.c index b3bbab0..b738447 100644 --- a/src/utils.c +++ b/src/utils.c @@ -918,7 +918,7 @@ perm_drop_privileges(struct error *err, uid_t uid, gid_t gid, bool drop_groups) } int -perm_drop_bounds(struct error *err) +perm_set_bounds(struct error *err, const cap_value_t caps[], size_t size) { uint32_t n; cap_value_t last_cap = CAP_LAST_CAP; @@ -929,10 +929,15 @@ perm_drop_bounds(struct error *err) last_cap = (cap_value_t)n; for (cap_value_t c = 0; c <= last_cap; ++c) { + for (size_t i = 0; caps != NULL && i < size; ++i) { + if (caps[i] == c) + goto next; + } if (prctl(PR_CAPBSET_READ, c) > 0 && prctl(PR_CAPBSET_DROP, c) < 0) { error_set(err, "capability change failed"); return (-1); } + next:; } return (0); } @@ -949,11 +954,9 @@ perm_set_capabilities(struct error *err, cap_flag_t type, const cap_value_t caps /* Ambient capabilities are only supported since Linux 4.3 and are not available in libcap. */ if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_CLEAR_ALL, 0, 0, 0) < 0 && errno != EINVAL) goto fail; - if (caps != NULL && size > 0) { - for (size_t i = 0; i < size; ++i) { - if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, caps[i], 0, 0) < 0 && errno != EINVAL) - goto fail; - } + for (size_t i = 0; caps != NULL && i < size; ++i) { + if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, caps[i], 0, 0) < 0 && errno != EINVAL) + goto fail; } return (0); } @@ -971,13 +974,11 @@ perm_set_capabilities(struct error *err, cap_flag_t type, const cap_value_t caps goto fail; if (cap_clear_flag(state, CAP_EFFECTIVE) < 0) goto fail; - if (caps != NULL && size > 0) { - for (size_t i = 0; i < size; ++i) { - if (cap_get_flag(tmp, caps[i], CAP_EFFECTIVE, &flag) < 0) - goto fail; - if (cap_set_flag(state, CAP_EFFECTIVE, 1, &caps[i], flag) < 0) - goto fail; - } + for (size_t i = 0; caps != NULL && i < size; ++i) { + if (cap_get_flag(tmp, caps[i], CAP_EFFECTIVE, &flag) < 0) + goto fail; + if (cap_set_flag(state, CAP_EFFECTIVE, 1, &caps[i], flag) < 0) + goto fail; } } if (cap_set_proc(state) < 0) diff --git a/src/utils.h b/src/utils.h index dfed55b..4866e5d 100644 --- a/src/utils.h +++ b/src/utils.h @@ -81,7 +81,7 @@ int path_resolve(struct error *, char *, const char *, const char *); int path_resolve_full(struct error *, char *, const char *, const char *); int perm_drop_privileges(struct error *, uid_t, gid_t, bool); -int perm_drop_bounds(struct error *); +int perm_set_bounds(struct error *, const cap_value_t [], size_t); int perm_set_capabilities(struct error *, cap_flag_t, const cap_value_t [], size_t); #endif /* HEADER_UTILS_H */ -- GitLab