diff --git a/libhttp/server.c b/libhttp/server.c index abe307f..d42326e 100644 --- a/libhttp/server.c +++ b/libhttp/server.c @@ -52,7 +52,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -138,8 +140,13 @@ int x_poll(struct pollfd *fds, nfds_t nfds, int timeout) { #define poll x_poll #endif +char unixDomainSocket[UNIX_PATH_MAX]; time_t currentTime; +int unixDomainUser; +int unixDomainGroup; +int unixDomainChmod; + struct PayLoad { int (*handler)(struct HttpConnection *, void *, const char *, int); void *arg; @@ -283,6 +290,38 @@ void initServer(struct Server *server, int localhostOnly, int portMin, server->numConnections = 0; int true = 1; + + if (unixDomainSocket[0]) { + server->serverFd = socket(AF_UNIX, SOCK_STREAM, 0); + check(server->serverFd >= 0); + check(!setsockopt(server->serverFd, SOL_SOCKET, SO_REUSEADDR, + &true, sizeof(true))); + struct sockaddr_un serverAddr = { 0 }; + + unlink(unixDomainSocket); + + serverAddr.sun_family = AF_UNIX; + strcpy(serverAddr.sun_path, unixDomainSocket); + int servlen = sizeof(serverAddr.sun_family) + strlen(unixDomainSocket); + + if (bind(server->serverFd, (struct sockaddr *)&serverAddr, servlen)) { + fatal("Failed to bind to unix socket"); + } + + check(!chown(unixDomainSocket, unixDomainUser, unixDomainGroup)); + check(!chmod(unixDomainSocket, unixDomainChmod)); + + check(!listen(server->serverFd, SOMAXCONN)); + check(server->pollFds = malloc(sizeof(struct pollfd))); + server->pollFds->fd = server->serverFd; + server->pollFds->events = POLLIN; + + initTrie(&server->handlers, serverDestroyHandlers, NULL); + serverRegisterStreamingHttpHandler(server, "/quit", serverQuitHandler, NULL); + initSSL(&server->ssl); + return; + } + server->serverFd = socket(PF_INET, SOCK_STREAM, 0); check(server->serverFd >= 0); check(!setsockopt(server->serverFd, SOL_SOCKET, SO_REUSEADDR, @@ -347,6 +386,8 @@ void destroyServer(struct Server *server) { free(server->pollFds); destroyTrie(&server->handlers); destroySSL(&server->ssl); + + if (unixDomainSocket[0]) unlink(unixDomainSocket); } } diff --git a/libhttp/server.h b/libhttp/server.h index 8900d99..ae7cc1b 100644 --- a/libhttp/server.h +++ b/libhttp/server.h @@ -52,6 +52,15 @@ #include "libhttp/http.h" #include "libhttp/ssl.h" +#ifndef UNIX_PATH_MAX +#define UNIX_PATH_MAX 108 +#endif + +extern int unixDomainUser; +extern int unixDomainGroup; +extern int unixDomainChmod; +extern char unixDomainSocket[UNIX_PATH_MAX]; + struct Server; struct ServerConnection { diff --git a/shellinabox/shellinaboxd.c b/shellinabox/shellinaboxd.c index c6f150e..3a69ac1 100644 --- a/shellinabox/shellinaboxd.c +++ b/shellinabox/shellinaboxd.c @@ -61,6 +61,9 @@ #include #include #include +#include +#include + #include #ifdef HAVE_SYS_PRCTL_H @@ -68,6 +71,7 @@ #endif #include "libhttp/http.h" +#include "libhttp/server.h" #include "logging/logging.h" #include "shellinabox/externalfile.h" #include "shellinabox/launcher.h" @@ -785,6 +789,7 @@ static void usage(void) { " -s, --service=SERVICE define one or more services\n" "%s" " -q, --quiet turn off all messages\n" + " --unixdomain-only=PATH:USER:GROUP:CHMOD listen on unix socket\n" " -u, --user=UID switch to this user (default: %s)\n" " --user-css=STYLES defines user-selectable CSS options\n" " -v, --verbose enable logging messages\n" @@ -891,6 +896,7 @@ static void parseArgs(int argc, char * const argv[]) { { "disable-ssl", 0, 0, 't' }, { "disable-ssl-menu", 0, 0, 0 }, { "quiet", 0, 0, 'q' }, + { "unixdomain-only", 1, 0, 0, }, { "user", 1, 0, 'u' }, { "user-css", 1, 0, 0 }, { "verbose", 0, 0, 'v' }, @@ -1126,6 +1132,43 @@ static void parseArgs(int argc, char * const argv[]) { } verbosity = MSG_QUIET; logSetLogLevel(verbosity); + } else if (!idx--) { + // Unix domain only + if (!optarg || !*optarg) { + fatal("Option \"--unixdomain-only\" expects an argument."); + } + char *ptr, *s, *tmp; + + s = optarg; + ptr = strchr(s, ':'); + if (ptr == NULL) { + fatal("Syntax error in unixdomain-only definition \"%s\".", optarg); + } + check(ptr - s < UNIX_PATH_MAX); + memcpy(unixDomainSocket, s, ptr - s); + unixDomainSocket[ptr - s] = '\000'; + + s = ptr + 1; + ptr = strchr(s, ':'); + if (ptr == NULL) { + fatal("Syntax error in unixdomain-only definition \"%s\".", optarg); + } + check(tmp = strndup(s, ptr - s)); + unixDomainUser = parseUserArg(tmp, NULL); + free(tmp); + + s = ptr + 1; + ptr = strchr(s, ':'); + if (ptr == NULL) { + fatal("Syntax error in unixdomain-only definition \"%s\".", optarg); + } + check(tmp = strndup(s, ptr - s)); + unixDomainGroup = parseGroupArg(tmp, NULL); + free(tmp); + + s = ptr + 1; + unixDomainChmod = strtol(s, NULL, 0); + } else if (!idx--) { // User if (runAsUser >= 0) {