pid_t ca(void) { pid_t pid; struct passwd *pw; struct event ev_sigint; struct event ev_sigterm; switch (pid = fork()) { case -1: fatal("ca: cannot fork"); case 0: post_fork(PROC_CA); break; default: return (pid); } purge_config(PURGE_LISTENERS|PURGE_TABLES|PURGE_RULES); if ((pw = getpwnam(SMTPD_USER)) == NULL) fatalx("unknown user " SMTPD_USER); if (chroot(PATH_CHROOT) == -1) fatal("ca: chroot"); if (chdir("/") == -1) fatal("ca: chdir(\"/\")"); config_process(PROC_CA); if (setgroups(1, &pw->pw_gid) || setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) fatal("ca: cannot drop privileges"); imsg_callback = ca_imsg; event_init(); signal_set(&ev_sigint, SIGINT, ca_sig_handler, NULL); signal_set(&ev_sigterm, SIGTERM, ca_sig_handler, NULL); signal_add(&ev_sigint, NULL); signal_add(&ev_sigterm, NULL); signal(SIGPIPE, SIG_IGN); signal(SIGHUP, SIG_IGN); config_peer(PROC_CONTROL); config_peer(PROC_PARENT); config_peer(PROC_PONY); config_done(); /* Ignore them until we get our config */ mproc_disable(p_pony); if (event_dispatch() < 0) fatal("event_dispatch"); ca_shutdown(); return (0); }
int control(void) { struct passwd *pw; purge_config(PURGE_EVERYTHING); if ((pw = getpwnam(SMTPD_USER)) == NULL) fatalx("unknown user " SMTPD_USER); stat_backend = env->sc_stat; stat_backend->init(); if (chroot(PATH_CHROOT) == -1) fatal("control: chroot"); if (chdir("/") == -1) fatal("control: chdir(\"/\")"); config_process(PROC_CONTROL); if (setgroups(1, &pw->pw_gid) || setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) fatal("control: cannot drop privileges"); imsg_callback = control_imsg; event_init(); signal(SIGINT, SIG_IGN); signal(SIGTERM, SIG_IGN); signal(SIGPIPE, SIG_IGN); signal(SIGHUP, SIG_IGN); tree_init(&ctl_conns); tree_init(&ctl_count); memset(&digest, 0, sizeof digest); digest.startup = time(NULL); config_peer(PROC_SCHEDULER); config_peer(PROC_QUEUE); config_peer(PROC_PARENT); config_peer(PROC_LKA); config_peer(PROC_PONY); config_peer(PROC_CA); control_listen(); if (pledge("stdio unix recvfd sendfd", NULL) == -1) err(1, "pledge"); event_dispatch(); fatalx("exited event loop"); return (0); }
pid_t queue(void) { pid_t pid; struct passwd *pw; struct timeval tv; struct event ev_qload; struct event ev_sigint; struct event ev_sigterm; switch (pid = fork()) { case -1: fatal("queue: cannot fork"); case 0: post_fork(PROC_QUEUE); break; default: return (pid); } purge_config(PURGE_EVERYTHING); if ((pw = getpwnam(SMTPD_QUEUE_USER)) == NULL) if ((pw = getpwnam(SMTPD_USER)) == NULL) fatalx("unknown user " SMTPD_USER); env->sc_queue_flags |= QUEUE_EVPCACHE; env->sc_queue_evpcache_size = 1024; if (chroot(PATH_SPOOL) == -1) fatal("queue: chroot"); if (chdir("/") == -1) fatal("queue: chdir(\"/\")"); config_process(PROC_QUEUE); if (env->sc_queue_flags & QUEUE_COMPRESSION) log_info("queue: queue compression enabled"); if (env->sc_queue_key) { if (! crypto_setup(env->sc_queue_key, strlen(env->sc_queue_key))) fatalx("crypto_setup: invalid key for queue encryption"); log_info("queue: queue encryption enabled"); } if (setgroups(1, &pw->pw_gid) || setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) fatal("queue: cannot drop privileges"); imsg_callback = queue_imsg; event_init(); signal_set(&ev_sigint, SIGINT, queue_sig_handler, NULL); signal_set(&ev_sigterm, SIGTERM, queue_sig_handler, NULL); signal_add(&ev_sigint, NULL); signal_add(&ev_sigterm, NULL); signal(SIGPIPE, SIG_IGN); signal(SIGHUP, SIG_IGN); config_peer(PROC_PARENT); config_peer(PROC_CONTROL); config_peer(PROC_LKA); config_peer(PROC_SCHEDULER); config_peer(PROC_PONY); config_done(); /* setup queue loading task */ evtimer_set(&ev_qload, queue_timeout, &ev_qload); tv.tv_sec = 0; tv.tv_usec = 10; evtimer_add(&ev_qload, &tv); if (event_dispatch() < 0) fatal("event_dispatch"); queue_shutdown(); return (0); }
pid_t mta(void) { pid_t pid; struct passwd *pw; struct event ev_sigint; struct event ev_sigterm; struct peer peers[] = { { PROC_PARENT, imsg_dispatch }, { PROC_QUEUE, imsg_dispatch }, { PROC_LKA, imsg_dispatch }, { PROC_CONTROL, imsg_dispatch } }; switch (pid = fork()) { case -1: fatal("mta: cannot fork"); case 0: break; default: return (pid); } purge_config(PURGE_EVERYTHING); pw = env->sc_pw; if (chroot(pw->pw_dir) == -1) fatal("mta: chroot"); if (chdir("/") == -1) fatal("mta: chdir(\"/\")"); smtpd_process = PROC_MTA; log_debug("start %s",env->sc_title[smtpd_process]); setproctitle("%s", env->sc_title[smtpd_process]); if (setgroups(1, &pw->pw_gid) || setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) fatal("mta: cannot drop privileges"); imsg_callback = mta_imsg; event_init(); signal_set(&ev_sigint, SIGINT, mta_sig_handler, NULL); signal_set(&ev_sigterm, SIGTERM, mta_sig_handler, NULL); signal_add(&ev_sigint, NULL); signal_add(&ev_sigterm, NULL); signal(SIGPIPE, SIG_IGN); signal(SIGHUP, SIG_IGN); config_pipes(peers, nitems(peers)); config_peers(peers, nitems(peers)); if (event_dispatch() < 0) fatal("event_dispatch"); mta_shutdown(); return (0); }
pid_t control(void) { pid_t pid; struct passwd *pw; struct event ev_sigint; struct event ev_sigterm; switch (pid = fork()) { case -1: fatal("control: cannot fork"); case 0: post_fork(PROC_CONTROL); break; default: return (pid); } purge_config(PURGE_EVERYTHING); if ((pw = getpwnam(SMTPD_USER)) == NULL) fatalx("unknown user " SMTPD_USER); stat_backend = env->sc_stat; stat_backend->init(); if (chroot(PATH_CHROOT) == -1) fatal("control: chroot"); if (chdir("/") == -1) fatal("control: chdir(\"/\")"); config_process(PROC_CONTROL); if (setgroups(1, &pw->pw_gid) || setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) fatal("control: cannot drop privileges"); imsg_callback = control_imsg; event_init(); signal_set(&ev_sigint, SIGINT, control_sig_handler, NULL); signal_set(&ev_sigterm, SIGTERM, control_sig_handler, NULL); signal_add(&ev_sigint, NULL); signal_add(&ev_sigterm, NULL); signal(SIGPIPE, SIG_IGN); signal(SIGHUP, SIG_IGN); tree_init(&ctl_conns); tree_init(&ctl_count); memset(&digest, 0, sizeof digest); digest.startup = time(NULL); config_peer(PROC_SCHEDULER); config_peer(PROC_QUEUE); config_peer(PROC_PARENT); config_peer(PROC_LKA); config_peer(PROC_PONY); config_peer(PROC_CA); config_done(); control_listen(); if (event_dispatch() < 0) fatal("event_dispatch"); control_shutdown(); return (0); }
pid_t runner(void) { pid_t pid; struct passwd *pw; struct event ev_sigint; struct event ev_sigterm; struct peer peers[] = { { PROC_CONTROL, imsg_dispatch }, { PROC_QUEUE, imsg_dispatch } }; switch (pid = fork()) { case -1: fatal("runner: cannot fork"); case 0: break; default: return (pid); } purge_config(PURGE_EVERYTHING); pw = env->sc_pw; if (chroot(PATH_SPOOL) == -1) fatal("runner: chroot"); if (chdir("/") == -1) fatal("runner: chdir(\"/\")"); smtpd_process = PROC_RUNNER; setproctitle("%s", env->sc_title[smtpd_process]); if (setgroups(1, &pw->pw_gid) || setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) fatal("runner: cannot drop privileges"); /* see fdlimit()-related comment in queue.c */ fdlimit(1.0); if ((env->sc_maxconn = availdesc() / 4) < 1) fatalx("runner: fd starvation"); env->sc_scheduler = scheduler_backend_lookup(SCHED_RAMQUEUE); scheduler = env->sc_scheduler; scheduler->init(); imsg_callback = runner_imsg; event_init(); signal_set(&ev_sigint, SIGINT, runner_sig_handler, NULL); signal_set(&ev_sigterm, SIGTERM, runner_sig_handler, NULL); signal_add(&ev_sigint, NULL); signal_add(&ev_sigterm, NULL); signal(SIGPIPE, SIG_IGN); signal(SIGHUP, SIG_IGN); config_pipes(peers, nitems(peers)); config_peers(peers, nitems(peers)); runner_setup_events(); event_dispatch(); runner_disable_events(); runner_shutdown(); return (0); }
static void lka_imsg(struct mproc *p, struct imsg *imsg) { struct rule *rule; struct table *table; void *tmp; int ret; const char *key, *val; struct ssl *ssl; struct iovec iov[3]; static struct dict *ssl_dict; static struct dict *tables_dict; static struct table *table_last; static struct ca_vrfy_req_msg *req_ca_vrfy_smtp = NULL; static struct ca_vrfy_req_msg *req_ca_vrfy_mta = NULL; struct ca_vrfy_req_msg *req_ca_vrfy_chain; struct ca_vrfy_resp_msg resp_ca_vrfy; struct ca_cert_req_msg *req_ca_cert; struct ca_cert_resp_msg resp_ca_cert; struct sockaddr_storage ss; struct userinfo userinfo; struct addrname addrname; struct envelope evp; struct msg m; union lookup lk; char buf[SMTPD_MAXLINESIZE]; const char *tablename, *username, *password, *label; uint64_t reqid; size_t i; int v; if (imsg->hdr.type == IMSG_DNS_HOST || imsg->hdr.type == IMSG_DNS_PTR || imsg->hdr.type == IMSG_DNS_MX || imsg->hdr.type == IMSG_DNS_MX_PREFERENCE) { dns_imsg(p, imsg); return; } if (p->proc == PROC_SMTP) { switch (imsg->hdr.type) { case IMSG_LKA_EXPAND_RCPT: m_msg(&m, imsg); m_get_id(&m, &reqid); m_get_envelope(&m, &evp); m_end(&m); lka_session(reqid, &evp); return; case IMSG_LKA_SSL_INIT: req_ca_cert = imsg->data; resp_ca_cert.reqid = req_ca_cert->reqid; ssl = dict_get(env->sc_ssl_dict, req_ca_cert->name); if (ssl == NULL) { resp_ca_cert.status = CA_FAIL; m_compose(p, IMSG_LKA_SSL_INIT, 0, 0, -1, &resp_ca_cert, sizeof(resp_ca_cert)); return; } resp_ca_cert.status = CA_OK; resp_ca_cert.cert_len = ssl->ssl_cert_len; resp_ca_cert.key_len = ssl->ssl_key_len; iov[0].iov_base = &resp_ca_cert; iov[0].iov_len = sizeof(resp_ca_cert); iov[1].iov_base = ssl->ssl_cert; iov[1].iov_len = ssl->ssl_cert_len; iov[2].iov_base = ssl->ssl_key; iov[2].iov_len = ssl->ssl_key_len; m_composev(p, IMSG_LKA_SSL_INIT, 0, 0, -1, iov, nitems(iov)); return; case IMSG_LKA_SSL_VERIFY_CERT: req_ca_vrfy_smtp = xmemdup(imsg->data, sizeof *req_ca_vrfy_smtp, "lka:ca_vrfy"); if (req_ca_vrfy_smtp == NULL) fatal(NULL); req_ca_vrfy_smtp->cert = xmemdup((char *)imsg->data + sizeof *req_ca_vrfy_smtp, req_ca_vrfy_smtp->cert_len, "lka:ca_vrfy"); req_ca_vrfy_smtp->chain_cert = xcalloc(req_ca_vrfy_smtp->n_chain, sizeof (unsigned char *), "lka:ca_vrfy"); req_ca_vrfy_smtp->chain_cert_len = xcalloc(req_ca_vrfy_smtp->n_chain, sizeof (off_t), "lka:ca_vrfy"); return; case IMSG_LKA_SSL_VERIFY_CHAIN: if (req_ca_vrfy_smtp == NULL) fatalx("lka:ca_vrfy: chain without a certificate"); req_ca_vrfy_chain = imsg->data; req_ca_vrfy_smtp->chain_cert[req_ca_vrfy_smtp->chain_offset] = xmemdup((char *)imsg->data + sizeof *req_ca_vrfy_chain, req_ca_vrfy_chain->cert_len, "lka:ca_vrfy"); req_ca_vrfy_smtp->chain_cert_len[req_ca_vrfy_smtp->chain_offset] = req_ca_vrfy_chain->cert_len; req_ca_vrfy_smtp->chain_offset++; return; case IMSG_LKA_SSL_VERIFY: if (req_ca_vrfy_smtp == NULL) fatalx("lka:ca_vrfy: verify without a certificate"); resp_ca_vrfy.reqid = req_ca_vrfy_smtp->reqid; if (! lka_X509_verify(req_ca_vrfy_smtp, CA_FILE, NULL)) resp_ca_vrfy.status = CA_FAIL; else resp_ca_vrfy.status = CA_OK; m_compose(p, IMSG_LKA_SSL_VERIFY, 0, 0, -1, &resp_ca_vrfy, sizeof resp_ca_vrfy); for (i = 0; i < req_ca_vrfy_smtp->n_chain; ++i) free(req_ca_vrfy_smtp->chain_cert[i]); free(req_ca_vrfy_smtp->chain_cert); free(req_ca_vrfy_smtp->chain_cert_len); free(req_ca_vrfy_smtp->cert); free(req_ca_vrfy_smtp); return; case IMSG_LKA_AUTHENTICATE: m_msg(&m, imsg); m_get_id(&m, &reqid); m_get_string(&m, &tablename); m_get_string(&m, &username); m_get_string(&m, &password); m_end(&m); if (!tablename[0]) { m_create(p_parent, IMSG_LKA_AUTHENTICATE, 0, 0, -1); m_add_id(p_parent, reqid); m_add_string(p_parent, username); m_add_string(p_parent, password); m_close(p_parent); return; } ret = lka_authenticate(tablename, username, password); m_create(p, IMSG_LKA_AUTHENTICATE, 0, 0, -1); m_add_id(p, reqid); m_add_int(p, ret); m_close(p); return; } } if (p->proc == PROC_MDA) { switch (imsg->hdr.type) { case IMSG_LKA_USERINFO: m_msg(&m, imsg); m_get_string(&m, &tablename); m_get_string(&m, &username); m_end(&m); ret = lka_userinfo(tablename, username, &userinfo); m_create(p, IMSG_LKA_USERINFO, 0, 0, -1); m_add_string(p, tablename); m_add_string(p, username); m_add_int(p, ret); if (ret == LKA_OK) m_add_data(p, &userinfo, sizeof(userinfo)); m_close(p); return; } } if (p->proc == PROC_MTA) { switch (imsg->hdr.type) { case IMSG_LKA_SSL_INIT: req_ca_cert = imsg->data; resp_ca_cert.reqid = req_ca_cert->reqid; ssl = dict_get(env->sc_ssl_dict, req_ca_cert->name); if (ssl == NULL) { resp_ca_cert.status = CA_FAIL; m_compose(p, IMSG_LKA_SSL_INIT, 0, 0, -1, &resp_ca_cert, sizeof(resp_ca_cert)); return; } resp_ca_cert.status = CA_OK; resp_ca_cert.cert_len = ssl->ssl_cert_len; resp_ca_cert.key_len = ssl->ssl_key_len; iov[0].iov_base = &resp_ca_cert; iov[0].iov_len = sizeof(resp_ca_cert); iov[1].iov_base = ssl->ssl_cert; iov[1].iov_len = ssl->ssl_cert_len; iov[2].iov_base = ssl->ssl_key; iov[2].iov_len = ssl->ssl_key_len; m_composev(p, IMSG_LKA_SSL_INIT, 0, 0, -1, iov, nitems(iov)); return; case IMSG_LKA_SSL_VERIFY_CERT: req_ca_vrfy_mta = xmemdup(imsg->data, sizeof *req_ca_vrfy_mta, "lka:ca_vrfy"); if (req_ca_vrfy_mta == NULL) fatal(NULL); req_ca_vrfy_mta->cert = xmemdup((char *)imsg->data + sizeof *req_ca_vrfy_mta, req_ca_vrfy_mta->cert_len, "lka:ca_vrfy"); req_ca_vrfy_mta->chain_cert = xcalloc(req_ca_vrfy_mta->n_chain, sizeof (unsigned char *), "lka:ca_vrfy"); req_ca_vrfy_mta->chain_cert_len = xcalloc(req_ca_vrfy_mta->n_chain, sizeof (off_t), "lka:ca_vrfy"); return; case IMSG_LKA_SSL_VERIFY_CHAIN: if (req_ca_vrfy_mta == NULL) fatalx("lka:ca_vrfy: verify without a certificate"); req_ca_vrfy_chain = imsg->data; req_ca_vrfy_mta->chain_cert[req_ca_vrfy_mta->chain_offset] = xmemdup((char *)imsg->data + sizeof *req_ca_vrfy_chain, req_ca_vrfy_chain->cert_len, "lka:ca_vrfy"); req_ca_vrfy_mta->chain_cert_len[req_ca_vrfy_mta->chain_offset] = req_ca_vrfy_chain->cert_len; req_ca_vrfy_mta->chain_offset++; return; case IMSG_LKA_SSL_VERIFY: if (req_ca_vrfy_mta == NULL) fatalx("lka:ca_vrfy: verify without a certificate"); resp_ca_vrfy.reqid = req_ca_vrfy_mta->reqid; if (! lka_X509_verify(req_ca_vrfy_mta, CA_FILE, NULL)) resp_ca_vrfy.status = CA_FAIL; else resp_ca_vrfy.status = CA_OK; m_compose(p, IMSG_LKA_SSL_VERIFY, 0, 0, -1, &resp_ca_vrfy, sizeof resp_ca_vrfy); for (i = 0; i < req_ca_vrfy_mta->n_chain; ++i) free(req_ca_vrfy_mta->chain_cert[i]); free(req_ca_vrfy_mta->chain_cert); free(req_ca_vrfy_mta->chain_cert_len); free(req_ca_vrfy_mta->cert); free(req_ca_vrfy_mta); return; case IMSG_LKA_SECRET: m_msg(&m, imsg); m_get_id(&m, &reqid); m_get_string(&m, &tablename); m_get_string(&m, &label); m_end(&m); lka_credentials(tablename, label, buf, sizeof(buf)); m_create(p, IMSG_LKA_SECRET, 0, 0, -1); m_add_id(p, reqid); m_add_string(p, buf); m_close(p); return; case IMSG_LKA_SOURCE: m_msg(&m, imsg); m_get_id(&m, &reqid); m_get_string(&m, &tablename); table = table_find(tablename, NULL); m_create(p, IMSG_LKA_SOURCE, 0, 0, -1); m_add_id(p, reqid); if (table == NULL) { log_warn("warn: source address table %s missing", tablename); m_add_int(p, LKA_TEMPFAIL); } else { ret = table_fetch(table, K_SOURCE, &lk); if (ret == -1) m_add_int(p, LKA_TEMPFAIL); else if (ret == 0) m_add_int(p, LKA_PERMFAIL); else { m_add_int(p, LKA_OK); m_add_sockaddr(p, (struct sockaddr *)&lk.source.addr); } } m_close(p); return; case IMSG_LKA_HELO: m_msg(&m, imsg); m_get_id(&m, &reqid); m_get_string(&m, &tablename); m_get_sockaddr(&m, (struct sockaddr *)&ss); m_end(&m); ret = lka_addrname(tablename, (struct sockaddr*)&ss, &addrname); m_create(p, IMSG_LKA_HELO, 0, 0, -1); m_add_id(p, reqid); m_add_int(p, ret); if (ret == LKA_OK) m_add_string(p, addrname.name); m_close(p); return; } } if (p->proc == PROC_PARENT) { switch (imsg->hdr.type) { case IMSG_CONF_START: env->sc_rules_reload = xcalloc(1, sizeof *env->sc_rules, "lka:sc_rules_reload"); tables_dict = xcalloc(1, sizeof *tables_dict, "lka:tables_dict"); ssl_dict = calloc(1, sizeof *ssl_dict); if (ssl_dict == NULL) fatal(NULL); dict_init(ssl_dict); dict_init(tables_dict); TAILQ_INIT(env->sc_rules_reload); return; case IMSG_CONF_SSL: ssl = calloc(1, sizeof *ssl); if (ssl == NULL) fatal(NULL); *ssl = *(struct ssl *)imsg->data; ssl->ssl_cert = xstrdup((char *)imsg->data + sizeof *ssl, "smtp:ssl_cert"); ssl->ssl_key = xstrdup((char *)imsg->data + sizeof *ssl + ssl->ssl_cert_len, "smtp:ssl_key"); if (ssl->ssl_dhparams_len) { ssl->ssl_dhparams = xstrdup((char *)imsg->data + sizeof *ssl + ssl->ssl_cert_len + ssl->ssl_key_len, "smtp:ssl_dhparams"); } if (ssl->ssl_ca_len) { ssl->ssl_ca = xstrdup((char *)imsg->data + sizeof *ssl + ssl->ssl_cert_len + ssl->ssl_key_len + ssl->ssl_dhparams_len, "smtp:ssl_ca"); } dict_set(ssl_dict, ssl->ssl_name, ssl); return; case IMSG_CONF_RULE: rule = xmemdup(imsg->data, sizeof *rule, "lka:rule"); TAILQ_INSERT_TAIL(env->sc_rules_reload, rule, r_entry); return; case IMSG_CONF_TABLE: table_last = table = xmemdup(imsg->data, sizeof *table, "lka:table"); dict_init(&table->t_dict); dict_set(tables_dict, table->t_name, table); return; case IMSG_CONF_RULE_SOURCE: rule = TAILQ_LAST(env->sc_rules_reload, rulelist); tmp = env->sc_tables_dict; env->sc_tables_dict = tables_dict; rule->r_sources = table_find(imsg->data, NULL); if (rule->r_sources == NULL) fatalx("lka: tables inconsistency"); env->sc_tables_dict = tmp; return; case IMSG_CONF_RULE_SENDER: rule = TAILQ_LAST(env->sc_rules_reload, rulelist); tmp = env->sc_tables_dict; env->sc_tables_dict = tables_dict; rule->r_senders = table_find(imsg->data, NULL); if (rule->r_senders == NULL) fatalx("lka: tables inconsistency"); env->sc_tables_dict = tmp; return; case IMSG_CONF_RULE_DESTINATION: rule = TAILQ_LAST(env->sc_rules_reload, rulelist); tmp = env->sc_tables_dict; env->sc_tables_dict = tables_dict; rule->r_destination = table_find(imsg->data, NULL); if (rule->r_destination == NULL) fatalx("lka: tables inconsistency"); env->sc_tables_dict = tmp; return; case IMSG_CONF_RULE_MAPPING: rule = TAILQ_LAST(env->sc_rules_reload, rulelist); tmp = env->sc_tables_dict; env->sc_tables_dict = tables_dict; rule->r_mapping = table_find(imsg->data, NULL); if (rule->r_mapping == NULL) fatalx("lka: tables inconsistency"); env->sc_tables_dict = tmp; return; case IMSG_CONF_RULE_USERS: rule = TAILQ_LAST(env->sc_rules_reload, rulelist); tmp = env->sc_tables_dict; env->sc_tables_dict = tables_dict; rule->r_userbase = table_find(imsg->data, NULL); if (rule->r_userbase == NULL) fatalx("lka: tables inconsistency"); env->sc_tables_dict = tmp; return; case IMSG_CONF_TABLE_CONTENT: table = table_last; if (table == NULL) fatalx("lka: tables inconsistency"); key = imsg->data; if (table->t_type == T_HASH) val = key + strlen(key) + 1; else val = NULL; dict_set(&table->t_dict, key, val ? xstrdup(val, "lka:dict_set") : NULL); return; case IMSG_CONF_END: if (env->sc_rules) purge_config(PURGE_RULES); if (env->sc_tables_dict) { table_close_all(); purge_config(PURGE_TABLES); } env->sc_rules = env->sc_rules_reload; env->sc_ssl_dict = ssl_dict; env->sc_tables_dict = tables_dict; if (verbose & TRACE_TABLES) table_dump_all(); table_open_all(); ssl_dict = NULL; table_last = NULL; tables_dict = NULL; /* Start fulfilling requests */ mproc_enable(p_mda); mproc_enable(p_mta); mproc_enable(p_smtp); return; case IMSG_CTL_VERBOSE: m_msg(&m, imsg); m_get_int(&m, &v); m_end(&m); log_verbose(v); return; case IMSG_CTL_PROFILE: m_msg(&m, imsg); m_get_int(&m, &v); m_end(&m); profiling = v; return; case IMSG_PARENT_FORWARD_OPEN: lka_session_forward_reply(imsg->data, imsg->fd); return; case IMSG_LKA_AUTHENTICATE: m_forward(p_smtp, imsg); return; } } if (p->proc == PROC_CONTROL) { switch (imsg->hdr.type) { case IMSG_LKA_UPDATE_TABLE: table = table_find(imsg->data, NULL); if (table == NULL) { log_warnx("warn: Lookup table not found: " "\"%s\"", (char *)imsg->data); return; } table_update(table); return; } } errx(1, "lka_imsg: unexpected %s imsg", imsg_to_str(imsg->hdr.type)); }
pid_t lka(void) { pid_t pid; struct passwd *pw; struct event ev_sigint; struct event ev_sigterm; struct event ev_sigchld; switch (pid = fork()) { case -1: fatal("lka: cannot fork"); case 0: break; default: return (pid); } purge_config(PURGE_EVERYTHING); if ((pw = getpwnam(SMTPD_USER)) == NULL) fatalx("unknown user " SMTPD_USER); config_process(PROC_LKA); if (setgroups(1, &pw->pw_gid) || setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) fatal("lka: cannot drop privileges"); imsg_callback = lka_imsg; event_init(); signal_set(&ev_sigint, SIGINT, lka_sig_handler, NULL); signal_set(&ev_sigterm, SIGTERM, lka_sig_handler, NULL); signal_set(&ev_sigchld, SIGCHLD, lka_sig_handler, NULL); signal_add(&ev_sigint, NULL); signal_add(&ev_sigterm, NULL); signal_add(&ev_sigchld, NULL); signal(SIGPIPE, SIG_IGN); signal(SIGHUP, SIG_IGN); /* * lka opens all kinds of files and sockets, so bump the limit to max. * XXX: need to analyse the exact hard limit. */ fdlimit(1.0); config_peer(PROC_PARENT); config_peer(PROC_QUEUE); config_peer(PROC_SMTP); config_peer(PROC_MDA); config_peer(PROC_MTA); config_peer(PROC_CONTROL); config_done(); /* Ignore them until we get our config */ mproc_disable(p_mda); mproc_disable(p_mta); mproc_disable(p_smtp); if (event_dispatch() < 0) fatal("event_dispatch"); lka_shutdown(); return (0); }
pid_t control(void) { struct sockaddr_un sun; int fd; mode_t old_umask; pid_t pid; struct passwd *pw; struct event ev_sigint; struct event ev_sigterm; struct peer peers [] = { { PROC_RUNNER, imsg_dispatch }, { PROC_QUEUE, imsg_dispatch }, { PROC_SMTP, imsg_dispatch }, { PROC_MFA, imsg_dispatch }, { PROC_PARENT, imsg_dispatch }, }; switch (pid = fork()) { case -1: fatal("control: cannot fork"); case 0: break; default: return (pid); } purge_config(PURGE_EVERYTHING); pw = env->sc_pw; if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) fatal("control: socket"); bzero(&sun, sizeof(sun)); sun.sun_family = AF_UNIX; if (strlcpy(sun.sun_path, SMTPD_SOCKET, sizeof(sun.sun_path)) >= sizeof(sun.sun_path)) fatal("control: socket name too long"); if (connect(fd, (struct sockaddr *)&sun, sizeof(sun)) == 0) fatalx("control socket already listening"); if (unlink(SMTPD_SOCKET) == -1) if (errno != ENOENT) fatal("control: cannot unlink socket"); old_umask = umask(S_IXUSR|S_IXGRP|S_IWOTH|S_IROTH|S_IXOTH); if (bind(fd, (struct sockaddr *)&sun, sizeof(sun)) == -1) { (void)umask(old_umask); fatal("control: bind"); } (void)umask(old_umask); if (chmod(SMTPD_SOCKET, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH) == -1) { (void)unlink(SMTPD_SOCKET); fatal("control: chmod"); } session_socket_blockmode(fd, BM_NONBLOCK); control_state.fd = fd; if (chroot(pw->pw_dir) == -1) fatal("control: chroot"); if (chdir("/") == -1) fatal("control: chdir(\"/\")"); smtpd_process = PROC_CONTROL; setproctitle("%s", env->sc_title[smtpd_process]); if (setgroups(1, &pw->pw_gid) || setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) fatal("control: cannot drop privileges"); imsg_callback = control_imsg; event_init(); signal_set(&ev_sigint, SIGINT, control_sig_handler, NULL); signal_set(&ev_sigterm, SIGTERM, control_sig_handler, NULL); signal_add(&ev_sigint, NULL); signal_add(&ev_sigterm, NULL); signal(SIGPIPE, SIG_IGN); signal(SIGHUP, SIG_IGN); TAILQ_INIT(&ctl_conns); config_pipes(peers, nitems(peers)); config_peers(peers, nitems(peers)); control_listen(); if (event_dispatch() < 0) fatal("event_dispatch"); control_shutdown(); return (0); }
pid_t pony(void) { pid_t pid; struct passwd *pw; struct event ev_sigint; struct event ev_sigterm; switch (pid = fork()) { case -1: fatal("pony: cannot fork"); case 0: post_fork(PROC_PONY); break; default: return (pid); } mda_postfork(); mta_postfork(); smtp_postfork(); filter_postfork(); /* do not purge listeners and pki, they are purged * in smtp_configure() */ purge_config(PURGE_TABLES|PURGE_RULES); if ((pw = getpwnam(SMTPD_USER)) == NULL) fatalx("unknown user " SMTPD_USER); if (chroot(PATH_CHROOT) == -1) fatal("pony: chroot"); if (chdir("/") == -1) fatal("pony: chdir(\"/\")"); config_process(PROC_PONY); if (setgroups(1, &pw->pw_gid) || setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) fatal("pony: cannot drop privileges"); imsg_callback = pony_imsg; event_init(); mda_postprivdrop(); mta_postprivdrop(); smtp_postprivdrop(); signal_set(&ev_sigint, SIGINT, pony_sig_handler, NULL); signal_set(&ev_sigterm, SIGTERM, pony_sig_handler, NULL); signal_add(&ev_sigint, NULL); signal_add(&ev_sigterm, NULL); signal(SIGPIPE, SIG_IGN); signal(SIGHUP, SIG_IGN); config_peer(PROC_PARENT); config_peer(PROC_QUEUE); config_peer(PROC_LKA); config_peer(PROC_CONTROL); config_peer(PROC_CA); config_done(); ca_engine_init(); if (event_dispatch() < 0) fatal("event_dispatch"); pony_shutdown(); return (0); }
pid_t mfa(void) { pid_t pid; struct passwd *pw; struct event ev_sigint; struct event ev_sigterm; struct event ev_sigchld; switch (pid = fork()) { case -1: fatal("filter: cannot fork"); case 0: break; default: return (pid); } mfa_filter_prepare(); purge_config(PURGE_EVERYTHING); if ((pw = getpwnam(SMTPD_USER)) == NULL) fatalx("unknown user " SMTPD_USER); config_process(PROC_MFA); if (chroot(PATH_CHROOT) == -1) fatal("scheduler: chroot"); if (chdir("/") == -1) fatal("scheduler: chdir(\"/\")"); if (setgroups(1, &pw->pw_gid) || setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) fatal("filter: cannot drop privileges"); imsg_callback = mfa_imsg; event_init(); signal_set(&ev_sigint, SIGINT, mfa_sig_handler, NULL); signal_set(&ev_sigterm, SIGTERM, mfa_sig_handler, NULL); signal_set(&ev_sigchld, SIGCHLD, mfa_sig_handler, NULL); signal_add(&ev_sigint, NULL); signal_add(&ev_sigterm, NULL); signal_add(&ev_sigchld, NULL); signal(SIGPIPE, SIG_IGN); signal(SIGHUP, SIG_IGN); config_peer(PROC_PARENT); config_peer(PROC_SMTP); config_peer(PROC_CONTROL); config_done(); mproc_disable(p_smtp); if (event_dispatch() < 0) fatal("event_dispatch"); mfa_shutdown(); return (0); }
int queue(void) { struct passwd *pw; struct timeval tv; struct event ev_qload; purge_config(PURGE_EVERYTHING); if ((pw = getpwnam(SMTPD_QUEUE_USER)) == NULL) if ((pw = getpwnam(SMTPD_USER)) == NULL) fatalx("unknown user " SMTPD_USER); env->sc_queue_flags |= QUEUE_EVPCACHE; env->sc_queue_evpcache_size = 1024; if (chroot(PATH_SPOOL) == -1) fatal("queue: chroot"); if (chdir("/") == -1) fatal("queue: chdir(\"/\")"); config_process(PROC_QUEUE); if (env->sc_queue_flags & QUEUE_COMPRESSION) log_info("queue: queue compression enabled"); if (env->sc_queue_key) { if (!crypto_setup(env->sc_queue_key, strlen(env->sc_queue_key))) fatalx("crypto_setup: invalid key for queue encryption"); log_info("queue: queue encryption enabled"); } if (setgroups(1, &pw->pw_gid) || setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) fatal("queue: cannot drop privileges"); imsg_callback = queue_imsg; event_init(); signal(SIGINT, SIG_IGN); signal(SIGTERM, SIG_IGN); signal(SIGPIPE, SIG_IGN); signal(SIGHUP, SIG_IGN); config_peer(PROC_PARENT); config_peer(PROC_CONTROL); config_peer(PROC_LKA); config_peer(PROC_SCHEDULER); config_peer(PROC_PONY); /* setup queue loading task */ evtimer_set(&ev_qload, queue_timeout, &ev_qload); tv.tv_sec = 0; tv.tv_usec = 10; evtimer_add(&ev_qload, &tv); if (pledge("stdio rpath wpath cpath flock recvfd sendfd", NULL) == -1) err(1, "pledge"); event_dispatch(); fatalx("exited event loop"); return (0); }
static void lka_imsg(struct imsgev *iev, struct imsg *imsg) { struct submit_status *ss; struct secret *secret; struct mapel *mapel; struct rule *rule; struct map *map; struct map *mp; void *tmp; if (imsg->hdr.type == IMSG_DNS_HOST || imsg->hdr.type == IMSG_DNS_MX || imsg->hdr.type == IMSG_DNS_PTR) { dns_async(iev, imsg->hdr.type, imsg->data); return; } if (iev->proc == PROC_MFA) { switch (imsg->hdr.type) { case IMSG_LKA_MAIL: ss = imsg->data; ss->code = 530; if (ss->u.maddr.user[0] == '\0' && ss->u.maddr.domain[0] == '\0') ss->code = 250; else if (lka_verify_mail(&ss->u.maddr)) ss->code = 250; imsg_compose_event(iev, IMSG_LKA_MAIL, 0, 0, -1, ss, sizeof *ss); return; case IMSG_LKA_RULEMATCH: ss = imsg->data; rule = ruleset_match(&ss->envelope); if (rule == NULL) ss->code = (errno == EAGAIN) ? 451 : 530; else ss->code = (rule->r_decision == R_ACCEPT) ? 250 : 530; imsg_compose_event(iev, IMSG_LKA_RULEMATCH, 0, 0, -1, ss, sizeof *ss); return; case IMSG_LKA_RCPT: lka_session(imsg->data); return; } } if (iev->proc == PROC_MTA) { switch (imsg->hdr.type) { case IMSG_LKA_SECRET: { struct map_credentials *map_credentials; secret = imsg->data; map = map_findbyname(secret->mapname); if (map == NULL) { log_warn("warn: lka: credentials map %s is missing", secret->mapname); imsg_compose_event(iev, IMSG_LKA_SECRET, 0, 0, -1, secret, sizeof *secret); return; } map_credentials = map_lookup(map->m_id, secret->host, K_CREDENTIALS); log_debug("debug: lka: %s credentials lookup (%d)", secret->host, map_credentials != NULL); secret->secret[0] = '\0'; if (map_credentials == NULL) log_warnx("warn: %s credentials not found", secret->host); else if (lka_encode_credentials(secret->secret, sizeof secret->secret, map_credentials) == 0) log_warnx("warn: %s credentials parse fail", secret->host); imsg_compose_event(iev, IMSG_LKA_SECRET, 0, 0, -1, secret, sizeof *secret); free(map_credentials); return; } } } if (iev->proc == PROC_PARENT) { switch (imsg->hdr.type) { case IMSG_CONF_START: env->sc_rules_reload = xcalloc(1, sizeof *env->sc_rules, "lka:sc_rules_reload"); env->sc_maps_reload = xcalloc(1, sizeof *env->sc_maps, "lka:sc_maps_reload"); TAILQ_INIT(env->sc_rules_reload); TAILQ_INIT(env->sc_maps_reload); return; case IMSG_CONF_RULE: rule = xmemdup(imsg->data, sizeof *rule, "lka:rule"); TAILQ_INSERT_TAIL(env->sc_rules_reload, rule, r_entry); return; case IMSG_CONF_MAP: map = xmemdup(imsg->data, sizeof *map, "lka:map"); TAILQ_INIT(&map->m_contents); TAILQ_INSERT_TAIL(env->sc_maps_reload, map, m_entry); tmp = env->sc_maps; env->sc_maps = env->sc_maps_reload; mp = map_open(map); if (mp == NULL) errx(1, "lka: could not open map \"%s\"", map->m_name); map_close(map, mp); env->sc_maps = tmp; return; case IMSG_CONF_RULE_SOURCE: rule = TAILQ_LAST(env->sc_rules_reload, rulelist); tmp = env->sc_maps; env->sc_maps = env->sc_maps_reload; rule->r_sources = map_findbyname(imsg->data); if (rule->r_sources == NULL) fatalx("lka: maps inconsistency"); env->sc_maps = tmp; return; case IMSG_CONF_MAP_CONTENT: map = TAILQ_LAST(env->sc_maps_reload, maplist); mapel = xmemdup(imsg->data, sizeof *mapel, "lka:mapel"); TAILQ_INSERT_TAIL(&map->m_contents, mapel, me_entry); return; case IMSG_CONF_END: if (env->sc_rules) purge_config(PURGE_RULES); if (env->sc_maps) purge_config(PURGE_MAPS); env->sc_rules = env->sc_rules_reload; env->sc_maps = env->sc_maps_reload; /* start fulfilling requests */ event_add(&env->sc_ievs[PROC_MTA]->ev, NULL); event_add(&env->sc_ievs[PROC_MFA]->ev, NULL); event_add(&env->sc_ievs[PROC_SMTP]->ev, NULL); return; case IMSG_CTL_VERBOSE: log_verbose(*(int *)imsg->data); return; case IMSG_PARENT_FORWARD_OPEN: lka_session_forward_reply(imsg->data, imsg->fd); return; } } if (iev->proc == PROC_CONTROL) { switch (imsg->hdr.type) { case IMSG_LKA_UPDATE_MAP: map = map_findbyname(imsg->data); if (map == NULL) { log_warnx("warn: lka: no such map \"%s\"", (char *)imsg->data); return; } map_update(map); return; } } errx(1, "lka_imsg: unexpected %s imsg", imsg_to_str(imsg->hdr.type)); }
pid_t lka(void) { pid_t pid; struct passwd *pw; struct event ev_sigint; struct event ev_sigterm; struct event ev_sigchld; struct peer peers[] = { { PROC_PARENT, imsg_dispatch }, { PROC_MFA, imsg_dispatch }, { PROC_QUEUE, imsg_dispatch }, { PROC_SMTP, imsg_dispatch }, { PROC_MTA, imsg_dispatch }, { PROC_CONTROL, imsg_dispatch } }; switch (pid = fork()) { case -1: fatal("lka: cannot fork"); case 0: break; default: return (pid); } purge_config(PURGE_EVERYTHING); pw = env->sc_pw; smtpd_process = PROC_LKA; setproctitle("%s", env->sc_title[smtpd_process]); if (setgroups(1, &pw->pw_gid) || setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) fatal("lka: cannot drop privileges"); imsg_callback = lka_imsg; event_init(); SPLAY_INIT(&env->lka_sessions); signal_set(&ev_sigint, SIGINT, lka_sig_handler, NULL); signal_set(&ev_sigterm, SIGTERM, lka_sig_handler, NULL); signal_set(&ev_sigchld, SIGCHLD, lka_sig_handler, NULL); signal_add(&ev_sigint, NULL); signal_add(&ev_sigterm, NULL); signal_add(&ev_sigchld, NULL); signal(SIGPIPE, SIG_IGN); signal(SIGHUP, SIG_IGN); /* * lka opens all kinds of files and sockets, so bump the limit to max. * XXX: need to analyse the exact hard limit. */ fdlimit(1.0); config_pipes(peers, nitems(peers)); config_peers(peers, nitems(peers)); /* ignore them until we get our config */ event_del(&env->sc_ievs[PROC_MTA]->ev); event_del(&env->sc_ievs[PROC_MFA]->ev); event_del(&env->sc_ievs[PROC_SMTP]->ev); if (event_dispatch() < 0) fatal("event_dispatch"); lka_shutdown(); return (0); }
pid_t control(void) { struct sockaddr_un sun; int fd; mode_t old_umask; pid_t pid; struct passwd *pw; struct event ev_sigint; struct event ev_sigterm; switch (pid = fork()) { case -1: fatal("control: cannot fork"); case 0: break; default: return (pid); } purge_config(PURGE_EVERYTHING); pw = env->sc_pw; if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) fatal("control: socket"); bzero(&sun, sizeof(sun)); sun.sun_family = AF_UNIX; if (strlcpy(sun.sun_path, SMTPD_SOCKET, sizeof(sun.sun_path)) >= sizeof(sun.sun_path)) fatal("control: socket name too long"); if (connect(fd, (struct sockaddr *)&sun, sizeof(sun)) == 0) fatalx("control socket already listening"); if (unlink(SMTPD_SOCKET) == -1) if (errno != ENOENT) fatal("control: cannot unlink socket"); old_umask = umask(S_IXUSR|S_IXGRP|S_IWOTH|S_IROTH|S_IXOTH); if (bind(fd, (struct sockaddr *)&sun, sizeof(sun)) == -1) { (void)umask(old_umask); fatal("control: bind"); } (void)umask(old_umask); if (chmod(SMTPD_SOCKET, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH) == -1) { (void)unlink(SMTPD_SOCKET); fatal("control: chmod"); } session_socket_blockmode(fd, BM_NONBLOCK); control_state.fd = fd; stat_backend = env->sc_stat; stat_backend->init(); if (chroot(PATH_CHROOT) == -1) fatal("control: chroot"); if (chdir("/") == -1) fatal("control: chdir(\"/\")"); config_process(PROC_CONTROL); if (setgroups(1, &pw->pw_gid) || setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) fatal("control: cannot drop privileges"); imsg_callback = control_imsg; event_init(); signal_set(&ev_sigint, SIGINT, control_sig_handler, NULL); signal_set(&ev_sigterm, SIGTERM, control_sig_handler, NULL); signal_add(&ev_sigint, NULL); signal_add(&ev_sigterm, NULL); signal(SIGPIPE, SIG_IGN); signal(SIGHUP, SIG_IGN); tree_init(&ctl_conns); bzero(&digest, sizeof digest); digest.startup = time(NULL); config_peer(PROC_SCHEDULER); config_peer(PROC_QUEUE); config_peer(PROC_SMTP); config_peer(PROC_MFA); config_peer(PROC_PARENT); config_peer(PROC_LKA); config_peer(PROC_MDA); config_peer(PROC_MTA); config_done(); control_listen(); if (event_dispatch() < 0) fatal("event_dispatch"); control_shutdown(); return (0); }