summaryrefslogtreecommitdiff
path: root/src/conn-tools/s6-sudod.c
diff options
context:
space:
mode:
authorLaurent Bercot <ska-skaware@skarnet.org>2015-01-15 20:14:44 +0000
committerLaurent Bercot <ska-skaware@skarnet.org>2015-01-15 20:14:44 +0000
commit87c5b2118efcee65eeda3f743d081ea9c2b866d9 (patch)
tree31ca07d6134adf44bc3d58f4fcf4ea8be9cb7dbb /src/conn-tools/s6-sudod.c
parentcd2500fcc704287c4994a3253b593593c867913e (diff)
downloads6-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.c233
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 ;
+}