1 /* ISC license. */
2 
3 #include <sys/uio.h>
4 #include <sys/stat.h>
5 #include <sys/resource.h>
6 #include <string.h>
7 #include <stdint.h>
8 #include <unistd.h>
9 #include <fcntl.h>
10 #include <errno.h>
11 #include <signal.h>
12 #include <regex.h>
13 
14 #include <skalibs/posixplz.h>
15 #include <skalibs/posixishard.h>
16 #include <skalibs/types.h>
17 #include <skalibs/allreadwrite.h>
18 #include <skalibs/sgetopt.h>
19 #include <skalibs/env.h>
20 #include <skalibs/bytestr.h>
21 #include <skalibs/error.h>
22 #include <skalibs/strerr2.h>
23 #include <skalibs/tai.h>
24 #include <skalibs/djbunix.h>
25 #include <skalibs/sig.h>
26 #include <skalibs/iopause.h>
27 #include <skalibs/selfpipe.h>
28 #include <skalibs/cdb.h>
29 #include <skalibs/getpeereid.h>
30 #include <skalibs/socket.h>
31 #include <skalibs/genset.h>
32 #include <skalibs/avltreen.h>
33 #include <skalibs/unixmessage.h>
34 #include <skalibs/unixconnection.h>
35 
36 #include <s6/accessrules.h>
37 #include <s6/s6-fdholder.h>
38 
39 #define USAGE "s6-fdholderd [ -v verbosity ] [ -1 ] [ -c maxconn ] [ -n maxfds ] [ -t timeout ] [ -T lameducktimeout ] [ -i rulesdir | -x rulesfile ]"
40 #define dieusage() strerr_dieusage(100, USAGE) ;
41 
42 static unsigned int verbosity = 1 ;
43 static int cont = 1 ;
44 static tain_t answertto = TAIN_INFINITE_RELATIVE ;
45 static tain_t lameduckdeadline = TAIN_INFINITE_RELATIVE ;
46 static tain_t const nano1 = { .sec = TAI_ZERO, .nano = 1 } ;
47 
48 static unsigned int rulestype = 0 ;
49 static char const *rules = 0 ;
50 static int cdbfd = -1 ;
51 static struct cdb cdbmap = CDB_ZERO ;
52 
handle_signals(void)53 static void handle_signals (void)
54 {
55   for (;;) switch (selfpipe_read())
56   {
57     case -1 : strerr_diefu1sys(111, "selfpipe_read()") ;
58     case 0 : return ;
59     case SIGTERM :
60     {
61       if (cont)
62       {
63         cont = 0 ;
64         tain_add_g(&lameduckdeadline, &lameduckdeadline) ;
65       }
66       break ;
67     }
68     case SIGHUP :
69     {
70       int fd ;
71       struct cdb c = CDB_ZERO ;
72       if (rulestype != 2) break ;
73       fd = open_readb(rules) ;
74       if (fd < 0) break ;
75       if (cdb_init(&c, fd) < 0)
76       {
77         fd_close(fd) ;
78         break ;
79       }
80       cdb_free(&cdbmap) ;
81       fd_close(cdbfd) ;
82       cdbfd = fd ;
83       cdbmap = c ;
84     }
85     break ;
86     default : break ;
87   }
88 }
89 
90 
91  /* fd store */
92 
93 static genset *fdstore ;
94 #define FD(i) genset_p(s6_fdholder_fd_t, fdstore, (i))
95 static unsigned int maxfds = 1000 ;
96 #define numfds genset_n(fdstore)
97 static avltreen *fds_by_id ;
98 static avltreen *fds_by_deadline ;
99 
fds_id_dtok(uint32_t d,void * x)100 static void *fds_id_dtok (uint32_t d, void *x)
101 {
102   (void)x ;
103   return FD(d)->id ;
104 }
105 
fds_id_cmp(void const * a,void const * b,void * x)106 static int fds_id_cmp (void const *a, void const *b, void *x)
107 {
108   (void)x ;
109   return strcmp((char const *)a, (char const *)b) ;
110 }
111 
fds_deadline_dtok(uint32_t d,void * x)112 static void *fds_deadline_dtok (uint32_t d, void *x)
113 {
114   (void)x ;
115   return &FD(d)->limit ;
116 }
117 
fds_deadline_cmp(void const * a,void const * b,void * x)118 static int fds_deadline_cmp (void const *a, void const *b, void *x)
119 {
120   tain_t const *aa = (tain_t const *)a ;
121   tain_t const *bb = (tain_t const *)b ;
122   (void)x ;
123   return tain_less(aa, bb) ? -1 : tain_less(bb, aa) ;
124 }
125 
fds_delete(uint32_t pp)126 static void fds_delete (uint32_t pp)
127 {
128   avltreen_delete(fds_by_id, fds_id_dtok(pp, 0)) ;
129   avltreen_delete(fds_by_deadline, fds_deadline_dtok(pp, 0)) ;
130   genset_delete(fdstore, pp) ;
131 }
132 
fds_close_and_delete(uint32_t pp)133 static void fds_close_and_delete (uint32_t pp)
134 {
135   fd_close(FD(pp)->fd) ;
136   fds_delete(pp) ;
137 }
138 
139 
140  /* client connection */
141 
142 typedef struct client_s client_t, *client_t_ref ;
143 struct client_s
144 {
145   uint32_t next ;
146   uint32_t xindex ;
147   tain_t deadline ;
148   regex_t rre ;
149   regex_t wre ;
150   unsigned int dumping ;
151   unsigned int flags ;
152   unixconnection_t connection ;
153 } ;
154 
155 static genset *clients ;
156 static uint32_t sentinel ;
157 #define CLIENT(i) genset_p(client_t, clients, (i))
158 #define numconn (genset_n(clients) - 1)
159 
client_free(client_t * c)160 static inline void client_free (client_t *c)
161 {
162   fd_close(unixmessage_sender_fd(&c->connection.out)) ;
163   unixconnection_free(&c->connection) ;
164   regfree(&c->rre) ;
165   regfree(&c->wre) ;
166 }
167 
client_delete(uint32_t cc,uint32_t prev)168 static inline void client_delete (uint32_t cc, uint32_t prev)
169 {
170   client_t *c = CLIENT(cc) ;
171   CLIENT(prev)->next = c->next ;
172   client_free(c) ;
173   genset_delete(clients, cc) ;
174 }
175 
removeclient(uint32_t * i,uint32_t j)176 static void removeclient (uint32_t *i, uint32_t j)
177 {
178   client_delete(*i, j) ;
179   *i = j ;
180 }
181 
client_setdeadline(client_t * c)182 static void client_setdeadline (client_t *c)
183 {
184   tain_t blah ;
185   tain_half(&blah, &tain_infinite_relative) ;
186   tain_add_g(&blah, &blah) ;
187   if (tain_less(&blah, &c->deadline))
188     tain_add_g(&c->deadline, &answertto) ;
189 }
190 
client_prepare_iopause(uint32_t i,tain_t * deadline,iopause_fd * x,uint32_t * j)191 static inline int client_prepare_iopause (uint32_t i, tain_t *deadline, iopause_fd *x, uint32_t *j)
192 {
193   client_t *c = CLIENT(i) ;
194   if (tain_less(&c->deadline, deadline)) *deadline = c->deadline ;
195   if (!unixmessage_sender_isempty(&c->connection.out) || !unixmessage_receiver_isempty(&c->connection.in) || (cont && !unixmessage_receiver_isfull(&c->connection.in)))
196   {
197     x[*j].fd = unixmessage_sender_fd(&c->connection.out) ;
198     x[*j].events = (!unixmessage_receiver_isempty(&c->connection.in) || (cont && !unixmessage_receiver_isfull(&c->connection.in)) ? IOPAUSE_READ : 0) | (!unixmessage_sender_isempty(&c->connection.out) ? IOPAUSE_WRITE : 0) ;
199     c->xindex = (*j)++ ;
200   }
201   else c->xindex = 0 ;
202   return !!c->xindex ;
203 }
204 
client_add(uint32_t * cc,int fd,regex_t const * rre,regex_t const * wre,unsigned int flags)205 static inline void client_add (uint32_t *cc, int fd, regex_t const *rre, regex_t const *wre, unsigned int flags)
206 {
207   uint32_t i ;
208   client_t *c ;
209   i = genset_new(clients) ;
210   c = CLIENT(i) ;
211   tain_add_g(&c->deadline, &answertto) ;
212   c->rre = *rre ;
213   c->wre = *wre ;
214   c->dumping = 0 ;
215   c->flags = flags ;
216   unixconnection_init(&c->connection, fd, fd) ;
217   c->next = CLIENT(sentinel)->next ;
218   CLIENT(sentinel)->next = i ;
219   *cc = i ;
220 }
221 
client_flush(uint32_t i,iopause_fd const * x)222 static inline int client_flush (uint32_t i, iopause_fd const *x)
223 {
224   client_t *c = CLIENT(i) ;
225   if (c->xindex && (x[c->xindex].revents & IOPAUSE_WRITE))
226   {
227     if (unixconnection_flush(&c->connection))
228       tain_add_g(&c->deadline, &tain_infinite_relative) ;
229     else if (!error_isagain(errno)) return 0 ;
230   }
231   return 1 ;
232 }
233 
answer(client_t * c,char e)234 static int answer (client_t *c, char e)
235 {
236   unixmessage_t m = { .s = &e, .len = 1, .fds = 0, .nfds = 0 } ;
237   if (!unixmessage_put(&c->connection.out, &m)) return 0 ;
238   client_setdeadline(c) ;
239   return 1 ;
240 }
241 
do_store(uint32_t cc,unixmessage_t const * m)242 static int do_store (uint32_t cc, unixmessage_t const *m)
243 {
244   uint32_t pp, idlen ;
245   client_t *c = CLIENT(cc) ;
246   s6_fdholder_fd_t *p ;
247   if (c->dumping || m->len < TAIN_PACK + 3 || m->nfds != 1) return (errno = EPROTO, 0) ;
248   idlen = (unsigned char)m->s[TAIN_PACK] ;
249   if (idlen > S6_FDHOLDER_ID_SIZE || idlen + 2 + TAIN_PACK != m->len || m->s[1 + TAIN_PACK + idlen]) return (errno = EPROTO, 0) ;
250   if (regexec(&c->wre, m->s + TAIN_PACK + 1, 0, 0, 0))
251   {
252     unixmessage_drop(m) ;
253     return answer(c, EPERM) ;
254   }
255   if (numfds >= maxfds)
256   {
257     unixmessage_drop(m) ;
258     return answer(c, ENFILE) ;
259   }
260   if (avltreen_search(fds_by_id, m->s + TAIN_PACK + 1, &pp))
261   {
262     unixmessage_drop(m) ;
263     return answer(c, EBUSY) ;
264   }
265   if (!answer(c, 0)) return 0 ;
266   pp = genset_new(fdstore) ; p = FD(pp) ;
267   tain_unpack(m->s, &p->limit) ; p->fd = m->fds[0] ;
268   memcpy(p->id, m->s + TAIN_PACK + 1, idlen) ;
269   memset(p->id + idlen, 0, S6_FDHOLDER_ID_SIZE + 1 - idlen) ;
270   for (;;)
271   {
272     uint32_t dummy ;
273     if (!avltreen_search(fds_by_deadline, &p->limit, &dummy)) break ;
274     tain_add(&p->limit, &p->limit, &nano1) ;
275   }
276   avltreen_insert(fds_by_id, pp) ;
277   avltreen_insert(fds_by_deadline, pp) ;
278   return 1 ;
279 }
280 
do_delete(uint32_t cc,unixmessage_t const * m)281 static int do_delete (uint32_t cc, unixmessage_t const *m)
282 {
283   uint32_t pp, idlen ;
284   client_t *c = CLIENT(cc) ;
285   if (c->dumping || m->len < 3 || m->nfds) return (errno = EPROTO, 0) ;
286   idlen = (unsigned char)m->s[0] ;
287   if (idlen > S6_FDHOLDER_ID_SIZE || idlen + 2 != m->len || m->s[idlen + 1]) return (errno = EPROTO, 0) ;
288   if (regexec(&c->wre, m->s + 1, 0, 0, 0)) return answer(c, EPERM) ;
289   if (!avltreen_search(fds_by_id, m->s + 1, &pp)) return answer(c, ENOENT) ;
290   if (!answer(c, 0)) return 0 ;
291   fds_close_and_delete(pp) ;
292   return 1 ;
293 }
294 
do_retrieve(uint32_t cc,unixmessage_t const * m)295 static int do_retrieve (uint32_t cc, unixmessage_t const *m)
296 {
297   int fd ;
298   unixmessage_t ans = { .s = "", .len = 1, .fds = &fd, .nfds = 1 } ;
299   uint32_t pp, idlen ;
300   client_t *c = CLIENT(cc) ;
301   if (c->dumping || m->len < 4 || m->nfds) return (errno = EPROTO, 0) ;
302   idlen = (unsigned char)m->s[1] ;
303   if (idlen > S6_FDHOLDER_ID_SIZE || idlen + 3 != m->len || m->s[idlen + 2]) return (errno = EPROTO, 0) ;
304   if (regexec(&c->rre, m->s + 2, 0, 0, 0)) return answer(c, EPERM) ;
305   if (m->s[0] && regexec(&c->wre, m->s + 2, 0, 0, 0)) return answer(c, EPERM) ;
306   if (!avltreen_search(fds_by_id, m->s + 2, &pp)) return answer(c, ENOENT) ;
307   fd = FD(pp)->fd ;
308   if (!unixmessage_put_and_close(&c->connection.out, &ans, m->s[0] ? unixmessage_bits_closeall : unixmessage_bits_closenone)) return 0 ;
309   if (m->s[0]) fds_delete(pp) ;
310   return 1 ;
311 }
312 
fill_siovec_with_ids_iter(char * thing,void * data)313 static int fill_siovec_with_ids_iter (char *thing, void *data)
314 {
315   struct iovec *v = (*(struct iovec **)data)++ ;
316   s6_fdholder_fd_t *p = (s6_fdholder_fd_t *)thing ;
317   v->iov_base = p->id ;
318   v->iov_len = strlen(p->id) + 1 ;
319   return 1 ;
320 }
321 
do_list(uint32_t cc,unixmessage_t const * m)322 static int do_list (uint32_t cc, unixmessage_t const *m)
323 {
324   client_t *c = CLIENT(cc) ;
325   struct iovec v[1+numfds] ;
326   unixmessage_v_t ans = { .v = v, .vlen = 1+numfds, .fds = 0, .nfds = 0 } ;
327   struct iovec *vp = v + 1 ;
328   char pack[5] = "" ;
329   if (c->dumping || m->len || m->nfds) return (errno = EPROTO, 0) ;
330   if (!(c->flags & 4)) return answer(c, EPERM) ;
331   uint32_pack_big(pack + 1, (uint32_t)numfds) ;
332   v[0].iov_base = pack ; v[0].iov_len = 5 ;
333   genset_iter(fdstore, &fill_siovec_with_ids_iter, &vp) ;
334   if (!unixmessage_putv(&c->connection.out, &ans)) return answer(c, errno) ;
335   client_setdeadline(c) ;
336   return 1 ;
337 }
338 
339 typedef struct getdumpiter_s getdumpiter_t, *getdumpiter_t_ref ;
340 struct getdumpiter_s
341 {
342   struct iovec *v ;
343   int *fd ;
344   char *limit ;
345 } ;
346 
getdump_iter(char * thing,void * stuff)347 static int getdump_iter (char *thing, void *stuff)
348 {
349   s6_fdholder_fd_t *p = (s6_fdholder_fd_t *)thing ;
350   getdumpiter_t *blah = stuff ;
351   unsigned char len = strlen(p->id) ;
352   tain_pack(blah->limit, &p->limit) ;
353   blah->limit[TAIN_PACK] = len ;
354   blah->v->iov_base = p->id ;
355   blah->v->iov_len = len + 1 ;
356   *blah->fd++ = p->fd ;
357   blah->v += 2 ;
358   blah->limit += TAIN_PACK + 1 ;
359   return 1 ;
360 }
361 
do_getdump(uint32_t cc,unixmessage_t const * m)362 static int do_getdump (uint32_t cc, unixmessage_t const *m)
363 {
364   uint32_t n = numfds ? 1 + ((numfds-1) / UNIXMESSAGE_MAXFDS) : 0 ;
365   client_t *c = CLIENT(cc) ;
366   if (c->dumping || m->len || m->nfds) return (errno = EPROTO, 0) ;
367   if (!(c->flags & 1)) return answer(c, EPERM) ;
368   {
369     char pack[9] = "" ;
370     unixmessage_t ans = { .s = pack, .len = 9, .fds = 0, .nfds = 0 } ;
371     uint32_pack_big(pack+1, n) ;
372     uint32_pack_big(pack+5, numfds) ;
373     if (!unixmessage_put(&c->connection.out, &ans)) return answer(c, errno) ;
374   }
375   if (n)
376   {
377     uint32_t i = 0 ;
378     unixmessage_v_t ans[n] ;
379     struct iovec v[numfds << 1] ;
380     int fds[numfds] ;
381     char limits[(TAIN_PACK+1) * numfds] ;
382     for (; i < numfds ; i++)
383     {
384       v[i<<1].iov_base = limits + i * (TAIN_PACK+1) ;
385       v[i<<1].iov_len = TAIN_PACK+1 ;
386     }
387     {
388       getdumpiter_t state = { .v = v+1, .fd = fds, .limit = limits } ;
389       genset_iter(fdstore, &getdump_iter, &state) ;
390     }
391     for (i = 0 ; i < n-1 ; i++)
392     {
393       ans[i].v = v + (i<<1) * UNIXMESSAGE_MAXFDS ;
394       ans[i].vlen = UNIXMESSAGE_MAXFDS << 1 ;
395       ans[i].fds = fds + i * UNIXMESSAGE_MAXFDS ;
396       ans[i].nfds = UNIXMESSAGE_MAXFDS ;
397     }
398     ans[n-1].v = v + ((n-1)<<1) * UNIXMESSAGE_MAXFDS ;
399     ans[n-1].vlen = (1 + (numfds-1) % UNIXMESSAGE_MAXFDS) << 1 ;
400     ans[n-1].fds = fds + (n-1) * UNIXMESSAGE_MAXFDS ;
401     ans[n-1].nfds = 1 + (numfds - 1) % UNIXMESSAGE_MAXFDS ;
402     for (i = 0 ; i < n ; i++)
403     {
404       if (!unixmessage_putv(&c->connection.out, ans + i))
405       {
406         int e = errno ;
407         ++i ;
408         while (i--) unixmessage_unput(&c->connection.out) ;
409         return answer(c, e) ;
410       }
411     }
412   }
413   client_setdeadline(c) ;
414   return 1 ;
415 }
416 
do_setdump(uint32_t cc,unixmessage_t const * m)417 static int do_setdump (uint32_t cc, unixmessage_t const *m)
418 {
419   char pack[5] = "" ;
420   uint32_t n ;
421   unixmessage_t ans = { .s = pack, .len = 5, .fds = 0, .nfds = 0 } ;
422   client_t *c = CLIENT(cc) ;
423   if (c->dumping || m->len != 4 || m->nfds) return (errno = EPROTO, 0) ;
424   if (!(c->flags & 2)) return answer(c, EPERM) ;
425   uint32_unpack_big(m->s, &n) ;
426   if (n > maxfds || n + numfds > maxfds) return answer(c, ENFILE) ;
427   c->dumping = n ;
428   n = n ? 1 + (n-1) / UNIXMESSAGE_MAXFDS : 0 ;
429   uint32_pack_big(pack+1, n) ;
430   if (!unixmessage_put(&c->connection.out, &ans)) return answer(c, errno) ;
431   return 1 ;
432 }
433 
do_setdump_data(uint32_t cc,unixmessage_t const * m)434 static int do_setdump_data (uint32_t cc, unixmessage_t const *m)
435 {
436   client_t *c = CLIENT(cc) ;
437   if (!m->nfds || m->nfds > c->dumping || m->len < m->nfds * (TAIN_PACK + 3))
438     return (errno = EPROTO, 0) ;
439   if (!(c->flags & 2))
440   {
441     unixmessage_drop(m) ;
442     return answer(c, EPERM) ;
443   }
444   if (numfds + m->nfds > maxfds)
445   {
446     unixmessage_drop(m) ;
447     return answer(c, ENFILE) ;
448   }
449   {
450     char const *s = m->s ;
451     size_t len = m->len ;
452     uint32_t i = 0 ;
453     uint32_t indices[m->nfds] ;
454     for (; i < m->nfds ; i++)
455     {
456       s6_fdholder_fd_t *p ;
457       uint32_t oldid, idlen ;
458       if (len < TAIN_PACK + 3) break ;
459       idlen = (unsigned char)s[TAIN_PACK] ;
460       if (idlen > S6_FDHOLDER_ID_SIZE || len < TAIN_PACK + 2 + idlen || s[TAIN_PACK + 1 + idlen]) break ;
461       indices[i] = genset_new(fdstore) ;
462       p = FD(indices[i]) ;
463       tain_unpack(s, &p->limit) ;
464       memcpy(p->id, s + TAIN_PACK + 1, idlen+1) ;
465       p->fd = m->fds[i] ;
466       if (avltreen_search(fds_by_id, p->id, &oldid)) fds_close_and_delete(oldid) ;
467       avltreen_insert(fds_by_id, indices[i]) ;
468       avltreen_insert(fds_by_deadline, indices[i]) ;
469       s += TAIN_PACK + 2 + idlen ; len -= TAIN_PACK + 2 + idlen ;
470     }
471     if (i < m->nfds || len)
472     {
473       while (i--) fds_delete(indices[i]) ;
474       return (errno = EPROTO, 0) ;
475     }
476   }
477   c->dumping -= m->nfds ;
478   return answer(c, c->dumping ? EAGAIN : 0) ;
479 }
480 
do_error(uint32_t cc,unixmessage_t const * m)481 static int do_error (uint32_t cc, unixmessage_t const *m)
482 {
483   (void)cc ;
484   (void)m ;
485   return (errno = EPROTO, 0) ;
486 }
487 
488 typedef int parsefunc_t (uint32_t, unixmessage_t const *) ;
489 typedef parsefunc_t *parsefunc_t_ref ;
490 
parse_protocol(unixmessage_t const * m,void * p)491 static inline int parse_protocol (unixmessage_t const *m, void *p)
492 {
493   static parsefunc_t_ref const f[8] =
494   {
495     &do_store,
496     &do_delete,
497     &do_retrieve,
498     &do_list,
499     &do_getdump,
500     &do_setdump,
501     &do_setdump_data,
502     &do_error
503   } ;
504   unixmessage_t mcopy = { .s = m->s + 1, .len = m->len - 1, .fds = m->fds, .nfds = m->nfds } ;
505   if (!m->len)
506   {
507     unixmessage_drop(m) ;
508     return (errno = EPROTO, 0) ;
509   }
510   if (!(*f[byte_chr("SDRL?!.", 7, m->s[0])])(*(uint32_t *)p, &mcopy))
511   {
512     unixmessage_drop(m) ;
513     return 0 ;
514   }
515   return 1 ;
516 }
517 
client_read(uint32_t cc,iopause_fd const * x)518 static inline int client_read (uint32_t cc, iopause_fd const *x)
519 {
520   client_t *c = CLIENT(cc) ;
521   return !unixmessage_receiver_isempty(&c->connection.in) || (c->xindex && (x[c->xindex].revents & IOPAUSE_READ)) ?
522     unixmessage_handle(&c->connection.in, &parse_protocol, &cc) > 0 : 1 ;
523 }
524 
525 
526  /* Environment on new connections */
527 
makere(regex_t * re,char const * s,char const * var)528 static int makere (regex_t *re, char const *s, char const *var)
529 {
530   size_t varlen = strlen(var) ;
531   if (str_start(s, var) && (s[varlen] == '='))
532   {
533     int r = skalibs_regcomp(re, s + varlen + 1, REG_EXTENDED | REG_NOSUB) ;
534     if (r)
535     {
536       if (verbosity)
537       {
538         char buf[256] ;
539         regerror(r, re, buf, 256) ;
540         strerr_warnw6x("invalid ", var, " value: ", s + varlen + 1, ": ", buf) ;
541       }
542       return -1 ;
543     }
544     else return 1 ;
545   }
546   return 0 ;
547 }
548 
defaultre(regex_t * re)549 static void defaultre (regex_t *re)
550 {
551   int r = skalibs_regcomp(re, ".^", REG_EXTENDED | REG_NOSUB) ;
552   if (r)
553   {
554     char buf[256] ;
555     regerror(r, re, buf, 256) ;
556     strerr_diefu2x(100, "compile .^ into a regular expression: ", buf) ;
557   }
558 }
559 
parse_env(char const * const * envp,regex_t * rre,regex_t * wre,unsigned int * flags,unsigned int * donep)560 static inline int parse_env (char const *const *envp, regex_t *rre, regex_t *wre, unsigned int *flags, unsigned int *donep)
561 {
562   unsigned int done = 0 ;
563   unsigned int fl = 0 ;
564   for (; *envp ; envp++)
565   {
566     if (str_start(*envp, "S6_FDHOLDER_GETDUMP=")) fl |= 1 ;
567     if (str_start(*envp, "S6_FDHOLDER_SETDUMP=")) fl |= 2 ;
568     if (str_start(*envp, "S6_FDHOLDER_LIST=")) fl |= 4 ;
569     if (!(done & 1))
570     {
571       int r = makere(rre, *envp, "S6_FDHOLDER_RETRIEVE_REGEX") ;
572       if (r < 0)
573       {
574         if (done & 2) regfree(wre) ;
575         return 0 ;
576       }
577       else if (r) done |= 1 ;
578     }
579     if (!(done & 2))
580     {
581       int r = makere(wre, *envp, "S6_FDHOLDER_STORE_REGEX") ;
582       if (r < 0)
583       {
584         if (done & 1) regfree(rre) ;
585         return 0 ;
586       }
587       else if (r) done |= 2 ;
588     }
589   }
590   *flags = fl ;
591   *donep = done ;
592   return 1 ;
593 }
594 
new_connection(int fd,regex_t * rre,regex_t * wre,unsigned int * flags)595 static inline int new_connection (int fd, regex_t *rre, regex_t *wre, unsigned int *flags)
596 {
597   s6_accessrules_params_t params = S6_ACCESSRULES_PARAMS_ZERO ;
598   s6_accessrules_result_t result = S6_ACCESSRULES_ERROR ;
599   uid_t uid ;
600   gid_t gid ;
601   unsigned int done = 0 ;
602 
603   if (getpeereid(fd, &uid, &gid) < 0)
604   {
605     if (verbosity) strerr_warnwu1sys("getpeereid") ;
606     return 0 ;
607   }
608 
609   switch (rulestype)
610   {
611     case 1 :
612       result = s6_accessrules_uidgid_fs(uid, gid, rules, &params) ; break ;
613     case 2 :
614       result = s6_accessrules_uidgid_cdb(uid, gid, &cdbmap, &params) ; break ;
615     default : break ;
616   }
617   if (result != S6_ACCESSRULES_ALLOW)
618   {
619     if (verbosity && (result == S6_ACCESSRULES_ERROR))
620        strerr_warnw1sys("error while checking rules") ;
621     return 0 ;
622   }
623   if (params.exec.len && verbosity)
624   {
625     char fmtuid[UID_FMT] ;
626     char fmtgid[GID_FMT] ;
627     fmtuid[uid_fmt(fmtuid, uid)] = 0 ;
628     fmtgid[gid_fmt(fmtgid, gid)] = 0 ;
629     strerr_warnw4x("unused exec string in rules for uid ", fmtuid, " gid ", fmtgid) ;
630   }
631   if (params.env.s)
632   {
633     size_t n = byte_count(params.env.s, params.env.len, '\0') ;
634     char const *envp[n+1] ;
635     if (!env_make(envp, n, params.env.s, params.env.len))
636     {
637       if (verbosity) strerr_warnwu1sys("env_make") ;
638       s6_accessrules_params_free(&params) ;
639       return 0 ;
640     }
641     envp[n] = 0 ;
642     if (!parse_env(envp, rre, wre, flags, &done))
643     {
644       if (verbosity) strerr_warnwu1sys("parse_env") ;
645       s6_accessrules_params_free(&params) ;
646       return 0 ;
647     }
648   }
649   s6_accessrules_params_free(&params) ;
650   if (!(done & 1)) defaultre(rre) ;
651   if (!(done & 2)) defaultre(wre) ;
652   return 1 ;
653 }
654 
655 
main(int argc,char const * const * argv,char const * const * envp)656 int main (int argc, char const *const *argv, char const *const *envp)
657 {
658   int spfd ;
659   int flag1 = 0 ;
660   uint32_t maxconn = 16 ;
661   PROG = "s6-fdholderd" ;
662 
663   {
664     subgetopt_t l = SUBGETOPT_ZERO ;
665     unsigned int t = 0, T = 0 ;
666     for (;;)
667     {
668       int opt = subgetopt_r(argc, argv, "v:1c:n:i:x:t:T:", &l) ;
669       if (opt == -1) break ;
670       switch (opt)
671       {
672         case 'v' : if (!uint0_scan(l.arg, &verbosity)) dieusage() ; break ;
673         case '1' : flag1 = 1 ; break ;
674         case 'i' : rules = l.arg ; rulestype = 1 ; break ;
675         case 'x' : rules = l.arg ; rulestype = 2 ; break ;
676         case 't' : if (!uint0_scan(l.arg, &t)) dieusage() ; break ;
677         case 'T' : if (!uint0_scan(l.arg, &T)) dieusage() ; break ;
678         case 'c' : if (!uint0_scan(l.arg, &maxconn)) dieusage() ; break ;
679         case 'n' : if (!uint0_scan(l.arg, &maxfds)) dieusage() ; break ;
680         default : dieusage() ;
681       }
682     }
683     argc -= l.ind ; argv += l.ind ;
684     if (t) tain_from_millisecs(&answertto, t) ;
685     if (T) tain_from_millisecs(&lameduckdeadline, T) ;
686   }
687   if (!rulestype) strerr_dief1x(100, "no access rights specified!") ;
688   if (maxconn > S6_FDHOLDER_MAX) maxconn = S6_FDHOLDER_MAX ;
689   if (!maxconn) maxconn = 1 ;
690   {
691     struct rlimit fdlimit ;
692     if (getrlimit(RLIMIT_NOFILE, &fdlimit) < 0)
693       strerr_diefu1sys(111, "getrlimit") ;
694     if (fdlimit.rlim_cur != RLIM_INFINITY)
695     {
696       if (fdlimit.rlim_cur < 7)
697         strerr_dief1x(111, "open file limit too low") ;
698       if (maxfds > fdlimit.rlim_cur) maxfds = fdlimit.rlim_cur - 6 ;
699     }
700   }
701   if (!maxfds) maxfds = 1 ;
702   {
703     struct stat st ;
704     if (fstat(0, &st) < 0) strerr_diefu1sys(111, "fstat stdin") ;
705     if (!S_ISSOCK(st.st_mode)) strerr_dief1x(100, "stdin is not a socket") ;
706   }
707   if (flag1)
708   {
709     if (fcntl(1, F_GETFD) < 0)
710       strerr_dief1sys(100, "called with option -1 but stdout said") ;
711   }
712   else close(1) ;
713   spfd = selfpipe_init() ;
714   if (spfd < 0) strerr_diefu1sys(111, "selfpipe_init") ;
715   if (sig_ignore(SIGPIPE) < 0) strerr_diefu1sys(111, "ignore SIGPIPE") ;
716   {
717     sigset_t set ;
718     sigemptyset(&set) ;
719     sigaddset(&set, SIGTERM) ;
720     sigaddset(&set, SIGHUP) ;
721     if (selfpipe_trapset(&set) < 0) strerr_diefu1sys(111, "trap signals") ;
722   }
723 
724   if (rulestype == 2)
725   {
726     cdbfd = open_readb(rules) ;
727     if (cdbfd < 0) strerr_diefu3sys(111, "open ", rules, " for reading") ;
728     if (cdb_init(&cdbmap, cdbfd) < 0)
729       strerr_diefu2sys(111, "cdb_init ", rules) ;
730   }
731 
732   {
733      /* Hello, stack. I have a present for you. */
734     genset clientinfo, fdinfo ;
735     avltreen fdidinfo, fddeadlineinfo ;
736     iopause_fd x[2 + maxconn] ;
737     client_t clientstorage[1+maxconn] ;
738     uint32_t clientfreelist[1+maxconn] ;
739     s6_fdholder_fd_t fdstorage[maxfds] ;
740     uint32_t fdfreelist[maxfds] ;
741     avlnode fdidstorage[maxfds] ;
742     uint32_t fdidfreelist[maxfds] ;
743     avlnode fddeadlinestorage[maxfds] ;
744     uint32_t fddeadlinefreelist[maxfds] ;
745      /* Hope you enjoyed it! Have a nice day! */
746 
747     GENSET_init(&clientinfo, client_t, clientstorage, clientfreelist, 1+maxconn) ;
748     clients = &clientinfo ;
749     sentinel = genset_new(clients) ;
750     clientstorage[sentinel].next = sentinel ;
751     GENSET_init(&fdinfo, s6_fdholder_fd_t, fdstorage, fdfreelist, maxfds) ;
752     fdstore = &fdinfo ;
753     avltreen_init(&fdidinfo, fdidstorage, fdidfreelist, maxfds, &fds_id_dtok, &fds_id_cmp, 0) ;
754     fds_by_id = &fdidinfo ;
755     avltreen_init(&fddeadlineinfo, fddeadlinestorage, fddeadlinefreelist, maxfds, &fds_deadline_dtok, &fds_deadline_cmp, 0) ;
756     fds_by_deadline = &fddeadlineinfo ;
757 
758     x[0].fd = spfd ; x[0].events = IOPAUSE_READ ;
759     x[1].fd = 0 ;
760 
761     if (flag1)
762     {
763       fd_write(1, "\n", 1) ;
764       fd_close(1) ;
765     }
766 
767     /* We are long-lived and have to check absolute fd deadlines,
768        so we purposefully remain in wallclock mode. */
769     tain_now_g() ;
770 
771     for (;;)
772     {
773       tain_t deadline ;
774       uint32_t j = 2 ;
775       uint32_t i ;
776       int r = 1 ;
777 
778       if (cont) tain_add_g(&deadline, &tain_infinite_relative) ;
779       else deadline = lameduckdeadline ;
780       if (avltreen_min(fds_by_deadline, &i) && tain_less(&FD(i)->limit, &deadline)) deadline = FD(i)->limit ;
781       x[1].events = (cont && (numconn < maxconn)) ? IOPAUSE_READ : 0 ;
782       for (i = clientstorage[sentinel].next ; i != sentinel ; i = clientstorage[i].next)
783         if (client_prepare_iopause(i, &deadline, x, &j)) r = 0 ;
784       if (!cont && r) break ;
785 
786       r = iopause_g(x, j, &deadline) ;
787       if (r < 0) strerr_diefu1sys(111, "iopause") ;
788 
789       if (!r)
790       {
791         if (!cont && !tain_future(&lameduckdeadline)) break ;
792         for (;;)
793         {
794           if (!avltreen_min(fds_by_deadline, &i)) break ;
795           if (tain_future(&FD(i)->limit)) break ;
796           fds_close_and_delete(i) ;
797         }
798         for (j = sentinel, i = clientstorage[sentinel].next ; i != sentinel ; j = i, i = clientstorage[i].next)
799           if (!tain_future(&clientstorage[i].deadline)) removeclient(&i, j) ;
800         continue ;
801       }
802 
803       if (x[0].revents & IOPAUSE_READ) handle_signals() ;
804 
805       for (j = sentinel, i = clientstorage[sentinel].next ; i != sentinel ; j = i, i = clientstorage[i].next)
806         if (!client_flush(i, x)) removeclient(&i, j) ;
807 
808       for (j = sentinel, i = clientstorage[sentinel].next ; i != sentinel ; j = i, i = clientstorage[i].next)
809         if (!client_read(i, x)) removeclient(&i, j) ;
810 
811       if (x[1].revents & IOPAUSE_READ)
812       {
813         regex_t rre, wre ;
814         unsigned int flags = 0 ;
815         int dummy ;
816         int fd = ipc_accept_nb(x[1].fd, 0, 0, &dummy) ;
817         if (fd < 0)
818           if (!error_isagain(errno)) strerr_diefu1sys(111, "accept") ;
819           else continue ;
820         else if (!new_connection(fd, &rre, &wre, &flags)) fd_close(fd) ;
821         else client_add(&i, fd, &rre, &wre, flags) ;
822       }
823     }
824     return ((!!numfds) | (!!numconn << 1)) ;
825   }
826 }
827