The following pull request was submitted through Github. It can be accessed and reviewed at: https://github.com/lxc/lxc/pull/3505
This e-mail was sent by the LXC bot, direct replies will not reach the author unless they happen to be subscribed to this list. === Description (from pull-request) === This was a year long journey which seems to finally have come to an end. Closes: #1620. Signed-off-by: Christian Brauner <christian.brau...@ubuntu.com>
From e3f52b415de0a78cda9a13a830b59478fb8d0047 Mon Sep 17 00:00:00 2001 From: Christian Brauner <christian.brau...@ubuntu.com> Date: Wed, 5 Aug 2020 12:03:41 +0200 Subject: [PATCH] terminal: safely allocate pts devices from inside the container This was a year long journey which seems to finally have come to an end. Closes: #1620. Signed-off-by: Christian Brauner <christian.brau...@ubuntu.com> --- doc/api-extensions.md | 5 ++++ src/lxc/api_extensions.h | 1 + src/lxc/attach.c | 6 ++--- src/lxc/commands.c | 44 +++++++++++++++++++++++++++++++ src/lxc/commands.h | 2 ++ src/lxc/conf.c | 15 +++++++++-- src/lxc/conf.h | 2 ++ src/lxc/lxccontainer.c | 11 ++++++++ src/lxc/lxccontainer.h | 9 +++++++ src/lxc/start.c | 6 +++++ src/lxc/terminal.c | 57 ++++++++++++++++++++++++++++++++++++++-- src/lxc/terminal.h | 3 ++- 12 files changed, 153 insertions(+), 8 deletions(-) diff --git a/doc/api-extensions.md b/doc/api-extensions.md index 64cd4bdad4..21cb55d111 100644 --- a/doc/api-extensions.md +++ b/doc/api-extensions.md @@ -131,3 +131,8 @@ This adds time namespace support to LXC. ## seccomp\_allow\_deny\_syntax This adds the ability to use "denylist" and "allowlist" in seccomp v2 policies. + +## devpts\_fd + +This adds the ability to allocate a file descriptor for the devpts instance of +the container. diff --git a/src/lxc/api_extensions.h b/src/lxc/api_extensions.h index 6d47b4cef4..4d97504887 100644 --- a/src/lxc/api_extensions.h +++ b/src/lxc/api_extensions.h @@ -43,6 +43,7 @@ static char *api_extensions[] = { "network_bridge_vlan", "time_namespace", "seccomp_allow_deny_syntax", + "devpts_fd", }; static size_t nr_api_extensions = sizeof(api_extensions) / sizeof(*api_extensions); diff --git a/src/lxc/attach.c b/src/lxc/attach.c index ad25aada9e..654a7d8fd0 100644 --- a/src/lxc/attach.c +++ b/src/lxc/attach.c @@ -889,14 +889,14 @@ static int attach_child_main(struct attach_clone_payload *payload) _exit(EXIT_FAILURE); } -static int lxc_attach_terminal(struct lxc_conf *conf, +static int lxc_attach_terminal(const char *name, const char *lxcpath, struct lxc_conf *conf, struct lxc_terminal *terminal) { int ret; lxc_terminal_init(terminal); - ret = lxc_terminal_create(terminal); + ret = lxc_terminal_create(name, lxcpath, terminal); if (ret < 0) return log_error(-1, "Failed to create terminal"); @@ -1091,7 +1091,7 @@ int lxc_attach(struct lxc_container *container, lxc_attach_exec_t exec_function, } if (options->attach_flags & LXC_ATTACH_TERMINAL) { - ret = lxc_attach_terminal(conf, &terminal); + ret = lxc_attach_terminal(name, lxcpath, conf, &terminal); if (ret < 0) { ERROR("Failed to setup new terminal"); free(cwd); diff --git a/src/lxc/commands.c b/src/lxc/commands.c index 726d57ae0a..22fbb04bb4 100644 --- a/src/lxc/commands.c +++ b/src/lxc/commands.c @@ -86,6 +86,7 @@ static const char *lxc_cmd_str(lxc_cmd_t cmd) [LXC_CMD_GET_INIT_PIDFD] = "get_init_pidfd", [LXC_CMD_GET_LIMITING_CGROUP] = "get_limiting_cgroup", [LXC_CMD_GET_LIMITING_CGROUP2_FD] = "get_limiting_cgroup2_fd", + [LXC_CMD_GET_DEVPTS_FD] = "get_devpts_fd", }; if (cmd >= LXC_CMD_MAX) @@ -156,6 +157,11 @@ static int lxc_cmd_rsp_recv(int sock, struct lxc_cmd_rr *cmd) rsp->data = INT_TO_PTR(init_pidfd); } + if (cmd->req.cmd == LXC_CMD_GET_DEVPTS_FD) { + int devpts_fd = move_fd(fd_rsp); + rsp->data = INT_TO_PTR(devpts_fd); + } + if (rsp->datalen == 0) return log_debug(ret, "Response data length for command \"%s\" is 0", @@ -447,6 +453,43 @@ static int lxc_cmd_get_init_pidfd_callback(int fd, struct lxc_cmd_req *req, return 0; } +int lxc_cmd_get_devpts_fd(const char *name, const char *lxcpath) +{ + int ret, stopped; + struct lxc_cmd_rr cmd = { + .req = { + .cmd = LXC_CMD_GET_DEVPTS_FD, + }, + }; + + ret = lxc_cmd(name, &cmd, &stopped, lxcpath, NULL); + if (ret < 0) + return log_debug_errno(-1, errno, "Failed to process devpts fd command"); + + if (cmd.rsp.ret < 0) + return log_debug_errno(-EBADF, errno, "Failed to receive devpts fd"); + + return PTR_TO_INT(cmd.rsp.data); +} + +static int lxc_cmd_get_devpts_fd_callback(int fd, struct lxc_cmd_req *req, + struct lxc_handler *handler, + struct lxc_epoll_descr *descr) +{ + struct lxc_cmd_rsp rsp = { + .ret = 0, + }; + int ret; + + if (!handler->conf || handler->conf->devpts_fd < 0) + rsp.ret = -EBADF; + ret = lxc_abstract_unix_send_fds(fd, &handler->conf->devpts_fd, 1, &rsp, sizeof(rsp)); + if (ret < 0) + return log_error(LXC_CMD_REAP_CLIENT_FD, "Failed to send devpts fd"); + + return 0; +} + /* * lxc_cmd_get_clone_flags: Get clone flags container was spawned with * @@ -1505,6 +1548,7 @@ static int lxc_cmd_process(int fd, struct lxc_cmd_req *req, [LXC_CMD_GET_INIT_PIDFD] = lxc_cmd_get_init_pidfd_callback, [LXC_CMD_GET_LIMITING_CGROUP] = lxc_cmd_get_limiting_cgroup_callback, [LXC_CMD_GET_LIMITING_CGROUP2_FD] = lxc_cmd_get_limiting_cgroup2_fd_callback, + [LXC_CMD_GET_DEVPTS_FD] = lxc_cmd_get_devpts_fd_callback, }; if (req->cmd >= LXC_CMD_MAX) diff --git a/src/lxc/commands.h b/src/lxc/commands.h index fad71cee1b..ef545e23ae 100644 --- a/src/lxc/commands.h +++ b/src/lxc/commands.h @@ -41,6 +41,7 @@ typedef enum { LXC_CMD_GET_INIT_PIDFD, LXC_CMD_GET_LIMITING_CGROUP, LXC_CMD_GET_LIMITING_CGROUP2_FD, + LXC_CMD_GET_DEVPTS_FD, LXC_CMD_MAX, } lxc_cmd_t; @@ -132,5 +133,6 @@ __hidden extern int lxc_cmd_get_cgroup2_fd(const char *name, const char *lxcpath __hidden extern char *lxc_cmd_get_limiting_cgroup_path(const char *name, const char *lxcpath, const char *subsystem); __hidden extern int lxc_cmd_get_limiting_cgroup2_fd(const char *name, const char *lxcpath); +__hidden extern int lxc_cmd_get_devpts_fd(const char *name, const char *lxcpath); #endif /* __commands_h */ diff --git a/src/lxc/conf.c b/src/lxc/conf.c index 9410cac920..ab53f094fe 100644 --- a/src/lxc/conf.c +++ b/src/lxc/conf.c @@ -1472,13 +1472,16 @@ static const struct id_map *find_mapped_nsid_entry(const struct lxc_conf *conf, return retmap; } -static int lxc_setup_devpts(struct lxc_conf *conf) +static int lxc_setup_devpts(struct lxc_handler *handler) { + __do_close int devpts_fd = -EBADF; int ret; char **opts; char devpts_mntopts[256]; char *mntopt_sets[5]; char default_devpts_mntopts[256] = "gid=5,newinstance,ptmxmode=0666,mode=0620"; + struct lxc_conf *conf = handler->conf; + int sock = handler->data_sock[0]; if (conf->pty_max <= 0) return log_debug(0, "No new devpts instance will be mounted since no pts devices are requested"); @@ -1521,6 +1524,14 @@ static int lxc_setup_devpts(struct lxc_conf *conf) return log_error_errno(-1, errno, "Failed to mount new devpts instance"); DEBUG("Mount new devpts instance with options \"%s\"", *opts); + devpts_fd = open_tree(-EBADF, "/dev/pts", OPEN_TREE_CLONE | OPEN_TREE_CLOEXEC | AT_EMPTY_PATH); + if (devpts_fd < 0) + TRACE("Failed to create detached devpts mount"); + + ret = lxc_abstract_unix_send_fds(sock, &devpts_fd, 1, NULL, 0); + if (ret < 0) + return log_error_errno(-1, errno, "Failed to send devpts fd to parent"); + /* Remove any pre-existing /dev/ptmx file. */ ret = remove("/dev/ptmx"); if (ret < 0) { @@ -3327,7 +3338,7 @@ int lxc_setup(struct lxc_handler *handler) if (lxc_conf->autodev > 0) (void)lxc_setup_boot_id(); - ret = lxc_setup_devpts(lxc_conf); + ret = lxc_setup_devpts(handler); if (ret < 0) return log_error(-1, "Failed to setup new devpts instance"); diff --git a/src/lxc/conf.h b/src/lxc/conf.h index d78bfffb3e..5de2aa2bf2 100644 --- a/src/lxc/conf.h +++ b/src/lxc/conf.h @@ -292,6 +292,8 @@ struct lxc_conf { struct lxc_terminal console; /* maximum pty devices allowed by devpts mount */ size_t pty_max; + /* file descriptor for the container's /dev/pts mount */ + int devpts_fd; /* set to true when rootfs has been setup */ bool rootfs_setup; diff --git a/src/lxc/lxccontainer.c b/src/lxc/lxccontainer.c index baffaae78a..8d854aaf13 100644 --- a/src/lxc/lxccontainer.c +++ b/src/lxc/lxccontainer.c @@ -608,6 +608,16 @@ static int do_lxcapi_init_pidfd(struct lxc_container *c) WRAP_API(int, lxcapi_init_pidfd) +static int do_lxcapi_devpts_fd(struct lxc_container *c) +{ + if (!c) + return ret_errno(EBADF); + + return lxc_cmd_get_devpts_fd(c->name, c->config_path); +} + +WRAP_API(int, lxcapi_devpts_fd) + static bool load_config_locked(struct lxc_container *c, const char *fname) { if (!c->lxc_conf) @@ -5319,6 +5329,7 @@ struct lxc_container *lxc_container_new(const char *name, const char *configpath c->unfreeze = lxcapi_unfreeze; c->console = lxcapi_console; c->console_getfd = lxcapi_console_getfd; + c->devpts_fd = lxcapi_devpts_fd; c->init_pid = lxcapi_init_pid; c->init_pidfd = lxcapi_init_pidfd; c->load_config = lxcapi_load_config; diff --git a/src/lxc/lxccontainer.h b/src/lxc/lxccontainer.h index 71086f764d..3437550d7e 100644 --- a/src/lxc/lxccontainer.h +++ b/src/lxc/lxccontainer.h @@ -865,6 +865,15 @@ struct lxc_container { * \return pidfd of init process of the container. */ int (*init_pidfd)(struct lxc_container *c); + + /*! + * \brief Retrieve a mount fd for the container's devpts instance. + * + * \param c Container. + * + * \return Mount fd of the container's devpts instance. + */ + int (*devpts_fd)(struct lxc_container *c); }; /*! diff --git a/src/lxc/start.c b/src/lxc/start.c index 4d356af157..37d037e028 100644 --- a/src/lxc/start.c +++ b/src/lxc/start.c @@ -1959,6 +1959,12 @@ static int lxc_spawn(struct lxc_handler *handler) } } + ret = lxc_abstract_unix_recv_fds(data_sock1, &handler->conf->devpts_fd, 1, NULL, 0); + if (ret < 0) { + SYSERROR("Failed to receive devpts fd from child process"); + goto out_delete_net; + } + /* Now all networks are created, network devices are moved into place, * and the correct names and ifindices in the respective namespaces have * been recorded. The corresponding structs have now all been filled. So diff --git a/src/lxc/terminal.c b/src/lxc/terminal.c index 7d70218974..aeb2432795 100644 --- a/src/lxc/terminal.c +++ b/src/lxc/terminal.c @@ -828,7 +828,7 @@ int lxc_terminal_create_log_file(struct lxc_terminal *terminal) return 0; } -int lxc_terminal_create(struct lxc_terminal *terminal) +static int lxc_terminal_create_foreign(struct lxc_terminal *terminal) { int ret; @@ -869,6 +869,59 @@ int lxc_terminal_create(struct lxc_terminal *terminal) return -ENODEV; } +static int lxc_terminal_create_native(const char *name, const char *lxcpath, + struct lxc_terminal *terminal) +{ + __do_close int devpts_fd = -EBADF, ptx_fd = -EBADF, pty_fd = -EBADF; + int ret; + + devpts_fd = lxc_cmd_get_devpts_fd(name, lxcpath); + if (devpts_fd < 0) + return log_error_errno(-1, errno, "Failed to receive devpts fd"); + + ptx_fd = openat(devpts_fd, "ptmx", O_RDWR | O_NOCTTY | O_CLOEXEC); + if (ptx_fd < 0) + return log_error_errno(-1, errno, "Failed to open terminal multiplexer device"); + + ret = grantpt(ptx_fd); + if (ret < 0) + return log_error_errno(-1, errno, "Failed to grant access to multiplexer device"); + + ret = unlockpt(ptx_fd); + if (ret < 0) + return log_error_errno(-1, errno, "Failed to unlock multiplexer device device"); + + pty_fd = ioctl(ptx_fd, TIOCGPTPEER, O_RDWR | O_NOCTTY | O_CLOEXEC); + if (pty_fd < 0) + return log_error_errno(-1, errno, "Failed to allocate new pty device"); + + ret = ttyname_r(terminal->pty, terminal->name, sizeof(terminal->name)); + if (ret < 0) + return log_error_errno(-1, errno, "Failed to retrieve name of terminal pty"); + + terminal->ptx = move_fd(ptx_fd); + terminal->pty = move_fd(pty_fd); + ret = lxc_terminal_peer_default(terminal); + if (ret < 0) { + ERROR("Failed to allocate proxy terminal"); + goto err; + } + + return 0; + +err: + lxc_terminal_delete(terminal); + return -ENODEV; +} + +int lxc_terminal_create(const char *name, const char *lxcpath, struct lxc_terminal *terminal) +{ + if (!lxc_terminal_create_native(name, lxcpath, terminal)) + return 0; + + return lxc_terminal_create_foreign(terminal); +} + int lxc_terminal_setup(struct lxc_conf *conf) { int ret; @@ -879,7 +932,7 @@ int lxc_terminal_setup(struct lxc_conf *conf) return 0; } - ret = lxc_terminal_create(terminal); + ret = lxc_terminal_create_foreign(terminal); if (ret < 0) return -1; diff --git a/src/lxc/terminal.h b/src/lxc/terminal.h index e17a7a9fef..838993d37f 100644 --- a/src/lxc/terminal.h +++ b/src/lxc/terminal.h @@ -110,7 +110,8 @@ __hidden extern int lxc_terminal_allocate(struct lxc_conf *conf, int sockfd, in * - sets up SIGWINCH handler, winsz, and new terminal settings * (Handlers for SIGWINCH and I/O are not registered in a mainloop.) */ -__hidden extern int lxc_terminal_create(struct lxc_terminal *console); +__hidden extern int lxc_terminal_create(const char *name, const char *lxcpath, + struct lxc_terminal *console); /** * lxc_terminal_setup: Create a new terminal.
_______________________________________________ lxc-devel mailing list lxc-devel@lists.linuxcontainers.org http://lists.linuxcontainers.org/listinfo/lxc-devel