2 * Copyright (c) 2016, 2019, 2020-2021 Tracey Emery <tracey@traceyemery.net>
3 * Copyright (c) 2015 Mike Larkin <mlarkin@openbsd.org>
4 * Copyright (c) 2013 David Gwynne <dlg@openbsd.org>
5 * Copyright (c) 2013 Florian Obser <florian@openbsd.org>
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20 #include "got_compat.h"
22 #include <sys/param.h>
23 #include <sys/ioctl.h>
24 #include <sys/queue.h>
27 #include <sys/resource.h>
28 #include <sys/socket.h>
31 #include <sys/types.h>
36 #include <netinet/in.h>
52 #include "got_error.h"
53 #include "got_opentemp.h"
54 #include "got_reference.h"
55 #include "got_repository.h"
61 #define SOCKS_BACKLOG 5
62 #define MAXIMUM(a, b) (((a) > (b)) ? (a) : (b))
65 volatile int client_cnt
;
67 static struct timeval timeout
= { TIMEOUT_DEFAULT
, 0 };
69 static void sockets_sighdlr(int, short, void *);
70 static void sockets_run(struct privsep
*, struct privsep_proc
*, void *);
71 static void sockets_launch(void);
72 static void sockets_purge(struct gotwebd
*);
73 static void sockets_accept_paused(int, short, void *);
74 static void sockets_rlimit(int);
76 static int sockets_dispatch_gotwebd(int, struct privsep_proc
*,
78 static int sockets_unix_socket_listen(struct privsep
*, struct socket
*);
79 static int sockets_create_socket(struct address
*, in_port_t
);
80 static int sockets_accept_reserve(int, struct sockaddr
*, socklen_t
*,
83 static struct socket
*sockets_conf_new_socket_unix(struct gotwebd
*,
84 struct server
*, int);
85 static struct socket
*sockets_conf_new_socket_fcgi(struct gotwebd
*,
86 struct server
*, int, struct address
*);
90 static struct privsep_proc procs
[] = {
91 { "gotwebd", PROC_GOTWEBD
, sockets_dispatch_gotwebd
},
95 sockets(struct privsep
*ps
, struct privsep_proc
*p
)
97 proc_run(ps
, p
, procs
, nitems(procs
), sockets_run
, NULL
);
101 sockets_run(struct privsep
*ps
, struct privsep_proc
*p
, void *arg
)
103 if (config_init(ps
->ps_env
) == -1)
104 fatal("failed to initialize configuration");
106 p
->p_shutdown
= sockets_shutdown
;
110 signal_del(&ps
->ps_evsigchld
);
111 signal_set(&ps
->ps_evsigchld
, SIGCHLD
, sockets_sighdlr
, ps
);
112 signal_add(&ps
->ps_evsigchld
, NULL
);
115 if (pledge("stdio rpath wpath cpath inet recvfd proc exec sendfd",
122 sockets_parse_sockets(struct gotwebd
*env
)
126 struct socket
*new_sock
= NULL
;
129 TAILQ_FOREACH(srv
, &env
->servers
, entry
) {
130 if (srv
->unix_socket
) {
131 new_sock
= sockets_conf_new_socket_unix(env
, srv
,
135 TAILQ_INSERT_TAIL(&env
->sockets
, new_sock
,
140 if (srv
->fcgi_socket
) {
141 if (TAILQ_EMPTY(&srv
->al
)) {
142 fatalx("%s: server %s has no IP addresses to "
143 "listen for FCGI connections", __func__
,
146 TAILQ_FOREACH(a
, &srv
->al
, entry
) {
147 if (a
->ss
.ss_family
!= AF_INET
&&
148 a
->ss
.ss_family
!= AF_INET6
)
150 new_sock
= sockets_conf_new_socket_fcgi(env
,
154 TAILQ_INSERT_TAIL(&env
->sockets
,
162 static struct socket
*
163 sockets_conf_new_socket_unix(struct gotwebd
*env
, struct server
*srv
, int id
)
168 if ((sock
= calloc(1, sizeof(*sock
))) == NULL
)
169 fatalx("%s: calloc", __func__
);
173 sock
->conf
.af_type
= AF_UNIX
;
175 if (strlcpy(sock
->conf
.unix_socket_name
,
176 srv
->unix_socket_name
,
177 sizeof(sock
->conf
.unix_socket_name
)) >=
178 sizeof(sock
->conf
.unix_socket_name
)) {
180 fatalx("%s: strlcpy", __func__
);
183 n
= snprintf(sock
->conf
.name
, GOTWEBD_MAXTEXT
, "%s_parent",
185 if (n
< 0 || (size_t)n
>= GOTWEBD_MAXTEXT
) {
187 fatalx("%s: snprintf", __func__
);
190 if (strlcpy(sock
->conf
.srv_name
, srv
->name
,
191 sizeof(sock
->conf
.srv_name
)) >= sizeof(sock
->conf
.srv_name
)) {
193 fatalx("%s: strlcpy", __func__
);
199 static struct socket
*
200 sockets_conf_new_socket_fcgi(struct gotwebd
*env
, struct server
*srv
, int id
,
207 if ((sock
= calloc(1, sizeof(*sock
))) == NULL
)
208 fatalx("%s: calloc", __func__
);
212 sock
->conf
.af_type
= a
->ss
.ss_family
;
214 sock
->conf
.fcgi_socket_port
= a
->port
;
216 n
= snprintf(sock
->conf
.name
, GOTWEBD_MAXTEXT
, "%s_parent",
218 if (n
< 0 || (size_t)n
>= GOTWEBD_MAXTEXT
) {
220 fatalx("%s: snprintf", __func__
);
223 if (strlcpy(sock
->conf
.srv_name
, srv
->name
,
224 sizeof(sock
->conf
.srv_name
)) >= sizeof(sock
->conf
.srv_name
)) {
226 fatalx("%s: strlcpy", __func__
);
229 acp
= &sock
->conf
.addr
;
231 memcpy(&acp
->ss
, &a
->ss
, sizeof(acp
->ss
));
232 acp
->ipproto
= a
->ipproto
;
233 acp
->prefixlen
= a
->prefixlen
;
235 if (strlen(a
->ifname
) != 0) {
236 if (strlcpy(acp
->ifname
, a
->ifname
,
237 sizeof(acp
->ifname
)) >= sizeof(acp
->ifname
)) {
238 fatalx("%s: interface name truncated",
251 TAILQ_FOREACH(sock
, &gotwebd_env
->sockets
, entry
) {
252 log_debug("%s: configuring socket %d (%d)", __func__
,
253 sock
->conf
.id
, sock
->fd
);
255 event_set(&sock
->ev
, sock
->fd
, EV_READ
| EV_PERSIST
,
256 sockets_socket_accept
, sock
);
258 if (event_add(&sock
->ev
, NULL
))
259 fatalx("event add sock");
261 evtimer_set(&sock
->pause
, sockets_accept_paused
, sock
);
263 log_debug("%s: running socket listener %d", __func__
,
269 sockets_purge(struct gotwebd
*env
)
271 struct socket
*sock
, *tsock
;
273 /* shutdown and remove sockets */
274 TAILQ_FOREACH_SAFE(sock
, &env
->sockets
, entry
, tsock
) {
275 if (event_initialized(&sock
->ev
))
276 event_del(&sock
->ev
);
277 if (evtimer_initialized(&sock
->evt
))
278 evtimer_del(&sock
->evt
);
279 if (evtimer_initialized(&sock
->pause
))
280 evtimer_del(&sock
->pause
);
283 TAILQ_REMOVE(&env
->sockets
, sock
, entry
);
288 sockets_dispatch_gotwebd(int fd
, struct privsep_proc
*p
, struct imsg
*imsg
)
290 struct privsep
*ps
= p
->p_ps
;
291 int res
= 0, cmd
= 0, verbose
;
293 switch (imsg
->hdr
.type
) {
295 config_getserver(gotwebd_env
, imsg
);
298 config_getsock(gotwebd_env
, imsg
);
301 config_getfd(gotwebd_env
, imsg
);
304 config_getcfg(gotwebd_env
, imsg
);
309 case IMSG_CTL_VERBOSE
:
310 IMSG_SIZE_CHECK(imsg
, &verbose
);
311 memcpy(&verbose
, imsg
->data
, sizeof(verbose
));
312 log_setverbose(verbose
);
322 if (proc_compose_imsg(ps
, PROC_GOTWEBD
, -1, cmd
,
323 imsg
->hdr
.peerid
, -1, &res
, sizeof(res
)) == -1)
332 sockets_sighdlr(int sig
, short event
, void *arg
)
336 log_info("%s: ignoring SIGHUP", __func__
);
339 log_info("%s: ignoring SIGPIPE", __func__
);
342 log_info("%s: ignoring SIGUSR1", __func__
);
347 log_info("SIGNAL: %d", sig
);
348 fatalx("unexpected signal");
353 sockets_shutdown(void)
355 struct server
*srv
, *tsrv
;
356 struct socket
*sock
, *tsock
;
359 sockets_purge(gotwebd_env
);
362 TAILQ_FOREACH_SAFE(sock
, &gotwebd_env
->sockets
, entry
, tsock
) {
363 TAILQ_REMOVE(&gotwebd_env
->sockets
, sock
, entry
);
369 TAILQ_FOREACH_SAFE(srv
, &gotwebd_env
->servers
, entry
, tsrv
) {
370 for (i
= 0; i
< srv
->ncached_repos
; i
++)
371 got_repo_close(srv
->cached_repos
[i
].repo
);
379 sockets_privinit(struct gotwebd
*env
, struct socket
*sock
)
381 struct privsep
*ps
= env
->gotwebd_ps
;
383 if (sock
->conf
.af_type
== AF_UNIX
) {
384 log_debug("%s: initializing unix socket %s", __func__
,
385 sock
->conf
.unix_socket_name
);
386 sock
->fd
= sockets_unix_socket_listen(ps
, sock
);
387 if (sock
->fd
== -1) {
388 log_warnx("%s: create unix socket failed", __func__
);
393 if (sock
->conf
.af_type
== AF_INET
|| sock
->conf
.af_type
== AF_INET6
) {
394 log_debug("%s: initializing %s FCGI socket on port %d for %s",
395 __func__
, sock
->conf
.af_type
== AF_INET
? "inet" : "inet6",
396 sock
->conf
.fcgi_socket_port
, sock
->conf
.name
);
397 sock
->fd
= sockets_create_socket(&sock
->conf
.addr
,
398 sock
->conf
.fcgi_socket_port
);
399 if (sock
->fd
== -1) {
400 log_warnx("%s: create FCGI socket failed", __func__
);
409 sockets_unix_socket_listen(struct privsep
*ps
, struct socket
*sock
)
411 struct gotwebd
*env
= ps
->ps_env
;
412 struct sockaddr_un sun
;
413 struct socket
*tsock
;
415 mode_t old_umask
, mode
;
417 TAILQ_FOREACH(tsock
, &env
->sockets
, entry
) {
418 if (strcmp(tsock
->conf
.unix_socket_name
,
419 sock
->conf
.unix_socket_name
) == 0 &&
424 /* TA: FIXME: this needs upstreaming. */
425 int socket_flags
= SOCK_STREAM
| SOCK_NONBLOCK
;
427 socket_flags
|= SOCK_CLOEXEC
;
429 u_fd
= socket(AF_UNIX
, socket_flags
, 0);
431 log_warn("%s: socket", __func__
);
435 sun
.sun_family
= AF_UNIX
;
436 if (strlcpy(sun
.sun_path
, sock
->conf
.unix_socket_name
,
437 sizeof(sun
.sun_path
)) >= sizeof(sun
.sun_path
)) {
438 log_warn("%s: %s name too long", __func__
,
439 sock
->conf
.unix_socket_name
);
444 if (unlink(sock
->conf
.unix_socket_name
) == -1) {
445 if (errno
!= ENOENT
) {
446 log_warn("%s: unlink %s", __func__
,
447 sock
->conf
.unix_socket_name
);
453 old_umask
= umask(S_IXUSR
|S_IXGRP
|S_IWOTH
|S_IROTH
|S_IXOTH
);
454 mode
= S_IRUSR
|S_IWUSR
|S_IRGRP
|S_IWGRP
;
456 if (bind(u_fd
, (struct sockaddr
*)&sun
, sizeof(sun
)) == -1) {
457 log_warn("%s: bind: %s", __func__
, sock
->conf
.unix_socket_name
);
459 (void)umask(old_umask
);
463 (void)umask(old_umask
);
465 if (chmod(sock
->conf
.unix_socket_name
, mode
) == -1) {
466 log_warn("%s: chmod", __func__
);
468 (void)unlink(sock
->conf
.unix_socket_name
);
472 if (chown(sock
->conf
.unix_socket_name
, ps
->ps_pw
->pw_uid
,
473 ps
->ps_pw
->pw_gid
) == -1) {
474 log_warn("%s: chown", __func__
);
476 (void)unlink(sock
->conf
.unix_socket_name
);
480 if (listen(u_fd
, SOCKS_BACKLOG
) == -1) {
481 log_warn("%s: listen", __func__
);
489 sockets_create_socket(struct address
*a
, in_port_t port
)
491 struct addrinfo hints
;
492 int fd
= -1, o_val
= 1, flags
;
494 memset(&hints
, 0, sizeof(hints
));
495 hints
.ai_family
= AF_UNSPEC
;
496 hints
.ai_socktype
= SOCK_STREAM
;
497 hints
.ai_flags
|= AI_PASSIVE
;
499 switch (a
->ss
.ss_family
) {
501 ((struct sockaddr_in
*)(&a
->ss
))->sin_port
= htons(port
);
504 ((struct sockaddr_in6
*)(&a
->ss
))->sin6_port
= htons(port
);
507 log_warnx("%s: unknown address family", __func__
);
511 fd
= socket(a
->ss
.ss_family
, hints
.ai_socktype
, a
->ipproto
);
515 log_debug("%s: opened socket (%d) for %s", __func__
,
518 if (setsockopt(fd
, SOL_SOCKET
, SO_REUSEPORT
, &o_val
,
519 sizeof(int)) == -1) {
520 log_warn("%s: setsockopt error", __func__
);
526 flags
= fcntl(fd
, F_GETFL
);
528 if (fcntl(fd
, F_SETFL
, flags
) == -1) {
529 log_info("%s: could not enable non-blocking I/O", __func__
);
534 if (bind(fd
, (struct sockaddr
*)&a
->ss
, SS_LEN(&a
->ss
)) == -1) {
536 log_info("%s: can't bind to port %d", __func__
,
541 if (listen(fd
, SOMAXCONN
) == -1) {
542 log_warn("%s, unable to listen on socket", __func__
);
551 sockets_accept_reserve(int sockfd
, struct sockaddr
*addr
, socklen_t
*addrlen
,
552 int reserve
, volatile int *counter
)
556 if (getdtablecount() + reserve
+
557 ((*counter
+ 1) * FD_NEEDED
) >= getdtablesize()) {
558 log_debug("inflight fds exceeded");
562 /* TA: This needs fixing upstream. */
564 ret
= accept(sockfd
, addr
, addrlen
);
566 ret
= accept4(sockfd
, addr
, addrlen
, SOCK_NONBLOCK
| SOCK_CLOEXEC
);
571 log_debug("inflight incremented, now %d", *counter
);
578 sockets_accept_paused(int fd
, short events
, void *arg
)
580 struct socket
*sock
= (struct socket
*)arg
;
582 event_add(&sock
->ev
, NULL
);
586 sockets_socket_accept(int fd
, short event
, void *arg
)
588 struct socket
*sock
= (struct socket
*)arg
;
589 struct sockaddr_storage ss
;
590 struct timeval backoff
;
591 struct request
*c
= NULL
;
598 event_add(&sock
->ev
, NULL
);
599 if (event
& EV_TIMEOUT
)
604 s
= sockets_accept_reserve(fd
, (struct sockaddr
*)&ss
, &len
,
605 FD_RESERVE
, &cgi_inflight
);
615 event_del(&sock
->ev
);
616 evtimer_add(&sock
->pause
, &backoff
);
619 log_warn("%s: accept", __func__
);
623 if (client_cnt
> GOTWEBD_MAXCLIENTS
)
626 c
= calloc(1, sizeof(struct request
));
628 log_warn("%s", __func__
);
634 c
->tp
= template(c
, fcgi_puts
, fcgi_putc
);
636 log_warn("%s", __func__
);
645 memcpy(c
->priv_fd
, sock
->priv_fd
, sizeof(c
->priv_fd
));
648 c
->request_started
= 0;
649 c
->sock
->client_status
= CLIENT_CONNECT
;
651 event_set(&c
->ev
, s
, EV_READ
|EV_PERSIST
, fcgi_request
, c
);
652 event_add(&c
->ev
, NULL
);
654 evtimer_set(&c
->tmo
, fcgi_timeout
, c
);
655 evtimer_add(&c
->tmo
, &timeout
);
668 sockets_rlimit(int maxfd
)
672 if (getrlimit(RLIMIT_NOFILE
, &rl
) == -1)
673 fatal("%s: failed to get resource limit", __func__
);
674 log_debug("%s: max open files %llu", __func__
,
675 (unsigned long long)rl
.rlim_max
);
678 * Allow the maximum number of open file descriptors for this
679 * login class (which should be the class "daemon" by default).
682 rl
.rlim_cur
= rl
.rlim_max
;
684 rl
.rlim_cur
= MAXIMUM(rl
.rlim_max
, (rlim_t
)maxfd
);
685 if (setrlimit(RLIMIT_NOFILE
, &rl
) == -1)
686 fatal("%s: failed to set resource limit", __func__
);