2015-05-15 05:44:48 +08:00
|
|
|
/*
|
|
|
|
|
|
|
|
nsjail
|
|
|
|
-----------------------------------------
|
|
|
|
|
|
|
|
Copyright 2014 Google Inc. All Rights Reserved.
|
|
|
|
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
you may not use this file except in compliance with the License.
|
|
|
|
You may obtain a copy of the License at
|
|
|
|
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
See the License for the specific language governing permissions and
|
|
|
|
limitations under the License.
|
|
|
|
|
|
|
|
*/
|
2016-03-02 00:03:11 +08:00
|
|
|
|
2015-05-15 05:44:48 +08:00
|
|
|
#include "nsjail.h"
|
|
|
|
|
2020-02-13 19:24:28 +08:00
|
|
|
#include <fcntl.h>
|
|
|
|
#include <poll.h>
|
2015-05-15 05:44:48 +08:00
|
|
|
#include <signal.h>
|
|
|
|
#include <stdbool.h>
|
|
|
|
#include <stdio.h>
|
2017-09-14 04:03:21 +08:00
|
|
|
#include <stdlib.h>
|
2015-05-15 05:44:48 +08:00
|
|
|
#include <string.h>
|
2018-05-25 07:04:29 +08:00
|
|
|
#include <sys/ioctl.h>
|
2015-05-15 05:44:48 +08:00
|
|
|
#include <sys/time.h>
|
2018-05-25 07:04:29 +08:00
|
|
|
#include <termios.h>
|
2015-05-15 05:44:48 +08:00
|
|
|
#include <unistd.h>
|
|
|
|
|
2020-02-13 19:24:28 +08:00
|
|
|
#include <algorithm>
|
|
|
|
#include <cerrno>
|
2018-05-28 08:04:03 +08:00
|
|
|
#include <memory>
|
2020-02-13 19:24:28 +08:00
|
|
|
#include <vector>
|
2018-05-28 08:04:03 +08:00
|
|
|
|
2015-05-15 05:44:48 +08:00
|
|
|
#include "cmdline.h"
|
2018-02-11 00:49:15 +08:00
|
|
|
#include "logs.h"
|
2018-02-10 12:25:55 +08:00
|
|
|
#include "macros.h"
|
2015-05-15 05:44:48 +08:00
|
|
|
#include "net.h"
|
2018-02-12 10:11:58 +08:00
|
|
|
#include "sandbox.h"
|
2015-05-15 05:44:48 +08:00
|
|
|
#include "subproc.h"
|
2017-06-20 06:16:38 +08:00
|
|
|
#include "util.h"
|
2015-05-15 05:44:48 +08:00
|
|
|
|
2018-05-25 08:15:47 +08:00
|
|
|
namespace nsjail {
|
2015-05-15 05:44:48 +08:00
|
|
|
|
2018-05-25 08:15:47 +08:00
|
|
|
static __thread int sigFatal = 0;
|
|
|
|
static __thread bool showProc = false;
|
|
|
|
|
|
|
|
static void sigHandler(int sig) {
|
2020-02-13 19:24:28 +08:00
|
|
|
if (sig == SIGALRM || sig == SIGCHLD || sig == SIGPIPE) {
|
2015-05-15 05:44:48 +08:00
|
|
|
return;
|
|
|
|
}
|
2017-10-19 21:25:20 +08:00
|
|
|
if (sig == SIGUSR1 || sig == SIGQUIT) {
|
2018-05-25 08:15:47 +08:00
|
|
|
showProc = true;
|
2015-05-15 05:44:48 +08:00
|
|
|
return;
|
|
|
|
}
|
2018-05-25 08:15:47 +08:00
|
|
|
sigFatal = sig;
|
2015-05-15 05:44:48 +08:00
|
|
|
}
|
|
|
|
|
2018-05-25 08:15:47 +08:00
|
|
|
static bool setSigHandler(int sig) {
|
2018-02-11 04:19:47 +08:00
|
|
|
LOG_D("Setting sighandler for signal %s (%d)", util::sigName(sig).c_str(), sig);
|
2015-05-15 05:44:48 +08:00
|
|
|
|
|
|
|
sigset_t smask;
|
|
|
|
sigemptyset(&smask);
|
2018-02-08 22:24:17 +08:00
|
|
|
|
|
|
|
struct sigaction sa;
|
2018-05-25 08:15:47 +08:00
|
|
|
sa.sa_handler = sigHandler;
|
2018-02-08 22:24:17 +08:00
|
|
|
sa.sa_mask = smask;
|
|
|
|
sa.sa_flags = 0;
|
|
|
|
sa.sa_restorer = NULL;
|
2018-02-15 08:33:33 +08:00
|
|
|
|
|
|
|
if (sig == SIGTTIN || sig == SIGTTOU) {
|
|
|
|
sa.sa_handler = SIG_IGN;
|
2020-02-13 19:24:28 +08:00
|
|
|
}
|
2015-05-15 05:44:48 +08:00
|
|
|
if (sigaction(sig, &sa, NULL) == -1) {
|
|
|
|
PLOG_E("sigaction(%d)", sig);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2018-05-25 08:15:47 +08:00
|
|
|
static bool setSigHandlers(void) {
|
2018-05-22 20:27:18 +08:00
|
|
|
for (const auto& i : nssigs) {
|
2018-05-25 08:15:47 +08:00
|
|
|
if (!setSigHandler(i)) {
|
2017-10-18 18:33:24 +08:00
|
|
|
return false;
|
|
|
|
}
|
2015-05-15 05:44:48 +08:00
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2018-05-25 08:15:47 +08:00
|
|
|
static bool setTimer(nsjconf_t* nsjconf) {
|
2015-08-15 22:02:38 +08:00
|
|
|
if (nsjconf->mode == MODE_STANDALONE_EXECVE) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2015-05-15 05:44:48 +08:00
|
|
|
struct itimerval it = {
|
2018-02-08 22:24:17 +08:00
|
|
|
.it_interval =
|
2017-10-26 06:26:02 +08:00
|
|
|
{
|
2017-10-16 21:31:14 +08:00
|
|
|
.tv_sec = 1,
|
|
|
|
.tv_usec = 0,
|
|
|
|
},
|
2018-02-08 22:24:17 +08:00
|
|
|
.it_value =
|
2017-10-26 06:26:02 +08:00
|
|
|
{
|
2017-10-16 21:31:14 +08:00
|
|
|
.tv_sec = 1,
|
|
|
|
.tv_usec = 0,
|
|
|
|
},
|
2015-05-15 05:44:48 +08:00
|
|
|
};
|
|
|
|
if (setitimer(ITIMER_REAL, &it, NULL) == -1) {
|
|
|
|
PLOG_E("setitimer(ITIMER_REAL)");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-02-13 19:24:28 +08:00
|
|
|
static bool pipeTraffic(nsjconf_t* nsjconf, int listenfd) {
|
|
|
|
std::vector<struct pollfd> fds;
|
|
|
|
fds.reserve(nsjconf->pipes.size() * 2 + 1);
|
|
|
|
for (const auto& p : nsjconf->pipes) {
|
|
|
|
fds.push_back({
|
|
|
|
.fd = p.first,
|
|
|
|
.events = POLLIN,
|
|
|
|
.revents = 0,
|
|
|
|
});
|
|
|
|
fds.push_back({
|
|
|
|
.fd = p.second,
|
|
|
|
.events = POLLOUT,
|
|
|
|
.revents = 0,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
fds.push_back({
|
|
|
|
.fd = listenfd,
|
|
|
|
.events = POLLIN,
|
|
|
|
.revents = 0,
|
|
|
|
});
|
|
|
|
LOG_D("Waiting for fd activity");
|
|
|
|
while (poll(fds.data(), fds.size(), -1) > 0) {
|
|
|
|
if (fds.back().revents != 0) {
|
|
|
|
LOG_D("New connection ready");
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
bool cleanup = false;
|
|
|
|
for (size_t i = 0; i < fds.size() - 1; i += 2) {
|
|
|
|
bool read_ready = fds[i].events == 0 || (fds[i].revents & POLLIN) == POLLIN;
|
|
|
|
bool write_ready =
|
|
|
|
fds[i + 1].events == 0 || (fds[i + 1].revents & POLLOUT) == POLLOUT;
|
|
|
|
if (read_ready && write_ready) {
|
|
|
|
if (splice(fds[i].fd, nullptr, fds[i + 1].fd, nullptr, 4096,
|
|
|
|
SPLICE_F_NONBLOCK) == -1 &&
|
|
|
|
errno != EAGAIN) {
|
|
|
|
PLOG_E("splice fd pair #%ld {%d, %d}\n", i / 2, fds[i].fd,
|
|
|
|
fds[i + 1].fd);
|
|
|
|
}
|
|
|
|
fds[i].events = POLLIN;
|
|
|
|
fds[i + 1].events = POLLOUT;
|
|
|
|
} else if (read_ready) {
|
|
|
|
LOG_D("Read ready on %ld", i / 2);
|
|
|
|
fds[i].events = 0;
|
|
|
|
} else if (write_ready) {
|
|
|
|
LOG_D("Write ready on %ld", i / 2);
|
|
|
|
fds[i + 1].events = 0;
|
|
|
|
}
|
|
|
|
if ((fds[i].revents & (POLLHUP | POLLERR)) != 0 ||
|
|
|
|
(fds[i + 1].revents & (POLLHUP | POLLERR)) != 0) {
|
|
|
|
LOG_D("Hangup on %ld", i / 2);
|
|
|
|
cleanup = true;
|
|
|
|
close(fds[i].fd);
|
|
|
|
close(fds[i + 1].fd);
|
|
|
|
nsjconf->pipes[i / 2] = {0, 0};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (cleanup) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
nsjconf->pipes.erase(
|
|
|
|
std::remove(nsjconf->pipes.begin(), nsjconf->pipes.end(), std::pair<int, int>(0, 0)),
|
|
|
|
nsjconf->pipes.end());
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-05-26 07:24:58 +08:00
|
|
|
static int listenMode(nsjconf_t* nsjconf) {
|
2018-02-11 06:46:15 +08:00
|
|
|
int listenfd = net::getRecvSocket(nsjconf->bindhost.c_str(), nsjconf->port);
|
2015-05-15 05:44:48 +08:00
|
|
|
if (listenfd == -1) {
|
2018-05-28 07:40:02 +08:00
|
|
|
return EXIT_FAILURE;
|
2015-05-15 05:44:48 +08:00
|
|
|
}
|
|
|
|
for (;;) {
|
2018-05-25 08:15:47 +08:00
|
|
|
if (sigFatal > 0) {
|
2018-07-27 19:33:39 +08:00
|
|
|
subproc::killAndReapAll(nsjconf);
|
2018-05-25 08:15:47 +08:00
|
|
|
logs::logStop(sigFatal);
|
2016-07-29 21:38:22 +08:00
|
|
|
close(listenfd);
|
2018-05-28 07:40:02 +08:00
|
|
|
return EXIT_SUCCESS;
|
2015-05-15 05:44:48 +08:00
|
|
|
}
|
2018-05-25 08:15:47 +08:00
|
|
|
if (showProc) {
|
|
|
|
showProc = false;
|
2018-02-10 00:03:02 +08:00
|
|
|
subproc::displayProc(nsjconf);
|
2015-05-15 05:44:48 +08:00
|
|
|
}
|
2020-02-13 19:24:28 +08:00
|
|
|
if (pipeTraffic(nsjconf, listenfd)) {
|
|
|
|
int connfd = net::acceptConn(listenfd);
|
|
|
|
if (connfd >= 0) {
|
|
|
|
int in[2];
|
|
|
|
int out[2];
|
|
|
|
if (pipe(in) != 0 || pipe(out) != 0) {
|
|
|
|
PLOG_E("pipe");
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
nsjconf->pipes.emplace_back(connfd, in[1]);
|
|
|
|
nsjconf->pipes.emplace_back(out[0], connfd);
|
2020-02-17 05:34:19 +08:00
|
|
|
subproc::runChild(nsjconf, connfd, in[0], out[1], out[1]);
|
2020-02-13 19:24:28 +08:00
|
|
|
close(in[0]);
|
|
|
|
close(out[1]);
|
|
|
|
}
|
2015-05-15 05:44:48 +08:00
|
|
|
}
|
2018-02-10 00:03:02 +08:00
|
|
|
subproc::reapProc(nsjconf);
|
2015-05-15 05:44:48 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-05-25 08:15:47 +08:00
|
|
|
static int standaloneMode(nsjconf_t* nsjconf) {
|
2015-05-15 05:44:48 +08:00
|
|
|
for (;;) {
|
2020-02-17 05:34:19 +08:00
|
|
|
if (!subproc::runChild(
|
|
|
|
nsjconf, /* netfd= */ -1, STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO)) {
|
2018-07-24 06:23:44 +08:00
|
|
|
LOG_E("Couldn't launch the child process");
|
|
|
|
return 0xff;
|
|
|
|
}
|
|
|
|
for (;;) {
|
|
|
|
int child_status = subproc::reapProc(nsjconf);
|
|
|
|
if (subproc::countProc(nsjconf) == 0) {
|
|
|
|
if (nsjconf->mode == MODE_STANDALONE_ONCE) {
|
|
|
|
return child_status;
|
|
|
|
}
|
|
|
|
break;
|
2015-05-15 05:44:48 +08:00
|
|
|
}
|
2018-07-24 06:23:44 +08:00
|
|
|
if (showProc) {
|
|
|
|
showProc = false;
|
|
|
|
subproc::displayProc(nsjconf);
|
2018-07-23 23:13:17 +08:00
|
|
|
}
|
2018-07-24 06:23:44 +08:00
|
|
|
if (sigFatal > 0) {
|
2018-07-27 19:33:39 +08:00
|
|
|
subproc::killAndReapAll(nsjconf);
|
2018-07-24 06:23:44 +08:00
|
|
|
logs::logStop(sigFatal);
|
|
|
|
return (128 + sigFatal);
|
|
|
|
}
|
|
|
|
pause();
|
2015-05-15 05:44:48 +08:00
|
|
|
}
|
|
|
|
}
|
2015-07-08 00:33:10 +08:00
|
|
|
// not reached
|
2015-05-15 05:44:48 +08:00
|
|
|
}
|
|
|
|
|
2018-05-25 08:15:47 +08:00
|
|
|
std::unique_ptr<struct termios> getTC(int fd) {
|
2018-05-25 07:04:29 +08:00
|
|
|
std::unique_ptr<struct termios> trm(new struct termios);
|
|
|
|
|
|
|
|
if (ioctl(fd, TCGETS, trm.get()) == -1) {
|
|
|
|
PLOG_D("ioctl(fd=%d, TCGETS) failed", fd);
|
|
|
|
return nullptr;
|
|
|
|
}
|
2018-05-25 08:15:47 +08:00
|
|
|
LOG_D("Saved the current state of the TTY");
|
2018-05-25 07:04:29 +08:00
|
|
|
return trm;
|
|
|
|
}
|
|
|
|
|
2018-05-25 08:15:47 +08:00
|
|
|
void setTC(int fd, const struct termios* trm) {
|
2018-05-25 07:04:29 +08:00
|
|
|
if (!trm) {
|
|
|
|
return;
|
|
|
|
}
|
2018-05-25 08:05:12 +08:00
|
|
|
if (ioctl(fd, TCSETS, trm) == -1) {
|
2018-05-25 07:04:29 +08:00
|
|
|
PLOG_W("ioctl(fd=%d, TCSETS) failed", fd);
|
2018-05-25 08:15:47 +08:00
|
|
|
return;
|
2018-05-25 07:04:29 +08:00
|
|
|
}
|
2019-03-10 22:00:45 +08:00
|
|
|
if (tcflush(fd, TCIFLUSH) == -1) {
|
|
|
|
PLOG_W("tcflush(fd=%d, TCIFLUSH) failed", fd);
|
|
|
|
return;
|
|
|
|
}
|
2018-05-25 07:04:29 +08:00
|
|
|
}
|
|
|
|
|
2018-05-25 08:15:47 +08:00
|
|
|
} // namespace nsjail
|
|
|
|
|
2017-10-26 06:26:02 +08:00
|
|
|
int main(int argc, char* argv[]) {
|
2018-02-10 22:50:12 +08:00
|
|
|
std::unique_ptr<nsjconf_t> nsjconf = cmdline::parseArgs(argc, argv);
|
2018-05-25 08:15:47 +08:00
|
|
|
std::unique_ptr<struct termios> trm = nsjail::getTC(STDIN_FILENO);
|
2018-05-25 08:05:12 +08:00
|
|
|
|
2018-02-09 22:44:29 +08:00
|
|
|
if (!nsjconf) {
|
2017-09-25 18:00:57 +08:00
|
|
|
LOG_F("Couldn't parse cmdline options");
|
2015-05-15 05:44:48 +08:00
|
|
|
}
|
2018-02-09 22:44:29 +08:00
|
|
|
if (nsjconf->daemonize && (daemon(0, 0) == -1)) {
|
2015-05-15 05:44:48 +08:00
|
|
|
PLOG_F("daemon");
|
|
|
|
}
|
2018-02-09 22:44:29 +08:00
|
|
|
cmdline::logParams(nsjconf.get());
|
2018-05-25 08:15:47 +08:00
|
|
|
if (!nsjail::setSigHandlers()) {
|
|
|
|
LOG_F("nsjail::setSigHandlers() failed");
|
2015-05-15 05:44:48 +08:00
|
|
|
}
|
2018-05-25 08:15:47 +08:00
|
|
|
if (!nsjail::setTimer(nsjconf.get())) {
|
|
|
|
LOG_F("nsjail::setTimer() failed");
|
2015-05-15 05:44:48 +08:00
|
|
|
}
|
2018-02-12 10:11:58 +08:00
|
|
|
if (!sandbox::preparePolicy(nsjconf.get())) {
|
|
|
|
LOG_F("Couldn't prepare sandboxing policy");
|
|
|
|
}
|
2015-05-15 05:44:48 +08:00
|
|
|
|
2018-02-13 00:31:45 +08:00
|
|
|
int ret = 0;
|
2018-02-09 22:44:29 +08:00
|
|
|
if (nsjconf->mode == MODE_LISTEN_TCP) {
|
2018-05-26 07:24:58 +08:00
|
|
|
ret = nsjail::listenMode(nsjconf.get());
|
2015-05-15 05:44:48 +08:00
|
|
|
} else {
|
2018-05-25 08:15:47 +08:00
|
|
|
ret = nsjail::standaloneMode(nsjconf.get());
|
2015-05-15 05:44:48 +08:00
|
|
|
}
|
2018-02-17 10:14:54 +08:00
|
|
|
|
2018-02-13 00:31:45 +08:00
|
|
|
sandbox::closePolicy(nsjconf.get());
|
2018-05-25 08:05:12 +08:00
|
|
|
/* Try to restore the underlying console's params in case some program has changed it */
|
2019-10-04 06:33:29 +08:00
|
|
|
if (!nsjconf->daemonize) {
|
|
|
|
nsjail::setTC(STDIN_FILENO, trm.get());
|
|
|
|
}
|
2018-05-25 07:04:29 +08:00
|
|
|
|
2018-05-28 08:04:03 +08:00
|
|
|
LOG_D("Returning with %d", ret);
|
2018-02-13 00:09:45 +08:00
|
|
|
return ret;
|
2015-05-15 05:44:48 +08:00
|
|
|
}
|