diff options
author | Laurent Bercot <ska-skaware@skarnet.org> | 2015-01-15 20:14:44 +0000 |
---|---|---|
committer | Laurent Bercot <ska-skaware@skarnet.org> | 2015-01-15 20:14:44 +0000 |
commit | 87c5b2118efcee65eeda3f743d081ea9c2b866d9 (patch) | |
tree | 31ca07d6134adf44bc3d58f4fcf4ea8be9cb7dbb /src/conn-tools/s6-sudod.c | |
parent | cd2500fcc704287c4994a3253b593593c867913e (diff) | |
download | s6-87c5b2118efcee65eeda3f743d081ea9c2b866d9.tar.xz |
Move Unix domain utilities and access control utilites,
as well as the accessrules library, from s6-networking to here
Diffstat (limited to 'src/conn-tools/s6-sudod.c')
-rw-r--r-- | src/conn-tools/s6-sudod.c | 233 |
1 files changed, 233 insertions, 0 deletions
diff --git a/src/conn-tools/s6-sudod.c b/src/conn-tools/s6-sudod.c new file mode 100644 index 0000000..8c52bb9 --- /dev/null +++ b/src/conn-tools/s6-sudod.c @@ -0,0 +1,233 @@ +/* ISC license. */ + +#include <sys/types.h> +#include <unistd.h> +#include <errno.h> +#include <fcntl.h> +#include <signal.h> +#include <skalibs/uint.h> +#include <skalibs/allreadwrite.h> +#include <skalibs/sgetopt.h> +#include <skalibs/bytestr.h> +#include <skalibs/buffer.h> +#include <skalibs/stralloc.h> +#include <skalibs/strerr2.h> +#include <skalibs/tai.h> +#include <skalibs/iopause.h> +#include <skalibs/selfpipe.h> +#include <skalibs/env.h> +#include <skalibs/djbunix.h> +#include <skalibs/unix-timed.h> +#include <skalibs/unixmessage.h> +#include "s6-sudo.h" + +#define USAGE "s6-sudod [ -0 ] [ -1 ] [ -2 ] [ -t timeout ] args..." +#define dieusage() strerr_dieusage(100, USAGE) +#define dienomem() strerr_diefu1sys(111, "stralloc_catb") + +int main (int argc, char const *const *argv, char const *const *envp) +{ + subgetopt_t l = SUBGETOPT_ZERO ; + unixmessage_t m ; + unsigned int nullfds = 0, t = 2000 ; + pid_t pid ; + uint32 envc = env_len(envp) ; + uint32 cargc, cenvc, carglen, cenvlen ; + int spfd ; + tain_t deadline = TAIN_INFINITE_RELATIVE ; + PROG = "s6-sudod" ; + for (;;) + { + register int opt = subgetopt_r(argc, argv, "012t:", &l) ; + if (opt < 0) break ; + switch (opt) + { + case '0' : nullfds |= 1 ; break ; + case '1' : nullfds |= 2 ; break ; + case '2' : nullfds |= 4 ; break ; + case 't' : if (!uint0_scan(l.arg, &t)) dieusage() ; break ; + default : dieusage() ; + } + } + argc -= l.ind ; argv += l.ind ; + if (t) tain_from_millisecs(&deadline, t) ; + if ((ndelay_on(0) < 0) || (ndelay_on(1) < 0)) + strerr_diefu1sys(111, "make socket non-blocking") ; + + tain_now_g() ; + tain_add_g(&deadline, &deadline) ; + buffer_putnoflush(buffer_1small, S6_SUDO_BANNERB, S6_SUDO_BANNERB_LEN) ; + if (!buffer_timed_flush_g(buffer_1small, &deadline)) + strerr_diefu1sys(111, "write banner to client") ; + if (unixmessage_timed_receive_g(unixmessage_receiver_0, &m, &deadline) <= 0) + strerr_diefu1sys(111, "read message from client") ; + if (m.nfds != 3) + strerr_dief1x(100, "client did not send 3 fds") ; + if (m.len < 16 + S6_SUDO_BANNERA_LEN) + strerr_dief1x(100, "wrong client message") ; + if (str_diffn(m.s, S6_SUDO_BANNERA, S6_SUDO_BANNERA_LEN)) + strerr_dief1x(100, "wrong client banner") ; + uint32_unpack_big(m.s + S6_SUDO_BANNERA_LEN, &cargc) ; + uint32_unpack_big(m.s + S6_SUDO_BANNERA_LEN + 4, &cenvc) ; + uint32_unpack_big(m.s + S6_SUDO_BANNERA_LEN + 8, &carglen) ; + uint32_unpack_big(m.s + S6_SUDO_BANNERA_LEN + 12, &cenvlen) ; + if (S6_SUDO_BANNERA_LEN + 16 + carglen + cenvlen != m.len) + strerr_dief1x(100, "wrong client argc/envlen") ; + if ((cargc > 131072) || (cenvc > 131072)) + strerr_dief1x(100, "too many args/envvars from client") ; + + if (nullfds & 1) + { + close(m.fds[0]) ; + m.fds[0] = open2("/dev/null", O_RDONLY) ; + if (m.fds[0] < 0) strerr_diefu2sys(111, "open /dev/null for ", "reading") ; + } + if (nullfds & 2) + { + close(m.fds[1]) ; + m.fds[1] = open2("/dev/null", O_WRONLY) ; + if (m.fds[1] < 0) strerr_diefu2sys(111, "open /dev/null for ", "writing") ; + } + if (nullfds & 4) + { + close(m.fds[2]) ; + m.fds[2] = 2 ; + } + + { + char const *targv[argc + 1 + cargc] ; + char const *tenvp[envc + 1 + cenvc] ; + int p[2] ; + register unsigned int i = 0 ; + for (; i < (unsigned int)argc ; i++) targv[i] = argv[i] ; + for (i = 0 ; i <= envc ; i++) tenvp[i] = envp[i] ; + if (!env_make(targv + argc, cargc, m.s + S6_SUDO_BANNERA_LEN + 16, carglen)) + { + char c = errno ; + buffer_putnoflush(buffer_1small, &c, 1) ; + buffer_timed_flush_g(buffer_1small, &deadline) ; + errno = c ; + strerr_diefu1sys(111, "make child argv") ; + } + if (!env_make(tenvp + envc + 1, cenvc, m.s + S6_SUDO_BANNERA_LEN + 16 + carglen, cenvlen)) + { + char c = errno ; + buffer_putnoflush(buffer_1small, &c, 1) ; + buffer_timed_flush_g(buffer_1small, &deadline) ; + errno = c ; + strerr_diefu1sys(111, "make child envp") ; + } + targv[argc + cargc] = 0 ; + + for (i = 0 ; i < cenvc ; i++) + { + char const *var = tenvp[envc + 1 + i] ; + register unsigned int j = 0 ; + register unsigned int len = str_chr(var, '=') ; + if (!var[len]) + { + char c = EINVAL ; + buffer_putnoflush(buffer_1small, &c, 1) ; + buffer_timed_flush_g(buffer_1small, &deadline) ; + strerr_dief1x(100, "bad environment from client") ; + } + for (; j < envc ; j++) if (!str_diffn(var, tenvp[j], len+1)) break ; + if ((j < envc) && !tenvp[j][len+1]) tenvp[j] = var ; + } + + spfd = selfpipe_init() ; + if (spfd < 0) strerr_diefu1sys(111, "selfpipe_init") ; + if (selfpipe_trap(SIGCHLD) < 0) strerr_diefu1sys(111, "trap SIGCHLD") ; + if (pipe(p) < 0) strerr_diefu1sys(111, "pipe") ; + if (coe(p[1]) < 0) strerr_diefu1sys(111, "coe pipe") ; + pid = fork() ; + if (pid < 0) strerr_diefu1sys(111, "fork") ; + if (!pid) + { + PROG = "s6-sudod (child)" ; + fd_close(p[0]) ; + if ((fd_move(2, m.fds[2]) < 0) + || (fd_move(1, m.fds[1]) < 0) + || (fd_move(0, m.fds[0]) < 0)) + { + char c = errno ; + fd_write(p[1], &c, 1) ; + strerr_diefu1sys(111, "move fds") ; + } + selfpipe_finish() ; + pathexec0_run(targv, tenvp) ; + { + char c = errno ; + fd_write(p[1], &c, 1) ; + } + strerr_dieexec(111, targv[0]) ; + } + fd_close(p[1]) ; + { + char c ; + register int r = fd_read(p[0], &c, 1) ; + if (r < 0) strerr_diefu1sys(111, "read from child") ; + if (r) + { + buffer_putnoflush(buffer_1small, &c, 1) ; + buffer_timed_flush_g(buffer_1small, &deadline) ; + return 111 ; + } + } + fd_close(p[0]) ; + } + + fd_close(m.fds[0]) ; + fd_close(m.fds[1]) ; + if (!(nullfds & 4)) fd_close(m.fds[2]) ; + unixmessage_receiver_free(unixmessage_receiver_0) ; + buffer_putnoflush(buffer_1small, "", 1) ; + if (!buffer_timed_flush_g(buffer_1small, &deadline)) + strerr_diefu1sys(111, "send confirmation to client") ; + + { + iopause_fd x[2] = { { .fd = 0, .events = 0 }, { .fd = spfd, .events = IOPAUSE_READ } } ; + int cont = 1 ; + while (cont) + { + if (iopause_g(x, 2, 0) < 0) strerr_diefu1sys(111, "iopause") ; + if (x[1].revents) + { + for (;;) + { + int c = selfpipe_read() ; + if (c < 0) strerr_diefu1sys(111, "read from selfpipe") ; + else if (!c) break ; + else if (c == SIGCHLD) + { + int wstat ; + register int r = wait_pid_nohang(pid, &wstat) ; + if ((r < 0) && (errno != ECHILD)) + strerr_diefu1sys(111, "wait_pid_nohang") ; + else if (r > 0) + { + char pack[UINT_PACK] ; + uint_pack_big(pack, (unsigned int)wstat) ; + buffer_putnoflush(buffer_1small, pack, UINT_PACK) ; + cont = 0 ; + } + } + else + strerr_dief1sys(101, "internal inconsistency, please submit a bug-report") ; + } + } + if (x[0].revents && cont) + { + kill(pid, SIGTERM) ; + kill(pid, SIGCONT) ; + x[0].fd = -1 ; + return 1 ; + } + } + } + if (ndelay_off(1) < 0) + strerr_diefu1sys(111, "set stdout blocking") ; + if (!buffer_flush(buffer_1small)) + strerr_diefu1sys(111, "write status to client") ; + return 0 ; +} |