From 611ddd345e84ee67096eefac4a1529299f97a8d6 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Tue, 15 May 2018 21:33:48 +0200 Subject: [PATCH] capabilities: raise ambient capabilities Signed-off-by: Christian Brauner Suggested-by: Jonathan Calmels --- src/lxc/caps.c | 130 ++++++++++++++++++++++++++++++++++++++++++++++++ src/lxc/caps.h | 89 ++++++++++++++++++++------------- src/lxc/start.c | 14 +++++- 3 files changed, 198 insertions(+), 35 deletions(-) diff --git a/src/lxc/caps.c b/src/lxc/caps.c index 2d3261da0..6665af217 100644 --- a/src/lxc/caps.c +++ b/src/lxc/caps.c @@ -42,6 +42,27 @@ lxc_log_define(lxc_caps, lxc); #define PR_CAPBSET_READ 23 #endif +/* Control the ambient capability set */ +#ifndef PR_CAP_AMBIENT +#define PR_CAP_AMBIENT 47 +#endif + +#ifndef PR_CAP_AMBIENT_IS_SET +#define PR_CAP_AMBIENT_IS_SET 1 +#endif + +#ifndef PR_CAP_AMBIENT_RAISE +#define PR_CAP_AMBIENT_RAISE 2 +#endif + +#ifndef PR_CAP_AMBIENT_LOWER +#define PR_CAP_AMBIENT_LOWER 3 +#endif + +#ifndef PR_CAP_AMBIENT_CLEAR_ALL +#define PR_CAP_AMBIENT_CLEAR_ALL 4 +#endif + int lxc_caps_down(void) { cap_t caps; @@ -126,6 +147,115 @@ out: return 0; } +int lxc_ambient_caps_up(void) +{ + int ret; + cap_t caps; + cap_value_t cap; + int last_cap = CAP_LAST_CAP; + char *cap_names = NULL; + + /* When we are run as root, we don't want to play with the capabilities. */ + if (!getuid()) + return 0; + + caps = cap_get_proc(); + if (!caps) { + SYSERROR("Failed to retrieve capabilities"); + return -1; + } + + for (cap = 0; cap <= CAP_LAST_CAP; cap++) { + cap_flag_value_t flag; + + ret = cap_get_flag(caps, cap, CAP_PERMITTED, &flag); + if (ret < 0) { + if (errno == EINVAL) { + last_cap = (cap - 1); + INFO("Last supported cap was %d", last_cap); + break; + } + + SYSERROR("Failed to retrieve capability flag"); + goto out; + } + + ret = cap_set_flag(caps, CAP_INHERITABLE, 1, &cap, flag); + if (ret < 0) { + SYSERROR("Failed to set capability flag"); + goto out; + } + } + + ret = cap_set_proc(caps); + if (ret < 0) { + SYSERROR("Failed to set capabilities"); + goto out; + } + + for (cap = 0; cap <= last_cap; cap++) { + ret = prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, cap, 0, 0); + if (ret < 0) { + WARN("%s - Failed to raise ambient capability %d", + strerror(errno), cap); + goto out; + } + } + + cap_names = cap_to_text(caps, NULL); + if (!cap_names) + goto out; + + TRACE("Raised %s in inheritable and ambient capability set", cap_names); + +out: + + cap_free(cap_names); + cap_free(caps); + return 0; +} + +int lxc_ambient_caps_down(void) +{ + int ret; + cap_t caps; + cap_value_t cap; + + /* When we are run as root, we don't want to play with the capabilities. */ + if (!getuid()) + return 0; + + ret = prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_CLEAR_ALL, 0, 0, 0); + if (ret < 0) { + SYSERROR("Failed to clear ambient capability set"); + return -1; + } + + caps = cap_get_proc(); + if (!caps) { + SYSERROR("Failed to retrieve capabilities"); + return -1; + } + + for (cap = 0; cap <= CAP_LAST_CAP; cap++) { + ret = cap_set_flag(caps, CAP_INHERITABLE, 1, &cap, CAP_CLEAR); + if (ret < 0) { + SYSERROR("Failed to remove capability from inheritable set"); + goto out; + } + } + + ret = cap_set_proc(caps); + if (ret < 0) { + SYSERROR("Failed to set capabilities"); + goto out; + } + +out: + cap_free(caps); + return 0; +} + int lxc_caps_init(void) { uid_t uid = getuid(); diff --git a/src/lxc/caps.h b/src/lxc/caps.h index 2b3b1a502..14c498ae7 100644 --- a/src/lxc/caps.h +++ b/src/lxc/caps.h @@ -27,70 +27,91 @@ #include "config.h" #include - #if HAVE_LIBCAP #include /* workaround for libcap < 2.17 bug */ #include extern int lxc_caps_down(void); extern int lxc_caps_up(void); +extern int lxc_ambient_caps_up(void); +extern int lxc_ambient_caps_down(void); extern int lxc_caps_init(void); - extern int lxc_caps_last_cap(void); - extern bool lxc_proc_cap_is_set(cap_value_t cap, cap_flag_t flag); -extern bool lxc_file_cap_is_set(const char *path, cap_value_t cap, cap_flag_t flag); +extern bool lxc_file_cap_is_set(const char *path, cap_value_t cap, + cap_flag_t flag); #else -static inline int lxc_caps_down(void) { +static inline int lxc_caps_down(void) +{ return 0; } -static inline int lxc_caps_up(void) { + +static inline int lxc_caps_up(void) +{ return 0; } -static inline int lxc_caps_init(void) { + +static inline int lxc_ambient_caps_up(void) +{ + return 0; +} + +static inline int lxc_ambient_caps_down(void) +{ + return 0; +} + +static inline int lxc_caps_init(void) +{ return 0; } -static inline int lxc_caps_last_cap(void) { +static inline int lxc_caps_last_cap(void) +{ return 0; } typedef int cap_value_t; typedef int cap_flag_t; -static inline bool lxc_proc_cap_is_set(cap_value_t cap, cap_flag_t flag) { +static inline bool lxc_proc_cap_is_set(cap_value_t cap, cap_flag_t flag) +{ return false; } -static inline bool lxc_file_cap_is_set(const char *path, cap_value_t cap, cap_flag_t flag) { +static inline bool lxc_file_cap_is_set(const char *path, cap_value_t cap, + cap_flag_t flag) +{ return false; } #endif -#define lxc_priv(__lxc_function) \ - ({ \ - __label__ out; \ - int __ret, __ret2, ___errno = 0; \ - __ret = lxc_caps_up(); \ - if (__ret) \ - goto out; \ - __ret = __lxc_function; \ - if (__ret) \ - ___errno = errno; \ - __ret2 = lxc_caps_down(); \ - out: __ret ? errno = ___errno,__ret : __ret2; \ +#define lxc_priv(__lxc_function) \ + ({ \ + __label__ out; \ + int __ret, __ret2, ___errno = 0; \ + __ret = lxc_caps_up(); \ + if (__ret) \ + goto out; \ + __ret = __lxc_function; \ + if (__ret) \ + ___errno = errno; \ + __ret2 = lxc_caps_down(); \ + out: \ + __ret ? errno = ___errno, __ret : __ret2; \ }) -#define lxc_unpriv(__lxc_function) \ - ({ \ - __label__ out; \ - int __ret, __ret2, ___errno = 0; \ - __ret = lxc_caps_down(); \ - if (__ret) \ - goto out; \ - __ret = __lxc_function; \ - if (__ret) \ - ___errno = errno; \ - __ret2 = lxc_caps_up(); \ - out: __ret ? errno = ___errno,__ret : __ret2; \ +#define lxc_unpriv(__lxc_function) \ + ({ \ + __label__ out; \ + int __ret, __ret2, ___errno = 0; \ + __ret = lxc_caps_down(); \ + if (__ret) \ + goto out; \ + __ret = __lxc_function; \ + if (__ret) \ + ___errno = errno; \ + __ret2 = lxc_caps_up(); \ + out: \ + __ret ? errno = ___errno, __ret : __ret2; \ }) #endif diff --git a/src/lxc/start.c b/src/lxc/start.c index e9b0efe5f..b8404f99e 100644 --- a/src/lxc/start.c +++ b/src/lxc/start.c @@ -1049,6 +1049,12 @@ static int do_start(void *data) goto out_warn_father; } + ret = lxc_ambient_caps_up(); + if (ret < 0) { + SYSERROR("Failed to raise ambient capabilities"); + goto out_warn_father; + } + ret = sigprocmask(SIG_SETMASK, &handler->oldmask, NULL); if (ret < 0) { SYSERROR("Failed to set signal mask"); @@ -1081,7 +1087,7 @@ static int do_start(void *data) */ ret = lxc_sync_barrier_parent(handler, LXC_SYNC_CONFIGURE); if (ret < 0) - return -1; + goto out_error; ret = lxc_network_recv_veth_names_from_parent(handler); if (ret < 0) { @@ -1348,6 +1354,12 @@ static int do_start(void *data) if (ret < 0) goto out_warn_father; + ret = lxc_ambient_caps_down(); + if (ret < 0) { + SYSERROR("Failed to clear ambient capabilities"); + goto out_warn_father; + } + /* After this call, we are in error because this ops should not return * as it execs. */ -- 2.39.2