]> git.ipfire.org Git - thirdparty/lxc.git/commitdiff
terminal: safely allocate pts devices from inside the container 3505/head
authorChristian Brauner <christian.brauner@ubuntu.com>
Wed, 5 Aug 2020 10:03:41 +0000 (12:03 +0200)
committerChristian Brauner <christian.brauner@ubuntu.com>
Wed, 5 Aug 2020 13:16:51 +0000 (15:16 +0200)
This was a year long journey which seems to finally have come to an end.

Closes: #1620.
Signed-off-by: Christian Brauner <christian.brauner@ubuntu.com>
12 files changed:
doc/api-extensions.md
src/lxc/api_extensions.h
src/lxc/attach.c
src/lxc/commands.c
src/lxc/commands.h
src/lxc/conf.c
src/lxc/conf.h
src/lxc/lxccontainer.c
src/lxc/lxccontainer.h
src/lxc/start.c
src/lxc/terminal.c
src/lxc/terminal.h

index 64cd4bdad4353a30310d11cb5df08699c776dd21..21cb55d1117c6bd437723fc8d23b3bfe97edd69b 100644 (file)
@@ -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.
index 6d47b4cef4d94206b99c36707efbc5bacb83e985..4d975048872ee7f0f95a411491fab3ce188b1846 100644 (file)
@@ -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);
index ad25aada9e3308df26fd882c42416bf851f0ed08..654a7d8fd0ffbc27c9441ceecd52c0d2cb8e7051 100644 (file)
@@ -889,14 +889,14 @@ on_error:
        _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);
index 726d57ae0ac2b38565979afd5e8c3f1536a570ea..22fbb04bb4a3abb2bfb985fd900611b31aa3a020 100644 (file)
@@ -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)
index fad71cee1bfdc39cf1dadfb3fdd41f4bcd13a4e5..ef545e23aed3e01bf7ebf71bdc3c13204d36e4db 100644 (file)
@@ -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 */
index 9410cac92072d1bf1742de00c1fd36d25daa6004..4139a77baf74ddd651fb4ca9a54b410591574551 100644 (file)
@@ -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,16 @@ 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, NULL, 0, NULL, 0);
+       } else {
+               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 +3340,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");
 
index d78bfffb3ef17eda6cd0043fe5e486110e5767df..5de2aa2bf2990852f294656267c509d88dfdcac4 100644 (file)
@@ -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;
index baffaae78a8997be7036a4c74a79ea71c13a5476..8d854aaf136ff7aa8b0161e2be2cb8b4b633235c 100644 (file)
@@ -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;
index 71086f764d1885e7217ab363d6a1aae6786c3105..3437550d7ea7865fa81dde8a9187c41024ebbecd 100644 (file)
@@ -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);
 };
 
 /*!
index 4d356af157ef1259c2cd750029a5ba75fd026e88..49de132f9fc57565eb816a92ea546476fe1412c9 100644 (file)
@@ -980,6 +980,7 @@ void lxc_end(struct lxc_handler *handler)
 
        lxc_terminal_delete(&handler->conf->console);
        lxc_delete_tty(&handler->conf->ttys);
+       close_prot_errno_disarm(handler->conf->devpts_fd);
 
        /* The command socket is now closed, no more state clients can register
         * themselves from now on. So free the list of state clients.
@@ -1959,6 +1960,14 @@ 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");
+               goto out_delete_net;
+       }
+       if (ret == 0)
+               handler->conf->devpts_fd = -EBADF;
+
        /* 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
index 7d70218974ca0003c17df836d63ae0ba8aa67b25..aeb243279589f12728798e6c527c672649ba8b7b 100644 (file)
@@ -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 @@ err:
        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;
 
index e17a7a9fefcbddbaba4c71b77ded02b12014dfaf..838993d37fd5591be9a182f384efe3a46d719898 100644 (file)
@@ -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.