/* * lxc_cmd_rsp_recv: Receive a response to a command * * @sock : the socket connected to the container * @cmd : command to put response in * * Returns the size of the response message or < 0 on failure * * Note that if the command response datalen > 0, then data is * a malloc()ed buffer and should be free()ed by the caller. If * the response data is <= a void * worth of data, it will be * stored directly in data and datalen will be 0. * * As a special case, the response for LXC_CMD_CONSOLE is created * here as it contains an fd for the master pty passed through the * unix socket. */ static int lxc_cmd_rsp_recv(int sock, struct lxc_cmd_rr *cmd) { int ret, rspfd; struct lxc_cmd_rsp *rsp = &cmd->rsp; ret = lxc_abstract_unix_recv_fds(sock, &rspfd, 1, rsp, sizeof(*rsp)); if (ret < 0) { SYSWARN("Failed to receive response for command \"%s\"", lxc_cmd_str(cmd->req.cmd)); if (errno == ECONNRESET) return -1; return -1; } TRACE("Command \"%s\" received response", lxc_cmd_str(cmd->req.cmd)); if (cmd->req.cmd == LXC_CMD_CONSOLE) { struct lxc_cmd_console_rsp_data *rspdata; /* recv() returns 0 bytes when a tty cannot be allocated, * rsp->ret is < 0 when the peer permission check failed */ if (ret == 0 || rsp->ret < 0) return 0; rspdata = malloc(sizeof(*rspdata)); if (!rspdata) { errno = ENOMEM; ERROR("Failed to allocate response buffer for command \"%s\"", lxc_cmd_str(cmd->req.cmd)); return -1; } rspdata->masterfd = rspfd; rspdata->ttynum = PTR_TO_INT(rsp->data); rsp->data = rspdata; } if (rsp->datalen == 0) { DEBUG("Response data length for command \"%s\" is 0", lxc_cmd_str(cmd->req.cmd)); return ret; } if ((rsp->datalen > LXC_CMD_DATA_MAX) && (cmd->req.cmd != LXC_CMD_CONSOLE_LOG)) { ERROR("Response data for command \"%s\" is too long: %d bytes > %d", lxc_cmd_str(cmd->req.cmd), rsp->datalen, LXC_CMD_DATA_MAX); return -1; } if (cmd->req.cmd == LXC_CMD_CONSOLE_LOG) { rsp->data = malloc(rsp->datalen + 1); ((char *)rsp->data)[rsp->datalen] = '\0'; } else { rsp->data = malloc(rsp->datalen); } if (!rsp->data) { errno = ENOMEM; ERROR("Failed to allocate response buffer for command \"%s\"", lxc_cmd_str(cmd->req.cmd)); return -1; } ret = lxc_recv_nointr(sock, rsp->data, rsp->datalen, 0); if (ret != rsp->datalen) { SYSERROR("Failed to receive response data for command \"%s\"", lxc_cmd_str(cmd->req.cmd)); return -1; } return ret; }
static int attach_child_main(void* data) { struct attach_clone_payload* payload = (struct attach_clone_payload*)data; int ipc_socket = payload->ipc_socket; lxc_attach_options_t* options = payload->options; struct lxc_proc_context_info* init_ctx = payload->init_ctx; #if HAVE_SYS_PERSONALITY_H long new_personality; #endif int ret; int status; int expected; long flags; int fd; int lsm_labelfd; uid_t new_uid; gid_t new_gid; /* Wait for the initial thread to signal us that it's ready for us to * start initializing. */ expected = 0; status = -1; ret = lxc_read_nointr_expect(ipc_socket, &status, sizeof(status), &expected); if (ret <= 0) { ERROR("Expected to receive sequence number 0: %s.", strerror(errno)); shutdown(ipc_socket, SHUT_RDWR); rexit(-1); } /* A description of the purpose of this functionality is provided in the * lxc-attach(1) manual page. We have to remount here and not in the * parent process, otherwise /proc may not properly reflect the new pid * namespace. */ if (!(options->namespaces & CLONE_NEWNS) && (options->attach_flags & LXC_ATTACH_REMOUNT_PROC_SYS)) { ret = lxc_attach_remount_sys_proc(); if (ret < 0) { shutdown(ipc_socket, SHUT_RDWR); rexit(-1); } } /* Now perform additional attachments. */ #if HAVE_SYS_PERSONALITY_H if (options->personality < 0) new_personality = init_ctx->personality; else new_personality = options->personality; if (options->attach_flags & LXC_ATTACH_SET_PERSONALITY) { ret = personality(new_personality); if (ret < 0) { SYSERROR("Could not ensure correct architecture."); shutdown(ipc_socket, SHUT_RDWR); rexit(-1); } } #endif if (options->attach_flags & LXC_ATTACH_DROP_CAPABILITIES) { ret = lxc_attach_drop_privs(init_ctx); if (ret < 0) { ERROR("Could not drop privileges."); shutdown(ipc_socket, SHUT_RDWR); rexit(-1); } } /* Always set the environment (specify (LXC_ATTACH_KEEP_ENV, NULL, NULL) * if you want this to be a no-op). */ ret = lxc_attach_set_environment(options->env_policy, options->extra_env_vars, options->extra_keep_env); if (ret < 0) { ERROR("Could not set initial environment for attached process."); shutdown(ipc_socket, SHUT_RDWR); rexit(-1); } /* Set {u,g}id. */ new_uid = 0; new_gid = 0; /* Ignore errors, we will fall back to root in that case (/proc was not * mounted etc.). */ if (options->namespaces & CLONE_NEWUSER) lxc_attach_get_init_uidgid(&new_uid, &new_gid); if (options->uid != (uid_t)-1) new_uid = options->uid; if (options->gid != (gid_t)-1) new_gid = options->gid; /* Setup the controlling tty. */ if (options->stdin_fd && isatty(options->stdin_fd)) { if (setsid() < 0) { SYSERROR("Unable to setsid."); shutdown(ipc_socket, SHUT_RDWR); rexit(-1); } if (ioctl(options->stdin_fd, TIOCSCTTY, (char *)NULL) < 0) { SYSERROR("Unable to set TIOCSTTY."); shutdown(ipc_socket, SHUT_RDWR); rexit(-1); } } /* Try to set the {u,g}id combination. */ if ((new_gid != 0 || options->namespaces & CLONE_NEWUSER)) { if (setgid(new_gid) || setgroups(0, NULL)) { SYSERROR("Switching to container gid."); shutdown(ipc_socket, SHUT_RDWR); rexit(-1); } } if ((new_uid != 0 || options->namespaces & CLONE_NEWUSER) && setuid(new_uid)) { SYSERROR("Switching to container uid."); shutdown(ipc_socket, SHUT_RDWR); rexit(-1); } /* Tell initial process it may now put us into cgroups. */ status = 1; ret = lxc_write_nointr(ipc_socket, &status, sizeof(status)); if (ret != sizeof(status)) { ERROR("Intended to send sequence number 1: %s.", strerror(errno)); shutdown(ipc_socket, SHUT_RDWR); rexit(-1); } /* Wait for the initial thread to signal us that it has done everything * for us when it comes to cgroups etc. */ expected = 2; status = -1; ret = lxc_read_nointr_expect(ipc_socket, &status, sizeof(status), &expected); if (ret <= 0) { ERROR("Expected to receive sequence number 2: %s", strerror(errno)); shutdown(ipc_socket, SHUT_RDWR); rexit(-1); } if ((init_ctx->container && init_ctx->container->lxc_conf && init_ctx->container->lxc_conf->no_new_privs) || (options->attach_flags & LXC_ATTACH_NO_NEW_PRIVS)) { if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) < 0) { SYSERROR("PR_SET_NO_NEW_PRIVS could not be set. " "Process can use execve() gainable " "privileges."); shutdown(ipc_socket, SHUT_RDWR); rexit(-1); } INFO("PR_SET_NO_NEW_PRIVS is set. Process cannot use execve() " "gainable privileges."); } /* Tell the (grand)parent to send us LSM label fd. */ status = 3; ret = lxc_write_nointr(ipc_socket, &status, sizeof(status)); if (ret <= 0) { ERROR("Intended to send sequence number 3: %s.", strerror(errno)); shutdown(ipc_socket, SHUT_RDWR); rexit(-1); } if ((options->namespaces & CLONE_NEWNS) && (options->attach_flags & LXC_ATTACH_LSM) && init_ctx->lsm_label) { int on_exec; /* Receive fd for LSM security module. */ ret = lxc_abstract_unix_recv_fds(ipc_socket, &lsm_labelfd, 1, NULL, 0); if (ret <= 0) { ERROR("Expected to receive file descriptor: %s.", strerror(errno)); shutdown(ipc_socket, SHUT_RDWR); rexit(-1); } /* Change into our new LSM profile. */ on_exec = options->attach_flags & LXC_ATTACH_LSM_EXEC ? 1 : 0; if (lsm_set_label_at(lsm_labelfd, on_exec, init_ctx->lsm_label) < 0) { SYSERROR("Failed to set LSM label."); shutdown(ipc_socket, SHUT_RDWR); close(lsm_labelfd); rexit(-1); } close(lsm_labelfd); } if (init_ctx->container && init_ctx->container->lxc_conf && init_ctx->container->lxc_conf->seccomp && (lxc_seccomp_load(init_ctx->container->lxc_conf) != 0)) { ERROR("Failed to load seccomp policy."); shutdown(ipc_socket, SHUT_RDWR); rexit(-1); } shutdown(ipc_socket, SHUT_RDWR); close(ipc_socket); lxc_proc_put_context_info(init_ctx); /* The following is done after the communication socket is shut down. * That way, all errors that might (though unlikely) occur up until this * point will have their messages printed to the original stderr (if * logging is so configured) and not the fd the user supplied, if any. */ /* Fd handling for stdin, stdout and stderr; ignore errors here, user * may want to make sure the fds are closed, for example. */ if (options->stdin_fd >= 0 && options->stdin_fd != 0) dup2(options->stdin_fd, 0); if (options->stdout_fd >= 0 && options->stdout_fd != 1) dup2(options->stdout_fd, 1); if (options->stderr_fd >= 0 && options->stderr_fd != 2) dup2(options->stderr_fd, 2); /* close the old fds */ if (options->stdin_fd > 2) close(options->stdin_fd); if (options->stdout_fd > 2) close(options->stdout_fd); if (options->stderr_fd > 2) close(options->stderr_fd); /* Try to remove FD_CLOEXEC flag from stdin/stdout/stderr, but also * here, ignore errors. */ for (fd = 0; fd <= 2; fd++) { flags = fcntl(fd, F_GETFL); if (flags < 0) continue; if (flags & FD_CLOEXEC) if (fcntl(fd, F_SETFL, flags & ~FD_CLOEXEC) < 0) SYSERROR("Unable to clear FD_CLOEXEC from file descriptor."); } /* We're done, so we can now do whatever the user intended us to do. */ rexit(payload->exec_function(payload->exec_payload)); }