pipe socket traffic in and out of sandboxee
authorWiktor Garbacz <wiktorg@google.com>
Thu, 13 Feb 2020 11:24:28 +0000 (12:24 +0100)
committerWiktor Garbacz <wiktorg@google.com>
Fri, 14 Feb 2020 16:07:14 +0000 (17:07 +0100)
net.cc
nsjail.cc
nsjail.h

diff --git a/net.cc b/net.cc
index 6e5745a7e15577a2a44097b6d0789849c59fe651..cfe4e693a88f765b313577f473816a5e45390148 100644 (file)
--- a/net.cc
+++ b/net.cc
@@ -23,6 +23,7 @@
 
 #include <arpa/inet.h>
 #include <errno.h>
+#include <fcntl.h>
 #include <net/if.h>
 #include <net/route.h>
 #include <netinet/in.h>
@@ -232,6 +233,10 @@ int getRecvSocket(const char* bindhost, int port) {
                PLOG_E("socket(AF_INET6)");
                return -1;
        }
+       if (fcntl(sockfd, F_SETFL, O_NONBLOCK)) {
+               PLOG_E("fcntl(%d, F_SETFL, O_NONBLOCK)", sockfd);
+               return -1;
+       }
        int so = 1;
        if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &so, sizeof(so)) == -1) {
                PLOG_E("setsockopt(%d, SO_REUSEADDR)", sockfd);
@@ -264,7 +269,7 @@ int getRecvSocket(const char* bindhost, int port) {
 int acceptConn(int listenfd) {
        struct sockaddr_in6 cli_addr;
        socklen_t socklen = sizeof(cli_addr);
-       int connfd = accept(listenfd, (struct sockaddr*)&cli_addr, &socklen);
+       int connfd = accept4(listenfd, (struct sockaddr*)&cli_addr, &socklen, SOCK_NONBLOCK);
        if (connfd == -1) {
                if (errno != EINTR) {
                        PLOG_E("accept(%d)", listenfd);
index 691f4dd54e62ca598bb98d7088b251cbee60cbc5..ce11e078509ec1323f2b740073ae436f1064148b 100644 (file)
--- a/nsjail.cc
+++ b/nsjail.cc
@@ -21,6 +21,8 @@
 
 #include "nsjail.h"
 
+#include <fcntl.h>
+#include <poll.h>
 #include <signal.h>
 #include <stdbool.h>
 #include <stdio.h>
 #include <termios.h>
 #include <unistd.h>
 
+#include <algorithm>
+#include <cerrno>
 #include <memory>
+#include <vector>
 
 #include "cmdline.h"
 #include "logs.h"
@@ -47,10 +52,7 @@ static __thread int sigFatal = 0;
 static __thread bool showProc = false;
 
 static void sigHandler(int sig) {
-       if (sig == SIGALRM) {
-               return;
-       }
-       if (sig == SIGCHLD) {
+       if (sig == SIGALRM || sig == SIGCHLD || sig == SIGPIPE) {
                return;
        }
        if (sig == SIGUSR1 || sig == SIGQUIT) {
@@ -74,7 +76,7 @@ static bool setSigHandler(int sig) {
 
        if (sig == SIGTTIN || sig == SIGTTOU) {
                sa.sa_handler = SIG_IGN;
-       };
+       }
        if (sigaction(sig, &sa, NULL) == -1) {
                PLOG_E("sigaction(%d)", sig);
                return false;
@@ -115,6 +117,72 @@ static bool setTimer(nsjconf_t* nsjconf) {
        return true;
 }
 
+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;
+}
+
 static int listenMode(nsjconf_t* nsjconf) {
        int listenfd = net::getRecvSocket(nsjconf->bindhost.c_str(), nsjconf->port);
        if (listenfd == -1) {
@@ -131,10 +199,21 @@ static int listenMode(nsjconf_t* nsjconf) {
                        showProc = false;
                        subproc::displayProc(nsjconf);
                }
-               int connfd = net::acceptConn(listenfd);
-               if (connfd >= 0) {
-                       subproc::runChild(nsjconf, connfd, connfd, connfd);
-                       close(connfd);
+               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);
+                               subproc::runChild(nsjconf, in[0], out[1], out[1]);
+                               close(in[0]);
+                               close(out[1]);
+                       }
                }
                subproc::reapProc(nsjconf);
        }
index 68b1253aa9fe0f5b4802c2cf68fc7bb2d17cbbe0..98c3661ee24e525bb23004bccf43b18a4b86c67a 100644 (file)
--- a/nsjail.h
+++ b/nsjail.h
@@ -44,6 +44,7 @@ static const int nssigs[] = {
     SIGTERM,
     SIGTTIN,
     SIGTTOU,
+    SIGPIPE,
 };
 
 struct pids_t {
@@ -157,6 +158,7 @@ struct nsjconf_t {
        std::vector<int> openfds;
        std::vector<int> caps;
        std::vector<std::string> ifaces;
+       std::vector<std::pair<int, int>> pipes;
 };
 
 #endif /* _NSJAIL_H */