1 /* $OpenBSD: slowcgi.c,v 1.7 2024/01/26 18:11:49 job Exp $ */
2 /*
3 * Copyright (c) 2020 Claudio Jeker <claudio@openbsd.org>
4 * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv>
5 * Copyright (c) 2013 David Gwynne <dlg@openbsd.org>
6 * Copyright (c) 2013 Florian Obser <florian@openbsd.org>
7 *
8 * Permission to use, copy, modify, and distribute this software for any
9 * purpose with or without fee is hereby granted, provided that the above
10 * copyright notice and this permission notice appear in all copies.
11 *
12 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 */
20
21 #include <sys/types.h>
22 #include <sys/ioctl.h>
23 #include <sys/queue.h>
24 #include <sys/socket.h>
25 #include <sys/stat.h>
26 #include <sys/time.h>
27 #include <sys/un.h>
28 #include <sys/wait.h>
29 #include <arpa/inet.h>
30 #include <err.h>
31 #include <fcntl.h>
32 #include <errno.h>
33 #include <event.h>
34 #include <limits.h>
35 #include <pwd.h>
36 #include <signal.h>
37 #include <stdio.h>
38 #include <stdarg.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <syslog.h>
42 #include <unistd.h>
43
44 #include "slowcgi.h"
45 #include "bgplgd.h"
46 #include "http.h"
47 #include "version.h"
48
49 #define TIMEOUT_DEFAULT 30
50 #define WWW_USER "www"
51 #define BGPLGD_USER "_bgplgd"
52
53 #define FCGI_CONTENT_SIZE 65535
54 #define FCGI_PADDING_SIZE 255
55 #define FCGI_RECORD_SIZE \
56 (sizeof(struct fcgi_record_header) + FCGI_CONTENT_SIZE + FCGI_PADDING_SIZE)
57
58 #define FCGI_ALIGNMENT 8
59 #define FCGI_ALIGN(n) \
60 (((n) + (FCGI_ALIGNMENT - 1)) & ~(FCGI_ALIGNMENT - 1))
61
62 #define STDOUT_DONE 0x1
63 #define STDERR_DONE 0x2
64 #define SCRIPT_DONE 0x4
65
66 #define FCGI_REQUEST_COMPLETE 0
67 #define FCGI_CANT_MPX_CONN 1
68 #define FCGI_OVERLOADED 2
69 #define FCGI_UNKNOWN_ROLE 3
70
71 #define FD_RESERVE 5
72 #define FD_NEEDED 6
73 int cgi_inflight = 0;
74
75 struct listener {
76 struct event ev, pause;
77 };
78
79 struct env_val {
80 SLIST_ENTRY(env_val) entry;
81 char *key;
82 char *val;
83 };
84 SLIST_HEAD(env_head, env_val);
85
86 struct fcgi_record_header {
87 uint8_t version;
88 uint8_t type;
89 uint16_t id;
90 uint16_t content_len;
91 uint8_t padding_len;
92 uint8_t reserved;
93 }__packed;
94
95 struct fcgi_response {
96 TAILQ_ENTRY(fcgi_response) entry;
97 uint8_t data[FCGI_RECORD_SIZE];
98 size_t data_pos;
99 size_t data_len;
100 };
101 TAILQ_HEAD(fcgi_response_head, fcgi_response);
102
103 struct request {
104 LIST_ENTRY(request) entry;
105 struct event ev;
106 struct event resp_ev;
107 struct event tmo;
108 struct event script_ev;
109 struct event script_err_ev;
110 struct fcgi_response_head response_head;
111 struct env_head env;
112 uint8_t buf[FCGI_RECORD_SIZE];
113 size_t buf_pos;
114 size_t buf_len;
115 int fd;
116 int env_count;
117 int inflight_fds_accounted;
118 pid_t command_pid;
119 int command_status;
120 int script_flags;
121 uint16_t id;
122 uint8_t request_started;
123 uint8_t request_done;
124 uint8_t timeout_fired;
125 };
126
127 LIST_HEAD(requests_head, request);
128
129 struct slowcgi_proc {
130 struct requests_head requests;
131 struct event ev_sigchld;
132 };
133
134 struct fcgi_begin_request_body {
135 uint16_t role;
136 uint8_t flags;
137 uint8_t reserved[5];
138 }__packed;
139
140 struct fcgi_end_request_body {
141 uint32_t app_status;
142 uint8_t protocol_status;
143 uint8_t reserved[3];
144 }__packed;
145
146 __dead void usage(void);
147 int slowcgi_listen(char *, struct passwd *);
148 void slowcgi_paused(int, short, void *);
149 int accept_reserve(int, struct sockaddr *, socklen_t *, int,
150 volatile int *);
151 void slowcgi_accept(int, short, void *);
152 void slowcgi_request(int, short, void *);
153 void slowcgi_response(int, short, void *);
154 void slowcgi_add_response(struct request *, struct fcgi_response *);
155 void slowcgi_timeout(int, short, void *);
156 void slowcgi_sig_handler(int, short, void *);
157 size_t parse_record(uint8_t * , size_t, struct request *);
158 void parse_begin_request(uint8_t *, uint16_t, struct request *,
159 uint16_t);
160 void parse_params(uint8_t *, uint16_t, struct request *, uint16_t);
161 void parse_stdin(uint8_t *, uint16_t, struct request *, uint16_t);
162 char *env_get(struct request *, const char *);
163 void error_response(struct request *, int);
164 void exec_cgi(struct request *);
165 void script_std_in(int, short, void *);
166 void script_err_in(int, short, void *);
167 void create_data_record(struct request *, uint8_t, const void *,
168 size_t);
169 void create_end_record(struct request *);
170 void cleanup_request(struct request *);
171 void dump_fcgi_record(const char *,
172 struct fcgi_record_header *);
173 void dump_fcgi_record_header(const char *,
174 struct fcgi_record_header *);
175 void dump_fcgi_begin_request_body(const char *,
176 struct fcgi_begin_request_body *);
177 void dump_fcgi_end_request_body(const char *,
178 struct fcgi_end_request_body *);
179
180 const struct loggers conslogger = {
181 err,
182 errx,
183 warn,
184 warnx,
185 warnx, /* info */
186 warnx /* debug */
187 };
188
189 __dead void syslog_err(int, const char *, ...)
190 __attribute__((__format__ (printf, 2, 3)));
191 __dead void syslog_errx(int, const char *, ...)
192 __attribute__((__format__ (printf, 2, 3)));
193 void syslog_warn(const char *, ...)
194 __attribute__((__format__ (printf, 1, 2)));
195 void syslog_warnx(const char *, ...)
196 __attribute__((__format__ (printf, 1, 2)));
197 void syslog_info(const char *, ...)
198 __attribute__((__format__ (printf, 1, 2)));
199 void syslog_debug(const char *, ...)
200 __attribute__((__format__ (printf, 1, 2)));
201 void syslog_vstrerror(int, int, const char *, va_list)
202 __attribute__((__format__ (printf, 3, 0)));
203
204 const struct loggers syslogger = {
205 syslog_err,
206 syslog_errx,
207 syslog_warn,
208 syslog_warnx,
209 syslog_info,
210 syslog_debug
211 };
212
213 const struct loggers *logger = &conslogger;
214
215 __dead void
usage(void)216 usage(void)
217 {
218 extern char *__progname;
219 fprintf(stderr,
220 "usage: %s [-d] [-p path] [-S socket] [-s socket] [-U user]\n",
221 __progname);
222 exit(1);
223 }
224
225 struct timeval timeout = { TIMEOUT_DEFAULT, 0 };
226 struct timeval kill_timeout = { 5, 0 };
227 struct slowcgi_proc slowcgi_proc;
228 int debug = 0;
229 int on = 1;
230 char *fcgi_socket = "/var/www/run/bgplgd.sock";
231 char *bgpctlpath = "bgpctl";
232 char *bgpctlsock = "/var/run/bgpd.rsock";
233
234
235 /*
236 * Unveil the command we want to run.
237 * If this has a pathname component in it, interpret as a file
238 * and unveil the file directly.
239 * Otherwise, look up the command in our PATH.
240 */
241 static void
unveil_command(const char * prog)242 unveil_command(const char *prog)
243 {
244 const char *pp;
245 char *save, *cmd, *path;
246 struct stat st;
247
248 if (strchr(prog, '/') != NULL) {
249 if (unveil(prog, "x") == -1)
250 err(1, "%s: unveil", prog);
251 return;
252 }
253
254 if (getenv("PATH") == NULL)
255 lerrx(1, "PATH is unset");
256 if ((path = strdup(getenv("PATH"))) == NULL)
257 lerr(1, NULL);
258 save = path;
259 while ((pp = strsep(&path, ":")) != NULL) {
260 if (*pp == '\0')
261 continue;
262 if (asprintf(&cmd, "%s/%s", pp, prog) == -1)
263 lerr(1, NULL);
264 if (lstat(cmd, &st) == -1) {
265 free(cmd);
266 continue;
267 }
268 if (unveil(cmd, "x") == -1)
269 lerr(1, "%s: unveil", cmd);
270 free(cmd);
271 break;
272 }
273 free(save);
274 }
275
276 int
main(int argc,char * argv[])277 main(int argc, char *argv[])
278 {
279 extern char *__progname;
280 struct listener *l = NULL;
281 struct passwd *pw;
282 struct stat sb;
283 int c, fd;
284 const char *sock_user = WWW_USER;
285 const char *cgi_user = BGPLGD_USER;
286
287 /*
288 * Ensure we have fds 0-2 open so that we have no fd overlaps
289 * in exec_cgi() later. Just exit on error, we don't have enough
290 * fds open to output an error message anywhere.
291 */
292 for (c=0; c < 3; c++) {
293 if (fstat(c, &sb) == -1) {
294 if ((fd = open("/dev/null", O_RDWR)) != -1) {
295 if (dup2(fd, c) == -1)
296 exit(1);
297 if (fd > c)
298 close(fd);
299 } else
300 exit(1);
301 }
302 }
303
304 while ((c = getopt(argc, argv, "dp:S:s:U:u:V")) != -1) {
305 switch (c) {
306 case 'd':
307 debug++;
308 break;
309 case 'p':
310 bgpctlpath = optarg;
311 break;
312 case 'S':
313 bgpctlsock = optarg;
314 break;
315 case 's':
316 fcgi_socket = optarg;
317 break;
318 case 'U':
319 sock_user = optarg;
320 break;
321 case 'V':
322 fprintf(stderr, "OpenBGPD %s\n", BGPD_VERSION);
323 return 0;
324 default:
325 usage();
326 /* NOTREACHED */
327 }
328 }
329
330 if (geteuid() != 0)
331 errx(1, "need root privileges");
332
333 if (!debug && daemon(0, 0) == -1)
334 err(1, "daemon");
335
336 if (!debug) {
337 openlog(__progname, LOG_PID|LOG_NDELAY, LOG_DAEMON);
338 logger = &syslogger;
339 }
340
341 ldebug("sock_user: %s", sock_user);
342 pw = getpwnam(sock_user);
343 if (pw == NULL)
344 lerrx(1, "no %s user", sock_user);
345
346 fd = slowcgi_listen(fcgi_socket, pw);
347
348 ldebug("cgi_user: %s", cgi_user);
349 pw = getpwnam(cgi_user);
350 if (pw == NULL)
351 lerrx(1, "no %s user", cgi_user);
352
353 if (setgroups(1, &pw->pw_gid) ||
354 setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
355 setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
356 lerr(1, "unable to revoke privs");
357
358 unveil_command(bgpctlpath);
359
360 if (pledge("stdio rpath unix proc exec", NULL) == -1)
361 lerr(1, "pledge");
362
363 LIST_INIT(&slowcgi_proc.requests);
364 event_init();
365
366 l = calloc(1, sizeof(*l));
367 if (l == NULL)
368 lerr(1, "listener ev alloc");
369
370 event_set(&l->ev, fd, EV_READ | EV_PERSIST, slowcgi_accept, l);
371 event_add(&l->ev, NULL);
372 evtimer_set(&l->pause, slowcgi_paused, l);
373
374 signal_set(&slowcgi_proc.ev_sigchld, SIGCHLD, slowcgi_sig_handler,
375 &slowcgi_proc);
376 signal_add(&slowcgi_proc.ev_sigchld, NULL);
377
378 signal(SIGPIPE, SIG_IGN);
379
380 event_dispatch();
381 return (0);
382 }
383
384 int
slowcgi_listen(char * path,struct passwd * pw)385 slowcgi_listen(char *path, struct passwd *pw)
386 {
387 struct sockaddr_un sun;
388 mode_t old_umask;
389 int fd;
390
391 if ((fd = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC,
392 0)) == -1)
393 lerr(1, "slowcgi_listen: socket");
394
395 memset(&sun, 0, sizeof(sun));
396 sun.sun_family = AF_UNIX;
397 if (strlcpy(sun.sun_path, path, sizeof(sun.sun_path)) >=
398 sizeof(sun.sun_path))
399 lerrx(1, "socket path too long");
400
401 if (unlink(path) == -1)
402 if (errno != ENOENT)
403 lerr(1, "slowcgi_listen: unlink %s", path);
404
405 old_umask = umask(S_IXUSR|S_IXGRP|S_IWOTH|S_IROTH|S_IXOTH);
406
407 if (bind(fd, (struct sockaddr *)&sun, sizeof(sun)) == -1)
408 lerr(1,"slowcgi_listen: bind: %s", path);
409
410 umask(old_umask);
411
412 if (chown(path, pw->pw_uid, pw->pw_gid) == -1)
413 lerr(1, "slowcgi_listen: chown: %s", path);
414
415 if (listen(fd, 5) == -1)
416 lerr(1, "listen");
417
418 ldebug("socket: %s", path);
419 return fd;
420 }
421
422 void
slowcgi_paused(int fd,short events,void * arg)423 slowcgi_paused(int fd, short events, void *arg)
424 {
425 struct listener *l = arg;
426 event_add(&l->ev, NULL);
427 }
428
429 int
accept_reserve(int sockfd,struct sockaddr * addr,socklen_t * addrlen,int reserve,volatile int * counter)430 accept_reserve(int sockfd, struct sockaddr *addr, socklen_t *addrlen,
431 int reserve, volatile int *counter)
432 {
433 int ret;
434 if (getdtablecount() + reserve +
435 ((*counter + 1) * FD_NEEDED) >= getdtablesize()) {
436 ldebug("inflight fds exceeded");
437 errno = EMFILE;
438 return -1;
439 }
440
441 if ((ret = accept4(sockfd, addr, addrlen, SOCK_NONBLOCK | SOCK_CLOEXEC))
442 > -1) {
443 (*counter)++;
444 ldebug("inflight incremented, now %d", *counter);
445 }
446 return ret;
447 }
448
449 void
slowcgi_accept(int fd,short events,void * arg)450 slowcgi_accept(int fd, short events, void *arg)
451 {
452 struct listener *l;
453 struct sockaddr_storage ss;
454 struct timeval backoff;
455 struct request *c;
456 socklen_t len;
457 int s;
458
459 l = arg;
460 backoff.tv_sec = 1;
461 backoff.tv_usec = 0;
462 c = NULL;
463
464 len = sizeof(ss);
465 if ((s = accept_reserve(fd, (struct sockaddr *)&ss,
466 &len, FD_RESERVE, &cgi_inflight)) == -1) {
467 switch (errno) {
468 case EINTR:
469 case EWOULDBLOCK:
470 case ECONNABORTED:
471 return;
472 case EMFILE:
473 case ENFILE:
474 event_del(&l->ev);
475 evtimer_add(&l->pause, &backoff);
476 return;
477 default:
478 lerr(1, "accept");
479 }
480 }
481
482 c = calloc(1, sizeof(*c));
483 if (c == NULL) {
484 lwarn("cannot calloc request");
485 close(s);
486 cgi_inflight--;
487 return;
488 }
489 c->fd = s;
490 c->buf_pos = 0;
491 c->buf_len = 0;
492 c->request_started = 0;
493 c->inflight_fds_accounted = 0;
494 TAILQ_INIT(&c->response_head);
495
496 event_set(&c->ev, s, EV_READ | EV_PERSIST, slowcgi_request, c);
497 event_add(&c->ev, NULL);
498 event_set(&c->resp_ev, s, EV_WRITE | EV_PERSIST, slowcgi_response, c);
499 evtimer_set(&c->tmo, slowcgi_timeout, c);
500 evtimer_add(&c->tmo, &timeout);
501 LIST_INSERT_HEAD(&slowcgi_proc.requests, c, entry);
502 }
503
504 void
slowcgi_timeout(int fd,short events,void * arg)505 slowcgi_timeout(int fd, short events, void *arg)
506 {
507 struct request *c = arg;
508 int sig = SIGTERM;
509
510 if (c->script_flags & SCRIPT_DONE)
511 return;
512
513 if (c->command_pid == 0) {
514 c->command_status = SIGALRM;
515 error_response(c, 408);
516 return;
517 }
518
519 ldebug("timeout fired for pid %d", c->command_pid);
520
521 if (c->timeout_fired)
522 sig = SIGKILL;
523
524 if (kill(c->command_pid, sig) == -1) {
525 lwarn("kill child %d after timeout", c->command_pid);
526 if (event_initialized(&c->script_ev)) {
527 close(EVENT_FD(&c->script_ev));
528 event_del(&c->script_ev);
529 }
530 if (event_initialized(&c->script_err_ev)) {
531 close(EVENT_FD(&c->script_err_ev));
532 event_del(&c->script_err_ev);
533 }
534
535 c->command_status = SIGALRM;
536 c->script_flags = STDOUT_DONE | STDERR_DONE | SCRIPT_DONE;
537 create_end_record(c);
538 }
539
540 if (c->timeout_fired++ == 0)
541 evtimer_add(&c->tmo, &kill_timeout);
542 }
543
544 void
slowcgi_sig_handler(int sig,short event,void * arg)545 slowcgi_sig_handler(int sig, short event, void *arg)
546 {
547 struct request *c;
548 struct slowcgi_proc *p;
549 pid_t pid;
550 int status;
551
552 p = arg;
553
554 switch (sig) {
555 case SIGCHLD:
556 while ((pid = waitpid(WAIT_ANY, &status, WNOHANG)) > 0) {
557 LIST_FOREACH(c, &p->requests, entry)
558 if (c->command_pid == pid)
559 break;
560 if (c == NULL) {
561 lwarnx("caught exit of unknown child %d", pid);
562 continue;
563 }
564
565 if (WIFSIGNALED(status))
566 c->command_status = WTERMSIG(status);
567 else
568 c->command_status = WEXITSTATUS(status);
569
570 ldebug("pid %d exit %s%d", pid,
571 WIFSIGNALED(status) ? "signal " : "",
572 c->command_status);
573
574 c->script_flags |= SCRIPT_DONE;
575
576 if (c->script_flags == (STDOUT_DONE | STDERR_DONE |
577 SCRIPT_DONE))
578 create_end_record(c);
579 }
580 if (pid == -1 && errno != ECHILD)
581 lwarn("waitpid");
582 break;
583 default:
584 lerr(1, "unexpected signal: %d", sig);
585 break;
586 }
587 }
588
589 void
slowcgi_add_response(struct request * c,struct fcgi_response * resp)590 slowcgi_add_response(struct request *c, struct fcgi_response *resp)
591 {
592 struct fcgi_record_header *header;
593 size_t padded_len;
594
595 header = (struct fcgi_record_header*)resp->data;
596
597 /* The FastCGI spec suggests to align the output buffer */
598 padded_len = FCGI_ALIGN(resp->data_len);
599 if (padded_len > resp->data_len) {
600 /* There should always be FCGI_PADDING_SIZE bytes left */
601 if (padded_len > FCGI_RECORD_SIZE)
602 lerr(1, "response too long");
603 header->padding_len = padded_len - resp->data_len;
604 resp->data_len = padded_len;
605 }
606
607 TAILQ_INSERT_TAIL(&c->response_head, resp, entry);
608 event_add(&c->resp_ev, NULL);
609 }
610
611 void
slowcgi_response(int fd,short events,void * arg)612 slowcgi_response(int fd, short events, void *arg)
613 {
614 struct request *c;
615 struct fcgi_record_header *header;
616 struct fcgi_response *resp;
617 ssize_t n;
618
619 c = arg;
620
621 while ((resp = TAILQ_FIRST(&c->response_head))) {
622 header = (struct fcgi_record_header*) resp->data;
623 if (debug > 1)
624 dump_fcgi_record("resp ", header);
625
626 n = write(fd, resp->data + resp->data_pos, resp->data_len);
627 if (n == -1) {
628 if (errno == EAGAIN || errno == EINTR)
629 return;
630 cleanup_request(c);
631 return;
632 }
633 resp->data_pos += n;
634 resp->data_len -= n;
635 if (resp->data_len == 0) {
636 TAILQ_REMOVE(&c->response_head, resp, entry);
637 free(resp);
638 }
639 }
640
641 if (TAILQ_EMPTY(&c->response_head)) {
642 if (c->request_done)
643 cleanup_request(c);
644 else
645 event_del(&c->resp_ev);
646 }
647 }
648
649 void
slowcgi_request(int fd,short events,void * arg)650 slowcgi_request(int fd, short events, void *arg)
651 {
652 struct request *c;
653 ssize_t n;
654 size_t parsed;
655
656 c = arg;
657
658 n = read(fd, c->buf + c->buf_pos + c->buf_len,
659 FCGI_RECORD_SIZE - c->buf_pos-c->buf_len);
660
661 switch (n) {
662 case -1:
663 switch (errno) {
664 case EINTR:
665 case EAGAIN:
666 return;
667 default:
668 goto fail;
669 }
670 break;
671
672 case 0:
673 ldebug("closed connection");
674 goto fail;
675 default:
676 break;
677 }
678
679 c->buf_len += n;
680
681 /*
682 * Parse the records as they are received. Per the FastCGI
683 * specification, the server need only receive the FastCGI
684 * parameter records in full; it is free to begin execution
685 * at that point, which is what happens here.
686 */
687 do {
688 parsed = parse_record(c->buf + c->buf_pos, c->buf_len, c);
689 c->buf_pos += parsed;
690 c->buf_len -= parsed;
691 } while (parsed > 0 && c->buf_len > 0);
692
693 /* Make space for further reads */
694 if (c->buf_len > 0) {
695 memmove(c->buf, c->buf + c->buf_pos, c->buf_len);
696 c->buf_pos = 0;
697 }
698 return;
699 fail:
700 cleanup_request(c);
701 }
702
703 void
parse_begin_request(uint8_t * buf,uint16_t n,struct request * c,uint16_t id)704 parse_begin_request(uint8_t *buf, uint16_t n, struct request *c, uint16_t id)
705 {
706 /* XXX -- FCGI_CANT_MPX_CONN */
707 if (c->request_started) {
708 lwarnx("unexpected FCGI_BEGIN_REQUEST, ignoring");
709 return;
710 }
711
712 if (n != sizeof(struct fcgi_begin_request_body)) {
713 lwarnx("wrong size %d != %lu", n,
714 sizeof(struct fcgi_begin_request_body));
715 return;
716 }
717
718 c->request_started = 1;
719
720 c->id = id;
721 SLIST_INIT(&c->env);
722 c->env_count = 0;
723 }
724
725 void
parse_params(uint8_t * buf,uint16_t n,struct request * c,uint16_t id)726 parse_params(uint8_t *buf, uint16_t n, struct request *c, uint16_t id)
727 {
728 struct env_val *env_entry;
729 uint32_t name_len, val_len;
730
731 if (!c->request_started) {
732 lwarnx("FCGI_PARAMS without FCGI_BEGIN_REQUEST, ignoring");
733 return;
734 }
735
736 if (c->id != id) {
737 lwarnx("unexpected id, ignoring");
738 return;
739 }
740
741 /*
742 * If this is the last FastCGI parameter record,
743 * begin execution of the CGI script.
744 */
745 if (n == 0) {
746 exec_cgi(c);
747 return;
748 }
749
750 while (n > 0) {
751 if (buf[0] >> 7 == 0) {
752 name_len = buf[0];
753 n--;
754 buf++;
755 } else {
756 if (n > 3) {
757 name_len = ((buf[0] & 0x7f) << 24) +
758 (buf[1] << 16) + (buf[2] << 8) + buf[3];
759 n -= 4;
760 buf += 4;
761 } else
762 return;
763 }
764
765 if (n > 0) {
766 if (buf[0] >> 7 == 0) {
767 val_len = buf[0];
768 n--;
769 buf++;
770 } else {
771 if (n > 3) {
772 val_len = ((buf[0] & 0x7f) << 24) +
773 (buf[1] << 16) + (buf[2] << 8) +
774 buf[3];
775 n -= 4;
776 buf += 4;
777 } else
778 return;
779 }
780 } else
781 return;
782
783 if (n < name_len + val_len)
784 return;
785
786 if ((env_entry = malloc(sizeof(struct env_val))) == NULL) {
787 lwarnx("cannot allocate env_entry");
788 return;
789 }
790
791 if ((env_entry->key = malloc(name_len + 1)) == NULL) {
792 lwarnx("cannot allocate env_entry->key");
793 free(env_entry);
794 return;
795 }
796 if ((env_entry->val = malloc(val_len + 1)) == NULL) {
797 lwarnx("cannot allocate env_entry->val");
798 free(env_entry->key);
799 free(env_entry);
800 return;
801 }
802
803 memcpy(env_entry->key, buf, name_len);
804 buf += name_len;
805 n -= name_len;
806 env_entry->key[name_len] = '\0';
807 memcpy(env_entry->val, buf, val_len);
808 buf += val_len;
809 n -= val_len;
810 env_entry->val[val_len] = '\0';
811
812 SLIST_INSERT_HEAD(&c->env, env_entry, entry);
813 ldebug("env[%d], %s=%s", c->env_count, env_entry->key,
814 env_entry->val);
815 c->env_count++;
816 }
817 }
818
819 void
parse_stdin(uint8_t * buf,uint16_t n,struct request * c,uint16_t id)820 parse_stdin(uint8_t *buf, uint16_t n, struct request *c, uint16_t id)
821 {
822 if (c->id != id) {
823 lwarnx("unexpected id, ignoring");
824 return;
825 }
826
827 if (n != 0)
828 lwarnx("unexpected stdin input, ignoring");
829 }
830
831 size_t
parse_record(uint8_t * buf,size_t n,struct request * c)832 parse_record(uint8_t *buf, size_t n, struct request *c)
833 {
834 struct fcgi_record_header *h;
835
836 if (n < sizeof(struct fcgi_record_header))
837 return (0);
838
839 h = (struct fcgi_record_header*) buf;
840
841 if (debug > 1)
842 dump_fcgi_record("", h);
843
844 if (n < sizeof(struct fcgi_record_header) + ntohs(h->content_len)
845 + h->padding_len)
846 return (0);
847
848 if (h->version != 1)
849 lerrx(1, "wrong version");
850
851 switch (h->type) {
852 case FCGI_BEGIN_REQUEST:
853 parse_begin_request(buf + sizeof(struct fcgi_record_header),
854 ntohs(h->content_len), c, ntohs(h->id));
855 break;
856 case FCGI_PARAMS:
857 parse_params(buf + sizeof(struct fcgi_record_header),
858 ntohs(h->content_len), c, ntohs(h->id));
859 break;
860 case FCGI_STDIN:
861 parse_stdin(buf + sizeof(struct fcgi_record_header),
862 ntohs(h->content_len), c, ntohs(h->id));
863 break;
864 default:
865 lwarnx("unimplemented type %d", h->type);
866 break;
867 }
868
869 return (sizeof(struct fcgi_record_header) + ntohs(h->content_len)
870 + h->padding_len);
871 }
872
873 char *
env_get(struct request * c,const char * key)874 env_get(struct request *c, const char *key)
875 {
876 struct env_val *env;
877
878 SLIST_FOREACH(env, &c->env, entry) {
879 if (strcmp(env->key, key) == 0)
880 return (env->val);
881 }
882 return (NULL);
883 }
884
885 static const char *
http_error(int * res)886 http_error(int *res)
887 {
888 const struct http_error errors[] = HTTP_ERRORS;
889 size_t i;
890
891 for (i = 0; errors[i].error_code != 0; i++)
892 if (errors[i].error_code == *res)
893 return errors[i].error_name;
894
895 /* unknown error - change to 500 */
896 lwarnx("unknown http error %d", *res);
897 *res = 500;
898 return "Internal Server Error";
899 }
900
901 void
error_response(struct request * c,int res)902 error_response(struct request *c, int res)
903 {
904 const char *type = "text/html";
905 const char *errstr = http_error(&res);
906 char *buf;
907 int len;
908
909 lwarnx("HTTP status %d: %s", res, errstr);
910
911 len = asprintf(&buf,
912 "Content-Type: %s\n"
913 "Status: %d\n"
914 "Cache-Control: no-cache\n"
915 "\n"
916 "<!DOCTYPE html>\n"
917 "<html>\n"
918 " <head>\n"
919 " <meta http-equiv=\"Content-Type\" "
920 "content=\"%s; charset=utf-8\"/>\n"
921 " <title>%d %s</title>\n"
922 " </head>\n"
923 " <body>\n"
924 " <h1>%d %s</h1>\n"
925 " <hr>\n"
926 " <address>OpenBSD bgplgd</address>\n"
927 " </body>\n"
928 "</html>\n",
929 type, res, type, res, errstr, res, errstr);
930
931 if (len == -1)
932 lerr(1, NULL);
933
934 create_data_record(c, FCGI_STDOUT, buf, len);
935 free(buf);
936 c->script_flags = (STDOUT_DONE | STDERR_DONE | SCRIPT_DONE);
937 create_end_record(c);
938 }
939
940 /*
941 * Fork a new CGI process to handle the request, translating
942 * between FastCGI parameter records and CGI's environment variables,
943 * as well as between the CGI process' stdin/stdout and the
944 * corresponding FastCGI records.
945 */
946 void
exec_cgi(struct request * c)947 exec_cgi(struct request *c)
948 {
949 struct lg_ctx ctx = { 0 };
950 int s_in[2], s_out[2], s_err[2], res;
951 pid_t pid;
952
953 res = prep_request(&ctx, env_get(c, "REQUEST_METHOD"),
954 env_get(c, "PATH_INFO"), env_get(c, "QUERY_STRING"));
955 if (res != 0) {
956 error_response(c, res);
957 return;
958 }
959
960 if (pipe(s_in) == -1)
961 lerr(1, "pipe");
962 if (pipe(s_out) == -1)
963 lerr(1, "pipe");
964 if (pipe(s_err) == -1)
965 lerr(1, "pipe");
966 cgi_inflight--;
967 c->inflight_fds_accounted = 1;
968
969 switch (pid = fork()) {
970 case -1:
971 c->command_status = errno;
972
973 lwarn("fork");
974
975 close(s_in[0]);
976 close(s_out[0]);
977 close(s_err[0]);
978
979 close(s_in[1]);
980 close(s_out[1]);
981 close(s_err[1]);
982
983 c->script_flags = (STDOUT_DONE | STDERR_DONE | SCRIPT_DONE);
984 create_end_record(c);
985 return;
986 case 0:
987 /* Child process */
988 if (pledge("stdio rpath exec", NULL) == -1)
989 lerr(1, "pledge");
990 close(s_in[0]);
991 close(s_out[0]);
992 close(s_err[0]);
993
994 if (dup2(s_in[1], STDIN_FILENO) == -1)
995 _exit(1);
996 if (dup2(s_out[1], STDOUT_FILENO) == -1)
997 _exit(1);
998 if (dup2(s_err[1], STDERR_FILENO) == -1)
999 _exit(1);
1000
1001 close(s_in[1]);
1002 close(s_out[1]);
1003 close(s_err[1]);
1004
1005 bgpctl_call(&ctx);
1006
1007 /* should not be reached */
1008 _exit(1);
1009
1010 }
1011
1012 ldebug("fork %d", pid);
1013
1014 /* Parent process*/
1015 close(s_in[1]);
1016 close(s_out[1]);
1017 close(s_err[1]);
1018
1019 fcntl(s_in[0], F_SETFD, FD_CLOEXEC);
1020 fcntl(s_out[0], F_SETFD, FD_CLOEXEC);
1021 fcntl(s_err[0], F_SETFD, FD_CLOEXEC);
1022
1023 if (ioctl(s_in[0], FIONBIO, &on) == -1)
1024 lerr(1, "script ioctl(FIONBIO)");
1025 if (ioctl(s_out[0], FIONBIO, &on) == -1)
1026 lerr(1, "script ioctl(FIONBIO)");
1027 if (ioctl(s_err[0], FIONBIO, &on) == -1)
1028 lerr(1, "script ioctl(FIONBIO)");
1029
1030 close(s_in[0]); /* close stdin, bgpctl does not expect anything */
1031
1032 c->command_pid = pid;
1033 event_set(&c->script_ev, s_out[0], EV_READ | EV_PERSIST,
1034 script_std_in, c);
1035 event_add(&c->script_ev, NULL);
1036 event_set(&c->script_err_ev, s_err[0], EV_READ | EV_PERSIST,
1037 script_err_in, c);
1038 event_add(&c->script_err_ev, NULL);
1039 }
1040
1041 static void
script_in(int fd,struct event * ev,struct request * c,uint8_t type)1042 script_in(int fd, struct event *ev, struct request *c, uint8_t type)
1043 {
1044 struct fcgi_response *resp;
1045 struct fcgi_record_header *header;
1046 ssize_t n;
1047
1048 if ((resp = calloc(1, sizeof(struct fcgi_response))) == NULL) {
1049 lwarnx("cannot malloc fcgi_response");
1050 return;
1051 }
1052 header = (struct fcgi_record_header*) resp->data;
1053 header->version = 1;
1054 header->type = type;
1055 header->id = htons(c->id);
1056 header->padding_len = 0;
1057 header->reserved = 0;
1058
1059 n = read(fd, resp->data + sizeof(struct fcgi_record_header),
1060 FCGI_CONTENT_SIZE);
1061
1062 if (n == -1) {
1063 switch (errno) {
1064 case EINTR:
1065 case EAGAIN:
1066 free(resp);
1067 return;
1068 default:
1069 n = 0; /* fake empty FCGI_STD{OUT,ERR} response */
1070 }
1071 }
1072 header->content_len = htons(n);
1073 resp->data_pos = 0;
1074 resp->data_len = n + sizeof(struct fcgi_record_header);
1075 slowcgi_add_response(c, resp);
1076
1077 if (n == 0) {
1078 if (type == FCGI_STDOUT)
1079 c->script_flags |= STDOUT_DONE;
1080 else
1081 c->script_flags |= STDERR_DONE;
1082
1083 if (c->script_flags == (STDOUT_DONE | STDERR_DONE |
1084 SCRIPT_DONE))
1085 create_end_record(c);
1086 event_del(ev);
1087 close(fd);
1088 }
1089 }
1090
1091 void
script_std_in(int fd,short events,void * arg)1092 script_std_in(int fd, short events, void *arg)
1093 {
1094 struct request *c = arg;
1095 script_in(fd, &c->script_ev, c, FCGI_STDOUT);
1096 }
1097
1098 void
script_err_in(int fd,short events,void * arg)1099 script_err_in(int fd, short events, void *arg)
1100 {
1101 struct request *c = arg;
1102 script_in(fd, &c->script_err_ev, c, FCGI_STDERR);
1103 }
1104
1105 void
create_data_record(struct request * c,uint8_t type,const void * buf,size_t len)1106 create_data_record(struct request *c, uint8_t type, const void *buf, size_t len)
1107 {
1108 struct fcgi_response *resp;
1109 struct fcgi_record_header *header;
1110 const char *d = buf;
1111 size_t n;
1112
1113 do {
1114 if ((resp = calloc(1, sizeof(struct fcgi_response))) == NULL) {
1115 lwarnx("cannot malloc fcgi_response");
1116 return;
1117 }
1118 header = (struct fcgi_record_header*) resp->data;
1119 header->version = 1;
1120 header->type = type;
1121 header->id = htons(c->id);
1122 header->padding_len = 0;
1123 header->reserved = 0;
1124
1125 n = len > FCGI_CONTENT_SIZE ? FCGI_CONTENT_SIZE : len;
1126 memcpy(resp->data + sizeof(struct fcgi_record_header), d, n);
1127
1128 header->content_len = htons(n);
1129 resp->data_pos = 0;
1130 resp->data_len = n + sizeof(struct fcgi_record_header);
1131 slowcgi_add_response(c, resp);
1132
1133 len -= n;
1134 d += n;
1135 } while (len > 0);
1136 }
1137
1138 void
create_end_record(struct request * c)1139 create_end_record(struct request *c)
1140 {
1141 struct fcgi_response *resp;
1142 struct fcgi_record_header *header;
1143 struct fcgi_end_request_body *end_request;
1144
1145 if ((resp = calloc(1, sizeof(struct fcgi_response))) == NULL) {
1146 lwarnx("cannot malloc fcgi_response");
1147 return;
1148 }
1149 header = (struct fcgi_record_header*) resp->data;
1150 header->version = 1;
1151 header->type = FCGI_END_REQUEST;
1152 header->id = htons(c->id);
1153 header->content_len = htons(sizeof(struct
1154 fcgi_end_request_body));
1155 header->padding_len = 0;
1156 header->reserved = 0;
1157 end_request = (struct fcgi_end_request_body *) (resp->data +
1158 sizeof(struct fcgi_record_header));
1159 end_request->app_status = htonl(c->command_status);
1160 end_request->protocol_status = FCGI_REQUEST_COMPLETE;
1161 end_request->reserved[0] = 0;
1162 end_request->reserved[1] = 0;
1163 end_request->reserved[2] = 0;
1164 resp->data_pos = 0;
1165 resp->data_len = sizeof(struct fcgi_end_request_body) +
1166 sizeof(struct fcgi_record_header);
1167 slowcgi_add_response(c, resp);
1168 c->request_done = 1;
1169 }
1170
1171 void
cleanup_request(struct request * c)1172 cleanup_request(struct request *c)
1173 {
1174 struct fcgi_response *resp;
1175 struct env_val *env_entry;
1176
1177 evtimer_del(&c->tmo);
1178 if (event_initialized(&c->ev))
1179 event_del(&c->ev);
1180 if (event_initialized(&c->resp_ev))
1181 event_del(&c->resp_ev);
1182 if (event_initialized(&c->script_ev)) {
1183 close(EVENT_FD(&c->script_ev));
1184 event_del(&c->script_ev);
1185 }
1186 if (event_initialized(&c->script_err_ev)) {
1187 close(EVENT_FD(&c->script_err_ev));
1188 event_del(&c->script_err_ev);
1189 }
1190
1191 close(c->fd);
1192 while (!SLIST_EMPTY(&c->env)) {
1193 env_entry = SLIST_FIRST(&c->env);
1194 SLIST_REMOVE_HEAD(&c->env, entry);
1195 free(env_entry->key);
1196 free(env_entry->val);
1197 free(env_entry);
1198 }
1199
1200 while ((resp = TAILQ_FIRST(&c->response_head))) {
1201 TAILQ_REMOVE(&c->response_head, resp, entry);
1202 free(resp);
1203 }
1204 LIST_REMOVE(c, entry);
1205 if (! c->inflight_fds_accounted)
1206 cgi_inflight--;
1207 free(c);
1208 }
1209
1210 void
dump_fcgi_record(const char * p,struct fcgi_record_header * h)1211 dump_fcgi_record(const char *p, struct fcgi_record_header *h)
1212 {
1213 dump_fcgi_record_header(p, h);
1214
1215 if (h->type == FCGI_BEGIN_REQUEST)
1216 dump_fcgi_begin_request_body(p,
1217 (struct fcgi_begin_request_body *)(h + 1));
1218 else if (h->type == FCGI_END_REQUEST)
1219 dump_fcgi_end_request_body(p,
1220 (struct fcgi_end_request_body *)(h + 1));
1221 }
1222
1223 void
dump_fcgi_record_header(const char * p,struct fcgi_record_header * h)1224 dump_fcgi_record_header(const char* p, struct fcgi_record_header *h)
1225 {
1226 ldebug("%sversion: %d", p, h->version);
1227 ldebug("%stype: %d", p, h->type);
1228 ldebug("%srequestId: %d", p, ntohs(h->id));
1229 ldebug("%scontentLength: %d", p, ntohs(h->content_len));
1230 ldebug("%spaddingLength: %d", p, h->padding_len);
1231 ldebug("%sreserved: %d", p, h->reserved);
1232 }
1233
1234 void
dump_fcgi_begin_request_body(const char * p,struct fcgi_begin_request_body * b)1235 dump_fcgi_begin_request_body(const char *p, struct fcgi_begin_request_body *b)
1236 {
1237 ldebug("%srole %d", p, ntohs(b->role));
1238 ldebug("%sflags %d", p, b->flags);
1239 }
1240
1241 void
dump_fcgi_end_request_body(const char * p,struct fcgi_end_request_body * b)1242 dump_fcgi_end_request_body(const char *p, struct fcgi_end_request_body *b)
1243 {
1244 ldebug("%sappStatus: %d", p, ntohl(b->app_status));
1245 ldebug("%sprotocolStatus: %d", p, b->protocol_status);
1246 }
1247
1248 void
syslog_vstrerror(int e,int priority,const char * fmt,va_list ap)1249 syslog_vstrerror(int e, int priority, const char *fmt, va_list ap)
1250 {
1251 char *s;
1252
1253 if (vasprintf(&s, fmt, ap) == -1) {
1254 syslog(LOG_EMERG, "unable to alloc in syslog_vstrerror");
1255 exit(1);
1256 }
1257 syslog(priority, "%s: %s", s, strerror(e));
1258 free(s);
1259 }
1260
1261 __dead void
syslog_err(int ecode,const char * fmt,...)1262 syslog_err(int ecode, const char *fmt, ...)
1263 {
1264 va_list ap;
1265
1266 va_start(ap, fmt);
1267 syslog_vstrerror(errno, LOG_CRIT, fmt, ap);
1268 va_end(ap);
1269 exit(ecode);
1270 }
1271
1272 __dead void
syslog_errx(int ecode,const char * fmt,...)1273 syslog_errx(int ecode, const char *fmt, ...)
1274 {
1275 va_list ap;
1276
1277 va_start(ap, fmt);
1278 vsyslog(LOG_CRIT, fmt, ap);
1279 va_end(ap);
1280 exit(ecode);
1281 }
1282
1283 void
syslog_warn(const char * fmt,...)1284 syslog_warn(const char *fmt, ...)
1285 {
1286 va_list ap;
1287
1288 va_start(ap, fmt);
1289 syslog_vstrerror(errno, LOG_ERR, fmt, ap);
1290 va_end(ap);
1291 }
1292
1293 void
syslog_warnx(const char * fmt,...)1294 syslog_warnx(const char *fmt, ...)
1295 {
1296 va_list ap;
1297
1298 va_start(ap, fmt);
1299 vsyslog(LOG_ERR, fmt, ap);
1300 va_end(ap);
1301 }
1302
1303 void
syslog_info(const char * fmt,...)1304 syslog_info(const char *fmt, ...)
1305 {
1306 va_list ap;
1307
1308 va_start(ap, fmt);
1309 vsyslog(LOG_INFO, fmt, ap);
1310 va_end(ap);
1311 }
1312
1313 void
syslog_debug(const char * fmt,...)1314 syslog_debug(const char *fmt, ...)
1315 {
1316 va_list ap;
1317
1318 va_start(ap, fmt);
1319 vsyslog(LOG_DEBUG, fmt, ap);
1320 va_end(ap);
1321 }
1322