1*16b0c81bSclaudio /* $OpenBSD: smtpd.c,v 1.354 2024/11/21 13:22:21 claudio Exp $ */ 21f3c2bcfSsobrado 33ef9cbf7Sgilles /* 465c4fdfbSgilles * Copyright (c) 2008 Gilles Chehade <gilles@poolp.org> 53ef9cbf7Sgilles * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org> 6e5b07014Sgilles * Copyright (c) 2009 Jacek Masiulaniec <jacekm@dobremiasto.net> 73ef9cbf7Sgilles * 83ef9cbf7Sgilles * Permission to use, copy, modify, and distribute this software for any 93ef9cbf7Sgilles * purpose with or without fee is hereby granted, provided that the above 103ef9cbf7Sgilles * copyright notice and this permission notice appear in all copies. 113ef9cbf7Sgilles * 123ef9cbf7Sgilles * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 133ef9cbf7Sgilles * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 143ef9cbf7Sgilles * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 153ef9cbf7Sgilles * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 163ef9cbf7Sgilles * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 173ef9cbf7Sgilles * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 183ef9cbf7Sgilles * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 193ef9cbf7Sgilles */ 203ef9cbf7Sgilles 213ef9cbf7Sgilles #include <sys/wait.h> 223ef9cbf7Sgilles #include <sys/stat.h> 233ef9cbf7Sgilles 2465c4fdfbSgilles #include <bsd_auth.h> 2514f5466bSeric #include <dirent.h> 263ef9cbf7Sgilles #include <errno.h> 2765c4fdfbSgilles #include <fcntl.h> 285f4bd98fSgilles #include <fts.h> 29a93e09f5Sgilles #include <grp.h> 301c6ac251Seric #include <inttypes.h> 313ef9cbf7Sgilles #include <paths.h> 32b88ab68dSeric #include <poll.h> 333ef9cbf7Sgilles #include <pwd.h> 343ef9cbf7Sgilles #include <signal.h> 35ccfb4053Seric #include <syslog.h> 363ef9cbf7Sgilles #include <stdlib.h> 373ef9cbf7Sgilles #include <string.h> 38eb268927Sgilles #include <sysexits.h> 390dcffd0dSop #include <time.h> 40eed85469Seric #include <tls.h> 413ef9cbf7Sgilles #include <unistd.h> 423ef9cbf7Sgilles 433ef9cbf7Sgilles #include "smtpd.h" 445eb8dddaSgilles #include "log.h" 4565c4fdfbSgilles #include "ssl.h" 463ef9cbf7Sgilles 47b88ab68dSeric #define SMTPD_MAXARG 32 48b88ab68dSeric 4965c4fdfbSgilles static void parent_imsg(struct mproc *, struct imsg *); 50be925435Sgilles static void usage(void); 51b88ab68dSeric static int smtpd(void); 5243962b9cSeric static void parent_shutdown(void); 53be925435Sgilles static void parent_send_config(int, short, void *); 5465c4fdfbSgilles static void parent_send_config_lka(void); 551a5b831aSmartijn static void parent_send_config_dispatcher(void); 5676eb97c5Sreyk static void parent_send_config_ca(void); 57be925435Sgilles static void parent_sig_handler(int, short, void *); 5865c4fdfbSgilles static void forkmda(struct mproc *, uint64_t, struct deliver *); 5965c4fdfbSgilles static int parent_forward_open(char *, char *, uid_t, gid_t); 603a305da9Seric static struct child *child_add(pid_t, int, const char *); 61b88ab68dSeric static struct mproc *start_child(int, char **, char *); 62b88ab68dSeric static struct mproc *setup_peer(enum smtp_proc_type, pid_t, int); 63b88ab68dSeric static void setup_peers(struct mproc *, struct mproc *); 64b88ab68dSeric static void setup_done(struct mproc *); 65b88ab68dSeric static void setup_proc(void); 66b88ab68dSeric static struct mproc *setup_peer(enum smtp_proc_type, pid_t, int); 67b88ab68dSeric static int imsg_wait(struct imsgbuf *, struct imsg *, int); 6834abc65bSjacekm 6914f5466bSeric static void offline_scan(int, short, void *); 700228dab0Smillert static int offline_add(char *, uid_t, gid_t); 7114f5466bSeric static void offline_done(void); 720228dab0Smillert static int offline_enqueue(char *, uid_t, gid_t); 739a5146a5Seric 74b35655ddSgilles static void purge_task(void); 7565c4fdfbSgilles static int parent_auth_user(const char *, const char *); 761c3ac238Seric static void load_pki_tree(void); 77c52bd758Sreyk static void load_pki_keys(void); 78591aa05bSeric 795d1bf438Sgilles static void fork_filter_processes(void); 805d1bf438Sgilles static void fork_filter_process(const char *, const char *, const char *, const char *, const char *, uint32_t); 81a93e09f5Sgilles 823a305da9Seric enum child_type { 833a305da9Seric CHILD_DAEMON, 843a305da9Seric CHILD_MDA, 85a93e09f5Sgilles CHILD_PROCESSOR, 863a305da9Seric CHILD_ENQUEUE_OFFLINE, 873a305da9Seric }; 883a305da9Seric 893a305da9Seric struct child { 903a305da9Seric pid_t pid; 913a305da9Seric enum child_type type; 923a305da9Seric const char *title; 933a305da9Seric int mda_out; 9465c4fdfbSgilles uint64_t mda_id; 953a305da9Seric char *path; 96719ffb95Seric char *cause; 973a305da9Seric }; 98591aa05bSeric 9914f5466bSeric struct offline { 10014f5466bSeric TAILQ_ENTRY(offline) entry; 1010228dab0Smillert uid_t uid; 1020228dab0Smillert gid_t gid; 1039a5146a5Seric char *path; 1049a5146a5Seric }; 1059a5146a5Seric 10614f5466bSeric #define OFFLINE_READMAX 20 10714f5466bSeric #define OFFLINE_QUEUEMAX 5 10814f5466bSeric static size_t offline_running = 0; 10914f5466bSeric TAILQ_HEAD(, offline) offline_q; 11014f5466bSeric 111299c4efeSeric static struct event config_ev; 11214f5466bSeric static struct event offline_ev; 11314f5466bSeric static struct timeval offline_timeout; 11414f5466bSeric 115b35655ddSgilles static pid_t purge_pid = -1; 1169a5146a5Seric 11734abc65bSjacekm extern char **environ; 11865c4fdfbSgilles void (*imsg_callback)(struct mproc *, struct imsg *); 11965c4fdfbSgilles 12065c4fdfbSgilles enum smtp_proc_type smtpd_process; 121e4d36f12Seric 122e4d36f12Seric struct smtpd *env = NULL; 1233ef9cbf7Sgilles 12465c4fdfbSgilles struct mproc *p_control = NULL; 12565c4fdfbSgilles struct mproc *p_lka = NULL; 12665c4fdfbSgilles struct mproc *p_parent = NULL; 12765c4fdfbSgilles struct mproc *p_queue = NULL; 12865c4fdfbSgilles struct mproc *p_scheduler = NULL; 1291a5b831aSmartijn struct mproc *p_dispatcher = NULL; 13076eb97c5Sreyk struct mproc *p_ca = NULL; 13165c4fdfbSgilles 132945f2d46Seric const char *backend_queue = "fs"; 133945f2d46Seric const char *backend_scheduler = "ramqueue"; 1349ed3223cSgilles const char *backend_stat = "ram"; 135945f2d46Seric 13665c4fdfbSgilles int profiling = 0; 13765c4fdfbSgilles int debug = 0; 138299c4efeSeric int foreground = 0; 1395894db6eSeric int control_socket = -1; 14021312c12Sgilles 1413a305da9Seric struct tree children; 1423a305da9Seric 143be925435Sgilles static void 14465c4fdfbSgilles parent_imsg(struct mproc *p, struct imsg *imsg) 145ed1929b6Sjacekm { 146ed1929b6Sjacekm struct forward_req *fwreq; 14726bbc7b9Sgilles struct filter_proc *processor; 14865c4fdfbSgilles struct deliver deliver; 149719ffb95Seric struct child *c; 15065c4fdfbSgilles struct msg m; 15165c4fdfbSgilles const void *data; 152dbc3cea0Smartijn const char *username, *password, *cause, *procname; 15365c4fdfbSgilles uint64_t reqid; 15465c4fdfbSgilles size_t sz; 155719ffb95Seric void *i; 15665c4fdfbSgilles int fd, n, v, ret; 157ed1929b6Sjacekm 1586ba78a98Sgilles if (imsg == NULL) 15943962b9cSeric fatalx("process %s socket closed", p->name); 1606ba78a98Sgilles 161ed1929b6Sjacekm switch (imsg->hdr.type) { 162aa1d5973Seric case IMSG_LKA_OPEN_FORWARD: 16372bef77dSsunil CHECK_IMSG_DATA_SIZE(imsg, sizeof *fwreq); 164ed1929b6Sjacekm fwreq = imsg->data; 16565c4fdfbSgilles fd = parent_forward_open(fwreq->user, fwreq->directory, 16665c4fdfbSgilles fwreq->uid, fwreq->gid); 167ed1929b6Sjacekm fwreq->status = 0; 16865c4fdfbSgilles if (fd == -1 && errno != ENOENT) { 16965c4fdfbSgilles if (errno == EAGAIN) 17065c4fdfbSgilles fwreq->status = -1; 17165c4fdfbSgilles } 17265c4fdfbSgilles else 173ed1929b6Sjacekm fwreq->status = 1; 174aa1d5973Seric m_compose(p, IMSG_LKA_OPEN_FORWARD, 0, 0, fd, 17565c4fdfbSgilles fwreq, sizeof *fwreq); 17665c4fdfbSgilles return; 17765c4fdfbSgilles 17865c4fdfbSgilles case IMSG_LKA_AUTHENTICATE: 17965c4fdfbSgilles /* 18065c4fdfbSgilles * If we reached here, it means we want root to lookup 18165c4fdfbSgilles * system user. 18265c4fdfbSgilles */ 18365c4fdfbSgilles m_msg(&m, imsg); 18465c4fdfbSgilles m_get_id(&m, &reqid); 18565c4fdfbSgilles m_get_string(&m, &username); 18665c4fdfbSgilles m_get_string(&m, &password); 18765c4fdfbSgilles m_end(&m); 18865c4fdfbSgilles 18965c4fdfbSgilles ret = parent_auth_user(username, password); 19065c4fdfbSgilles 191299c4efeSeric m_create(p, IMSG_LKA_AUTHENTICATE, 0, 0, -1); 19265c4fdfbSgilles m_add_id(p, reqid); 19365c4fdfbSgilles m_add_int(p, ret); 19465c4fdfbSgilles m_close(p); 195ed1929b6Sjacekm return; 196ed1929b6Sjacekm 197aa1d5973Seric case IMSG_MDA_FORK: 19865c4fdfbSgilles m_msg(&m, imsg); 19965c4fdfbSgilles m_get_id(&m, &reqid); 20065c4fdfbSgilles m_get_data(&m, &data, &sz); 20165c4fdfbSgilles m_end(&m); 20265c4fdfbSgilles if (sz != sizeof(deliver)) 20365c4fdfbSgilles fatalx("expected deliver"); 20465c4fdfbSgilles memmove(&deliver, data, sz); 20565c4fdfbSgilles forkmda(p, reqid, &deliver); 206ed1929b6Sjacekm return; 207719ffb95Seric 208aa1d5973Seric case IMSG_MDA_KILL: 20965c4fdfbSgilles m_msg(&m, imsg); 21065c4fdfbSgilles m_get_id(&m, &reqid); 21165c4fdfbSgilles m_get_string(&m, &cause); 21265c4fdfbSgilles m_end(&m); 21365c4fdfbSgilles 214719ffb95Seric i = NULL; 215719ffb95Seric while ((n = tree_iter(&children, &i, NULL, (void**)&c))) 216719ffb95Seric if (c->type == CHILD_MDA && 21765c4fdfbSgilles c->mda_id == reqid && 218719ffb95Seric c->cause == NULL) 219719ffb95Seric break; 220719ffb95Seric if (!n) { 221299c4efeSeric log_debug("debug: smtpd: " 22265c4fdfbSgilles "kill request: proc not found"); 223719ffb95Seric return; 224719ffb95Seric } 22565c4fdfbSgilles 226118c16f3Sgilles c->cause = xstrdup(cause); 227299c4efeSeric log_debug("debug: smtpd: kill requested for %u: %s", 228719ffb95Seric c->pid, c->cause); 229719ffb95Seric kill(c->pid, SIGTERM); 230719ffb95Seric return; 231ed1929b6Sjacekm 232ed1929b6Sjacekm case IMSG_CTL_VERBOSE: 23365c4fdfbSgilles m_msg(&m, imsg); 23465c4fdfbSgilles m_get_int(&m, &v); 23565c4fdfbSgilles m_end(&m); 236f24248b7Sreyk log_trace_verbose(v); 23765c4fdfbSgilles return; 238ed1929b6Sjacekm 23957d312f7Seric case IMSG_CTL_PROFILE: 24065c4fdfbSgilles m_msg(&m, imsg); 24165c4fdfbSgilles m_get_int(&m, &v); 24265c4fdfbSgilles m_end(&m); 24357d312f7Seric profiling = v; 244ed1929b6Sjacekm return; 245dbc3cea0Smartijn 246dbc3cea0Smartijn case IMSG_LKA_PROCESSOR_ERRFD: 247dbc3cea0Smartijn m_msg(&m, imsg); 248dbc3cea0Smartijn m_get_string(&m, &procname); 249dbc3cea0Smartijn m_end(&m); 250dbc3cea0Smartijn 2515d1bf438Sgilles processor = dict_xget(env->sc_filter_processes_dict, procname); 252dbc3cea0Smartijn m_create(p_lka, IMSG_LKA_PROCESSOR_ERRFD, 0, 0, processor->errfd); 253dbc3cea0Smartijn m_add_string(p_lka, procname); 254dbc3cea0Smartijn m_close(p_lka); 255dbc3cea0Smartijn return; 256ed1929b6Sjacekm } 257ed1929b6Sjacekm 258ff01b044Seric fatalx("parent_imsg: unexpected %s imsg from %s", 25976eb97c5Sreyk imsg_to_str(imsg->hdr.type), proc_title(p->proc)); 260ed1929b6Sjacekm } 261ed1929b6Sjacekm 262be925435Sgilles static void 2633ef9cbf7Sgilles usage(void) 2643ef9cbf7Sgilles { 2653ef9cbf7Sgilles extern char *__progname; 2663ef9cbf7Sgilles 267f573e212Sjmc fprintf(stderr, "usage: %s [-dFhnv] [-D macro=value] " 2687ea7d21fSeric "[-f file] [-P system] [-T trace]\n", __progname); 2693ef9cbf7Sgilles exit(1); 2703ef9cbf7Sgilles } 2713ef9cbf7Sgilles 272be925435Sgilles static void 27343962b9cSeric parent_shutdown(void) 2743ef9cbf7Sgilles { 2753ef9cbf7Sgilles pid_t pid; 2763ef9cbf7Sgilles 27743962b9cSeric mproc_clear(p_ca); 2781a5b831aSmartijn mproc_clear(p_dispatcher); 27943962b9cSeric mproc_clear(p_control); 28043962b9cSeric mproc_clear(p_lka); 28143962b9cSeric mproc_clear(p_scheduler); 28243962b9cSeric mproc_clear(p_queue); 2833ef9cbf7Sgilles 2843ef9cbf7Sgilles do { 28534abc65bSjacekm pid = waitpid(WAIT_MYPGRP, NULL, 0); 2863ef9cbf7Sgilles } while (pid != -1 || (pid == -1 && errno == EINTR)); 2873ef9cbf7Sgilles 288eb351e24Seric unlink(SMTPD_SOCKET); 289eb351e24Seric 29043962b9cSeric log_info("Exiting"); 29143962b9cSeric exit(0); 2923ef9cbf7Sgilles } 2933ef9cbf7Sgilles 294be925435Sgilles static void 2953ef9cbf7Sgilles parent_send_config(int fd, short event, void *p) 2963ef9cbf7Sgilles { 29765c4fdfbSgilles parent_send_config_lka(); 2981a5b831aSmartijn parent_send_config_dispatcher(); 29976eb97c5Sreyk parent_send_config_ca(); 3001c3ac238Seric purge_config(PURGE_PKI); 3019aa27f7fSgilles } 3029aa27f7fSgilles 303be925435Sgilles static void 3041a5b831aSmartijn parent_send_config_dispatcher(void) 3059aa27f7fSgilles { 3061a5b831aSmartijn log_debug("debug: parent_send_config: configuring dispatcher process"); 3071a5b831aSmartijn m_compose(p_dispatcher, IMSG_CONF_START, 0, 0, -1, NULL, 0); 3081a5b831aSmartijn m_compose(p_dispatcher, IMSG_CONF_END, 0, 0, -1, NULL, 0); 3093ef9cbf7Sgilles } 3103ef9cbf7Sgilles 31165c4fdfbSgilles void 31204fee684Stb parent_send_config_lka(void) 31365c4fdfbSgilles { 31465c4fdfbSgilles log_debug("debug: parent_send_config_ruleset: reloading"); 31565c4fdfbSgilles m_compose(p_lka, IMSG_CONF_START, 0, 0, -1, NULL, 0); 31665c4fdfbSgilles m_compose(p_lka, IMSG_CONF_END, 0, 0, -1, NULL, 0); 3179aa27f7fSgilles } 3189aa27f7fSgilles 319be925435Sgilles static void 32076eb97c5Sreyk parent_send_config_ca(void) 32176eb97c5Sreyk { 32276eb97c5Sreyk log_debug("debug: parent_send_config: configuring ca process"); 32376eb97c5Sreyk m_compose(p_ca, IMSG_CONF_START, 0, 0, -1, NULL, 0); 32476eb97c5Sreyk m_compose(p_ca, IMSG_CONF_END, 0, 0, -1, NULL, 0); 32576eb97c5Sreyk } 32676eb97c5Sreyk 32776eb97c5Sreyk static void 3283ef9cbf7Sgilles parent_sig_handler(int sig, short event, void *p) 3293ef9cbf7Sgilles { 33034abc65bSjacekm struct child *child; 33143962b9cSeric int status, fail; 33234abc65bSjacekm pid_t pid; 33334abc65bSjacekm char *cause; 3343ef9cbf7Sgilles 3353ef9cbf7Sgilles switch (sig) { 3363ef9cbf7Sgilles case SIGTERM: 3373ef9cbf7Sgilles case SIGINT: 33843962b9cSeric log_debug("debug: got signal %d", sig); 33943962b9cSeric parent_shutdown(); 34043962b9cSeric /* NOT REACHED */ 34143962b9cSeric 3423ef9cbf7Sgilles case SIGCHLD: 3433ef9cbf7Sgilles do { 344aa48e8d1Smillert int len; 345eb268927Sgilles enum mda_resp_status mda_status; 346eb268927Sgilles int mda_sysexit; 347aa48e8d1Smillert 3483ef9cbf7Sgilles pid = waitpid(-1, &status, WNOHANG); 34934abc65bSjacekm if (pid <= 0) 35034abc65bSjacekm continue; 35134abc65bSjacekm 35234abc65bSjacekm fail = 0; 35334abc65bSjacekm if (WIFSIGNALED(status)) { 35434abc65bSjacekm fail = 1; 355aa48e8d1Smillert len = asprintf(&cause, "terminated; signal %d", 35634abc65bSjacekm WTERMSIG(status)); 357eb268927Sgilles mda_status = MDA_TEMPFAIL; 358eb268927Sgilles mda_sysexit = 0; 35934abc65bSjacekm } else if (WIFEXITED(status)) { 36034abc65bSjacekm if (WEXITSTATUS(status) != 0) { 36134abc65bSjacekm fail = 1; 362aa48e8d1Smillert len = asprintf(&cause, 363aa48e8d1Smillert "exited abnormally"); 364eb268927Sgilles mda_sysexit = WEXITSTATUS(status); 365eb268927Sgilles if (mda_sysexit == EX_OSERR || 366eb268927Sgilles mda_sysexit == EX_TEMPFAIL) 367eb268927Sgilles mda_status = MDA_TEMPFAIL; 368eb268927Sgilles else 369eb268927Sgilles mda_status = MDA_PERMFAIL; 370eb268927Sgilles } else { 371aa48e8d1Smillert len = asprintf(&cause, "exited okay"); 372eb268927Sgilles mda_status = MDA_OK; 373eb268927Sgilles mda_sysexit = 0; 374eb268927Sgilles } 37534abc65bSjacekm } else 376cb6e8661Sgilles /* WIFSTOPPED or WIFCONTINUED */ 377cb6e8661Sgilles continue; 37834abc65bSjacekm 379aa48e8d1Smillert if (len == -1) 380aa48e8d1Smillert fatal("asprintf"); 381aa48e8d1Smillert 382591aa05bSeric if (pid == purge_pid) 383591aa05bSeric purge_pid = -1; 384591aa05bSeric 3853a305da9Seric child = tree_pop(&children, pid); 386591aa05bSeric if (child == NULL) 387591aa05bSeric goto skip; 388591aa05bSeric 38934abc65bSjacekm switch (child->type) { 390a93e09f5Sgilles case CHILD_PROCESSOR: 391a93e09f5Sgilles if (fail) { 392a93e09f5Sgilles log_warnx("warn: lost processor: %s %s", 393a93e09f5Sgilles child->title, cause); 394a93e09f5Sgilles parent_shutdown(); 395a93e09f5Sgilles } 396a93e09f5Sgilles break; 397a93e09f5Sgilles 39834abc65bSjacekm case CHILD_DAEMON: 39934abc65bSjacekm if (fail) 40082614934Seric log_warnx("warn: lost child: %s %s", 4013a305da9Seric child->title, cause); 40234abc65bSjacekm break; 40334abc65bSjacekm 40434abc65bSjacekm case CHILD_MDA: 40511dbc40fSjacekm if (WIFSIGNALED(status) && 40611dbc40fSjacekm WTERMSIG(status) == SIGALRM) { 407aa48e8d1Smillert char *tmp; 408aa48e8d1Smillert if (asprintf(&tmp, 409aa48e8d1Smillert "terminated; timeout") != -1) { 41011dbc40fSjacekm free(cause); 411aa48e8d1Smillert cause = tmp; 412aa48e8d1Smillert } 41311dbc40fSjacekm } 414719ffb95Seric else if (child->cause && 415719ffb95Seric WIFSIGNALED(status) && 416719ffb95Seric WTERMSIG(status) == SIGTERM) { 417719ffb95Seric free(cause); 418719ffb95Seric cause = child->cause; 419719ffb95Seric child->cause = NULL; 420719ffb95Seric } 421719ffb95Seric free(child->cause); 4221c6ac251Seric log_debug("debug: smtpd: mda process done " 4231c6ac251Seric "for session %016"PRIx64 ": %s", 4241c6ac251Seric child->mda_id, cause); 425eb268927Sgilles 4261a5b831aSmartijn m_create(p_dispatcher, IMSG_MDA_DONE, 0, 0, 427299c4efeSeric child->mda_out); 4281a5b831aSmartijn m_add_id(p_dispatcher, child->mda_id); 4291a5b831aSmartijn m_add_int(p_dispatcher, mda_status); 4301a5b831aSmartijn m_add_int(p_dispatcher, mda_sysexit); 4311a5b831aSmartijn m_add_string(p_dispatcher, cause); 4321a5b831aSmartijn m_close(p_dispatcher); 433eb268927Sgilles 43434abc65bSjacekm break; 43534abc65bSjacekm 43634abc65bSjacekm case CHILD_ENQUEUE_OFFLINE: 43734abc65bSjacekm if (fail) 4387e8a7d6bSeric log_warnx("warn: smtpd: " 4397e8a7d6bSeric "couldn't enqueue offline " 4407e8a7d6bSeric "message %s; smtpctl %s", 4417e8a7d6bSeric child->path, cause); 442d44f36d1Seric else 443d44f36d1Seric unlink(child->path); 444d44f36d1Seric free(child->path); 44514f5466bSeric offline_done(); 4463ef9cbf7Sgilles break; 44734abc65bSjacekm 4483ef9cbf7Sgilles default: 449c5ad255fSeric fatalx("smtpd: unexpected child type"); 4503ef9cbf7Sgilles } 4513a305da9Seric free(child); 452591aa05bSeric skip: 45334abc65bSjacekm free(cause); 4543ef9cbf7Sgilles } while (pid > 0 || (pid == -1 && errno == EINTR)); 4553ef9cbf7Sgilles 4563ef9cbf7Sgilles break; 4573ef9cbf7Sgilles default: 458c5ad255fSeric fatalx("smtpd: unexpected signal"); 4593ef9cbf7Sgilles } 4603ef9cbf7Sgilles } 4613ef9cbf7Sgilles 4623ef9cbf7Sgilles int 4633ef9cbf7Sgilles main(int argc, char *argv[]) 4643ef9cbf7Sgilles { 46565c4fdfbSgilles int c, i; 466f98e7cc0Seric int opts, flags; 4673ef9cbf7Sgilles const char *conffile = CONF_FILE; 468b88ab68dSeric int save_argc = argc; 469b88ab68dSeric char **save_argv = argv; 470b88ab68dSeric char *rexec = NULL; 471942f9647Sgilles struct smtpd *conf; 4723ef9cbf7Sgilles 473f98e7cc0Seric flags = 0; 4743ef9cbf7Sgilles opts = 0; 4753ef9cbf7Sgilles debug = 0; 476f24248b7Sreyk tracing = 0; 4773ef9cbf7Sgilles 478f24248b7Sreyk log_init(1, LOG_MAIL); 4793ef9cbf7Sgilles 480ff01b044Seric if ((conf = config_default()) == NULL) 481ff01b044Seric fatal("config_default"); 482ff01b044Seric env = conf; 483ff01b044Seric 48414f5466bSeric TAILQ_INIT(&offline_q); 4859a5146a5Seric 486b88ab68dSeric while ((c = getopt(argc, argv, "B:dD:hnP:f:FT:vx:")) != -1) { 4873ef9cbf7Sgilles switch (c) { 488945f2d46Seric case 'B': 489945f2d46Seric if (strstr(optarg, "queue=") == optarg) 490945f2d46Seric backend_queue = strchr(optarg, '=') + 1; 491945f2d46Seric else if (strstr(optarg, "scheduler=") == optarg) 492945f2d46Seric backend_scheduler = strchr(optarg, '=') + 1; 4939ed3223cSgilles else if (strstr(optarg, "stat=") == optarg) 4949ed3223cSgilles backend_stat = strchr(optarg, '=') + 1; 495945f2d46Seric else 49665c4fdfbSgilles log_warnx("warn: " 49765c4fdfbSgilles "invalid backend specifier %s", 4987e8a7d6bSeric optarg); 499945f2d46Seric break; 5003ef9cbf7Sgilles case 'd': 501299c4efeSeric foreground = 1; 502833159efSsunil foreground_log = 1; 5033ef9cbf7Sgilles break; 5043ef9cbf7Sgilles case 'D': 5053ef9cbf7Sgilles if (cmdline_symset(optarg) < 0) 50665c4fdfbSgilles log_warnx("warn: " 50765c4fdfbSgilles "could not parse macro definition %s", 5083ef9cbf7Sgilles optarg); 5093ef9cbf7Sgilles break; 5107ea7d21fSeric case 'h': 51198cf77ddSjung log_info("version: " SMTPD_NAME " " SMTPD_VERSION); 5127ea7d21fSeric usage(); 5137ea7d21fSeric break; 5143ef9cbf7Sgilles case 'n': 5153ef9cbf7Sgilles debug = 2; 5163ef9cbf7Sgilles opts |= SMTPD_OPT_NOACTION; 5173ef9cbf7Sgilles break; 5183ef9cbf7Sgilles case 'f': 5193ef9cbf7Sgilles conffile = optarg; 5203ef9cbf7Sgilles break; 521833159efSsunil case 'F': 522833159efSsunil foreground = 1; 523833159efSsunil break; 524833159efSsunil 525bfe8e0bcSeric case 'T': 526bfe8e0bcSeric if (!strcmp(optarg, "imsg")) 527f24248b7Sreyk tracing |= TRACE_IMSG; 528b6d81129Seric else if (!strcmp(optarg, "io")) 529f24248b7Sreyk tracing |= TRACE_IO; 530b6d81129Seric else if (!strcmp(optarg, "smtp")) 531f24248b7Sreyk tracing |= TRACE_SMTP; 532417409c3Sgilles else if (!strcmp(optarg, "filters")) 533f24248b7Sreyk tracing |= TRACE_FILTERS; 534998b3f5fSeric else if (!strcmp(optarg, "mta") || 535df2a7d50Seric !strcmp(optarg, "transfer")) 536f24248b7Sreyk tracing |= TRACE_MTA; 537df2a7d50Seric else if (!strcmp(optarg, "bounce") || 538df2a7d50Seric !strcmp(optarg, "bounces")) 539f24248b7Sreyk tracing |= TRACE_BOUNCE; 5404744da7eSgilles else if (!strcmp(optarg, "scheduler")) 541f24248b7Sreyk tracing |= TRACE_SCHEDULER; 5420cf935dfSgilles else if (!strcmp(optarg, "lookup")) 543f24248b7Sreyk tracing |= TRACE_LOOKUP; 544df2a7d50Seric else if (!strcmp(optarg, "stat") || 545df2a7d50Seric !strcmp(optarg, "stats")) 546f24248b7Sreyk tracing |= TRACE_STAT; 54765c4fdfbSgilles else if (!strcmp(optarg, "rules")) 548f24248b7Sreyk tracing |= TRACE_RULES; 549299c4efeSeric else if (!strcmp(optarg, "mproc")) 550f24248b7Sreyk tracing |= TRACE_MPROC; 55159a46edcSgilles else if (!strcmp(optarg, "expand")) 552f24248b7Sreyk tracing |= TRACE_EXPAND; 553df2a7d50Seric else if (!strcmp(optarg, "table") || 554df2a7d50Seric !strcmp(optarg, "tables")) 555f24248b7Sreyk tracing |= TRACE_TABLES; 556299c4efeSeric else if (!strcmp(optarg, "queue")) 557f24248b7Sreyk tracing |= TRACE_QUEUE; 558b6d81129Seric else if (!strcmp(optarg, "all")) 559f24248b7Sreyk tracing |= ~TRACE_DEBUG; 56065c4fdfbSgilles else if (!strcmp(optarg, "profstat")) 56165c4fdfbSgilles profiling |= PROFILE_TOSTAT; 56265c4fdfbSgilles else if (!strcmp(optarg, "profile-imsg")) 56365c4fdfbSgilles profiling |= PROFILE_IMSG; 56465c4fdfbSgilles else if (!strcmp(optarg, "profile-queue")) 56565c4fdfbSgilles profiling |= PROFILE_QUEUE; 566bfe8e0bcSeric else 5677e8a7d6bSeric log_warnx("warn: unknown trace flag \"%s\"", 5687e8a7d6bSeric optarg); 569bfe8e0bcSeric break; 570f98e7cc0Seric case 'P': 571f98e7cc0Seric if (!strcmp(optarg, "smtp")) 572f98e7cc0Seric flags |= SMTPD_SMTP_PAUSED; 573f98e7cc0Seric else if (!strcmp(optarg, "mta")) 574f98e7cc0Seric flags |= SMTPD_MTA_PAUSED; 575f98e7cc0Seric else if (!strcmp(optarg, "mda")) 576f98e7cc0Seric flags |= SMTPD_MDA_PAUSED; 577f98e7cc0Seric break; 5783ef9cbf7Sgilles case 'v': 579f24248b7Sreyk tracing |= TRACE_DEBUG; 5803ef9cbf7Sgilles break; 581b88ab68dSeric case 'x': 582b88ab68dSeric rexec = optarg; 583b88ab68dSeric break; 5843ef9cbf7Sgilles default: 5853ef9cbf7Sgilles usage(); 5863ef9cbf7Sgilles } 5873ef9cbf7Sgilles } 5883ef9cbf7Sgilles 5893ef9cbf7Sgilles argv += optind; 5903ef9cbf7Sgilles argc -= optind; 5913ef9cbf7Sgilles 5921f49c3f6Sgilles if (argc || *argv) 5931f49c3f6Sgilles usage(); 5941f49c3f6Sgilles 595d632a6afStim env->sc_opts |= opts; 596d632a6afStim 597942f9647Sgilles if (parse_config(conf, conffile, opts)) 5983ef9cbf7Sgilles exit(1); 5993ef9cbf7Sgilles 600953aae25Sderaadt if (strlcpy(env->sc_conffile, conffile, PATH_MAX) 601953aae25Sderaadt >= PATH_MAX) 602ff01b044Seric fatalx("config file exceeds PATH_MAX"); 603374cd968Sgilles 604e4d36f12Seric if (env->sc_opts & SMTPD_OPT_NOACTION) { 6050f7ce8c3Ssunil if (env->sc_queue_key && 6060f7ce8c3Ssunil crypto_setup(env->sc_queue_key, 6070f7ce8c3Ssunil strlen(env->sc_queue_key)) == 0) { 6080f7ce8c3Ssunil fatalx("crypto_setup:" 6090f7ce8c3Ssunil "invalid key for queue encryption"); 6100f7ce8c3Ssunil } 6111c3ac238Seric load_pki_tree(); 612c52bd758Sreyk load_pki_keys(); 6133ef9cbf7Sgilles fprintf(stderr, "configuration OK\n"); 6143ef9cbf7Sgilles exit(0); 6153ef9cbf7Sgilles } 6163ef9cbf7Sgilles 617f98e7cc0Seric env->sc_flags |= flags; 618f98e7cc0Seric 6193ef9cbf7Sgilles /* check for root privileges */ 6203ef9cbf7Sgilles if (geteuid()) 621ff01b044Seric fatalx("need root privileges"); 6223ef9cbf7Sgilles 623f24248b7Sreyk log_init(foreground_log, LOG_MAIL); 624f24248b7Sreyk log_trace_verbose(tracing); 625b88ab68dSeric load_pki_tree(); 626b88ab68dSeric load_pki_keys(); 6273f522ce8Sgilles 628b88ab68dSeric log_debug("debug: using \"%s\" queue backend", backend_queue); 629b88ab68dSeric log_debug("debug: using \"%s\" scheduler backend", backend_scheduler); 630b88ab68dSeric log_debug("debug: using \"%s\" stat backend", backend_stat); 631b88ab68dSeric 632b88ab68dSeric if (env->sc_hostname[0] == '\0') 633ff01b044Seric fatalx("machine does not have a hostname set"); 634b88ab68dSeric env->sc_uptime = time(NULL); 635b88ab68dSeric 636b88ab68dSeric if (rexec == NULL) { 637b88ab68dSeric smtpd_process = PROC_PARENT; 6389ed3223cSgilles 639bf397b35Seric if (env->sc_queue_flags & QUEUE_ENCRYPTION) { 640bf397b35Seric if (env->sc_queue_key == NULL) { 641bf397b35Seric char *password; 642bf397b35Seric 643bf397b35Seric password = getpass("queue key: "); 644bf397b35Seric if (password == NULL) 645ff01b044Seric fatal("getpass"); 646bf397b35Seric 647bf397b35Seric env->sc_queue_key = strdup(password); 6488fbd7fcbSdoug explicit_bzero(password, strlen(password)); 649bf397b35Seric if (env->sc_queue_key == NULL) 650ff01b044Seric fatal("strdup"); 651bf397b35Seric } 652bf397b35Seric else { 6530882b00eSsunil char *buf = NULL; 6540882b00eSsunil size_t sz = 0; 6550882b00eSsunil ssize_t len; 656bf397b35Seric 657bf397b35Seric if (strcasecmp(env->sc_queue_key, "stdin") == 0) { 6580882b00eSsunil if ((len = getline(&buf, &sz, stdin)) == -1) 659ff01b044Seric fatal("getline"); 6600882b00eSsunil if (buf[len - 1] == '\n') 6610882b00eSsunil buf[len - 1] = '\0'; 6620882b00eSsunil env->sc_queue_key = buf; 663bf397b35Seric } 664bf397b35Seric } 665bf397b35Seric } 666bf397b35Seric 667e6a16d48Sgilles log_info("info: %s %s starting", SMTPD_NAME, SMTPD_VERSION); 668e6a16d48Sgilles 669299c4efeSeric if (!foreground) 6701150d065Sgilles if (daemon(0, 0) == -1) 671ff01b044Seric fatal("failed to daemonize"); 6723ef9cbf7Sgilles 673b88ab68dSeric /* setup all processes */ 674b88ab68dSeric 675b88ab68dSeric p_ca = start_child(save_argc, save_argv, "ca"); 676b88ab68dSeric p_ca->proc = PROC_CA; 677b88ab68dSeric 678b88ab68dSeric p_control = start_child(save_argc, save_argv, "control"); 679b88ab68dSeric p_control->proc = PROC_CONTROL; 680b88ab68dSeric 681b88ab68dSeric p_lka = start_child(save_argc, save_argv, "lka"); 682b88ab68dSeric p_lka->proc = PROC_LKA; 683b88ab68dSeric 6841a5b831aSmartijn p_dispatcher = start_child(save_argc, save_argv, "dispatcher"); 6851a5b831aSmartijn p_dispatcher->proc = PROC_DISPATCHER; 686b88ab68dSeric 687b88ab68dSeric p_queue = start_child(save_argc, save_argv, "queue"); 688b88ab68dSeric p_queue->proc = PROC_QUEUE; 689b88ab68dSeric 690b88ab68dSeric p_scheduler = start_child(save_argc, save_argv, "scheduler"); 691b88ab68dSeric p_scheduler->proc = PROC_SCHEDULER; 692b88ab68dSeric 693b88ab68dSeric setup_peers(p_control, p_ca); 694b88ab68dSeric setup_peers(p_control, p_lka); 6951a5b831aSmartijn setup_peers(p_control, p_dispatcher); 696b88ab68dSeric setup_peers(p_control, p_queue); 697b88ab68dSeric setup_peers(p_control, p_scheduler); 6981a5b831aSmartijn setup_peers(p_dispatcher, p_ca); 6991a5b831aSmartijn setup_peers(p_dispatcher, p_lka); 7001a5b831aSmartijn setup_peers(p_dispatcher, p_queue); 701b88ab68dSeric setup_peers(p_queue, p_lka); 702b88ab68dSeric setup_peers(p_queue, p_scheduler); 703b88ab68dSeric 704b88ab68dSeric if (env->sc_queue_key) { 705b88ab68dSeric if (imsg_compose(&p_queue->imsgbuf, IMSG_SETUP_KEY, 0, 706b88ab68dSeric 0, -1, env->sc_queue_key, strlen(env->sc_queue_key) 707b88ab68dSeric + 1) == -1) 708b88ab68dSeric fatal("imsg_compose"); 709dd7efffeSclaudio if (imsgbuf_flush(&p_queue->imsgbuf) == -1) 710dd7efffeSclaudio fatal("imsgbuf_flush"); 711b88ab68dSeric } 712b88ab68dSeric 713b88ab68dSeric setup_done(p_ca); 714b88ab68dSeric setup_done(p_control); 715b88ab68dSeric setup_done(p_lka); 7161a5b831aSmartijn setup_done(p_dispatcher); 717b88ab68dSeric setup_done(p_queue); 718b88ab68dSeric setup_done(p_scheduler); 719b88ab68dSeric 7202f742ef6Seric log_debug("smtpd: setup done"); 721b88ab68dSeric 722b88ab68dSeric return smtpd(); 723b88ab68dSeric } 724b88ab68dSeric 725b88ab68dSeric if (!strcmp(rexec, "ca")) { 726b88ab68dSeric smtpd_process = PROC_CA; 727b88ab68dSeric setup_proc(); 728b88ab68dSeric 729b88ab68dSeric return ca(); 730b88ab68dSeric } 731b88ab68dSeric 732b88ab68dSeric else if (!strcmp(rexec, "control")) { 733b88ab68dSeric smtpd_process = PROC_CONTROL; 734b88ab68dSeric setup_proc(); 735b88ab68dSeric 736b88ab68dSeric /* the control socket ensures that only one smtpd instance is running */ 737b88ab68dSeric control_socket = control_create_socket(); 738b88ab68dSeric 739b88ab68dSeric env->sc_stat = stat_backend_lookup(backend_stat); 740b88ab68dSeric if (env->sc_stat == NULL) 741ff01b044Seric fatalx("could not find stat backend \"%s\"", backend_stat); 742b88ab68dSeric 743b88ab68dSeric return control(); 744b88ab68dSeric } 745b88ab68dSeric 746b88ab68dSeric else if (!strcmp(rexec, "lka")) { 747b88ab68dSeric smtpd_process = PROC_LKA; 748b88ab68dSeric setup_proc(); 749b88ab68dSeric 750b88ab68dSeric return lka(); 751b88ab68dSeric } 752b88ab68dSeric 7531a5b831aSmartijn else if (!strcmp(rexec, "dispatcher")) { 7541a5b831aSmartijn smtpd_process = PROC_DISPATCHER; 755b88ab68dSeric setup_proc(); 756b88ab68dSeric 7571a5b831aSmartijn return dispatcher(); 758b88ab68dSeric } 759b88ab68dSeric 760b88ab68dSeric else if (!strcmp(rexec, "queue")) { 761b88ab68dSeric smtpd_process = PROC_QUEUE; 762b88ab68dSeric setup_proc(); 763b88ab68dSeric 764b88ab68dSeric if (env->sc_queue_flags & QUEUE_COMPRESSION) 765b88ab68dSeric env->sc_comp = compress_backend_lookup("gzip"); 766b88ab68dSeric 767b88ab68dSeric if (!queue_init(backend_queue, 1)) 768ff01b044Seric fatalx("could not initialize queue backend"); 769b88ab68dSeric 770b88ab68dSeric return queue(); 771b88ab68dSeric } 772b88ab68dSeric 773b88ab68dSeric else if (!strcmp(rexec, "scheduler")) { 774b88ab68dSeric smtpd_process = PROC_SCHEDULER; 775b88ab68dSeric setup_proc(); 776b88ab68dSeric 77765c4fdfbSgilles for (i = 0; i < MAX_BOUNCE_WARN; i++) { 77865c4fdfbSgilles if (env->sc_bounce_warn[i] == 0) 77965c4fdfbSgilles break; 78065c4fdfbSgilles log_debug("debug: bounce warning after %s", 78165c4fdfbSgilles duration_to_text(env->sc_bounce_warn[i])); 78265c4fdfbSgilles } 78365c4fdfbSgilles 784b88ab68dSeric return scheduler(); 785b88ab68dSeric } 7863ef9cbf7Sgilles 787b88ab68dSeric fatalx("bad rexec: %s", rexec); 788e27f1753Sjacekm 789b88ab68dSeric return (1); 790b88ab68dSeric } 791b88ab68dSeric 792b88ab68dSeric static struct mproc * 793b88ab68dSeric start_child(int save_argc, char **save_argv, char *rexec) 794b88ab68dSeric { 795b88ab68dSeric struct mproc *p; 796b88ab68dSeric char *argv[SMTPD_MAXARG]; 797b88ab68dSeric int sp[2], argc = 0; 798b88ab68dSeric pid_t pid; 799b88ab68dSeric 800b88ab68dSeric if (save_argc >= SMTPD_MAXARG - 2) 801b88ab68dSeric fatalx("too many arguments"); 802b88ab68dSeric 8039877f962Sgilles if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, sp) == -1) 804b88ab68dSeric fatal("socketpair"); 805b88ab68dSeric 8069877f962Sgilles io_set_nonblocking(sp[0]); 8079877f962Sgilles io_set_nonblocking(sp[1]); 8089877f962Sgilles 809b88ab68dSeric switch (pid = fork()) { 810b88ab68dSeric case -1: 811b88ab68dSeric fatal("%s: fork", save_argv[0]); 812b88ab68dSeric case 0: 813b88ab68dSeric break; 814b88ab68dSeric default: 815b88ab68dSeric close(sp[0]); 816b88ab68dSeric p = calloc(1, sizeof(*p)); 817b88ab68dSeric if (p == NULL) 818b88ab68dSeric fatal("calloc"); 819b88ab68dSeric if((p->name = strdup(rexec)) == NULL) 820b88ab68dSeric fatal("strdup"); 821b88ab68dSeric mproc_init(p, sp[1]); 822b88ab68dSeric p->pid = pid; 823b88ab68dSeric p->handler = parent_imsg; 824b88ab68dSeric return p; 825b88ab68dSeric } 826b88ab68dSeric 827ef4f5895Syasuoka if (sp[0] != 3) { 828b88ab68dSeric if (dup2(sp[0], 3) == -1) 829b88ab68dSeric fatal("%s: dup2", rexec); 830ef4f5895Syasuoka } else if (fcntl(sp[0], F_SETFD, 0) == -1) 831ef4f5895Syasuoka fatal("%s: fcntl", rexec); 832b88ab68dSeric 833366735a1Seric if (closefrom(4) == -1) 834366735a1Seric fatal("%s: closefrom", rexec); 835366735a1Seric 836b88ab68dSeric for (argc = 0; argc < save_argc; argc++) 837b88ab68dSeric argv[argc] = save_argv[argc]; 838b88ab68dSeric argv[argc++] = "-x"; 839b88ab68dSeric argv[argc++] = rexec; 840b88ab68dSeric argv[argc++] = NULL; 841b88ab68dSeric 842b88ab68dSeric execvp(argv[0], argv); 843b88ab68dSeric fatal("%s: execvp", rexec); 844b88ab68dSeric } 845b88ab68dSeric 846b88ab68dSeric static void 847b88ab68dSeric setup_peers(struct mproc *a, struct mproc *b) 848b88ab68dSeric { 849b88ab68dSeric int sp[2]; 850b88ab68dSeric 8519877f962Sgilles if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, sp) == -1) 852b88ab68dSeric fatal("socketpair"); 853b88ab68dSeric 8549877f962Sgilles io_set_nonblocking(sp[0]); 8559877f962Sgilles io_set_nonblocking(sp[1]); 8569877f962Sgilles 857b88ab68dSeric if (imsg_compose(&a->imsgbuf, IMSG_SETUP_PEER, b->proc, b->pid, sp[0], 858b88ab68dSeric NULL, 0) == -1) 859b88ab68dSeric fatal("imsg_compose"); 860dd7efffeSclaudio if (imsgbuf_flush(&a->imsgbuf) == -1) 861dd7efffeSclaudio fatal("imsgbuf_flush"); 862b88ab68dSeric 863b88ab68dSeric if (imsg_compose(&b->imsgbuf, IMSG_SETUP_PEER, a->proc, a->pid, sp[1], 864b88ab68dSeric NULL, 0) == -1) 865b88ab68dSeric fatal("imsg_compose"); 866dd7efffeSclaudio if (imsgbuf_flush(&b->imsgbuf) == -1) 867dd7efffeSclaudio fatal("imsgbuf_flush"); 868b88ab68dSeric } 869b88ab68dSeric 870b88ab68dSeric static void 871b88ab68dSeric setup_done(struct mproc *p) 872b88ab68dSeric { 873b88ab68dSeric struct imsg imsg; 874b88ab68dSeric 875b88ab68dSeric if (imsg_compose(&p->imsgbuf, IMSG_SETUP_DONE, 0, 0, -1, NULL, 0) == -1) 876b88ab68dSeric fatal("imsg_compose"); 877dd7efffeSclaudio if (imsgbuf_flush(&p->imsgbuf) == -1) 878dd7efffeSclaudio fatal("imsgbuf_flush"); 879b88ab68dSeric 880b88ab68dSeric if (imsg_wait(&p->imsgbuf, &imsg, 10000) == -1) 881b88ab68dSeric fatal("imsg_wait"); 882b88ab68dSeric 883b88ab68dSeric if (imsg.hdr.type != IMSG_SETUP_DONE) 884b88ab68dSeric fatalx("expect IMSG_SETUP_DONE"); 885b88ab68dSeric 8862f742ef6Seric log_debug("setup_done: %s[%d] done", p->name, p->pid); 887b88ab68dSeric 888b88ab68dSeric imsg_free(&imsg); 889b88ab68dSeric } 890b88ab68dSeric 891b88ab68dSeric static void 892b88ab68dSeric setup_proc(void) 893b88ab68dSeric { 894b88ab68dSeric struct imsgbuf *ibuf; 895b88ab68dSeric struct imsg imsg; 896b88ab68dSeric int setup = 1; 897b88ab68dSeric 898f24248b7Sreyk log_procinit(proc_title(smtpd_process)); 899f24248b7Sreyk 900b88ab68dSeric p_parent = calloc(1, sizeof(*p_parent)); 901b88ab68dSeric if (p_parent == NULL) 902b88ab68dSeric fatal("calloc"); 903b88ab68dSeric if((p_parent->name = strdup("parent")) == NULL) 904b88ab68dSeric fatal("strdup"); 905b88ab68dSeric p_parent->proc = PROC_PARENT; 906b88ab68dSeric p_parent->handler = imsg_dispatch; 907b88ab68dSeric mproc_init(p_parent, 3); 908b88ab68dSeric 909b88ab68dSeric ibuf = &p_parent->imsgbuf; 910b88ab68dSeric 911b88ab68dSeric while (setup) { 912b88ab68dSeric if (imsg_wait(ibuf, &imsg, 10000) == -1) 913b88ab68dSeric fatal("imsg_wait"); 914b88ab68dSeric 915b88ab68dSeric switch (imsg.hdr.type) { 916b88ab68dSeric case IMSG_SETUP_KEY: 917b88ab68dSeric env->sc_queue_key = strdup(imsg.data); 918b88ab68dSeric break; 919b88ab68dSeric case IMSG_SETUP_PEER: 920510586acSclaudio setup_peer(imsg.hdr.peerid, imsg.hdr.pid, 921510586acSclaudio imsg_get_fd(&imsg)); 922b88ab68dSeric break; 923b88ab68dSeric case IMSG_SETUP_DONE: 924b88ab68dSeric setup = 0; 925b88ab68dSeric break; 926b88ab68dSeric default: 927b88ab68dSeric fatal("bad imsg %d", imsg.hdr.type); 928b88ab68dSeric } 929b88ab68dSeric imsg_free(&imsg); 930b88ab68dSeric } 931b88ab68dSeric 932b88ab68dSeric if (imsg_compose(ibuf, IMSG_SETUP_DONE, 0, 0, -1, NULL, 0) == -1) 933b88ab68dSeric fatal("imsg_compose"); 934b88ab68dSeric 935dd7efffeSclaudio if (imsgbuf_flush(ibuf) == -1) 936dd7efffeSclaudio fatal("imsgbuf_flush"); 937b88ab68dSeric 9382f742ef6Seric log_debug("setup_proc: %s done", proc_title(smtpd_process)); 939b88ab68dSeric } 940b88ab68dSeric 941b88ab68dSeric static struct mproc * 942b88ab68dSeric setup_peer(enum smtp_proc_type proc, pid_t pid, int sock) 943b88ab68dSeric { 944b88ab68dSeric struct mproc *p, **pp; 945b88ab68dSeric 9462f742ef6Seric log_debug("setup_peer: %s -> %s[%u] fd=%d", proc_title(smtpd_process), 947b88ab68dSeric proc_title(proc), pid, sock); 948b88ab68dSeric 949b88ab68dSeric if (sock == -1) 950b88ab68dSeric fatalx("peer socket not received"); 951b88ab68dSeric 952b88ab68dSeric switch (proc) { 953b88ab68dSeric case PROC_LKA: 954b88ab68dSeric pp = &p_lka; 955b88ab68dSeric break; 956b88ab68dSeric case PROC_QUEUE: 957b88ab68dSeric pp = &p_queue; 958b88ab68dSeric break; 959b88ab68dSeric case PROC_CONTROL: 960b88ab68dSeric pp = &p_control; 961b88ab68dSeric break; 962b88ab68dSeric case PROC_SCHEDULER: 963b88ab68dSeric pp = &p_scheduler; 964b88ab68dSeric break; 9651a5b831aSmartijn case PROC_DISPATCHER: 9661a5b831aSmartijn pp = &p_dispatcher; 967b88ab68dSeric break; 968b88ab68dSeric case PROC_CA: 969b88ab68dSeric pp = &p_ca; 970b88ab68dSeric break; 971b88ab68dSeric default: 972b88ab68dSeric fatalx("unknown peer"); 973b88ab68dSeric } 974b88ab68dSeric 975b88ab68dSeric if (*pp) 976b88ab68dSeric fatalx("peer already set"); 977b88ab68dSeric 978b88ab68dSeric p = calloc(1, sizeof(*p)); 979b88ab68dSeric if (p == NULL) 980b88ab68dSeric fatal("calloc"); 981b88ab68dSeric if((p->name = strdup(proc_title(proc))) == NULL) 982b88ab68dSeric fatal("strdup"); 983b88ab68dSeric mproc_init(p, sock); 984b88ab68dSeric p->pid = pid; 985b88ab68dSeric p->proc = proc; 986b88ab68dSeric p->handler = imsg_dispatch; 987b88ab68dSeric 988b88ab68dSeric *pp = p; 989b88ab68dSeric 990b88ab68dSeric return p; 991b88ab68dSeric } 992b88ab68dSeric 993b88ab68dSeric static int 994b88ab68dSeric imsg_wait(struct imsgbuf *ibuf, struct imsg *imsg, int timeout) 995b88ab68dSeric { 996b88ab68dSeric struct pollfd pfd[1]; 997b88ab68dSeric ssize_t n; 998b88ab68dSeric 999b88ab68dSeric pfd[0].fd = ibuf->fd; 1000b88ab68dSeric pfd[0].events = POLLIN; 1001b88ab68dSeric 1002b88ab68dSeric while (1) { 1003b88ab68dSeric if ((n = imsg_get(ibuf, imsg)) == -1) 1004b88ab68dSeric return -1; 1005b88ab68dSeric if (n) 1006b88ab68dSeric return 1; 1007b88ab68dSeric 1008b88ab68dSeric n = poll(pfd, 1, timeout); 1009b88ab68dSeric if (n == -1) 1010b88ab68dSeric return -1; 1011b88ab68dSeric if (n == 0) { 1012b88ab68dSeric errno = ETIMEDOUT; 1013b88ab68dSeric return -1; 1014b88ab68dSeric } 1015b88ab68dSeric 1016*16b0c81bSclaudio if (imsgbuf_read(ibuf) != 1) 1017b88ab68dSeric return -1; 1018b88ab68dSeric } 1019b88ab68dSeric } 1020b88ab68dSeric 1021b88ab68dSeric int 1022b88ab68dSeric smtpd(void) { 1023b88ab68dSeric struct event ev_sigint; 1024b88ab68dSeric struct event ev_sigterm; 1025b88ab68dSeric struct event ev_sigchld; 1026b88ab68dSeric struct event ev_sighup; 1027b88ab68dSeric struct timeval tv; 10283ef9cbf7Sgilles 1029ed1929b6Sjacekm imsg_callback = parent_imsg; 1030b88ab68dSeric 1031b88ab68dSeric tree_init(&children); 1032b88ab68dSeric 1033b88ab68dSeric child_add(p_queue->pid, CHILD_DAEMON, proc_title(PROC_QUEUE)); 1034b88ab68dSeric child_add(p_control->pid, CHILD_DAEMON, proc_title(PROC_CONTROL)); 1035b88ab68dSeric child_add(p_lka->pid, CHILD_DAEMON, proc_title(PROC_LKA)); 1036b88ab68dSeric child_add(p_scheduler->pid, CHILD_DAEMON, proc_title(PROC_SCHEDULER)); 10371a5b831aSmartijn child_add(p_dispatcher->pid, CHILD_DAEMON, proc_title(PROC_DISPATCHER)); 1038b88ab68dSeric child_add(p_ca->pid, CHILD_DAEMON, proc_title(PROC_CA)); 1039b88ab68dSeric 10403ef9cbf7Sgilles event_init(); 10413ef9cbf7Sgilles 1042e4d36f12Seric signal_set(&ev_sigint, SIGINT, parent_sig_handler, NULL); 1043e4d36f12Seric signal_set(&ev_sigterm, SIGTERM, parent_sig_handler, NULL); 1044e4d36f12Seric signal_set(&ev_sigchld, SIGCHLD, parent_sig_handler, NULL); 1045e4d36f12Seric signal_set(&ev_sighup, SIGHUP, parent_sig_handler, NULL); 10463ef9cbf7Sgilles signal_add(&ev_sigint, NULL); 10473ef9cbf7Sgilles signal_add(&ev_sigterm, NULL); 10483ef9cbf7Sgilles signal_add(&ev_sigchld, NULL); 10493ef9cbf7Sgilles signal_add(&ev_sighup, NULL); 10503ef9cbf7Sgilles signal(SIGPIPE, SIG_IGN); 10513ef9cbf7Sgilles 105265c4fdfbSgilles config_peer(PROC_CONTROL); 105365c4fdfbSgilles config_peer(PROC_LKA); 105465c4fdfbSgilles config_peer(PROC_QUEUE); 105576eb97c5Sreyk config_peer(PROC_CA); 10561a5b831aSmartijn config_peer(PROC_DISPATCHER); 10573ef9cbf7Sgilles 1058299c4efeSeric evtimer_set(&config_ev, parent_send_config, NULL); 1059c1392a69Seric memset(&tv, 0, sizeof(tv)); 1060299c4efeSeric evtimer_add(&config_ev, &tv); 10613ef9cbf7Sgilles 106214f5466bSeric /* defer offline scanning for a second */ 106314f5466bSeric evtimer_set(&offline_ev, offline_scan, NULL); 106414f5466bSeric offline_timeout.tv_sec = 1; 106514f5466bSeric offline_timeout.tv_usec = 0; 106614f5466bSeric evtimer_add(&offline_ev, &offline_timeout); 106714f5466bSeric 10685d1bf438Sgilles fork_filter_processes(); 1069a93e09f5Sgilles 107013a170a7Seric purge_task(); 107113a170a7Seric 1072b384c3c3Sgilles if (pledge("stdio rpath wpath cpath fattr tmppath " 107368213f7bSgilles "getpw sendfd proc exec id inet chown unix", NULL) == -1) 1074ff01b044Seric fatal("pledge"); 1075cc2f8927Sgilles 1076f94528c3Seric event_dispatch(); 1077f94528c3Seric fatalx("exited event loop"); 10783ef9cbf7Sgilles 10793ef9cbf7Sgilles return (0); 10803ef9cbf7Sgilles } 10813ef9cbf7Sgilles 1082be925435Sgilles static void 10831c3ac238Seric load_pki_tree(void) 1084299c4efeSeric { 10851c3ac238Seric struct pki *pki; 1086d658e598Sgilles struct ca *sca; 1087cc81b7c6Seric const char *k; 10881c3ac238Seric void *iter_dict; 1089299c4efeSeric 1090cc81b7c6Seric log_debug("debug: init ssl-tree"); 1091cc81b7c6Seric iter_dict = NULL; 10921c3ac238Seric while (dict_iter(env->sc_pki_dict, &iter_dict, &k, (void **)&pki)) { 109376a0ecd1Seric log_debug("info: loading pki information for %s", k); 10941c3ac238Seric if (pki->pki_cert_file == NULL) 10951c3ac238Seric fatalx("load_pki_tree: missing certificate file"); 10961c3ac238Seric if (pki->pki_key_file == NULL) 10971c3ac238Seric fatalx("load_pki_tree: missing key file"); 1098299c4efeSeric 10991c3ac238Seric if (!ssl_load_certificate(pki, pki->pki_cert_file)) 11001c3ac238Seric fatalx("load_pki_tree: failed to load certificate file"); 1101299c4efeSeric } 1102d658e598Sgilles 1103d658e598Sgilles log_debug("debug: init ca-tree"); 1104d658e598Sgilles iter_dict = NULL; 1105d658e598Sgilles while (dict_iter(env->sc_ca_dict, &iter_dict, &k, (void **)&sca)) { 1106d658e598Sgilles log_debug("info: loading CA information for %s", k); 1107f7aa1c30Sgilles if (!ssl_load_cafile(sca, sca->ca_cert_file)) 1108f7aa1c30Sgilles fatalx("load_pki_tree: failed to load CA file"); 1109d658e598Sgilles } 1110eb351e24Seric } 1111299c4efeSeric 1112c52bd758Sreyk void 1113c52bd758Sreyk load_pki_keys(void) 1114c52bd758Sreyk { 1115c52bd758Sreyk struct pki *pki; 1116c52bd758Sreyk const char *k; 1117c52bd758Sreyk void *iter_dict; 1118c52bd758Sreyk 1119c52bd758Sreyk log_debug("debug: init ssl-tree"); 1120c52bd758Sreyk iter_dict = NULL; 1121c52bd758Sreyk while (dict_iter(env->sc_pki_dict, &iter_dict, &k, (void **)&pki)) { 1122c52bd758Sreyk log_debug("info: loading pki keys for %s", k); 1123c52bd758Sreyk 1124c52bd758Sreyk if (!ssl_load_keyfile(pki, pki->pki_key_file, k)) 1125c52bd758Sreyk fatalx("load_pki_keys: failed to load key file"); 1126c52bd758Sreyk } 1127c52bd758Sreyk } 1128c52bd758Sreyk 1129d6f2ac01Seric int 11308380d000Sop fork_proc_backend(const char *key, const char *conf, const char *procname, 11318380d000Sop int do_stdout) 1132d6f2ac01Seric { 1133d6f2ac01Seric pid_t pid; 1134d6f2ac01Seric int sp[2]; 1135953aae25Sderaadt char path[PATH_MAX]; 1136953aae25Sderaadt char name[PATH_MAX]; 1137d6f2ac01Seric char *arg; 1138d6f2ac01Seric 1139d6f2ac01Seric if (strlcpy(name, conf, sizeof(name)) >= sizeof(name)) { 1140d6f2ac01Seric log_warnx("warn: %s-proc: conf too long", key); 1141a3f92dc1Sop return (-1); 1142d6f2ac01Seric } 1143d6f2ac01Seric 1144d6f2ac01Seric arg = strchr(name, ':'); 1145d6f2ac01Seric if (arg) 1146d6f2ac01Seric *arg++ = '\0'; 1147d6f2ac01Seric 1148ad8f062fSjung if (snprintf(path, sizeof(path), PATH_LIBEXEC "/%s-%s", key, name) >= 1149d6f2ac01Seric (ssize_t)sizeof(path)) { 1150d6f2ac01Seric log_warn("warn: %s-proc: exec path too long", key); 1151d6f2ac01Seric return (-1); 1152d6f2ac01Seric } 1153d6f2ac01Seric 1154d6f2ac01Seric if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, sp) == -1) { 1155d6f2ac01Seric log_warn("warn: %s-proc: socketpair", key); 1156d6f2ac01Seric return (-1); 1157d6f2ac01Seric } 1158d6f2ac01Seric 1159d6f2ac01Seric if ((pid = fork()) == -1) { 1160d6f2ac01Seric log_warn("warn: %s-proc: fork", key); 1161d6f2ac01Seric close(sp[0]); 1162d6f2ac01Seric close(sp[1]); 1163d6f2ac01Seric return (-1); 1164d6f2ac01Seric } 1165d6f2ac01Seric 1166d6f2ac01Seric if (pid == 0) { 1167d6f2ac01Seric /* child process */ 1168d6f2ac01Seric dup2(sp[0], STDIN_FILENO); 11698380d000Sop if (do_stdout) 11708380d000Sop dup2(sp[0], STDOUT_FILENO); 1171df69c215Sderaadt if (closefrom(STDERR_FILENO + 1) == -1) 1172d6f2ac01Seric exit(1); 1173d6f2ac01Seric 1174d6f2ac01Seric if (procname == NULL) 1175d6f2ac01Seric procname = name; 1176d6f2ac01Seric 117753408464Skrw execl(path, procname, arg, (char *)NULL); 1178ff01b044Seric fatal("execl: %s", path); 1179d6f2ac01Seric } 1180d6f2ac01Seric 1181d6f2ac01Seric /* parent process */ 1182d6f2ac01Seric close(sp[0]); 1183d6f2ac01Seric 1184d6f2ac01Seric return (sp[1]); 1185d6f2ac01Seric } 1186d6f2ac01Seric 118711dbc40fSjacekm struct child * 11883a305da9Seric child_add(pid_t pid, int type, const char *title) 118934abc65bSjacekm { 119034abc65bSjacekm struct child *child; 119134abc65bSjacekm 119234abc65bSjacekm if ((child = calloc(1, sizeof(*child))) == NULL) 1193c5ad255fSeric fatal("smtpd: child_add: calloc"); 119434abc65bSjacekm 119534abc65bSjacekm child->pid = pid; 119634abc65bSjacekm child->type = type; 119734abc65bSjacekm child->title = title; 119834abc65bSjacekm 11993a305da9Seric tree_xset(&children, pid, child); 120011dbc40fSjacekm 120111dbc40fSjacekm return (child); 120234abc65bSjacekm } 120334abc65bSjacekm 1204591aa05bSeric static void 1205b35655ddSgilles purge_task(void) 1206591aa05bSeric { 120711d04e02Seric struct passwd *pw; 1208591aa05bSeric DIR *d; 1209591aa05bSeric int n; 1210591aa05bSeric uid_t uid; 1211591aa05bSeric gid_t gid; 1212591aa05bSeric 1213591aa05bSeric n = 0; 1214591aa05bSeric if ((d = opendir(PATH_SPOOL PATH_PURGE))) { 12151c6ac251Seric while (readdir(d) != NULL) 1216591aa05bSeric n++; 1217591aa05bSeric closedir(d); 1218591aa05bSeric } else 121982614934Seric log_warn("warn: purge_task: opendir"); 1220591aa05bSeric 1221591aa05bSeric if (n > 2) { 1222591aa05bSeric switch (purge_pid = fork()) { 1223591aa05bSeric case -1: 122482614934Seric log_warn("warn: purge_task: fork"); 1225591aa05bSeric break; 1226591aa05bSeric case 0: 1227e9c4fe73Sgilles if ((pw = getpwnam(SMTPD_QUEUE_USER)) == NULL) 1228e9c4fe73Sgilles fatalx("unknown user " SMTPD_QUEUE_USER); 1229591aa05bSeric if (chroot(PATH_SPOOL PATH_PURGE) == -1) 1230591aa05bSeric fatal("smtpd: chroot"); 1231591aa05bSeric if (chdir("/") == -1) 1232591aa05bSeric fatal("smtpd: chdir"); 123311d04e02Seric uid = pw->pw_uid; 123411d04e02Seric gid = pw->pw_gid; 1235591aa05bSeric if (setgroups(1, &gid) || 1236591aa05bSeric setresgid(gid, gid, gid) || 1237591aa05bSeric setresuid(uid, uid, uid)) 1238591aa05bSeric fatal("smtpd: cannot drop privileges"); 123991039167Seric rmtree("/", 1); 1240591aa05bSeric _exit(0); 1241591aa05bSeric break; 1242591aa05bSeric default: 1243591aa05bSeric break; 1244591aa05bSeric } 1245591aa05bSeric } 1246591aa05bSeric } 1247591aa05bSeric 1248be925435Sgilles static void 12495d1bf438Sgilles fork_filter_processes(void) 1250a93e09f5Sgilles { 1251a93e09f5Sgilles const char *name; 1252a93e09f5Sgilles void *iter; 125326bbc7b9Sgilles const char *fn; 125426bbc7b9Sgilles struct filter_config *fc; 125526bbc7b9Sgilles struct filter_config *fcs; 125626bbc7b9Sgilles struct filter_proc *fp; 125726bbc7b9Sgilles size_t i; 125826bbc7b9Sgilles 125926bbc7b9Sgilles /* For each filter chain, assign the registered subsystem to subfilters */ 126026bbc7b9Sgilles iter = NULL; 126126bbc7b9Sgilles while (dict_iter(env->sc_filters_dict, &iter, (const char **)&fn, (void **)&fc)) { 126226bbc7b9Sgilles if (fc->chain) { 126326bbc7b9Sgilles for (i = 0; i < fc->chain_size; ++i) { 126426bbc7b9Sgilles fcs = dict_xget(env->sc_filters_dict, fc->chain[i]); 126526bbc7b9Sgilles fcs->filter_subsystem |= fc->filter_subsystem; 126626bbc7b9Sgilles } 126726bbc7b9Sgilles } 126826bbc7b9Sgilles } 126926bbc7b9Sgilles 127026bbc7b9Sgilles /* For each filter, assign the registered subsystem to underlying proc */ 127126bbc7b9Sgilles iter = NULL; 127226bbc7b9Sgilles while (dict_iter(env->sc_filters_dict, &iter, (const char **)&fn, (void **)&fc)) { 127326bbc7b9Sgilles if (fc->proc) { 12745d1bf438Sgilles fp = dict_xget(env->sc_filter_processes_dict, fc->proc); 127526bbc7b9Sgilles fp->filter_subsystem |= fc->filter_subsystem; 127626bbc7b9Sgilles } 127726bbc7b9Sgilles } 1278a93e09f5Sgilles 1279a93e09f5Sgilles iter = NULL; 12805d1bf438Sgilles while (dict_iter(env->sc_filter_processes_dict, &iter, &name, (void **)&fp)) 12815d1bf438Sgilles fork_filter_process(name, fp->command, fp->user, fp->group, fp->chroot, fp->filter_subsystem); 1282a93e09f5Sgilles } 1283a93e09f5Sgilles 1284a93e09f5Sgilles static void 12855d1bf438Sgilles fork_filter_process(const char *name, const char *command, const char *user, const char *group, const char *chroot_path, uint32_t subsystems) 1286a93e09f5Sgilles { 1287a93e09f5Sgilles pid_t pid; 128826bbc7b9Sgilles struct filter_proc *processor; 1289dbc3cea0Smartijn char buf; 1290dbc3cea0Smartijn int sp[2], errfd[2]; 1291a93e09f5Sgilles struct passwd *pw; 1292a93e09f5Sgilles struct group *gr; 12938dfdf75bSmartijn char exec[_POSIX_ARG_MAX]; 12948dfdf75bSmartijn int execr; 1295a93e09f5Sgilles 1296a93e09f5Sgilles if (user == NULL) 1297a93e09f5Sgilles user = SMTPD_USER; 1298a93e09f5Sgilles if ((pw = getpwnam(user)) == NULL) 1299ff01b044Seric fatal("getpwnam"); 1300a93e09f5Sgilles 1301a93e09f5Sgilles if (group) { 1302a93e09f5Sgilles if ((gr = getgrnam(group)) == NULL) 1303ff01b044Seric fatal("getgrnam"); 1304a93e09f5Sgilles } 1305a93e09f5Sgilles else { 1306a93e09f5Sgilles if ((gr = getgrgid(pw->pw_gid)) == NULL) 1307ff01b044Seric fatal("getgrgid"); 1308a93e09f5Sgilles } 1309a93e09f5Sgilles 1310a93e09f5Sgilles if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, sp) == -1) 1311ff01b044Seric fatal("socketpair"); 1312dbc3cea0Smartijn if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, errfd) == -1) 1313ff01b044Seric fatal("socketpair"); 1314a93e09f5Sgilles 1315df69c215Sderaadt if ((pid = fork()) == -1) 1316ff01b044Seric fatal("fork"); 1317a93e09f5Sgilles 1318a93e09f5Sgilles /* parent passes the child fd over to lka */ 1319a93e09f5Sgilles if (pid > 0) { 13205d1bf438Sgilles processor = dict_xget(env->sc_filter_processes_dict, name); 1321dbc3cea0Smartijn processor->errfd = errfd[1]; 1322a93e09f5Sgilles child_add(pid, CHILD_PROCESSOR, name); 1323a93e09f5Sgilles close(sp[0]); 1324dbc3cea0Smartijn close(errfd[0]); 1325a93e09f5Sgilles m_create(p_lka, IMSG_LKA_PROCESSOR_FORK, 0, 0, sp[1]); 1326a93e09f5Sgilles m_add_string(p_lka, name); 132726bbc7b9Sgilles m_add_u32(p_lka, (uint32_t)subsystems); 1328a93e09f5Sgilles m_close(p_lka); 1329a93e09f5Sgilles return; 1330a93e09f5Sgilles } 1331a93e09f5Sgilles 1332a93e09f5Sgilles close(sp[1]); 1333dbc3cea0Smartijn close(errfd[1]); 1334a93e09f5Sgilles dup2(sp[0], STDIN_FILENO); 1335a93e09f5Sgilles dup2(sp[0], STDOUT_FILENO); 1336dbc3cea0Smartijn dup2(errfd[0], STDERR_FILENO); 1337a93e09f5Sgilles 1338a93e09f5Sgilles if (chroot_path) { 1339a93e09f5Sgilles if (chroot(chroot_path) != 0 || chdir("/") != 0) 1340ff01b044Seric fatal("chroot: %s", chroot_path); 1341a93e09f5Sgilles } 1342a93e09f5Sgilles 1343a93e09f5Sgilles if (setgroups(1, &gr->gr_gid) || 1344a93e09f5Sgilles setresgid(gr->gr_gid, gr->gr_gid, gr->gr_gid) || 1345a93e09f5Sgilles setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) 1346ff01b044Seric fatal("fork_filter_process: cannot drop privileges"); 1347a93e09f5Sgilles 1348df69c215Sderaadt if (closefrom(STDERR_FILENO + 1) == -1) 1349ff01b044Seric fatal("closefrom"); 1350df69c215Sderaadt if (setsid() == -1) 1351ff01b044Seric fatal("setsid"); 1352a93e09f5Sgilles if (signal(SIGPIPE, SIG_DFL) == SIG_ERR || 1353a93e09f5Sgilles signal(SIGINT, SIG_DFL) == SIG_ERR || 1354a93e09f5Sgilles signal(SIGTERM, SIG_DFL) == SIG_ERR || 1355a93e09f5Sgilles signal(SIGCHLD, SIG_DFL) == SIG_ERR || 1356a93e09f5Sgilles signal(SIGHUP, SIG_DFL) == SIG_ERR) 1357ff01b044Seric fatal("signal"); 1358a93e09f5Sgilles 13598dfdf75bSmartijn if (command[0] == '/') 13608dfdf75bSmartijn execr = snprintf(exec, sizeof(exec), "exec %s", command); 13618dfdf75bSmartijn else 13628dfdf75bSmartijn execr = snprintf(exec, sizeof(exec), "exec %s/%s", 13638dfdf75bSmartijn PATH_LIBEXEC, command); 13648dfdf75bSmartijn if (execr >= (int) sizeof(exec)) 1365ff01b044Seric fatalx("%s: exec path too long", name); 13668dfdf75bSmartijn 1367dbc3cea0Smartijn /* 1368dbc3cea0Smartijn * Wait for lka to acknowledge that it received the fd. 1369dbc3cea0Smartijn * This prevents a race condition between the filter sending an error 1370dbc3cea0Smartijn * message, and exiting and lka not being able to log it because of 1371dbc3cea0Smartijn * SIGCHLD. 1372dbc3cea0Smartijn * (Ab)use read to determine if the fd is installed; since stderr is 1373dbc3cea0Smartijn * never going to be read from we can shutdown(2) the write-end in lka. 1374dbc3cea0Smartijn */ 1375dbc3cea0Smartijn if (read(STDERR_FILENO, &buf, 1) != 0) 1376ff01b044Seric fatalx("lka didn't properly close write end of error socket"); 13778dfdf75bSmartijn if (system(exec) == -1) 1378ff01b044Seric fatal("system"); 137940d55801Sgilles 138040d55801Sgilles /* there's no successful exit from a processor */ 1381fed76179Sgilles _exit(1); 1382a93e09f5Sgilles } 1383a93e09f5Sgilles 1384a93e09f5Sgilles static void 138565c4fdfbSgilles forkmda(struct mproc *p, uint64_t id, struct deliver *deliver) 13863ef9cbf7Sgilles { 13871743d5a3Sjacekm char ebuf[128], sfn[32]; 1388a8e22235Sgilles struct dispatcher *dsp; 138911dbc40fSjacekm struct child *child; 13903ef9cbf7Sgilles pid_t pid; 139101472467Sgilles int allout, pipefd[2]; 1392a8e22235Sgilles struct passwd *pw; 13932df8b630Sgilles const char *pw_name; 1394a8e22235Sgilles uid_t pw_uid; 1395a8e22235Sgilles gid_t pw_gid; 1396a8e22235Sgilles const char *pw_dir; 1397a8e22235Sgilles 1398a8e22235Sgilles dsp = dict_xget(env->sc_dispatchers, deliver->dispatcher); 1399b69a9274Smillert if (dsp->type != DISPATCHER_LOCAL) 1400b69a9274Smillert fatalx("non-local dispatcher called from forkmda()"); 14013ef9cbf7Sgilles 14021c6ac251Seric log_debug("debug: smtpd: forking mda for session %016"PRIx64 1403a8e22235Sgilles ": %s as %s", id, deliver->userinfo.username, 1404a8e22235Sgilles dsp->u.local.user ? dsp->u.local.user : deliver->userinfo.username); 14053ef9cbf7Sgilles 1406a8e22235Sgilles if (dsp->u.local.user) { 1407a8e22235Sgilles if ((pw = getpwnam(dsp->u.local.user)) == NULL) { 1408a8e22235Sgilles (void)snprintf(ebuf, sizeof ebuf, 1409a8e22235Sgilles "delivery user '%s' does not exist", 1410a8e22235Sgilles dsp->u.local.user); 14111a5b831aSmartijn m_create(p_dispatcher, IMSG_MDA_DONE, 0, 0, -1); 14121a5b831aSmartijn m_add_id(p_dispatcher, id); 14131a5b831aSmartijn m_add_int(p_dispatcher, MDA_PERMFAIL); 14141a5b831aSmartijn m_add_int(p_dispatcher, EX_NOUSER); 14151a5b831aSmartijn m_add_string(p_dispatcher, ebuf); 14161a5b831aSmartijn m_close(p_dispatcher); 1417a224a58cSgilles return; 14187a999a6cSgilles } 14192df8b630Sgilles pw_name = pw->pw_name; 1420a8e22235Sgilles pw_uid = pw->pw_uid; 1421a8e22235Sgilles pw_gid = pw->pw_gid; 1422a8e22235Sgilles pw_dir = pw->pw_dir; 1423a8e22235Sgilles } 1424a8e22235Sgilles else { 14252df8b630Sgilles pw_name = deliver->userinfo.username; 1426a8e22235Sgilles pw_uid = deliver->userinfo.uid; 1427a8e22235Sgilles pw_gid = deliver->userinfo.gid; 1428a8e22235Sgilles pw_dir = deliver->userinfo.directory; 1429a8e22235Sgilles } 1430a224a58cSgilles 143103e780bbSgilles if (pw_uid == 0 && (!dsp->u.local.is_mbox || deliver->mda_exec[0])) { 143203e780bbSgilles (void)snprintf(ebuf, sizeof ebuf, "MDA not allowed to deliver to: %s", 1433a8e22235Sgilles deliver->userinfo.username); 14341a5b831aSmartijn m_create(p_dispatcher, IMSG_MDA_DONE, 0, 0, -1); 14351a5b831aSmartijn m_add_id(p_dispatcher, id); 14361a5b831aSmartijn m_add_int(p_dispatcher, MDA_PERMFAIL); 14371a5b831aSmartijn m_add_int(p_dispatcher, EX_NOPERM); 14381a5b831aSmartijn m_add_string(p_dispatcher, ebuf); 14391a5b831aSmartijn m_close(p_dispatcher); 144069de2731Sgilles return; 144169de2731Sgilles } 144269de2731Sgilles 14431dd9d999Sop if (dsp->u.local.is_mbox && dsp->u.local.command != NULL) 14441dd9d999Sop fatalx("serious memory corruption in privileged process"); 14451dd9d999Sop 1446df69c215Sderaadt if (pipe(pipefd) == -1) { 1447095d1e54Sgilles (void)snprintf(ebuf, sizeof ebuf, "pipe: %s", strerror(errno)); 14481a5b831aSmartijn m_create(p_dispatcher, IMSG_MDA_DONE, 0, 0, -1); 14491a5b831aSmartijn m_add_id(p_dispatcher, id); 14501a5b831aSmartijn m_add_int(p_dispatcher, MDA_TEMPFAIL); 14511a5b831aSmartijn m_add_int(p_dispatcher, EX_OSERR); 14521a5b831aSmartijn m_add_string(p_dispatcher, ebuf); 14531a5b831aSmartijn m_close(p_dispatcher); 14541743d5a3Sjacekm return; 14551743d5a3Sjacekm } 14561743d5a3Sjacekm 14571743d5a3Sjacekm /* prepare file which captures stdout and stderr */ 1458095d1e54Sgilles (void)strlcpy(sfn, "/tmp/smtpd.out.XXXXXXXXXXX", sizeof(sfn)); 14591743d5a3Sjacekm allout = mkstemp(sfn); 1460c2d43ecaSderaadt if (allout == -1) { 1461095d1e54Sgilles (void)snprintf(ebuf, sizeof ebuf, "mkstemp: %s", strerror(errno)); 14621a5b831aSmartijn m_create(p_dispatcher, IMSG_MDA_DONE, 0, 0, -1); 14631a5b831aSmartijn m_add_id(p_dispatcher, id); 14641a5b831aSmartijn m_add_int(p_dispatcher, MDA_TEMPFAIL); 14651a5b831aSmartijn m_add_int(p_dispatcher, EX_OSERR); 14661a5b831aSmartijn m_add_string(p_dispatcher, ebuf); 14671a5b831aSmartijn m_close(p_dispatcher); 14683ef9cbf7Sgilles close(pipefd[0]); 14693ef9cbf7Sgilles close(pipefd[1]); 14701743d5a3Sjacekm return; 14711743d5a3Sjacekm } 14721743d5a3Sjacekm unlink(sfn); 14731743d5a3Sjacekm 14741743d5a3Sjacekm pid = fork(); 1475df69c215Sderaadt if (pid == -1) { 1476095d1e54Sgilles (void)snprintf(ebuf, sizeof ebuf, "fork: %s", strerror(errno)); 14771a5b831aSmartijn m_create(p_dispatcher, IMSG_MDA_DONE, 0, 0, -1); 14781a5b831aSmartijn m_add_id(p_dispatcher, id); 14791a5b831aSmartijn m_add_int(p_dispatcher, MDA_TEMPFAIL); 14801a5b831aSmartijn m_add_int(p_dispatcher, EX_OSERR); 14811a5b831aSmartijn m_add_string(p_dispatcher, ebuf); 14821a5b831aSmartijn m_close(p_dispatcher); 14831743d5a3Sjacekm close(pipefd[0]); 14841743d5a3Sjacekm close(pipefd[1]); 14851743d5a3Sjacekm close(allout); 14861743d5a3Sjacekm return; 14873ef9cbf7Sgilles } 14883ef9cbf7Sgilles 14891743d5a3Sjacekm /* parent passes the child fd over to mda */ 14901743d5a3Sjacekm if (pid > 0) { 14913a305da9Seric child = child_add(pid, CHILD_MDA, NULL); 14921743d5a3Sjacekm child->mda_out = allout; 14931743d5a3Sjacekm child->mda_id = id; 14941743d5a3Sjacekm close(pipefd[0]); 1495aa1d5973Seric m_create(p, IMSG_MDA_FORK, 0, 0, pipefd[1]); 149665c4fdfbSgilles m_add_id(p, id); 149765c4fdfbSgilles m_close(p); 14981743d5a3Sjacekm return; 14991743d5a3Sjacekm } 150068213f7bSgilles 150168213f7bSgilles /* mbox helper, create mailbox before privdrop if it doesn't exist */ 150268213f7bSgilles if (dsp->u.local.is_mbox) 150368213f7bSgilles mda_mbox_init(deliver); 150468213f7bSgilles 1505df69c215Sderaadt if (chdir(pw_dir) == -1 && chdir("/") == -1) 1506ff01b044Seric fatal("chdir"); 1507a8e22235Sgilles if (setgroups(1, &pw_gid) || 1508a8e22235Sgilles setresgid(pw_gid, pw_gid, pw_gid) || 1509a8e22235Sgilles setresuid(pw_uid, pw_uid, pw_uid)) 1510ff01b044Seric fatal("forkmda: cannot drop privileges"); 1511df69c215Sderaadt if (dup2(pipefd[0], STDIN_FILENO) == -1 || 1512df69c215Sderaadt dup2(allout, STDOUT_FILENO) == -1 || 1513df69c215Sderaadt dup2(allout, STDERR_FILENO) == -1) 1514ff01b044Seric fatal("forkmda: dup2"); 1515df69c215Sderaadt if (closefrom(STDERR_FILENO + 1) == -1) 1516ff01b044Seric fatal("closefrom"); 1517df69c215Sderaadt if (setsid() == -1) 1518ff01b044Seric fatal("setsid"); 15191743d5a3Sjacekm if (signal(SIGPIPE, SIG_DFL) == SIG_ERR || 15201743d5a3Sjacekm signal(SIGINT, SIG_DFL) == SIG_ERR || 15211743d5a3Sjacekm signal(SIGTERM, SIG_DFL) == SIG_ERR || 15221743d5a3Sjacekm signal(SIGCHLD, SIG_DFL) == SIG_ERR || 15231743d5a3Sjacekm signal(SIGHUP, SIG_DFL) == SIG_ERR) 1524ff01b044Seric fatal("signal"); 15253ef9cbf7Sgilles 15261743d5a3Sjacekm /* avoid hangs by setting 5m timeout */ 152711dbc40fSjacekm alarm(300); 152811dbc40fSjacekm 1529fea078e1Sgilles if (dsp->u.local.is_mbox && 1530fea078e1Sgilles dsp->u.local.mda_wrapper == NULL && 1531fea078e1Sgilles deliver->mda_exec[0] == '\0') 15322715edf2Sgilles mda_mbox(deliver); 15332715edf2Sgilles else 153401472467Sgilles mda_unpriv(dsp, deliver, pw_name, pw_dir); 15351743d5a3Sjacekm } 15363ef9cbf7Sgilles 153714f5466bSeric static void 153814f5466bSeric offline_scan(int fd, short ev, void *arg) 153914f5466bSeric { 15405f4bd98fSgilles char *path_argv[2]; 15415f4bd98fSgilles FTS *fts = arg; 15425f4bd98fSgilles FTSENT *e; 154314f5466bSeric int n = 0; 154414f5466bSeric 15455f4bd98fSgilles path_argv[0] = PATH_SPOOL PATH_OFFLINE; 15465f4bd98fSgilles path_argv[1] = NULL; 15475f4bd98fSgilles 15485f4bd98fSgilles if (fts == NULL) { 154982614934Seric log_debug("debug: smtpd: scanning offline queue..."); 15505f4bd98fSgilles fts = fts_open(path_argv, FTS_PHYSICAL | FTS_NOCHDIR, NULL); 15515f4bd98fSgilles if (fts == NULL) { 15525f4bd98fSgilles log_warn("fts_open: %s", path_argv[0]); 15535f4bd98fSgilles return; 15545f4bd98fSgilles } 155514f5466bSeric } 155614f5466bSeric 15575f4bd98fSgilles while ((e = fts_read(fts)) != NULL) { 15585f4bd98fSgilles if (e->fts_info != FTS_F) 155914f5466bSeric continue; 156014f5466bSeric 15615f4bd98fSgilles /* offline files must be at depth 1 */ 15625f4bd98fSgilles if (e->fts_level != 1) 15635f4bd98fSgilles continue; 15645f4bd98fSgilles 15655f4bd98fSgilles /* offline file group must match parent directory group */ 15665f4bd98fSgilles if (e->fts_statp->st_gid != e->fts_parent->fts_statp->st_gid) 15675f4bd98fSgilles continue; 15685f4bd98fSgilles 15695f4bd98fSgilles if (e->fts_statp->st_size == 0) { 15705f4bd98fSgilles if (unlink(e->fts_accpath) == -1) 15715f4bd98fSgilles log_warnx("warn: smtpd: could not unlink %s", e->fts_accpath); 15725f4bd98fSgilles continue; 15735f4bd98fSgilles } 15745f4bd98fSgilles 15750228dab0Smillert if (offline_add(e->fts_name, e->fts_statp->st_uid, 15760228dab0Smillert e->fts_statp->st_gid)) { 157765c4fdfbSgilles log_warnx("warn: smtpd: " 15785f4bd98fSgilles "could not add offline message %s", e->fts_name); 1579d44f36d1Seric continue; 1580d44f36d1Seric } 158114f5466bSeric 158214f5466bSeric if ((n++) == OFFLINE_READMAX) { 15835f4bd98fSgilles evtimer_set(&offline_ev, offline_scan, fts); 158414f5466bSeric offline_timeout.tv_sec = 0; 158514f5466bSeric offline_timeout.tv_usec = 100000; 158614f5466bSeric evtimer_add(&offline_ev, &offline_timeout); 158714f5466bSeric return; 158814f5466bSeric } 158914f5466bSeric } 159014f5466bSeric 159182614934Seric log_debug("debug: smtpd: offline scanning done"); 15925f4bd98fSgilles fts_close(fts); 159314f5466bSeric } 159414f5466bSeric 1595be925435Sgilles static int 15960228dab0Smillert offline_enqueue(char *name, uid_t uid, gid_t gid) 159725a5298cSjacekm { 159850e307aaSgilles char *path; 1599e5b07014Sgilles struct stat sb; 160025a5298cSjacekm pid_t pid; 1601d44f36d1Seric struct child *child; 160231805db2Sgilles struct passwd *pw; 160350e307aaSgilles int pathlen; 1604e5b07014Sgilles 160550e307aaSgilles pathlen = asprintf(&path, "%s/%s", PATH_SPOOL PATH_OFFLINE, name); 160650e307aaSgilles if (pathlen == -1) { 160750e307aaSgilles log_warnx("warn: smtpd: asprintf"); 1608d44f36d1Seric return (-1); 1609d44f36d1Seric } 1610e5b07014Sgilles 161150e307aaSgilles if (pathlen >= PATH_MAX) { 161250e307aaSgilles log_warnx("warn: smtpd: pathname exceeds PATH_MAX"); 161350e307aaSgilles free(path); 1614d44f36d1Seric return (-1); 1615d44f36d1Seric } 161614f5466bSeric 161782614934Seric log_debug("debug: smtpd: enqueueing offline message %s", path); 1618e5b07014Sgilles 1619d44f36d1Seric if ((pid = fork()) == -1) { 162082614934Seric log_warn("warn: smtpd: fork"); 1621d44f36d1Seric free(path); 1622d44f36d1Seric return (-1); 1623e5b07014Sgilles } 1624e5b07014Sgilles 1625e5b07014Sgilles if (pid == 0) { 16260882b00eSsunil char *envp[2], *p = NULL, *tmp; 1627cb6e8661Sgilles int fd; 1628e5b07014Sgilles FILE *fp; 16290882b00eSsunil size_t sz = 0; 16300882b00eSsunil ssize_t len; 163125a5298cSjacekm arglist args; 163225a5298cSjacekm 1633cb6e8661Sgilles if (closefrom(STDERR_FILENO + 1) == -1) 1634cb6e8661Sgilles _exit(1); 1635cb6e8661Sgilles 1636c1392a69Seric memset(&args, 0, sizeof(args)); 163725a5298cSjacekm 1638cb6e8661Sgilles if ((fd = open(path, O_RDONLY|O_NOFOLLOW|O_NONBLOCK)) == -1) { 1639cb6e8661Sgilles log_warn("warn: smtpd: open: %s", path); 164025a5298cSjacekm _exit(1); 16410beae34bSjacekm } 1642e5b07014Sgilles 1643cb6e8661Sgilles if (fstat(fd, &sb) == -1) { 1644cb6e8661Sgilles log_warn("warn: smtpd: fstat: %s", path); 1645cb6e8661Sgilles _exit(1); 1646cb6e8661Sgilles } 1647cb6e8661Sgilles 1648cb6e8661Sgilles if (!S_ISREG(sb.st_mode)) { 1649cb6e8661Sgilles log_warnx("warn: smtpd: file %s (uid %d) not regular", 1650cb6e8661Sgilles path, sb.st_uid); 1651cb6e8661Sgilles _exit(1); 1652cb6e8661Sgilles } 1653cb6e8661Sgilles 1654cb6e8661Sgilles if (sb.st_nlink != 1) { 1655cb6e8661Sgilles log_warnx("warn: smtpd: file %s is hard-link", path); 1656e5b07014Sgilles _exit(1); 1657e5b07014Sgilles } 1658d44f36d1Seric 16590228dab0Smillert if (sb.st_uid != uid) { 16600228dab0Smillert log_warnx("warn: smtpd: file %s has bad uid %d", 16610228dab0Smillert path, sb.st_uid); 16620228dab0Smillert _exit(1); 16630228dab0Smillert } 16640228dab0Smillert 16650228dab0Smillert if (sb.st_gid != gid) { 16660228dab0Smillert log_warnx("warn: smtpd: file %s has bad gid %d", 16670228dab0Smillert path, sb.st_gid); 16680228dab0Smillert _exit(1); 16690228dab0Smillert } 16700228dab0Smillert 167131805db2Sgilles pw = getpwuid(sb.st_uid); 167231805db2Sgilles if (pw == NULL) { 167382614934Seric log_warnx("warn: smtpd: getpwuid for uid %d failed", 1674d44f36d1Seric sb.st_uid); 1675d44f36d1Seric _exit(1); 1676d44f36d1Seric } 1677d44f36d1Seric 167831805db2Sgilles if (setgroups(1, &pw->pw_gid) || 167931805db2Sgilles setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || 1680cb6e8661Sgilles setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) 1681d44f36d1Seric _exit(1); 1682d44f36d1Seric 1683cb6e8661Sgilles if ((fp = fdopen(fd, "r")) == NULL) 1684d44f36d1Seric _exit(1); 1685e5b07014Sgilles 168631805db2Sgilles if (chdir(pw->pw_dir) == -1 && chdir("/") == -1) 1687e5b07014Sgilles _exit(1); 1688e5b07014Sgilles 1689e5b07014Sgilles if (setsid() == -1 || 1690e5b07014Sgilles signal(SIGPIPE, SIG_DFL) == SIG_ERR || 1691e5b07014Sgilles dup2(fileno(fp), STDIN_FILENO) == -1) 1692e5b07014Sgilles _exit(1); 1693e5b07014Sgilles 16940882b00eSsunil if ((len = getline(&p, &sz, fp)) == -1) 1695e5b07014Sgilles _exit(1); 1696e5b07014Sgilles 1697e5b07014Sgilles if (p[len - 1] != '\n') 1698e5b07014Sgilles _exit(1); 1699e5b07014Sgilles p[len - 1] = '\0'; 1700e5b07014Sgilles 1701e5b07014Sgilles addargs(&args, "%s", "sendmail"); 17022a157de5Sgilles addargs(&args, "%s", "-S"); 1703e5b07014Sgilles 1704e5b07014Sgilles while ((tmp = strsep(&p, "|")) != NULL) 1705e5b07014Sgilles addargs(&args, "%s", tmp); 1706e5b07014Sgilles 17070882b00eSsunil free(p); 1708e5b07014Sgilles if (lseek(fileno(fp), len, SEEK_SET) == -1) 1709e5b07014Sgilles _exit(1); 1710e5b07014Sgilles 171125a5298cSjacekm envp[0] = "PATH=" _PATH_DEFPATH; 171225a5298cSjacekm envp[1] = (char *)NULL; 171325a5298cSjacekm environ = envp; 1714e5b07014Sgilles 1715fb2f4678Sjacekm execvp(PATH_SMTPCTL, args.list); 171625a5298cSjacekm _exit(1); 171725a5298cSjacekm } 171825a5298cSjacekm 171914f5466bSeric offline_running++; 17203a305da9Seric child = child_add(pid, CHILD_ENQUEUE_OFFLINE, NULL); 1721d44f36d1Seric child->path = path; 1722e5b07014Sgilles 1723d44f36d1Seric return (0); 1724e5b07014Sgilles } 1725e5b07014Sgilles 1726be925435Sgilles static int 17270228dab0Smillert offline_add(char *path, uid_t uid, gid_t gid) 17289a5146a5Seric { 172914f5466bSeric struct offline *q; 17309a5146a5Seric 173114f5466bSeric if (offline_running < OFFLINE_QUEUEMAX) 17329a5146a5Seric /* skip queue */ 17330228dab0Smillert return offline_enqueue(path, uid, gid); 17349a5146a5Seric 17359a5146a5Seric q = malloc(sizeof(*q) + strlen(path) + 1); 17369a5146a5Seric if (q == NULL) 17379a5146a5Seric return (-1); 17380228dab0Smillert q->uid = uid; 17390228dab0Smillert q->gid = gid; 17409a5146a5Seric q->path = (char *)q + sizeof(*q); 17419a5146a5Seric memmove(q->path, path, strlen(path) + 1); 174214f5466bSeric TAILQ_INSERT_TAIL(&offline_q, q, entry); 17439a5146a5Seric 1744d44f36d1Seric return (0); 17459a5146a5Seric } 17469a5146a5Seric 17479a5146a5Seric static void 174814f5466bSeric offline_done(void) 17499a5146a5Seric { 175014f5466bSeric struct offline *q; 17519a5146a5Seric 175214f5466bSeric offline_running--; 17539a5146a5Seric 175414f5466bSeric while (offline_running < OFFLINE_QUEUEMAX) { 175514f5466bSeric if ((q = TAILQ_FIRST(&offline_q)) == NULL) 17569a5146a5Seric break; /* all done */ 175714f5466bSeric TAILQ_REMOVE(&offline_q, q, entry); 17580228dab0Smillert offline_enqueue(q->path, q->uid, q->gid); 17599a5146a5Seric free(q); 17609a5146a5Seric } 17619a5146a5Seric } 17629a5146a5Seric 17639a5146a5Seric static int 176465c4fdfbSgilles parent_forward_open(char *username, char *directory, uid_t uid, gid_t gid) 17657e2a8baeSgilles { 1766953aae25Sderaadt char pathname[PATH_MAX]; 17677e2a8baeSgilles int fd; 1768cc138232Seric struct stat sb; 17697e2a8baeSgilles 17707e8a7d6bSeric if (!bsnprintf(pathname, sizeof (pathname), "%s/.forward", 1771f2a89170Ssunil directory)) { 1772f2a89170Ssunil log_warnx("warn: smtpd: %s: pathname too large", pathname); 1773f2a89170Ssunil return -1; 1774f2a89170Ssunil } 17757e2a8baeSgilles 1776df69c215Sderaadt if (stat(directory, &sb) == -1) { 1777cc138232Seric log_warn("warn: smtpd: parent_forward_open: %s", directory); 1778cc138232Seric return -1; 1779cc138232Seric } 1780cc138232Seric if (sb.st_mode & S_ISVTX) { 1781cc138232Seric log_warnx("warn: smtpd: parent_forward_open: %s is sticky", 1782cc138232Seric directory); 1783cc138232Seric errno = EAGAIN; 1784cc138232Seric return -1; 1785cc138232Seric } 1786cc138232Seric 178765c4fdfbSgilles do { 1788cb6e8661Sgilles fd = open(pathname, O_RDONLY|O_NOFOLLOW|O_NONBLOCK); 178965c4fdfbSgilles } while (fd == -1 && errno == EINTR); 17907e2a8baeSgilles if (fd == -1) { 17917e2a8baeSgilles if (errno == ENOENT) 179265c4fdfbSgilles return -1; 179365c4fdfbSgilles if (errno == EMFILE || errno == ENFILE || errno == EIO) { 179465c4fdfbSgilles errno = EAGAIN; 179565c4fdfbSgilles return -1; 179665c4fdfbSgilles } 1797cb6e8661Sgilles if (errno == ELOOP) 1798cb6e8661Sgilles log_warnx("warn: smtpd: parent_forward_open: %s: " 1799cb6e8661Sgilles "cannot follow symbolic links", pathname); 1800cb6e8661Sgilles else 180182614934Seric log_warn("warn: smtpd: parent_forward_open: %s", pathname); 18027e2a8baeSgilles return -1; 18037e2a8baeSgilles } 18047e2a8baeSgilles 18051c8fd86dSgilles if (!secure_file(fd, pathname, directory, uid, 1)) { 1806ad8d242dSop log_warnx("warn: smtpd: %s: insecure file", pathname); 1807d0e4aaa3Sjacekm close(fd); 1808d0e4aaa3Sjacekm return -1; 1809d0e4aaa3Sjacekm } 18107e2a8baeSgilles 18117e2a8baeSgilles return fd; 18127e2a8baeSgilles } 18137e2a8baeSgilles 1814ed1929b6Sjacekm void 181565c4fdfbSgilles imsg_dispatch(struct mproc *p, struct imsg *imsg) 1816ed1929b6Sjacekm { 181721312c12Sgilles struct timespec t0, t1, dt; 1818cbc009c1Seric int msg; 1819ed1929b6Sjacekm 182065c4fdfbSgilles if (imsg == NULL) { 182143962b9cSeric imsg_callback(p, imsg); 1822ed1929b6Sjacekm return; 1823ed1929b6Sjacekm } 1824ed1929b6Sjacekm 182565c4fdfbSgilles log_imsg(smtpd_process, p->proc, imsg); 1826ed1929b6Sjacekm 182765c4fdfbSgilles if (profiling & PROFILE_IMSG) 182821312c12Sgilles clock_gettime(CLOCK_MONOTONIC, &t0); 182921312c12Sgilles 1830cbc009c1Seric msg = imsg->hdr.type; 183165c4fdfbSgilles imsg_callback(p, imsg); 183221312c12Sgilles 183365c4fdfbSgilles if (profiling & PROFILE_IMSG) { 183421312c12Sgilles clock_gettime(CLOCK_MONOTONIC, &t1); 183521312c12Sgilles timespecsub(&t1, &t0, &dt); 183621312c12Sgilles 1837e267740eSeric log_debug("profile-imsg: %s %s %s %d %lld.%09ld", 183865c4fdfbSgilles proc_name(smtpd_process), 183965c4fdfbSgilles proc_name(p->proc), 1840cbc009c1Seric imsg_to_str(msg), 184165c4fdfbSgilles (int)imsg->hdr.len, 1842e267740eSeric (long long)dt.tv_sec, 1843e267740eSeric dt.tv_nsec); 184421312c12Sgilles 184565c4fdfbSgilles if (profiling & PROFILE_TOSTAT) { 184621312c12Sgilles char key[STAT_KEY_SIZE]; 184721312c12Sgilles /* can't profstat control process yet */ 184821312c12Sgilles if (smtpd_process == PROC_CONTROL) 184921312c12Sgilles return; 185021312c12Sgilles 185121312c12Sgilles if (!bsnprintf(key, sizeof key, 185221312c12Sgilles "profiling.imsg.%s.%s.%s", 185365c4fdfbSgilles proc_name(smtpd_process), 185465c4fdfbSgilles proc_name(p->proc), 1855cbc009c1Seric imsg_to_str(msg))) 18561e7a2462Sgilles return; 185721312c12Sgilles stat_set(key, stat_timespec(&dt)); 185821312c12Sgilles } 185921312c12Sgilles } 1860ed1929b6Sjacekm } 1861ed1929b6Sjacekm 1862576d0b55Sreyk void 1863b041f822Seric log_imsg(int to, int from, struct imsg *imsg) 1864b041f822Seric { 186565c4fdfbSgilles 1866cc81b7c6Seric if (to == PROC_CONTROL && imsg->hdr.type == IMSG_STAT_SET) 186765c4fdfbSgilles return; 186865c4fdfbSgilles 1869bfe8e0bcSeric log_trace(TRACE_IMSG, "imsg: %s <- %s: %s (len=%zu)", 187065c4fdfbSgilles proc_name(to), 187165c4fdfbSgilles proc_name(from), 1872b041f822Seric imsg_to_str(imsg->hdr.type), 1873b041f822Seric imsg->hdr.len - IMSG_HEADER_SIZE); 1874b041f822Seric } 1875b041f822Seric 1876b041f822Seric const char * 187765c4fdfbSgilles proc_title(enum smtp_proc_type proc) 1878b041f822Seric { 1879b041f822Seric switch (proc) { 188065c4fdfbSgilles case PROC_PARENT: 188165c4fdfbSgilles return "[priv]"; 1882299c4efeSeric case PROC_LKA: 1883299c4efeSeric return "lookup"; 1884299c4efeSeric case PROC_QUEUE: 1885299c4efeSeric return "queue"; 1886299c4efeSeric case PROC_CONTROL: 1887299c4efeSeric return "control"; 1888299c4efeSeric case PROC_SCHEDULER: 1889299c4efeSeric return "scheduler"; 18901a5b831aSmartijn case PROC_DISPATCHER: 18911a5b831aSmartijn return "dispatcher"; 189276eb97c5Sreyk case PROC_CA: 18931a5b831aSmartijn return "crypto"; 1894fb531d31Santon case PROC_CLIENT: 1895fb531d31Santon return "client"; 1896a93e09f5Sgilles case PROC_PROCESSOR: 1897a93e09f5Sgilles return "processor"; 1898b041f822Seric } 1899fb531d31Santon return "unknown"; 1900b041f822Seric } 1901b041f822Seric 1902b041f822Seric const char * 190365c4fdfbSgilles proc_name(enum smtp_proc_type proc) 190465c4fdfbSgilles { 190565c4fdfbSgilles switch (proc) { 190665c4fdfbSgilles case PROC_PARENT: 190765c4fdfbSgilles return "parent"; 190865c4fdfbSgilles case PROC_LKA: 190965c4fdfbSgilles return "lka"; 191065c4fdfbSgilles case PROC_QUEUE: 191165c4fdfbSgilles return "queue"; 191265c4fdfbSgilles case PROC_CONTROL: 191365c4fdfbSgilles return "control"; 191465c4fdfbSgilles case PROC_SCHEDULER: 191565c4fdfbSgilles return "scheduler"; 19161a5b831aSmartijn case PROC_DISPATCHER: 19171a5b831aSmartijn return "dispatcher"; 191876eb97c5Sreyk case PROC_CA: 191976eb97c5Sreyk return "ca"; 1920299c4efeSeric case PROC_CLIENT: 1921299c4efeSeric return "client-proc"; 192265c4fdfbSgilles default: 192365c4fdfbSgilles return "unknown"; 192465c4fdfbSgilles } 192565c4fdfbSgilles } 192665c4fdfbSgilles 192765c4fdfbSgilles #define CASE(x) case x : return #x 192865c4fdfbSgilles 192965c4fdfbSgilles const char * 1930b041f822Seric imsg_to_str(int type) 1931b041f822Seric { 19326d095224Schl static char buf[32]; 19336d095224Schl 1934b041f822Seric switch (type) { 1935b041f822Seric CASE(IMSG_NONE); 1936aa1d5973Seric 1937b041f822Seric CASE(IMSG_CTL_OK); 1938b041f822Seric CASE(IMSG_CTL_FAIL); 1939aa1d5973Seric 1940aa1d5973Seric CASE(IMSG_CTL_GET_DIGEST); 1941aa1d5973Seric CASE(IMSG_CTL_GET_STATS); 194265c4fdfbSgilles CASE(IMSG_CTL_LIST_MESSAGES); 194365c4fdfbSgilles CASE(IMSG_CTL_LIST_ENVELOPES); 1944f9a337f4Seric CASE(IMSG_CTL_MTA_SHOW_HOSTS); 1945f9a337f4Seric CASE(IMSG_CTL_MTA_SHOW_RELAYS); 1946c5acbec8Seric CASE(IMSG_CTL_MTA_SHOW_ROUTES); 1947c5acbec8Seric CASE(IMSG_CTL_MTA_SHOW_HOSTSTATS); 19485b6a9ce9Seric CASE(IMSG_CTL_MTA_BLOCK); 19495b6a9ce9Seric CASE(IMSG_CTL_MTA_UNBLOCK); 19505b6a9ce9Seric CASE(IMSG_CTL_MTA_SHOW_BLOCK); 1951aa1d5973Seric CASE(IMSG_CTL_PAUSE_EVP); 1952aa1d5973Seric CASE(IMSG_CTL_PAUSE_MDA); 1953aa1d5973Seric CASE(IMSG_CTL_PAUSE_MTA); 1954aa1d5973Seric CASE(IMSG_CTL_PAUSE_SMTP); 1955aa1d5973Seric CASE(IMSG_CTL_PROFILE); 1956aa1d5973Seric CASE(IMSG_CTL_PROFILE_DISABLE); 1957aa1d5973Seric CASE(IMSG_CTL_PROFILE_ENABLE); 1958aa1d5973Seric CASE(IMSG_CTL_RESUME_EVP); 1959aa1d5973Seric CASE(IMSG_CTL_RESUME_MDA); 1960aa1d5973Seric CASE(IMSG_CTL_RESUME_MTA); 1961aa1d5973Seric CASE(IMSG_CTL_RESUME_SMTP); 1962aa1d5973Seric CASE(IMSG_CTL_RESUME_ROUTE); 1963aa1d5973Seric CASE(IMSG_CTL_REMOVE); 1964aa1d5973Seric CASE(IMSG_CTL_SCHEDULE); 1965aa1d5973Seric CASE(IMSG_CTL_SHOW_STATUS); 1966aa1d5973Seric CASE(IMSG_CTL_TRACE_DISABLE); 1967aa1d5973Seric CASE(IMSG_CTL_TRACE_ENABLE); 1968aa1d5973Seric CASE(IMSG_CTL_UPDATE_TABLE); 1969aa1d5973Seric CASE(IMSG_CTL_VERBOSE); 1970a9835440Ssunil CASE(IMSG_CTL_DISCOVER_EVPID); 1971a9835440Ssunil CASE(IMSG_CTL_DISCOVER_MSGID); 1972aa1d5973Seric 1973aa1d5973Seric CASE(IMSG_CTL_SMTP_SESSION); 1974c5acbec8Seric 197501eba458Seric CASE(IMSG_GETADDRINFO); 197601eba458Seric CASE(IMSG_GETADDRINFO_END); 197701eba458Seric CASE(IMSG_GETNAMEINFO); 197801361951Seric CASE(IMSG_RES_QUERY); 197901eba458Seric 1980b88ab68dSeric CASE(IMSG_SETUP_KEY); 1981b88ab68dSeric CASE(IMSG_SETUP_PEER); 1982b88ab68dSeric CASE(IMSG_SETUP_DONE); 1983b88ab68dSeric 1984b041f822Seric CASE(IMSG_CONF_START); 1985b041f822Seric CASE(IMSG_CONF_END); 1986caba6926Seric 1987e16a1b99Schl CASE(IMSG_STAT_INCREMENT); 1988e16a1b99Schl CASE(IMSG_STAT_DECREMENT); 1989e16a1b99Schl CASE(IMSG_STAT_SET); 1990e16a1b99Schl 1991aa1d5973Seric CASE(IMSG_LKA_AUTHENTICATE); 1992aa1d5973Seric CASE(IMSG_LKA_OPEN_FORWARD); 1993aa1d5973Seric CASE(IMSG_LKA_ENVELOPE_SUBMIT); 1994aa1d5973Seric CASE(IMSG_LKA_ENVELOPE_COMMIT); 199565c4fdfbSgilles 1996aa1d5973Seric CASE(IMSG_QUEUE_DELIVER); 1997aa1d5973Seric CASE(IMSG_QUEUE_DELIVERY_OK); 1998aa1d5973Seric CASE(IMSG_QUEUE_DELIVERY_TEMPFAIL); 1999aa1d5973Seric CASE(IMSG_QUEUE_DELIVERY_PERMFAIL); 2000aa1d5973Seric CASE(IMSG_QUEUE_DELIVERY_LOOP); 2001a9835440Ssunil CASE(IMSG_QUEUE_DISCOVER_EVPID); 2002a9835440Ssunil CASE(IMSG_QUEUE_DISCOVER_MSGID); 2003acfdf0daSeric CASE(IMSG_QUEUE_ENVELOPE_ACK); 2004aa1d5973Seric CASE(IMSG_QUEUE_ENVELOPE_COMMIT); 2005aa1d5973Seric CASE(IMSG_QUEUE_ENVELOPE_REMOVE); 2006aa1d5973Seric CASE(IMSG_QUEUE_ENVELOPE_SCHEDULE); 2007aa1d5973Seric CASE(IMSG_QUEUE_ENVELOPE_SUBMIT); 2008aa1d5973Seric CASE(IMSG_QUEUE_HOLDQ_HOLD); 2009aa1d5973Seric CASE(IMSG_QUEUE_HOLDQ_RELEASE); 2010aa1d5973Seric CASE(IMSG_QUEUE_MESSAGE_COMMIT); 2011aa1d5973Seric CASE(IMSG_QUEUE_MESSAGE_ROLLBACK); 2012aa1d5973Seric CASE(IMSG_QUEUE_SMTP_SESSION); 2013aa1d5973Seric CASE(IMSG_QUEUE_TRANSFER); 2014aa1d5973Seric 2015aa1d5973Seric CASE(IMSG_MDA_DELIVERY_OK); 2016aa1d5973Seric CASE(IMSG_MDA_DELIVERY_TEMPFAIL); 2017aa1d5973Seric CASE(IMSG_MDA_DELIVERY_PERMFAIL); 2018aa1d5973Seric CASE(IMSG_MDA_DELIVERY_LOOP); 2019aa1d5973Seric CASE(IMSG_MDA_DELIVERY_HOLD); 2020aa1d5973Seric CASE(IMSG_MDA_DONE); 2021aa1d5973Seric CASE(IMSG_MDA_FORK); 2022aa1d5973Seric CASE(IMSG_MDA_HOLDQ_RELEASE); 2023aa1d5973Seric CASE(IMSG_MDA_LOOKUP_USERINFO); 2024aa1d5973Seric CASE(IMSG_MDA_KILL); 2025aa1d5973Seric CASE(IMSG_MDA_OPEN_MESSAGE); 2026aa1d5973Seric 2027aa1d5973Seric CASE(IMSG_MTA_DELIVERY_OK); 2028aa1d5973Seric CASE(IMSG_MTA_DELIVERY_TEMPFAIL); 2029aa1d5973Seric CASE(IMSG_MTA_DELIVERY_PERMFAIL); 2030aa1d5973Seric CASE(IMSG_MTA_DELIVERY_LOOP); 2031aa1d5973Seric CASE(IMSG_MTA_DELIVERY_HOLD); 2032aa1d5973Seric CASE(IMSG_MTA_DNS_HOST); 2033aa1d5973Seric CASE(IMSG_MTA_DNS_HOST_END); 2034aa1d5973Seric CASE(IMSG_MTA_DNS_MX); 2035aa1d5973Seric CASE(IMSG_MTA_DNS_MX_PREFERENCE); 2036aa1d5973Seric CASE(IMSG_MTA_HOLDQ_RELEASE); 2037aa1d5973Seric CASE(IMSG_MTA_LOOKUP_CREDENTIALS); 2038aa1d5973Seric CASE(IMSG_MTA_LOOKUP_SOURCE); 2039aa1d5973Seric CASE(IMSG_MTA_LOOKUP_HELO); 2040a8e22235Sgilles CASE(IMSG_MTA_LOOKUP_SMARTHOST); 2041aa1d5973Seric CASE(IMSG_MTA_OPEN_MESSAGE); 2042aa1d5973Seric CASE(IMSG_MTA_SCHEDULE); 2043aa1d5973Seric 2044aa1d5973Seric CASE(IMSG_SCHED_ENVELOPE_BOUNCE); 2045aa1d5973Seric CASE(IMSG_SCHED_ENVELOPE_DELIVER); 2046aa1d5973Seric CASE(IMSG_SCHED_ENVELOPE_EXPIRE); 2047aa1d5973Seric CASE(IMSG_SCHED_ENVELOPE_INJECT); 2048aa1d5973Seric CASE(IMSG_SCHED_ENVELOPE_REMOVE); 2049aa1d5973Seric CASE(IMSG_SCHED_ENVELOPE_TRANSFER); 2050aa1d5973Seric 2051aa1d5973Seric CASE(IMSG_SMTP_AUTHENTICATE); 2052aa1d5973Seric CASE(IMSG_SMTP_MESSAGE_COMMIT); 2053aa1d5973Seric CASE(IMSG_SMTP_MESSAGE_CREATE); 2054aa1d5973Seric CASE(IMSG_SMTP_MESSAGE_ROLLBACK); 2055aa1d5973Seric CASE(IMSG_SMTP_MESSAGE_OPEN); 205606405042Ssunil CASE(IMSG_SMTP_CHECK_SENDER); 2057aa1d5973Seric CASE(IMSG_SMTP_EXPAND_RCPT); 2058aa1d5973Seric CASE(IMSG_SMTP_LOOKUP_HELO); 2059aa1d5973Seric 2060aa1d5973Seric CASE(IMSG_SMTP_REQ_CONNECT); 2061aa1d5973Seric CASE(IMSG_SMTP_REQ_HELO); 2062aa1d5973Seric CASE(IMSG_SMTP_REQ_MAIL); 2063aa1d5973Seric CASE(IMSG_SMTP_REQ_RCPT); 2064aa1d5973Seric CASE(IMSG_SMTP_REQ_DATA); 2065aa1d5973Seric CASE(IMSG_SMTP_REQ_EOM); 2066aa1d5973Seric CASE(IMSG_SMTP_EVENT_RSET); 2067aa1d5973Seric CASE(IMSG_SMTP_EVENT_COMMIT); 2068aa1d5973Seric CASE(IMSG_SMTP_EVENT_ROLLBACK); 2069aa1d5973Seric CASE(IMSG_SMTP_EVENT_DISCONNECT); 2070c4df3bf2Sreyk 2071a93e09f5Sgilles CASE(IMSG_LKA_PROCESSOR_FORK); 2072dbc3cea0Smartijn CASE(IMSG_LKA_PROCESSOR_ERRFD); 2073a93e09f5Sgilles 2074491f85b0Sgilles CASE(IMSG_REPORT_SMTP_LINK_CONNECT); 2075491f85b0Sgilles CASE(IMSG_REPORT_SMTP_LINK_DISCONNECT); 20762045dc71Sgilles CASE(IMSG_REPORT_SMTP_LINK_GREETING); 20772045dc71Sgilles CASE(IMSG_REPORT_SMTP_LINK_IDENTIFY); 207805eca6a7Sop CASE(IMSG_REPORT_SMTP_LINK_TLS); 20792045dc71Sgilles CASE(IMSG_REPORT_SMTP_LINK_AUTH); 20803ec0e812Sgilles CASE(IMSG_REPORT_SMTP_TX_RESET); 2081491f85b0Sgilles CASE(IMSG_REPORT_SMTP_TX_BEGIN); 208205eca6a7Sop CASE(IMSG_REPORT_SMTP_TX_MAIL); 208305eca6a7Sop CASE(IMSG_REPORT_SMTP_TX_RCPT); 2084491f85b0Sgilles CASE(IMSG_REPORT_SMTP_TX_ENVELOPE); 208505eca6a7Sop CASE(IMSG_REPORT_SMTP_TX_DATA); 2086491f85b0Sgilles CASE(IMSG_REPORT_SMTP_TX_COMMIT); 2087491f85b0Sgilles CASE(IMSG_REPORT_SMTP_TX_ROLLBACK); 2088491f85b0Sgilles CASE(IMSG_REPORT_SMTP_PROTOCOL_CLIENT); 2089491f85b0Sgilles CASE(IMSG_REPORT_SMTP_PROTOCOL_SERVER); 209005eca6a7Sop CASE(IMSG_REPORT_SMTP_FILTER_RESPONSE); 209105eca6a7Sop CASE(IMSG_REPORT_SMTP_TIMEOUT); 2092a055df58Seric 20932b1a70ccSgilles CASE(IMSG_FILTER_SMTP_BEGIN); 20942b1a70ccSgilles CASE(IMSG_FILTER_SMTP_END); 20952b1a70ccSgilles CASE(IMSG_FILTER_SMTP_PROTOCOL); 20962b1a70ccSgilles CASE(IMSG_FILTER_SMTP_DATA_BEGIN); 20972b1a70ccSgilles CASE(IMSG_FILTER_SMTP_DATA_END); 2098a055df58Seric 209941b8cf0bSmillert CASE(IMSG_CA_RSA_PRIVENC); 210041b8cf0bSmillert CASE(IMSG_CA_RSA_PRIVDEC); 210141b8cf0bSmillert CASE(IMSG_CA_ECDSA_SIGN); 210205eca6a7Sop 2103b041f822Seric default: 2104095d1e54Sgilles (void)snprintf(buf, sizeof(buf), "IMSG_??? (%d)", type); 21056d095224Schl 21066d095224Schl return buf; 2107b041f822Seric } 2108b041f822Seric } 210965c4fdfbSgilles 211065c4fdfbSgilles int 211165c4fdfbSgilles parent_auth_user(const char *username, const char *password) 211265c4fdfbSgilles { 2113953aae25Sderaadt char user[LOGIN_NAME_MAX]; 2114953aae25Sderaadt char pass[LINE_MAX]; 211565c4fdfbSgilles int ret; 211665c4fdfbSgilles 2117095d1e54Sgilles (void)strlcpy(user, username, sizeof(user)); 2118095d1e54Sgilles (void)strlcpy(pass, password, sizeof(pass)); 211965c4fdfbSgilles 212065c4fdfbSgilles ret = auth_userokay(user, NULL, "auth-smtp", pass); 212165c4fdfbSgilles if (ret) 212265c4fdfbSgilles return LKA_OK; 212365c4fdfbSgilles return LKA_PERMFAIL; 212465c4fdfbSgilles } 2125