#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;
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();
#include "config.h"
#include <stdbool.h>
-
#if HAVE_LIBCAP
#include <linux/types.h> /* workaround for libcap < 2.17 bug */
#include <sys/capability.h>
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
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");
*/
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) {
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.
*/