2 50794f47 2023-04-04 op * This file is in the public domain.
5 50794f47 2023-04-04 op #include <sys/socket.h>
6 50794f47 2023-04-04 op #include <sys/stat.h>
7 50794f47 2023-04-04 op #include <sys/tree.h>
8 50794f47 2023-04-04 op #include <sys/types.h>
9 50794f47 2023-04-04 op #include <sys/un.h>
10 50794f47 2023-04-04 op #include <sys/wait.h>
12 50794f47 2023-04-04 op #include <err.h>
13 50794f47 2023-04-04 op #include <errno.h>
14 50794f47 2023-04-04 op #include <event.h>
15 50794f47 2023-04-04 op #include <fcntl.h>
16 50794f47 2023-04-04 op #include <limits.h>
17 50794f47 2023-04-04 op #include <pwd.h>
18 50794f47 2023-04-04 op #include <signal.h>
19 50794f47 2023-04-04 op #include <stdarg.h>
20 50794f47 2023-04-04 op #include <stdio.h>
21 50794f47 2023-04-04 op #include <stdlib.h>
22 50794f47 2023-04-04 op #include <string.h>
23 50794f47 2023-04-04 op #include <syslog.h>
24 50794f47 2023-04-04 op #include <unistd.h>
26 50794f47 2023-04-04 op #include "msearchd.h"
28 50794f47 2023-04-04 op #ifndef MSEARCHD_DB
29 50794f47 2023-04-04 op #define MSEARCHD_DB "/msearchd/mails.sqlite3"
32 50794f47 2023-04-04 op #ifndef MSEARCHD_SOCK
33 50794f47 2023-04-04 op #define MSEARCHD_SOCK "/run/msearchd.sock"
36 50794f47 2023-04-04 op #ifndef MSEARCHD_USER
37 50794f47 2023-04-04 op #define MSEARCHD_USER "www"
40 8e5ae9ac 2023-05-05 op #ifndef MSEARCH_TMPL_DIR
41 38232a0a 2023-05-07 op #define MSEARCH_TMPL_DIR SYSCONFDIR "/smarc"
44 50794f47 2023-04-04 op #define MAX_CHILDREN 32
48 50794f47 2023-04-04 op int children = 3;
49 50794f47 2023-04-04 op pid_t pids[MAX_CHILDREN];
51 8e5ae9ac 2023-05-05 op const char *tmpl_head;
52 8e5ae9ac 2023-05-05 op const char *tmpl_search;
53 8e5ae9ac 2023-05-05 op const char *tmpl_search_header;
54 8e5ae9ac 2023-05-05 op const char *tmpl_foot;
56 50794f47 2023-04-04 op __dead void srch_syslog_fatal(int, const char *, ...);
57 50794f47 2023-04-04 op __dead void srch_syslog_fatalx(int, const char *, ...);
58 50794f47 2023-04-04 op void srch_syslog_warn(const char *, ...);
59 50794f47 2023-04-04 op void srch_syslog_warnx(const char *, ...);
60 50794f47 2023-04-04 op void srch_syslog_info(const char *, ...);
61 50794f47 2023-04-04 op void srch_syslog_debug(const char *, ...);
63 50794f47 2023-04-04 op const struct logger syslogger = {
64 50794f47 2023-04-04 op .fatal = &srch_syslog_fatal,
65 50794f47 2023-04-04 op .fatalx = &srch_syslog_fatalx,
66 50794f47 2023-04-04 op .warn = &srch_syslog_warn,
67 50794f47 2023-04-04 op .warnx = &srch_syslog_warnx,
68 50794f47 2023-04-04 op .info = &srch_syslog_info,
69 50794f47 2023-04-04 op .debug = &srch_syslog_debug,
72 50794f47 2023-04-04 op const struct logger dbglogger = {
73 50794f47 2023-04-04 op .fatal = &err,
74 50794f47 2023-04-04 op .fatalx = &errx,
75 50794f47 2023-04-04 op .warn = &warn,
76 50794f47 2023-04-04 op .warnx = &warnx,
77 50794f47 2023-04-04 op .info = &warnx,
78 50794f47 2023-04-04 op .debug = &warnx,
81 50794f47 2023-04-04 op const struct logger *logger = &dbglogger;
84 28c73ba9 2023-04-04 op sighdlr(int sig)
86 28c73ba9 2023-04-04 op static volatile sig_atomic_t got_sig;
87 50794f47 2023-04-04 op int i, save_errno;
93 50794f47 2023-04-04 op save_errno = errno;
94 50794f47 2023-04-04 op for (i = 0; i < children; ++i)
95 50794f47 2023-04-04 op (void)kill(pids[i], SIGTERM);
96 50794f47 2023-04-04 op errno = save_errno;
100 8e5ae9ac 2023-05-05 op load_tmpl(const char **ret, const char *dir, const char *name)
103 8e5ae9ac 2023-05-05 op struct stat sb;
105 8e5ae9ac 2023-05-05 op char path[PATH_MAX];
108 8e5ae9ac 2023-05-05 op r = snprintf(path, sizeof(path), "%s/%s", dir, name);
109 8e5ae9ac 2023-05-05 op if (r < 0 || (size_t)r >= sizeof(path))
110 8e5ae9ac 2023-05-05 op fatalx("path too long: %s/%s", dir, name);
112 8e5ae9ac 2023-05-05 op if ((fp = fopen(path, "r")) == NULL)
113 8e5ae9ac 2023-05-05 op fatal("can't open %s", path);
115 8e5ae9ac 2023-05-05 op if (fstat(fileno(fp), &sb) == -1)
116 8e5ae9ac 2023-05-05 op fatal("fstat");
118 8e5ae9ac 2023-05-05 op if (sb.st_size > SIZE_MAX)
119 8e5ae9ac 2023-05-05 op fatal("file too big %s", path);
121 8e5ae9ac 2023-05-05 op if ((t = malloc(sb.st_size + 1)) == NULL)
122 8e5ae9ac 2023-05-05 op fatal("malloc");
124 8e5ae9ac 2023-05-05 op if (fread(t, 1, sb.st_size, fp) != sb.st_size)
125 8e5ae9ac 2023-05-05 op fatal("fread %s", path);
129 8e5ae9ac 2023-05-05 op t[sb.st_size] = '\0';
134 50794f47 2023-04-04 op bind_socket(const char *path, struct passwd *pw)
136 50794f47 2023-04-04 op struct sockaddr_un sun;
137 50794f47 2023-04-04 op int fd, old_umask;
139 50794f47 2023-04-04 op if ((fd = socket(AF_UNIX, SOCK_STREAM|SOCK_NONBLOCK, 0)) == -1) {
140 50794f47 2023-04-04 op log_warn("%s: socket", __func__);
144 50794f47 2023-04-04 op memset(&sun, 0, sizeof(sun));
145 50794f47 2023-04-04 op sun.sun_family = AF_UNIX;
147 50794f47 2023-04-04 op if (strlcpy(sun.sun_path, path, sizeof(sun.sun_path)) >=
148 50794f47 2023-04-04 op sizeof(sun.sun_path)) {
149 50794f47 2023-04-04 op log_warnx("%s: path too long: %s", __func__, path);
154 50794f47 2023-04-04 op if (unlink(path) == -1 && errno != ENOENT) {
155 50794f47 2023-04-04 op log_warn("%s: unlink %s", __func__, path);
160 50794f47 2023-04-04 op old_umask = umask(0117);
161 50794f47 2023-04-04 op if (bind(fd, (struct sockaddr *)&sun, sizeof(sun)) == -1) {
162 50794f47 2023-04-04 op log_warn("%s: bind: %s (%d)", __func__, path, geteuid());
164 50794f47 2023-04-04 op umask(old_umask);
167 50794f47 2023-04-04 op umask(old_umask);
169 50794f47 2023-04-04 op if (chmod(path, 0660) == -1) {
170 50794f47 2023-04-04 op log_warn("%s: chmod 0660 %s", __func__, path);
172 50794f47 2023-04-04 op (void)unlink(path);
176 50794f47 2023-04-04 op if (chown(path, pw->pw_uid, pw->pw_gid) == -1) {
177 50794f47 2023-04-04 op log_warn("%s: chown %s %s", __func__, pw->pw_name, path);
179 50794f47 2023-04-04 op (void)unlink(path);
183 50794f47 2023-04-04 op if (listen(fd, 5) == -1) {
184 50794f47 2023-04-04 op log_warn("%s: listen", __func__);
186 50794f47 2023-04-04 op (void)unlink(path);
194 50794f47 2023-04-04 op start_child(const char *argv0, const char *root, const char *user,
195 8e5ae9ac 2023-05-05 op const char *db, const char *tmpl, int debug, int verbose, int fd)
197 8e5ae9ac 2023-05-05 op const char *argv[13];
198 50794f47 2023-04-04 op int argc = 0;
201 50794f47 2023-04-04 op switch (pid = fork()) {
203 50794f47 2023-04-04 op fatal("cannot fork");
208 50794f47 2023-04-04 op return (pid);
211 50794f47 2023-04-04 op if (fd != 3) {
212 50794f47 2023-04-04 op if (dup2(fd, 3) == -1)
213 50794f47 2023-04-04 op fatal("cannot setup socket fd");
214 50794f47 2023-04-04 op } else if (fcntl(fd, F_SETFD, 0) == -1)
215 50794f47 2023-04-04 op fatal("cannot setup socket fd");
217 50794f47 2023-04-04 op argv[argc++] = argv0;
218 50794f47 2023-04-04 op argv[argc++] = "-S";
219 50794f47 2023-04-04 op argv[argc++] = "-p"; argv[argc++] = root;
220 8e5ae9ac 2023-05-05 op argv[argc++] = "-t"; argv[argc++] = tmpl;
221 50794f47 2023-04-04 op argv[argc++] = "-u"; argv[argc++] = user;
223 50794f47 2023-04-04 op argv[argc++] = "-d";
224 50794f47 2023-04-04 op if (verbose--)
225 50794f47 2023-04-04 op argv[argc++] = "-v";
226 50794f47 2023-04-04 op if (verbose--)
227 50794f47 2023-04-04 op argv[argc++] = "-v";
228 50794f47 2023-04-04 op argv[argc++] = db;
229 50794f47 2023-04-04 op argv[argc++] = NULL;
231 50794f47 2023-04-04 op /* obnoxious cast */
232 50794f47 2023-04-04 op execvp(argv0, (char * const *) argv);
233 50794f47 2023-04-04 op fatal("execvp %s", argv0);
236 50794f47 2023-04-04 op static void __dead
239 8e5ae9ac 2023-05-05 op fprintf(stderr, "usage: %s [-dv] [-j n] [-p path] [-s socket]"
240 8e5ae9ac 2023-05-05 op " [-t tmpldir] [-u user] [db]\n",
241 50794f47 2023-04-04 op getprogname());
246 50794f47 2023-04-04 op main(int argc, char **argv)
248 50794f47 2023-04-04 op struct stat sb;
249 50794f47 2023-04-04 op struct passwd *pw;
250 50794f47 2023-04-04 op char sockp[PATH_MAX];
251 50794f47 2023-04-04 op const char *sock = MSEARCHD_SOCK;
252 50794f47 2023-04-04 op const char *user = MSEARCHD_USER;
253 50794f47 2023-04-04 op const char *root = NULL;
254 50794f47 2023-04-04 op const char *db = MSEARCHD_DB;
255 8e5ae9ac 2023-05-05 op const char *tmpldir = MSEARCH_TMPL_DIR;
256 50794f47 2023-04-04 op const char *errstr, *cause, *argv0;
258 50794f47 2023-04-04 op int ch, i, fd, ret, status, server = 0;
261 50794f47 2023-04-04 op * Ensure we have fds 0-2 open so that we have no issue with
262 50794f47 2023-04-04 op * calling bind_socket before daemon(3).
264 50794f47 2023-04-04 op for (i = 0; i < 3; ++i) {
265 50794f47 2023-04-04 op if (fstat(i, &sb) == -1) {
266 50794f47 2023-04-04 op if ((fd = open("/dev/null", O_RDWR)) != -1) {
267 50794f47 2023-04-04 op if (dup2(fd, i) == -1)
276 50794f47 2023-04-04 op if ((argv0 = argv[0]) == NULL)
277 50794f47 2023-04-04 op argv0 = "msearchd";
279 8e5ae9ac 2023-05-05 op while ((ch = getopt(argc, argv, "dj:p:Ss:t:u:v")) != -1) {
280 50794f47 2023-04-04 op switch (ch) {
285 50794f47 2023-04-04 op children = strtonum(optarg, 1, MAX_CHILDREN, &errstr);
287 50794f47 2023-04-04 op fatalx("number of children is %s: %s",
288 50794f47 2023-04-04 op errstr, optarg);
291 50794f47 2023-04-04 op root = optarg;
297 50794f47 2023-04-04 op sock = optarg;
300 8e5ae9ac 2023-05-05 op tmpldir = optarg;
303 50794f47 2023-04-04 op user = optarg;
312 50794f47 2023-04-04 op argc -= optind;
313 50794f47 2023-04-04 op argv += optind;
315 50794f47 2023-04-04 op if (argc > 0) {
316 50794f47 2023-04-04 op db = argv[0];
320 50794f47 2023-04-04 op if (argc != 0)
323 50794f47 2023-04-04 op if (geteuid())
324 50794f47 2023-04-04 op fatalx("need root privileges");
326 50794f47 2023-04-04 op pw = getpwnam(user);
327 50794f47 2023-04-04 op if (pw == NULL)
328 50794f47 2023-04-04 op fatalx("user %s not found", user);
329 50794f47 2023-04-04 op if (pw->pw_uid == 0)
330 50794f47 2023-04-04 op fatalx("cannot run as %s: must not be the superuser", user);
332 50794f47 2023-04-04 op if (root == NULL)
333 50794f47 2023-04-04 op root = pw->pw_dir;
336 2901dc0e 2023-04-04 op logger = &syslogger;
338 2901dc0e 2023-04-04 op if (!debug && !server && daemon(1, 0) == -1)
339 2901dc0e 2023-04-04 op fatal("daemon");
341 50794f47 2023-04-04 op if (!server) {
342 50794f47 2023-04-04 op sigset_t set;
344 50794f47 2023-04-04 op sigemptyset(&set);
345 50794f47 2023-04-04 op sigaddset(&set, SIGCHLD);
346 28c73ba9 2023-04-04 op sigaddset(&set, SIGINT);
347 28c73ba9 2023-04-04 op sigaddset(&set, SIGTERM);
348 50794f47 2023-04-04 op sigprocmask(SIG_BLOCK, &set, NULL);
350 50794f47 2023-04-04 op ret = snprintf(sockp, sizeof(sockp), "%s/%s", root, sock);
351 50794f47 2023-04-04 op if (ret < 0 || (size_t)ret >= sizeof(sockp))
352 50794f47 2023-04-04 op fatalx("socket path too long");
353 50794f47 2023-04-04 op if ((fd = bind_socket(sockp, pw)) == -1)
354 50794f47 2023-04-04 op fatalx("failed to open socket %s", sock);
355 50794f47 2023-04-04 op for (i = 0; i < children; ++i) {
358 50794f47 2023-04-04 op if ((d = dup(fd)) == -1)
359 50794f47 2023-04-04 op fatalx("dup");
360 8e5ae9ac 2023-05-05 op pids[i] = start_child(argv0, root, user, db, tmpldir,
361 8e5ae9ac 2023-05-05 op debug, verbose, d);
362 50794f47 2023-04-04 op log_debug("forking child %d (pid %lld)", i,
363 50794f47 2023-04-04 op (long long)pids[i]);
366 28c73ba9 2023-04-04 op signal(SIGINT, sighdlr);
367 28c73ba9 2023-04-04 op signal(SIGTERM, sighdlr);
368 28c73ba9 2023-04-04 op signal(SIGCHLD, sighdlr);
369 50794f47 2023-04-04 op signal(SIGHUP, SIG_IGN);
371 50794f47 2023-04-04 op sigprocmask(SIG_UNBLOCK, &set, NULL);
373 8e5ae9ac 2023-05-05 op load_tmpl(&tmpl_head, tmpldir, "head.html");
374 8e5ae9ac 2023-05-05 op load_tmpl(&tmpl_search, tmpldir, "search.html");
375 8e5ae9ac 2023-05-05 op load_tmpl(&tmpl_search_header, tmpldir, "search-header.html");
376 8e5ae9ac 2023-05-05 op load_tmpl(&tmpl_foot, tmpldir, "foot.html");
378 ab1569ee 2023-05-06 op setproctitle("server");
381 50794f47 2023-04-04 op if (chroot(root) == -1)
382 50794f47 2023-04-04 op fatal("chroot %s", root);
383 50794f47 2023-04-04 op if (chdir("/") == -1)
384 50794f47 2023-04-04 op fatal("chdir /");
386 50794f47 2023-04-04 op if (setgroups(1, &pw->pw_gid) == -1 ||
387 50794f47 2023-04-04 op setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) == -1 ||
388 50794f47 2023-04-04 op setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) == -1)
389 50794f47 2023-04-04 op fatal("failed to drop privileges");
392 50794f47 2023-04-04 op return (server_main(db));
394 50794f47 2023-04-04 op if (pledge("stdio proc", NULL) == -1)
395 50794f47 2023-04-04 op fatal("pledge");
399 50794f47 2023-04-04 op pid = waitpid(WAIT_ANY, &status, 0);
400 50794f47 2023-04-04 op } while (pid != -1 || errno == EINTR);
402 50794f47 2023-04-04 op if (pid == -1) {
403 50794f47 2023-04-04 op if (errno == ECHILD)
405 50794f47 2023-04-04 op fatal("waitpid");
408 50794f47 2023-04-04 op if (WIFSIGNALED(status))
409 50794f47 2023-04-04 op cause = "was terminated";
410 50794f47 2023-04-04 op else if (WIFEXITED(status)) {
411 50794f47 2023-04-04 op if (WEXITSTATUS(status) != 0)
412 50794f47 2023-04-04 op cause = "exited abnormally";
414 50794f47 2023-04-04 op cause = "exited successfully";
416 50794f47 2023-04-04 op cause = "died";
418 50794f47 2023-04-04 op log_warnx("child process %lld %s", (long long)pid, cause);
425 50794f47 2023-04-04 op srch_syslog_fatal(int eval, const char *fmt, ...)
427 50794f47 2023-04-04 op static char s[BUFSIZ];
429 50794f47 2023-04-04 op int r, save_errno;
431 50794f47 2023-04-04 op save_errno = errno;
433 50794f47 2023-04-04 op va_start(ap, fmt);
434 50794f47 2023-04-04 op r = vsnprintf(s, sizeof(s), fmt, ap);
437 50794f47 2023-04-04 op errno = save_errno;
439 50794f47 2023-04-04 op if (r > 0 && (size_t)r <= sizeof(s))
440 50794f47 2023-04-04 op syslog(LOG_DAEMON|LOG_CRIT, "%s: %s", s, strerror(errno));
446 50794f47 2023-04-04 op srch_syslog_fatalx(int eval, const char *fmt, ...)
450 50794f47 2023-04-04 op va_start(ap, fmt);
451 50794f47 2023-04-04 op vsyslog(LOG_DAEMON|LOG_CRIT, fmt, ap);
458 50794f47 2023-04-04 op srch_syslog_warn(const char *fmt, ...)
460 50794f47 2023-04-04 op static char s[BUFSIZ];
462 50794f47 2023-04-04 op int r, save_errno;
464 50794f47 2023-04-04 op save_errno = errno;
466 50794f47 2023-04-04 op va_start(ap, fmt);
467 50794f47 2023-04-04 op r = vsnprintf(s, sizeof(s), fmt, ap);
470 50794f47 2023-04-04 op errno = save_errno;
472 cae98100 2023-04-04 op if (r > 0 && (size_t)r < sizeof(s))
473 50794f47 2023-04-04 op syslog(LOG_DAEMON|LOG_ERR, "%s: %s", s, strerror(errno));
475 50794f47 2023-04-04 op errno = save_errno;
479 50794f47 2023-04-04 op srch_syslog_warnx(const char *fmt, ...)
482 50794f47 2023-04-04 op int save_errno;
484 50794f47 2023-04-04 op save_errno = errno;
485 50794f47 2023-04-04 op va_start(ap, fmt);
486 50794f47 2023-04-04 op vsyslog(LOG_DAEMON|LOG_ERR, fmt, ap);
488 50794f47 2023-04-04 op errno = save_errno;
492 50794f47 2023-04-04 op srch_syslog_info(const char *fmt, ...)
495 50794f47 2023-04-04 op int save_errno;
497 50794f47 2023-04-04 op if (verbose < 1)
500 50794f47 2023-04-04 op save_errno = errno;
501 50794f47 2023-04-04 op va_start(ap, fmt);
502 50794f47 2023-04-04 op vsyslog(LOG_DAEMON|LOG_INFO, fmt, ap);
504 50794f47 2023-04-04 op errno = save_errno;
508 50794f47 2023-04-04 op srch_syslog_debug(const char *fmt, ...)
511 50794f47 2023-04-04 op int save_errno;
513 50794f47 2023-04-04 op if (verbose < 2)
516 50794f47 2023-04-04 op save_errno = errno;
517 50794f47 2023-04-04 op va_start(ap, fmt);
518 50794f47 2023-04-04 op vsyslog(LOG_DAEMON|LOG_DEBUG, fmt, ap);
520 50794f47 2023-04-04 op errno = save_errno;