Blame


1 014c66b6 2023-06-25 op /*
2 014c66b6 2023-06-25 op * Copyright (c) 2022 Omar Polo <op@omarpolo.com>
3 014c66b6 2023-06-25 op *
4 014c66b6 2023-06-25 op * Permission to use, copy, modify, and distribute this software for any
5 014c66b6 2023-06-25 op * purpose with or without fee is hereby granted, provided that the above
6 014c66b6 2023-06-25 op * copyright notice and this permission notice appear in all copies.
7 014c66b6 2023-06-25 op *
8 014c66b6 2023-06-25 op * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 014c66b6 2023-06-25 op * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 014c66b6 2023-06-25 op * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 014c66b6 2023-06-25 op * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 014c66b6 2023-06-25 op * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 014c66b6 2023-06-25 op * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 014c66b6 2023-06-25 op * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 014c66b6 2023-06-25 op */
16 014c66b6 2023-06-25 op
17 014c66b6 2023-06-25 op #include <sys/socket.h>
18 014c66b6 2023-06-25 op #include <sys/stat.h>
19 014c66b6 2023-06-25 op #include <sys/tree.h>
20 014c66b6 2023-06-25 op #include <sys/types.h>
21 014c66b6 2023-06-25 op #include <sys/un.h>
22 014c66b6 2023-06-25 op #include <sys/wait.h>
23 014c66b6 2023-06-25 op
24 014c66b6 2023-06-25 op #include <errno.h>
25 014c66b6 2023-06-25 op #include <event.h>
26 014c66b6 2023-06-25 op #include <fcntl.h>
27 014c66b6 2023-06-25 op #include <grp.h>
28 014c66b6 2023-06-25 op #include <limits.h>
29 014c66b6 2023-06-25 op #include <pwd.h>
30 014c66b6 2023-06-25 op #include <signal.h>
31 014c66b6 2023-06-25 op #include <stdio.h>
32 014c66b6 2023-06-25 op #include <stdlib.h>
33 014c66b6 2023-06-25 op #include <string.h>
34 014c66b6 2023-06-25 op #include <syslog.h>
35 014c66b6 2023-06-25 op #include <unistd.h>
36 014c66b6 2023-06-25 op
37 014c66b6 2023-06-25 op #include "log.h"
38 014c66b6 2023-06-25 op #include "pkg.h"
39 014c66b6 2023-06-25 op
40 014c66b6 2023-06-25 op #ifndef PKG_FCGI_DB
41 014c66b6 2023-06-25 op #define PKG_FCGI_DB "/pkg_fcgi/pkgs.sqlite3"
42 014c66b6 2023-06-25 op #endif
43 014c66b6 2023-06-25 op
44 014c66b6 2023-06-25 op #ifndef PKG_FCGI_SOCK
45 014c66b6 2023-06-25 op #define PKG_FCGI_SOCK "/run/pkg_fcgi.sock"
46 014c66b6 2023-06-25 op #endif
47 014c66b6 2023-06-25 op
48 014c66b6 2023-06-25 op #ifndef PKG_FCGI_USER
49 014c66b6 2023-06-25 op #define PKG_FCGI_USER "www"
50 014c66b6 2023-06-25 op #endif
51 014c66b6 2023-06-25 op
52 014c66b6 2023-06-25 op #define MAX_CHILDREN 32
53 014c66b6 2023-06-25 op
54 014c66b6 2023-06-25 op static const char *argv0;
55 014c66b6 2023-06-25 op static pid_t pids[MAX_CHILDREN];
56 014c66b6 2023-06-25 op static int children = 3;
57 014c66b6 2023-06-25 op
58 014c66b6 2023-06-25 op static volatile sig_atomic_t got_sigchld;
59 014c66b6 2023-06-25 op
60 014c66b6 2023-06-25 op static void
61 014c66b6 2023-06-25 op handle_sigchld(int sig)
62 014c66b6 2023-06-25 op {
63 014c66b6 2023-06-25 op int i, saved_errno;
64 014c66b6 2023-06-25 op
65 014c66b6 2023-06-25 op if (got_sigchld)
66 014c66b6 2023-06-25 op return;
67 014c66b6 2023-06-25 op
68 014c66b6 2023-06-25 op got_sigchld = 1;
69 014c66b6 2023-06-25 op saved_errno = errno;
70 014c66b6 2023-06-25 op
71 014c66b6 2023-06-25 op for (i = 0; i < children; ++i)
72 014c66b6 2023-06-25 op (void) kill(pids[i], SIGTERM);
73 014c66b6 2023-06-25 op
74 014c66b6 2023-06-25 op errno = saved_errno;
75 014c66b6 2023-06-25 op }
76 014c66b6 2023-06-25 op
77 014c66b6 2023-06-25 op static int
78 014c66b6 2023-06-25 op bind_socket(const char *path, struct passwd *pw)
79 014c66b6 2023-06-25 op {
80 014c66b6 2023-06-25 op struct sockaddr_un sun;
81 014c66b6 2023-06-25 op int fd, old_umask;
82 014c66b6 2023-06-25 op
83 014c66b6 2023-06-25 op if ((fd = socket(AF_UNIX, SOCK_STREAM|SOCK_NONBLOCK, 0)) == -1) {
84 014c66b6 2023-06-25 op log_warn("%s: socket", __func__);
85 014c66b6 2023-06-25 op return (-1);
86 014c66b6 2023-06-25 op }
87 014c66b6 2023-06-25 op
88 014c66b6 2023-06-25 op memset(&sun, 0, sizeof(sun));
89 014c66b6 2023-06-25 op sun.sun_family = AF_UNIX;
90 014c66b6 2023-06-25 op
91 014c66b6 2023-06-25 op if (strlcpy(sun.sun_path, path, sizeof(sun.sun_path)) >=
92 014c66b6 2023-06-25 op sizeof(sun.sun_path)) {
93 014c66b6 2023-06-25 op log_warnx("%s: path too long: %s", __func__, path);
94 014c66b6 2023-06-25 op close(fd);
95 014c66b6 2023-06-25 op return (-1);
96 014c66b6 2023-06-25 op }
97 014c66b6 2023-06-25 op
98 014c66b6 2023-06-25 op if (unlink(path) == -1 && errno != ENOENT) {
99 014c66b6 2023-06-25 op log_warn("%s: unlink %s", __func__, path);
100 014c66b6 2023-06-25 op close(fd);
101 014c66b6 2023-06-25 op return (-1);
102 014c66b6 2023-06-25 op }
103 014c66b6 2023-06-25 op
104 014c66b6 2023-06-25 op old_umask = umask(S_IXUSR|S_IXGRP|S_IWOTH|S_IROTH|S_IXOTH);
105 014c66b6 2023-06-25 op if (bind(fd, (struct sockaddr *)&sun, sizeof(sun)) == -1) {
106 014c66b6 2023-06-25 op log_warn("%s: bind: %s (%d)", __func__, path, geteuid());
107 014c66b6 2023-06-25 op close(fd);
108 014c66b6 2023-06-25 op umask(old_umask);
109 014c66b6 2023-06-25 op return (-1);
110 014c66b6 2023-06-25 op }
111 014c66b6 2023-06-25 op umask(old_umask);
112 014c66b6 2023-06-25 op
113 014c66b6 2023-06-25 op if (chmod(path, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP) == -1) {
114 014c66b6 2023-06-25 op log_warn("%s: chmod %s", __func__, path);
115 014c66b6 2023-06-25 op close(fd);
116 014c66b6 2023-06-25 op (void) unlink(path);
117 014c66b6 2023-06-25 op return (-1);
118 014c66b6 2023-06-25 op }
119 014c66b6 2023-06-25 op
120 014c66b6 2023-06-25 op if (chown(path, pw->pw_uid, pw->pw_gid) == -1) {
121 014c66b6 2023-06-25 op log_warn("%s: chown %s %s", __func__, pw->pw_name, path);
122 014c66b6 2023-06-25 op close(fd);
123 014c66b6 2023-06-25 op (void) unlink(path);
124 014c66b6 2023-06-25 op return (-1);
125 014c66b6 2023-06-25 op }
126 014c66b6 2023-06-25 op
127 014c66b6 2023-06-25 op if (listen(fd, 5) == -1) {
128 014c66b6 2023-06-25 op log_warn("%s: listen", __func__);
129 014c66b6 2023-06-25 op close(fd);
130 014c66b6 2023-06-25 op (void) unlink(path);
131 014c66b6 2023-06-25 op return (-1);
132 014c66b6 2023-06-25 op }
133 014c66b6 2023-06-25 op
134 014c66b6 2023-06-25 op return (fd);
135 014c66b6 2023-06-25 op }
136 014c66b6 2023-06-25 op
137 014c66b6 2023-06-25 op static pid_t
138 014c66b6 2023-06-25 op start_child(const char *root, const char *user, const char *db,
139 014c66b6 2023-06-25 op int daemonize, int verbose, int fd)
140 014c66b6 2023-06-25 op {
141 014c66b6 2023-06-25 op char *argv[10];
142 014c66b6 2023-06-25 op int argc = 0;
143 014c66b6 2023-06-25 op pid_t pid;
144 014c66b6 2023-06-25 op
145 014c66b6 2023-06-25 op switch (pid = fork()) {
146 014c66b6 2023-06-25 op case -1:
147 014c66b6 2023-06-25 op fatal("cannot fork");
148 014c66b6 2023-06-25 op case 0:
149 014c66b6 2023-06-25 op break;
150 014c66b6 2023-06-25 op default:
151 014c66b6 2023-06-25 op close(fd);
152 014c66b6 2023-06-25 op return (pid);
153 014c66b6 2023-06-25 op }
154 014c66b6 2023-06-25 op
155 014c66b6 2023-06-25 op if (fd != 3) {
156 014c66b6 2023-06-25 op if (dup2(fd, 3) == -1)
157 014c66b6 2023-06-25 op fatal("cannot setup imsg fd");
158 014c66b6 2023-06-25 op } else if (fcntl(fd, F_SETFD, 0) == -1)
159 014c66b6 2023-06-25 op fatal("cannot setup imsg fd");
160 014c66b6 2023-06-25 op
161 014c66b6 2023-06-25 op argv[argc++] = (char *)argv0;
162 014c66b6 2023-06-25 op argv[argc++] = (char *)"-S";
163 014c66b6 2023-06-25 op argv[argc++] = (char *)"-p"; argv[argc++] = (char *)root;
164 014c66b6 2023-06-25 op argv[argc++] = (char *)"-u"; argv[argc++] = (char *)user;
165 014c66b6 2023-06-25 op if (!daemonize)
166 014c66b6 2023-06-25 op argv[argc++] = (char *)"-d";
167 014c66b6 2023-06-25 op if (verbose)
168 014c66b6 2023-06-25 op argv[argc++] = (char *)"-v";
169 014c66b6 2023-06-25 op argv[argc++] = (char *)db;
170 014c66b6 2023-06-25 op argv[argc++] = NULL;
171 014c66b6 2023-06-25 op
172 014c66b6 2023-06-25 op execvp(argv0, argv);
173 014c66b6 2023-06-25 op fatal("execvp");
174 014c66b6 2023-06-25 op }
175 014c66b6 2023-06-25 op
176 014c66b6 2023-06-25 op static void __dead
177 014c66b6 2023-06-25 op usage(void)
178 014c66b6 2023-06-25 op {
179 014c66b6 2023-06-25 op fprintf(stderr,
180 014c66b6 2023-06-25 op "usage: %s [-dv] [-j n] [-p path] [-s socket] [-u user] [db]\n",
181 014c66b6 2023-06-25 op getprogname());
182 014c66b6 2023-06-25 op exit(1);
183 014c66b6 2023-06-25 op }
184 014c66b6 2023-06-25 op
185 014c66b6 2023-06-25 op int
186 014c66b6 2023-06-25 op main(int argc, char **argv)
187 014c66b6 2023-06-25 op {
188 014c66b6 2023-06-25 op struct stat sb;
189 014c66b6 2023-06-25 op struct passwd *pw;
190 014c66b6 2023-06-25 op pid_t pid;
191 014c66b6 2023-06-25 op char path[PATH_MAX];
192 014c66b6 2023-06-25 op const char *cause;
193 014c66b6 2023-06-25 op const char *root = NULL;
194 014c66b6 2023-06-25 op const char *sock = PKG_FCGI_SOCK;
195 014c66b6 2023-06-25 op const char *user = PKG_FCGI_USER;
196 014c66b6 2023-06-25 op const char *db = PKG_FCGI_DB;
197 014c66b6 2023-06-25 op const char *errstr;
198 014c66b6 2023-06-25 op int ch, i, daemonize = 1, verbosity = 0;
199 014c66b6 2023-06-25 op int server = 0, fd = -1;
200 014c66b6 2023-06-25 op int status;
201 014c66b6 2023-06-25 op
202 014c66b6 2023-06-25 op /*
203 014c66b6 2023-06-25 op * Ensure we have fds 0-2 open so that we have no issue with
204 014c66b6 2023-06-25 op * calling bind_socket before daemon(3).
205 014c66b6 2023-06-25 op */
206 014c66b6 2023-06-25 op for (i = 0; i < 3; ++i) {
207 014c66b6 2023-06-25 op if (fstat(i, &sb) == -1) {
208 014c66b6 2023-06-25 op if ((fd = open("/dev/null", O_RDWR)) != -1) {
209 014c66b6 2023-06-25 op if (dup2(fd, i) == -1)
210 014c66b6 2023-06-25 op exit(1);
211 014c66b6 2023-06-25 op if (fd > i)
212 014c66b6 2023-06-25 op close(fd);
213 014c66b6 2023-06-25 op } else
214 014c66b6 2023-06-25 op exit(1);
215 014c66b6 2023-06-25 op }
216 014c66b6 2023-06-25 op }
217 014c66b6 2023-06-25 op
218 014c66b6 2023-06-25 op log_init(1, LOG_DAEMON); /* Log to stderr until daemonized. */
219 014c66b6 2023-06-25 op
220 014c66b6 2023-06-25 op if ((argv0 = argv[0]) == NULL)
221 014c66b6 2023-06-25 op fatalx("argv[0] is NULL");
222 014c66b6 2023-06-25 op
223 014c66b6 2023-06-25 op while ((ch = getopt(argc, argv, "dj:p:Ss:u:v")) != -1) {
224 014c66b6 2023-06-25 op switch (ch) {
225 014c66b6 2023-06-25 op case 'd':
226 014c66b6 2023-06-25 op daemonize = 0;
227 014c66b6 2023-06-25 op break;
228 014c66b6 2023-06-25 op case 'j':
229 014c66b6 2023-06-25 op children = strtonum(optarg, 1, MAX_CHILDREN, &errstr);
230 014c66b6 2023-06-25 op if (errstr)
231 014c66b6 2023-06-25 op fatalx("number of children is %s: %s",
232 014c66b6 2023-06-25 op errstr, optarg);
233 014c66b6 2023-06-25 op break;
234 014c66b6 2023-06-25 op case 'p':
235 014c66b6 2023-06-25 op root = optarg;
236 014c66b6 2023-06-25 op break;
237 014c66b6 2023-06-25 op case 'S':
238 014c66b6 2023-06-25 op server = 1;
239 014c66b6 2023-06-25 op break;
240 014c66b6 2023-06-25 op case 's':
241 014c66b6 2023-06-25 op sock = optarg;
242 014c66b6 2023-06-25 op break;
243 014c66b6 2023-06-25 op case 'u':
244 014c66b6 2023-06-25 op user = optarg;
245 014c66b6 2023-06-25 op break;
246 014c66b6 2023-06-25 op case 'v':
247 014c66b6 2023-06-25 op verbosity++;
248 014c66b6 2023-06-25 op break;
249 014c66b6 2023-06-25 op default:
250 014c66b6 2023-06-25 op usage();
251 014c66b6 2023-06-25 op }
252 014c66b6 2023-06-25 op }
253 014c66b6 2023-06-25 op argc -= optind;
254 014c66b6 2023-06-25 op argv += optind;
255 014c66b6 2023-06-25 op
256 014c66b6 2023-06-25 op if (argc > 1)
257 014c66b6 2023-06-25 op usage();
258 014c66b6 2023-06-25 op if (argc == 1)
259 014c66b6 2023-06-25 op db = argv[0];
260 014c66b6 2023-06-25 op
261 014c66b6 2023-06-25 op if (geteuid())
262 014c66b6 2023-06-25 op fatalx("need root privileges");
263 014c66b6 2023-06-25 op
264 014c66b6 2023-06-25 op pw = getpwnam(user);
265 014c66b6 2023-06-25 op if (pw == NULL)
266 014c66b6 2023-06-25 op fatalx("user %s not found", user);
267 014c66b6 2023-06-25 op if (pw->pw_uid == 0)
268 014c66b6 2023-06-25 op fatalx("cannot run as %s: must not be the superuser", user);
269 014c66b6 2023-06-25 op
270 014c66b6 2023-06-25 op if (root == NULL)
271 014c66b6 2023-06-25 op root = pw->pw_dir;
272 014c66b6 2023-06-25 op
273 014c66b6 2023-06-25 op if (!server) {
274 014c66b6 2023-06-25 op int ret;
275 014c66b6 2023-06-25 op
276 014c66b6 2023-06-25 op ret = snprintf(path, sizeof(path), "%s/%s", root, sock);
277 014c66b6 2023-06-25 op if (ret < 0 || (size_t)ret >= sizeof(path))
278 014c66b6 2023-06-25 op fatalx("socket path too long");
279 014c66b6 2023-06-25 op
280 014c66b6 2023-06-25 op if ((fd = bind_socket(path, pw)) == -1)
281 014c66b6 2023-06-25 op fatalx("failed to open socket %s", sock);
282 014c66b6 2023-06-25 op
283 014c66b6 2023-06-25 op for (i = 0; i < children; ++i) {
284 014c66b6 2023-06-25 op int d;
285 014c66b6 2023-06-25 op
286 014c66b6 2023-06-25 op if ((d = dup(fd)) == -1)
287 014c66b6 2023-06-25 op fatalx("dup");
288 014c66b6 2023-06-25 op pids[i] = start_child(root, user, db,
289 014c66b6 2023-06-25 op daemonize, verbosity, d);
290 014c66b6 2023-06-25 op log_debug("forking child %d (pid %lld)", i,
291 014c66b6 2023-06-25 op (long long)pids[i]);
292 014c66b6 2023-06-25 op }
293 014c66b6 2023-06-25 op
294 014c66b6 2023-06-25 op signal(SIGCHLD, handle_sigchld);
295 014c66b6 2023-06-25 op }
296 014c66b6 2023-06-25 op
297 014c66b6 2023-06-25 op if (chroot(root) == -1)
298 014c66b6 2023-06-25 op fatal("chroot %s", root);
299 014c66b6 2023-06-25 op if (chdir("/") == -1)
300 014c66b6 2023-06-25 op fatal("chdir /");
301 014c66b6 2023-06-25 op
302 014c66b6 2023-06-25 op if (setgroups(1, &pw->pw_gid) == -1 ||
303 014c66b6 2023-06-25 op setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) == -1 ||
304 014c66b6 2023-06-25 op setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) == -1)
305 014c66b6 2023-06-25 op fatal("failed to drop privileges");
306 014c66b6 2023-06-25 op
307 014c66b6 2023-06-25 op log_init(daemonize ? 0 : 1, LOG_DAEMON);
308 014c66b6 2023-06-25 op log_setverbose(verbosity);
309 014c66b6 2023-06-25 op
310 014c66b6 2023-06-25 op if (server)
311 014c66b6 2023-06-25 op exit(server_main(db));
312 014c66b6 2023-06-25 op
313 014c66b6 2023-06-25 op if (daemonize && daemon(1, 0) == -1)
314 014c66b6 2023-06-25 op fatal("daemon");
315 014c66b6 2023-06-25 op
316 014c66b6 2023-06-25 op if (pledge("stdio proc", NULL) == -1)
317 014c66b6 2023-06-25 op fatal("pledge");
318 014c66b6 2023-06-25 op
319 014c66b6 2023-06-25 op for (;;) {
320 014c66b6 2023-06-25 op do {
321 014c66b6 2023-06-25 op pid = waitpid(WAIT_ANY, &status, 0);
322 014c66b6 2023-06-25 op } while (pid != -1 || errno == EINTR);
323 014c66b6 2023-06-25 op
324 014c66b6 2023-06-25 op if (pid == -1) {
325 014c66b6 2023-06-25 op if (errno == ECHILD)
326 014c66b6 2023-06-25 op break;
327 014c66b6 2023-06-25 op fatal("waitpid failed");
328 014c66b6 2023-06-25 op }
329 014c66b6 2023-06-25 op
330 014c66b6 2023-06-25 op if (WIFSIGNALED(status))
331 014c66b6 2023-06-25 op cause = "was terminated";
332 014c66b6 2023-06-25 op else if (WIFEXITED(status)) {
333 014c66b6 2023-06-25 op if (WEXITSTATUS(status) != 0)
334 014c66b6 2023-06-25 op cause = "exited abnormally";
335 014c66b6 2023-06-25 op else
336 014c66b6 2023-06-25 op cause = "exited successfully";
337 014c66b6 2023-06-25 op } else
338 014c66b6 2023-06-25 op cause = "died";
339 014c66b6 2023-06-25 op
340 014c66b6 2023-06-25 op log_warnx("child process %lld %s", (long long)pid, cause);
341 014c66b6 2023-06-25 op }
342 014c66b6 2023-06-25 op
343 014c66b6 2023-06-25 op return (1);
344 014c66b6 2023-06-25 op }