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, ¶ms) ; break ;
613 case 2 :
614 result = s6_accessrules_uidgid_cdb(uid, gid, &cdbmap, ¶ms) ; 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(¶ms) ;
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(¶ms) ;
646 return 0 ;
647 }
648 }
649 s6_accessrules_params_free(¶ms) ;
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