From 3913749796a9abdae2f49e5dbe43da85f85b3af3 Mon Sep 17 00:00:00 2001 From: Sargun Dhillon Date: Tue, 11 Apr 2017 15:00:41 -0700 Subject: [PATCH 1/9] Add build environment dockerfile --- buildenv/Dockerfile | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 buildenv/Dockerfile diff --git a/buildenv/Dockerfile b/buildenv/Dockerfile new file mode 100644 index 0000000..98a182b --- /dev/null +++ b/buildenv/Dockerfile @@ -0,0 +1,2 @@ +FROM ubuntu:xenial +RUN apt-get update && apt-get install -y build-essential cmake git gdb valgrind python-dev libcap-dev python-pip python-virtualenv hardening-includes gnupg From fb4aa44ca98edc687d5b4eee1bd5f63f2377a561 Mon Sep 17 00:00:00 2001 From: Sargun Dhillon Date: Wed, 12 Apr 2017 01:50:05 -0700 Subject: [PATCH 2/9] Remove optimization flag from C_FLAGS, rely on Cmake to add it --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8936f18..d2b209a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -53,7 +53,7 @@ if(NOT HAS_BUILTIN_FORTIFY) add_definitions(-D_FORTIFY_SOURCE=2) endif() -set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu99 -Werror -Wextra -Wall -pedantic-errors -O2 -fstack-protector --param=ssp-buffer-size=4 -Wformat") +set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu99 -Werror -Wextra -Wall -pedantic-errors -fstack-protector --param=ssp-buffer-size=4 -Wformat") set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-Bsymbolic-functions -Wl,-z,relro -Wl,-s") # Build From 1f18862ed91d2b20f7b00978384b1b838f88e3ec Mon Sep 17 00:00:00 2001 From: Sargun Dhillon Date: Wed, 12 Apr 2017 01:50:45 -0700 Subject: [PATCH 3/9] Add code to send root fd over unix socket --- src/tini.c | 76 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/src/tini.c b/src/tini.c index 90ee3e8..a0d6328 100644 --- a/src/tini.c +++ b/src/tini.c @@ -14,6 +14,12 @@ #include #include +#include +#include +#include +#include +#include + #include "tiniConfig.h" #include "tiniLicense.h" @@ -483,6 +489,73 @@ int reap_zombies(const pid_t child_pid, int* const child_exitcode_ptr) { return 0; } +void maybe_unix_cb() { + struct sockaddr_un addr = { 0 }; + struct msghdr msg = { 0 }; + char data[] = "hello\n"; + struct cmsghdr *cmsg; + char *socket_path; + int rootfd = -1; + int sockfd = -1; + struct iovec io; + + char buf[CMSG_SPACE(sizeof(rootfd))]; + + memset(buf, '\0', sizeof(buf)); + + socket_path = getenv("UNIX_CB_PATH"); + if (!socket_path) { + PRINT_INFO("No UNIX_CB_PATH set, not connecting back to callback socket") + return; + } + + rootfd = open("/", O_RDONLY); + if (rootfd == -1) { + PRINT_FATAL("Unable to open /: '%s'", strerror(errno)); + goto error; + } + + sockfd = socket(AF_UNIX, SOCK_STREAM, 0); + if (sockfd == -1) { + PRINT_FATAL("Unable to open unix socket: '%s'", strerror(errno)); + goto error; + } + + addr.sun_family = AF_UNIX; + strncpy(addr.sun_path, socket_path, sizeof(addr.sun_path)-1); + if (connect(sockfd, (struct sockaddr*)&addr, sizeof(addr)) == -1) { + PRINT_FATAL("Unable to connect unix socket: '%s'", strerror(errno)); + goto error; + } + + io.iov_base = data; + io.iov_len = sizeof(data); + + msg.msg_iov = &io; + msg.msg_iovlen = 1; + msg.msg_control = buf; + msg.msg_controllen = sizeof(buf); + + cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + cmsg->cmsg_len = CMSG_LEN(sizeof(rootfd)); + msg.msg_controllen = cmsg->cmsg_len; + memcpy(CMSG_DATA(cmsg), &rootfd, sizeof(rootfd)); + + if (sendmsg(sockfd, &msg, 0) < 0) { + PRINT_FATAL("Unable to send message: '%s'", strerror(errno)); + goto error; + } + + return; + + error: + if (rootfd > 0) + close(rootfd); + if (sockfd > 0) + close(sockfd); +} int main(int argc, char *argv[]) { pid_t child_pid; @@ -529,6 +602,9 @@ int main(int argc, char *argv[]) { /* Are we going to reap zombies properly? If not, warn. */ reaper_check(); + /* Maybe pass our pid to the pid sock */ + maybe_unix_cb(); + /* Go on */ int spawn_ret = spawn(&child_sigconf, *child_args_ptr, &child_pid); if (spawn_ret) { From 0982ad2613d4ace40c5d14b0815f1285eac69451 Mon Sep 17 00:00:00 2001 From: Sargun Dhillon Date: Wed, 12 Apr 2017 02:27:36 -0700 Subject: [PATCH 4/9] Add FD swap / redirect stderr code --- src/tini.c | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/tini.c b/src/tini.c index a0d6328..5d8c42b 100644 --- a/src/tini.c +++ b/src/tini.c @@ -136,8 +136,29 @@ int isolate_child() { int spawn(const signal_configuration_t* const sigconf_ptr, char* const argv[], int* const child_pid_ptr) { + int new_stdout_fd = 1; + int new_stderr_fd = 2; + char *redir_path; pid_t pid; + redir_path = getenv("REDIR_STDOUT"); + if (redir_path) { + new_stdout_fd = open(redir_path, O_WRONLY | O_CREAT | O_APPEND); + if (new_stdout_fd == -1) { + PRINT_FATAL("Failed to open stdout redirect path: %s", strerror(errno)); + return 1; + } + } + + redir_path = getenv("REDIR_STDERR"); + if (redir_path) { + new_stderr_fd = open(redir_path, O_WRONLY | O_CREAT | O_APPEND); + if (new_stderr_fd == -1) { + PRINT_FATAL("Failed to open stderr redirect path: %s", strerror(errno)); + return 1; + } + } + // TODO: check if tini was a foreground process to begin with (it's not OK to "steal" the foreground!") pid = fork(); @@ -156,6 +177,17 @@ int spawn(const signal_configuration_t* const sigconf_ptr, char* const argv[], i return 1; } + // Do the FD swap + // No need to set CLO_EXEC on existing stdout, stderr FDs, because we're closing them anyway + if (dup2(new_stdout_fd, 1) == -1) { + PRINT_FATAL("Failed to duplicate stdout FD: %s", strerror(errno)); + return 1; + } + if (dup2(new_stderr_fd, 2) == -1) { + PRINT_FATAL("Failed to duplicate stdout FD: %s", strerror(errno)); + return 1; + } + execvp(argv[0], argv); // execvp will only return on an error so make sure that we check the errno From 118c10e8aa6a32f504bfa94962452296a5fb9cf6 Mon Sep 17 00:00:00 2001 From: Sargun Dhillon Date: Wed, 12 Apr 2017 12:28:30 -0700 Subject: [PATCH 5/9] Set default mode on open --- src/tini.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/tini.c b/src/tini.c index 5d8c42b..5527b78 100644 --- a/src/tini.c +++ b/src/tini.c @@ -23,6 +23,9 @@ #include "tiniConfig.h" #include "tiniLicense.h" +#define S_IWUGO (S_IWUSR|S_IWGRP|S_IWOTH) +#define S_IRUGO (S_IRUSR|S_IRGRP|S_IROTH) + #if TINI_MINIMAL #define PRINT_FATAL(...) fprintf(stderr, __VA_ARGS__); fprintf(stderr, "\n"); #define PRINT_WARNING(...) if (verbosity > 0) { fprintf(stderr, __VA_ARGS__); fprintf(stderr, "\n"); } @@ -143,7 +146,7 @@ int spawn(const signal_configuration_t* const sigconf_ptr, char* const argv[], i redir_path = getenv("REDIR_STDOUT"); if (redir_path) { - new_stdout_fd = open(redir_path, O_WRONLY | O_CREAT | O_APPEND); + new_stdout_fd = open(redir_path, O_WRONLY | O_CREAT | O_APPEND, S_IRUGO | S_IWUGO); if (new_stdout_fd == -1) { PRINT_FATAL("Failed to open stdout redirect path: %s", strerror(errno)); return 1; @@ -152,7 +155,7 @@ int spawn(const signal_configuration_t* const sigconf_ptr, char* const argv[], i redir_path = getenv("REDIR_STDERR"); if (redir_path) { - new_stderr_fd = open(redir_path, O_WRONLY | O_CREAT | O_APPEND); + new_stderr_fd = open(redir_path, O_WRONLY | O_CREAT | O_APPEND, S_IRUGO | S_IWUGO); if (new_stderr_fd == -1) { PRINT_FATAL("Failed to open stderr redirect path: %s", strerror(errno)); return 1; From 9012d088f2b3344482a14d36566db4e973177890 Mon Sep 17 00:00:00 2001 From: Sargun Dhillon Date: Wed, 12 Apr 2017 19:57:07 -0700 Subject: [PATCH 6/9] Change variables to be Titus specific --- src/tini.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/tini.c b/src/tini.c index 5527b78..6764cba 100644 --- a/src/tini.c +++ b/src/tini.c @@ -25,6 +25,10 @@ #define S_IWUGO (S_IWUSR|S_IWGRP|S_IWOTH) #define S_IRUGO (S_IRUSR|S_IRGRP|S_IROTH) +#define REDIRECT_STDERR "TITUS_REDIRECT_STDERR" +#define REDIRECT_STDOUT "TITUS_REDIRECT_STDOUT" +#define TITUS_CB_PATH "TITUS_UNIX_CB_PATH" + #if TINI_MINIMAL #define PRINT_FATAL(...) fprintf(stderr, __VA_ARGS__); fprintf(stderr, "\n"); @@ -144,7 +148,7 @@ int spawn(const signal_configuration_t* const sigconf_ptr, char* const argv[], i char *redir_path; pid_t pid; - redir_path = getenv("REDIR_STDOUT"); + redir_path = getenv(REDIRECT_STDOUT); if (redir_path) { new_stdout_fd = open(redir_path, O_WRONLY | O_CREAT | O_APPEND, S_IRUGO | S_IWUGO); if (new_stdout_fd == -1) { @@ -153,7 +157,7 @@ int spawn(const signal_configuration_t* const sigconf_ptr, char* const argv[], i } } - redir_path = getenv("REDIR_STDERR"); + redir_path = getenv(REDIRECT_STDERR); if (redir_path) { new_stderr_fd = open(redir_path, O_WRONLY | O_CREAT | O_APPEND, S_IRUGO | S_IWUGO); if (new_stderr_fd == -1) { @@ -538,7 +542,7 @@ void maybe_unix_cb() { memset(buf, '\0', sizeof(buf)); - socket_path = getenv("UNIX_CB_PATH"); + socket_path = getenv(TITUS_CB_PATH); if (!socket_path) { PRINT_INFO("No UNIX_CB_PATH set, not connecting back to callback socket") return; From 4f0a9723400989a3269849ee8c3c569b8f35d716 Mon Sep 17 00:00:00 2001 From: Sargun Dhillon Date: Thu, 13 Apr 2017 11:05:08 -0700 Subject: [PATCH 7/9] Unset Titus-specific environment variables --- src/tini.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/tini.c b/src/tini.c index 6764cba..867c8e3 100644 --- a/src/tini.c +++ b/src/tini.c @@ -195,6 +195,11 @@ int spawn(const signal_configuration_t* const sigconf_ptr, char* const argv[], i return 1; } + // Unset TINI specific environment variables + unsetenv(REDIRECT_STDERR); + unsetenv(REDIRECT_STDOUT); + unsetenv(TITUS_CB_PATH); + execvp(argv[0], argv); // execvp will only return on an error so make sure that we check the errno From e266387a86dd6677a3d9628871d463fd69302e8f Mon Sep 17 00:00:00 2001 From: Sargun Dhillon Date: Sat, 22 Apr 2017 16:21:23 -0700 Subject: [PATCH 8/9] Add stdio attribute --- src/tini.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/tini.c b/src/tini.c index 867c8e3..d0a02e5 100644 --- a/src/tini.c +++ b/src/tini.c @@ -19,6 +19,8 @@ #include #include #include +#include +#include #include "tiniConfig.h" #include "tiniLicense.h" @@ -29,6 +31,7 @@ #define REDIRECT_STDOUT "TITUS_REDIRECT_STDOUT" #define TITUS_CB_PATH "TITUS_UNIX_CB_PATH" +const char stdioattr[] = "user.stdio"; #if TINI_MINIMAL #define PRINT_FATAL(...) fprintf(stderr, __VA_ARGS__); fprintf(stderr, "\n"); @@ -148,6 +151,9 @@ int spawn(const signal_configuration_t* const sigconf_ptr, char* const argv[], i char *redir_path; pid_t pid; + // So, we might leak file descriptors here. For example, if we successfully wire up the stdout, + // but the stderr fd fails. Fortunately, this should make the init in the container bail entirely. + // This will have the side-effect of closing all of our file descriptors. redir_path = getenv(REDIRECT_STDOUT); if (redir_path) { new_stdout_fd = open(redir_path, O_WRONLY | O_CREAT | O_APPEND, S_IRUGO | S_IWUGO); @@ -155,6 +161,10 @@ int spawn(const signal_configuration_t* const sigconf_ptr, char* const argv[], i PRINT_FATAL("Failed to open stdout redirect path: %s", strerror(errno)); return 1; } + if (fsetxattr(new_stdout_fd, stdioattr, NULL, 0, 0) == -1) { + PRINT_FATAL("Unable to set stdio attribute on stdout redirect: %s", strerror(errno)); + return 1; + } } redir_path = getenv(REDIRECT_STDERR); @@ -164,6 +174,10 @@ int spawn(const signal_configuration_t* const sigconf_ptr, char* const argv[], i PRINT_FATAL("Failed to open stderr redirect path: %s", strerror(errno)); return 1; } + if (fsetxattr(new_stderr_fd, stdioattr, NULL, 0, 0) == -1) { + PRINT_FATAL("Unable to set stdio attribute on stderr redirect: %s", strerror(errno)); + return 1; + } } // TODO: check if tini was a foreground process to begin with (it's not OK to "steal" the foreground!") @@ -218,6 +232,11 @@ int spawn(const signal_configuration_t* const sigconf_ptr, char* const argv[], i return status; } else { // Parent + if (new_stdout_fd != 1) + close(new_stdout_fd); + if (new_stderr_fd != 2) + close(new_stderr_fd); + PRINT_INFO("Spawned child process '%s' with pid '%i'", argv[0], pid); *child_pid_ptr = pid; return 0; From 8f9385756eb2af282f8aedb5cbb371c3f5c52868 Mon Sep 17 00:00:00 2001 From: Sargun Dhillon Date: Mon, 24 Apr 2017 13:15:58 -0700 Subject: [PATCH 9/9] Add libattr1-dev to build deps --- buildenv/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buildenv/Dockerfile b/buildenv/Dockerfile index 98a182b..86c95a9 100644 --- a/buildenv/Dockerfile +++ b/buildenv/Dockerfile @@ -1,2 +1,2 @@ FROM ubuntu:xenial -RUN apt-get update && apt-get install -y build-essential cmake git gdb valgrind python-dev libcap-dev python-pip python-virtualenv hardening-includes gnupg +RUN apt-get update && apt-get install -y build-essential cmake git gdb valgrind python-dev libcap-dev python-pip python-virtualenv hardening-includes gnupg libattr1-dev