1 /* ISC license. */
2 
3 #include <string.h>
4 #include <stdint.h>
5 #include <errno.h>
6 #include <fcntl.h>
7 #include <signal.h>
8 #include <sys/uio.h>
9 #include <sys/wait.h>
10 
11 #include <skalibs/types.h>
12 #include <skalibs/sgetopt.h>
13 #include <skalibs/buffer.h>
14 #include <skalibs/stralloc.h>
15 #include <skalibs/strerr2.h>
16 #include <skalibs/djbunix.h>
17 #include <skalibs/tai.h>
18 #include <skalibs/env.h>
19 #include <skalibs/unix-timed.h>
20 #include <skalibs/unixmessage.h>
21 
22 #include "s6-sudo.h"
23 
24 #define USAGE "s6-sudoc [ -e ] [ -t timeoutconn ] [ -T timeoutrun ] [ args... ]"
25 #define dieusage() strerr_dieusage(100, USAGE)
26 #define dienomem() strerr_diefu1sys(111, "stralloc_catb")
27 
main(int argc,char const * const * argv,char const * const * envp)28 int main (int argc, char const *const *argv, char const *const *envp)
29 {
30   char buf6[64] ;
31   buffer b6 = BUFFER_INIT(&buffer_read, 6, buf6, 64) ;
32   unixmessage_sender_t b7 = UNIXMESSAGE_SENDER_INIT(7) ;
33   subgetopt_t l = SUBGETOPT_ZERO ;
34   unsigned int t = 0, T = 0 ;
35   int doenv = 1 ;
36 
37   tain_t deadline = TAIN_INFINITE_RELATIVE ;
38   PROG = "s6-sudoc" ;
39   for (;;)
40   {
41     int opt = subgetopt_r(argc, argv, "et:T:", &l) ;
42     if (opt < 0) break ;
43     switch (opt)
44     {
45       case 'e' : doenv = 0 ; break ;
46       case 't' : if (!uint0_scan(l.arg, &t)) dieusage() ; break ;
47       case 'T' : if (!uint0_scan(l.arg, &T)) dieusage() ; break ;
48       default : dieusage() ;
49     }
50   }
51   argc -= l.ind ; argv += l.ind ;
52   if (t) tain_from_millisecs(&deadline, t) ;
53   if ((ndelay_on(6) < 0) || (ndelay_on(7) < 0))
54     strerr_diefu1sys(111, "make socket non-blocking") ;
55   if (!fd_sanitize() || !fd_ensure_open(2, 1))
56     strerr_diefu1sys(111, "sanitize stdin/stdout/stderr") ;
57 
58   tain_now_set_stopwatch_g() ;
59   tain_add_g(&deadline, &deadline) ;
60   {
61     size_t r ;
62     char tmp[S6_SUDO_BANNERB_LEN] ;
63     r = buffer_timed_get_g(&b6, tmp, S6_SUDO_BANNERB_LEN, &deadline) ;
64     if (!r)
65       strerr_diefu1x(111, "connect to the s6-sudod server - check that you have appropriate permissions") ;
66     if (r < S6_SUDO_BANNERB_LEN)
67       strerr_diefu1sys(111, "read banner from s6-sudod") ;
68     if (strncmp(tmp, S6_SUDO_BANNERB, S6_SUDO_BANNERB_LEN))
69       strerr_dief1x(100, "wrong banner - check that you are connecting to a s6-sudod server") ;
70   }
71   {
72     int fds[3] = { 0, 1, 2 } ;
73     char pack[16] ;
74     struct iovec v[4] = {
75       { .iov_base = S6_SUDO_BANNERA, .iov_len = S6_SUDO_BANNERA_LEN },
76       { .iov_base = pack, .iov_len = 16 },
77       { .iov_base = 0, .iov_len = 0 },
78       { .iov_base = 0, .iov_len = 0 } } ;
79     unixmessage_v_t mv = { .v = v, .vlen = 4, .fds = fds, .nfds = 3 } ;
80     stralloc sa = STRALLOC_ZERO ;
81     size_t envlen = doenv ? env_len(envp) : 0 ;
82     uint32_pack_big(pack, (uint32_t)argc) ;
83     uint32_pack_big(pack + 4, (uint32_t)envlen) ;
84     if (!env_string(&sa, argv, argc)) dienomem() ;
85     v[2].iov_len = sa.len ;
86     uint32_pack_big(pack + 8, (uint32_t)v[2].iov_len) ;
87     if (doenv)
88     {
89       if (!env_string(&sa, envp, envlen)) dienomem() ;
90       v[3].iov_len = sa.len - v[2].iov_len ;
91     }
92     uint32_pack_big(pack + 12, (uint32_t)v[3].iov_len) ;
93     v[2].iov_base = sa.s ;
94     v[3].iov_base = sa.s + v[2].iov_len ;
95     if (!unixmessage_putv_and_close(&b7, &mv, (unsigned char const *)"\003"))
96       strerr_diefu1sys(111, "unixmessage_putv") ;
97     stralloc_free(&sa) ;
98   }
99   if (!unixmessage_sender_timed_flush_g(&b7, &deadline))
100     strerr_diefu1sys(111, "send args to server") ;
101   unixmessage_sender_free(&b7) ;
102   {
103     char c ;
104     if (buffer_timed_get_g(&b6, &c, 1, &deadline) < 1)
105       strerr_diefu1sys(111, "get confirmation from server") ;
106     if (c)
107     {
108       errno = c ;
109       strerr_diefu1sys(111, "start privileged program: server answered: ") ;
110     }
111   }
112 
113   if (T) tain_from_millisecs(&deadline, T) ; else deadline = tain_infinite_relative ;
114   tain_add_g(&deadline, &deadline) ;
115   {
116     char pack[UINT_PACK] ;
117     if (buffer_timed_get_g(&b6, pack, UINT_PACK, &deadline) < UINT_PACK)
118       strerr_diefu1sys(111, "get exit status from server") ;
119     uint_unpack_big(pack, &t) ;
120   }
121   return wait_estatus(t) ;
122 }
123