Unverified Commit b22ae843 by Stéphane Graber Committed by GitHub

Merge pull request #3646 from brauner/2021-02-02/fixes

attach & cgroup hardening
parents c7d64498 ac01a9b8
...@@ -229,7 +229,7 @@ static int userns_setup_ids(struct attach_context *ctx, ...@@ -229,7 +229,7 @@ static int userns_setup_ids(struct attach_context *ctx,
if (!(options->namespaces & CLONE_NEWUSER)) if (!(options->namespaces & CLONE_NEWUSER))
return 0; return 0;
f_uidmap = fdopen_at(ctx->dfd_init_pid, "uid_map", "re", PROTECT_OPEN, PROTECT_LOOKUP_ABSOLUTE); f_uidmap = fdopen_at(ctx->dfd_init_pid, "uid_map", "re", PROTECT_OPEN, PROTECT_LOOKUP_BENEATH);
if (!f_uidmap) if (!f_uidmap)
return log_error_errno(-errno, errno, "Failed to open uid_map"); return log_error_errno(-errno, errno, "Failed to open uid_map");
...@@ -249,7 +249,7 @@ static int userns_setup_ids(struct attach_context *ctx, ...@@ -249,7 +249,7 @@ static int userns_setup_ids(struct attach_context *ctx,
} }
} }
f_gidmap = fdopen_at(ctx->dfd_init_pid, "gid_map", "re", PROTECT_OPEN, PROTECT_LOOKUP_ABSOLUTE); f_gidmap = fdopen_at(ctx->dfd_init_pid, "gid_map", "re", PROTECT_OPEN, PROTECT_LOOKUP_BENEATH);
if (!f_gidmap) if (!f_gidmap)
return log_error_errno(-errno, errno, "Failed to open gid_map"); return log_error_errno(-errno, errno, "Failed to open gid_map");
...@@ -314,7 +314,7 @@ static int parse_init_status(struct attach_context *ctx, lxc_attach_options_t *o ...@@ -314,7 +314,7 @@ static int parse_init_status(struct attach_context *ctx, lxc_attach_options_t *o
bool caps_found = false; bool caps_found = false;
int ret; int ret;
f = fdopen_at(ctx->dfd_init_pid, "status", "re", PROTECT_OPEN, PROTECT_LOOKUP_ABSOLUTE); f = fdopen_at(ctx->dfd_init_pid, "status", "re", PROTECT_OPEN, PROTECT_LOOKUP_BENEATH);
if (!f) if (!f)
return log_error_errno(-errno, errno, "Failed to open status file"); return log_error_errno(-errno, errno, "Failed to open status file");
...@@ -572,6 +572,8 @@ static void put_attach_context(struct attach_context *ctx) ...@@ -572,6 +572,8 @@ static void put_attach_context(struct attach_context *ctx)
static int attach_context_container(struct attach_context *ctx) static int attach_context_container(struct attach_context *ctx)
{ {
int fret = 0;
for (int i = 0; i < LXC_NS_MAX; i++) { for (int i = 0; i < LXC_NS_MAX; i++) {
int ret; int ret;
...@@ -579,16 +581,19 @@ static int attach_context_container(struct attach_context *ctx) ...@@ -579,16 +581,19 @@ static int attach_context_container(struct attach_context *ctx)
continue; continue;
ret = setns(ctx->ns_fd[i], ns_info[i].clone_flag); ret = setns(ctx->ns_fd[i], ns_info[i].clone_flag);
if (ret < 0) if (ret)
return log_error_errno(-1, errno, return log_error_errno(-errno, errno, "Failed to attach to %s namespace of %d", ns_info[i].proc_name, ctx->init_pid);
"Failed to attach to %s namespace of %d",
ns_info[i].proc_name, ctx->init_pid);
DEBUG("Attached to %s namespace of %d", DEBUG("Attached to %s namespace of %d", ns_info[i].proc_name, ctx->init_pid);
ns_info[i].proc_name, ctx->init_pid);
if (close(ctx->ns_fd[i])) {
fret = -errno;
SYSERROR("Failed to close file descriptor for %s namespace", ns_info[i].proc_name);
}
ctx->ns_fd[i] = -EBADF;
} }
return 0; return fret;
} }
/* /*
...@@ -1125,18 +1130,6 @@ __noreturn static void do_attach(struct attach_payload *ap) ...@@ -1125,18 +1130,6 @@ __noreturn static void do_attach(struct attach_payload *ap)
TRACE("Set PR_SET_NO_NEW_PRIVS"); TRACE("Set PR_SET_NO_NEW_PRIVS");
} }
if (conf->seccomp.seccomp) {
ret = lxc_seccomp_load(conf);
if (ret < 0)
goto on_error;
TRACE("Loaded seccomp profile");
ret = lxc_seccomp_send_notifier_fd(&conf->seccomp, ap->ipc_socket);
if (ret < 0)
goto on_error;
}
/* The following is done after the communication socket is shut down. /* The following is done after the communication socket is shut down.
* That way, all errors that might (though unlikely) occur up until this * That way, all errors that might (though unlikely) occur up until this
* point will have their messages printed to the original stderr (if * point will have their messages printed to the original stderr (if
...@@ -1205,6 +1198,18 @@ __noreturn static void do_attach(struct attach_payload *ap) ...@@ -1205,6 +1198,18 @@ __noreturn static void do_attach(struct attach_payload *ap)
if (ret) if (ret)
INFO("Failed to adjust stdio permissions"); INFO("Failed to adjust stdio permissions");
if (conf->seccomp.seccomp) {
ret = lxc_seccomp_load(conf);
if (ret < 0)
goto on_error;
TRACE("Loaded seccomp profile");
ret = lxc_seccomp_send_notifier_fd(&conf->seccomp, ap->ipc_socket);
if (ret < 0)
goto on_error;
}
if (!lxc_switch_uid_gid(ctx->target_ns_uid, ctx->target_ns_gid)) if (!lxc_switch_uid_gid(ctx->target_ns_uid, ctx->target_ns_gid))
goto on_error; goto on_error;
...@@ -1436,9 +1441,6 @@ int lxc_attach(struct lxc_container *container, lxc_attach_exec_t exec_function, ...@@ -1436,9 +1441,6 @@ int lxc_attach(struct lxc_container *container, lxc_attach_exec_t exec_function,
_exit(EXIT_FAILURE); _exit(EXIT_FAILURE);
} }
/* close namespace file descriptors */
close_nsfds(ctx);
/* Attach succeeded, try to cwd. */ /* Attach succeeded, try to cwd. */
if (options->initial_cwd) if (options->initial_cwd)
new_cwd = options->initial_cwd; new_cwd = options->initial_cwd;
......
...@@ -3039,6 +3039,7 @@ __cgfsng_ops static bool cgfsng_devices_activate(struct cgroup_ops *ops, struct ...@@ -3039,6 +3039,7 @@ __cgfsng_ops static bool cgfsng_devices_activate(struct cgroup_ops *ops, struct
static bool __cgfsng_delegate_controllers(struct cgroup_ops *ops, const char *cgroup) static bool __cgfsng_delegate_controllers(struct cgroup_ops *ops, const char *cgroup)
{ {
__do_close int fd_base = -EBADF;
__do_free char *add_controllers = NULL, *base_path = NULL; __do_free char *add_controllers = NULL, *base_path = NULL;
__do_free_string_list char **parts = NULL; __do_free_string_list char **parts = NULL;
struct hierarchy *unified = ops->unified; struct hierarchy *unified = ops->unified;
...@@ -3070,6 +3071,14 @@ static bool __cgfsng_delegate_controllers(struct cgroup_ops *ops, const char *cg ...@@ -3070,6 +3071,14 @@ static bool __cgfsng_delegate_controllers(struct cgroup_ops *ops, const char *cg
(void)strlcat(add_controllers, " ", full_len + 1); (void)strlcat(add_controllers, " ", full_len + 1);
} }
base_path = must_make_path(unified->mountpoint, unified->container_base_path, NULL);
fd_base = lxc_open_dirfd(base_path);
if (fd_base < 0)
return false;
if (!unified_cgroup_fd(fd_base))
return log_error_errno(false, EINVAL, "File descriptor does not refer to cgroup2 filesystem");
parts = lxc_string_split(cgroup, '/'); parts = lxc_string_split(cgroup, '/');
if (!parts) if (!parts)
return false; return false;
...@@ -3078,19 +3087,26 @@ static bool __cgfsng_delegate_controllers(struct cgroup_ops *ops, const char *cg ...@@ -3078,19 +3087,26 @@ static bool __cgfsng_delegate_controllers(struct cgroup_ops *ops, const char *cg
if (parts_len > 0) if (parts_len > 0)
parts_len--; parts_len--;
base_path = must_make_path(unified->mountpoint, unified->container_base_path, NULL);
for (ssize_t i = -1; i < parts_len; i++) { for (ssize_t i = -1; i < parts_len; i++) {
int ret; int ret;
__do_free char *target = NULL;
if (i >= 0) if (i >= 0) {
base_path = must_append_path(base_path, parts[i], NULL); int fd_next;
target = must_make_path(base_path, "cgroup.subtree_control", NULL);
ret = lxc_writeat(-1, target, add_controllers, full_len); fd_next = openat(fd_base, parts[i], PROTECT_OPATH_DIRECTORY, PROTECT_LOOKUP_BENEATH);
if (fd_next < 0)
return log_error_errno(false, errno, "Failed to open %d(%s)", fd_next, parts[i]);
close_prot_errno_move(fd_base, fd_next);
}
ret = lxc_writeat(fd_base, "cgroup.subtree_control", add_controllers, full_len);
if (ret < 0) if (ret < 0)
return log_error_errno(false, errno, "Could not enable \"%s\" controllers in the unified cgroup \"%s\"", return log_error_errno(false, errno,
add_controllers, target); "Could not enable \"%s\" controllers in the unified cgroup %d(%s)",
TRACE("Enable \"%s\" controllers in the unified cgroup \"%s\"", add_controllers, target); add_controllers, fd_base, (i >= 0) ? parts[i] : unified->container_base_path);
TRACE("Enable \"%s\" controllers in the unified cgroup %d(%s)",
add_controllers, fd_base, (i >= 0) ? parts[i] : unified->container_base_path);
} }
return true; return true;
......
...@@ -98,3 +98,16 @@ int unified_cgroup_hierarchy(void) ...@@ -98,3 +98,16 @@ int unified_cgroup_hierarchy(void)
return 0; return 0;
} }
int unified_cgroup_fd(int fd)
{
int ret;
struct statfs fs;
ret = fstatfs(fd, &fs);
if (!ret && is_fs_type(&fs, CGROUP2_SUPER_MAGIC))
return true;
return false;
}
...@@ -30,4 +30,6 @@ __hidden extern bool test_writeable_v2(char *mountpoint, char *path); ...@@ -30,4 +30,6 @@ __hidden extern bool test_writeable_v2(char *mountpoint, char *path);
__hidden extern int unified_cgroup_hierarchy(void); __hidden extern int unified_cgroup_hierarchy(void);
__hidden extern int unified_cgroup_fd(int fd);
#endif /* __LXC_CGROUP_UTILS_H */ #endif /* __LXC_CGROUP_UTILS_H */
...@@ -23,7 +23,7 @@ ...@@ -23,7 +23,7 @@
int lxc_open_dirfd(const char *dir) int lxc_open_dirfd(const char *dir)
{ {
return open(dir, O_DIRECTORY | O_RDONLY | O_CLOEXEC | O_NOFOLLOW); return open_at(-EBADF, dir, PROTECT_OPATH_DIRECTORY, PROTECT_LOOKUP_ABSOLUTE & ~RESOLVE_NO_XDEV, 0);
} }
int lxc_readat(int dirfd, const char *filename, void *buf, size_t count) int lxc_readat(int dirfd, const char *filename, void *buf, size_t count)
...@@ -47,8 +47,7 @@ int lxc_writeat(int dirfd, const char *filename, const void *buf, size_t count) ...@@ -47,8 +47,7 @@ int lxc_writeat(int dirfd, const char *filename, const void *buf, size_t count)
__do_close int fd = -EBADF; __do_close int fd = -EBADF;
ssize_t ret; ssize_t ret;
fd = openat(dirfd, filename, fd = open_at(dirfd, filename, PROTECT_OPEN_W_WITH_TRAILING_SYMLINKS, PROTECT_LOOKUP_BENEATH, 0);
O_WRONLY | O_CLOEXEC | O_NOCTTY | O_NOFOLLOW);
if (fd < 0) if (fd < 0)
return -1; return -1;
......
...@@ -29,6 +29,15 @@ ...@@ -29,6 +29,15 @@
fd = -EBADF; \ fd = -EBADF; \
} }
#define close_prot_errno_move(fd, new_fd) \
if (fd >= 0) { \
int _e_ = errno; \
close(fd); \
errno = _e_; \
fd = new_fd; \
new_fd = -EBADF; \
}
static inline void close_prot_errno_disarm_function(int *fd) static inline void close_prot_errno_disarm_function(int *fd)
{ {
close_prot_errno_disarm(*fd); close_prot_errno_disarm(*fd);
......
...@@ -268,6 +268,9 @@ struct lxc_open_how { ...@@ -268,6 +268,9 @@ struct lxc_open_how {
#define PROTECT_OPEN_WITH_TRAILING_SYMLINKS (O_CLOEXEC | O_NOCTTY | O_RDONLY) #define PROTECT_OPEN_WITH_TRAILING_SYMLINKS (O_CLOEXEC | O_NOCTTY | O_RDONLY)
#define PROTECT_OPEN (PROTECT_OPEN_WITH_TRAILING_SYMLINKS | O_NOFOLLOW) #define PROTECT_OPEN (PROTECT_OPEN_WITH_TRAILING_SYMLINKS | O_NOFOLLOW)
#define PROTECT_OPEN_W_WITH_TRAILING_SYMLINKS (O_CLOEXEC | O_NOCTTY | O_WRONLY)
#define PROTECT_OPEN_W (PROTECT_OPEN_WITH_TRAILING_SYMLINKS | O_NOFOLLOW)
#ifndef HAVE_OPENAT2 #ifndef HAVE_OPENAT2
static inline int openat2(int dfd, const char *filename, struct lxc_open_how *how, size_t size) static inline int openat2(int dfd, const char *filename, struct lxc_open_how *how, size_t size)
{ {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment