[lxc-devel] [lxc/master] terminal: safely allocate pts devices from inside the container
brauner on Github
lxc-bot at linuxcontainers.org
Wed Aug 5 12:31:25 UTC 2020
A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 456 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20200805/0e250d8c/attachment.bin>
-------------- next part --------------
From e3f52b415de0a78cda9a13a830b59478fb8d0047 Mon Sep 17 00:00:00 2001
From: Christian Brauner <christian.brauner at 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.brauner at 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.
More information about the lxc-devel
mailing list