aboutsummaryrefslogtreecommitdiffstats
path: root/smtpd/config.c
diff options
context:
space:
mode:
Diffstat (limited to 'smtpd/config.c')
-rw-r--r--smtpd/config.c350
1 files changed, 350 insertions, 0 deletions
diff --git a/smtpd/config.c b/smtpd/config.c
new file mode 100644
index 00000000..8fe983d6
--- /dev/null
+++ b/smtpd/config.c
@@ -0,0 +1,350 @@
+/* $OpenBSD: config.c,v 1.51 2019/12/18 10:00:39 gilles Exp $ */
+
+/*
+ * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "includes.h"
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/tree.h>
+#include <sys/socket.h>
+#include <sys/resource.h>
+
+#include <event.h>
+#include <ifaddrs.h>
+#include <imsg.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <openssl/ssl.h>
+
+#include "smtpd.h"
+#include "log.h"
+#include "ssl.h"
+
+void set_local(struct smtpd *, const char *);
+void set_localaddrs(struct smtpd *, struct table *);
+
+struct smtpd *
+config_default(void)
+{
+ struct smtpd *conf = NULL;
+ struct mta_limits *limits = NULL;
+ struct table *t = NULL;
+ char hostname[HOST_NAME_MAX+1];
+
+ if (getmailname(hostname, sizeof hostname) == -1)
+ return NULL;
+
+ if ((conf = calloc(1, sizeof(*conf))) == NULL)
+ return conf;
+
+ (void)strlcpy(conf->sc_hostname, hostname, sizeof(conf->sc_hostname));
+
+ conf->sc_maxsize = DEFAULT_MAX_BODY_SIZE;
+ conf->sc_subaddressing_delim = SUBADDRESSING_DELIMITER;
+ conf->sc_ttl = SMTPD_QUEUE_EXPIRY;
+ conf->sc_srs_ttl = SMTPD_QUEUE_EXPIRY / 86400;
+
+ conf->sc_mta_max_deferred = 100;
+ conf->sc_scheduler_max_inflight = 5000;
+ conf->sc_scheduler_max_schedule = 10;
+ conf->sc_scheduler_max_evp_batch_size = 256;
+ conf->sc_scheduler_max_msg_batch_size = 1024;
+
+ conf->sc_session_max_rcpt = 1000;
+ conf->sc_session_max_mails = 100;
+
+ conf->sc_mda_max_session = 50;
+ conf->sc_mda_max_user_session = 7;
+ conf->sc_mda_task_hiwat = 50;
+ conf->sc_mda_task_lowat = 30;
+ conf->sc_mda_task_release = 10;
+
+ /* Report mails delayed for more than 4 hours */
+ conf->sc_bounce_warn[0] = 3600 * 4;
+
+ conf->sc_tables_dict = calloc(1, sizeof(*conf->sc_tables_dict));
+ conf->sc_rules = calloc(1, sizeof(*conf->sc_rules));
+ conf->sc_dispatchers = calloc(1, sizeof(*conf->sc_dispatchers));
+ conf->sc_listeners = calloc(1, sizeof(*conf->sc_listeners));
+ conf->sc_ca_dict = calloc(1, sizeof(*conf->sc_ca_dict));
+ conf->sc_pki_dict = calloc(1, sizeof(*conf->sc_pki_dict));
+ conf->sc_ssl_dict = calloc(1, sizeof(*conf->sc_ssl_dict));
+ conf->sc_limits_dict = calloc(1, sizeof(*conf->sc_limits_dict));
+ conf->sc_mda_wrappers = calloc(1, sizeof(*conf->sc_mda_wrappers));
+ conf->sc_filter_processes_dict = calloc(1, sizeof(*conf->sc_filter_processes_dict));
+ conf->sc_dispatcher_bounce = calloc(1, sizeof(*conf->sc_dispatcher_bounce));
+ conf->sc_filters_dict = calloc(1, sizeof(*conf->sc_filters_dict));
+ limits = calloc(1, sizeof(*limits));
+
+ if (conf->sc_tables_dict == NULL ||
+ conf->sc_rules == NULL ||
+ conf->sc_dispatchers == NULL ||
+ conf->sc_listeners == NULL ||
+ conf->sc_ca_dict == NULL ||
+ conf->sc_pki_dict == NULL ||
+ conf->sc_ssl_dict == NULL ||
+ conf->sc_limits_dict == NULL ||
+ conf->sc_mda_wrappers == NULL ||
+ conf->sc_filter_processes_dict == NULL ||
+ conf->sc_dispatcher_bounce == NULL ||
+ conf->sc_filters_dict == NULL ||
+ limits == NULL)
+ goto error;
+
+ dict_init(conf->sc_dispatchers);
+ dict_init(conf->sc_mda_wrappers);
+ dict_init(conf->sc_ca_dict);
+ dict_init(conf->sc_pki_dict);
+ dict_init(conf->sc_ssl_dict);
+ dict_init(conf->sc_tables_dict);
+ dict_init(conf->sc_limits_dict);
+ dict_init(conf->sc_filter_processes_dict);
+
+ limit_mta_set_defaults(limits);
+
+ dict_xset(conf->sc_limits_dict, "default", limits);
+
+ TAILQ_INIT(conf->sc_listeners);
+ TAILQ_INIT(conf->sc_rules);
+
+
+ /* bounce dispatcher */
+ conf->sc_dispatcher_bounce->type = DISPATCHER_BOUNCE;
+
+ /*
+ * declare special "localhost", "anyhost" and "localnames" tables
+ */
+ set_local(conf, conf->sc_hostname);
+
+ t = table_create(conf, "static", "<anydestination>", NULL);
+ table_add(t, "*", NULL);
+
+ hostname[strcspn(hostname, ".")] = '\0';
+ if (strcmp(conf->sc_hostname, hostname) != 0)
+ table_add(t, hostname, NULL);
+
+ table_create(conf, "getpwnam", "<getpwnam>", NULL);
+
+ return conf;
+
+error:
+ free(conf->sc_tables_dict);
+ free(conf->sc_rules);
+ free(conf->sc_dispatchers);
+ free(conf->sc_listeners);
+ free(conf->sc_ca_dict);
+ free(conf->sc_pki_dict);
+ free(conf->sc_ssl_dict);
+ free(conf->sc_limits_dict);
+ free(conf->sc_mda_wrappers);
+ free(conf->sc_filter_processes_dict);
+ free(conf->sc_dispatcher_bounce);
+ free(conf->sc_filters_dict);
+ free(limits);
+ free(conf);
+ return NULL;
+}
+
+void
+set_local(struct smtpd *conf, const char *hostname)
+{
+ struct table *t;
+
+ t = table_create(conf, "static", "<localnames>", NULL);
+ table_add(t, "localhost", NULL);
+ table_add(t, hostname, NULL);
+
+ set_localaddrs(conf, t);
+}
+
+void
+set_localaddrs(struct smtpd *conf, struct table *localnames)
+{
+ struct ifaddrs *ifap, *p;
+ struct sockaddr_storage ss;
+ struct sockaddr_in *sain;
+ struct sockaddr_in6 *sin6;
+ struct table *t;
+ char buf[NI_MAXHOST + 5];
+
+ t = table_create(conf, "static", "<anyhost>", NULL);
+ table_add(t, "local", NULL);
+ table_add(t, "0.0.0.0/0", NULL);
+ table_add(t, "::/0", NULL);
+
+ if (getifaddrs(&ifap) == -1)
+ fatal("getifaddrs");
+
+ t = table_create(conf, "static", "<localhost>", NULL);
+ table_add(t, "local", NULL);
+
+ for (p = ifap; p != NULL; p = p->ifa_next) {
+ if (p->ifa_addr == NULL)
+ continue;
+ switch (p->ifa_addr->sa_family) {
+ case AF_INET:
+ sain = (struct sockaddr_in *)&ss;
+ *sain = *(struct sockaddr_in *)p->ifa_addr;
+#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
+ sain->sin_len = sizeof(struct sockaddr_in);
+#endif
+ table_add(t, ss_to_text(&ss), NULL);
+ table_add(localnames, ss_to_text(&ss), NULL);
+ (void)snprintf(buf, sizeof buf, "[%s]", ss_to_text(&ss));
+ table_add(localnames, buf, NULL);
+ break;
+
+ case AF_INET6:
+ sin6 = (struct sockaddr_in6 *)&ss;
+ *sin6 = *(struct sockaddr_in6 *)p->ifa_addr;
+#ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_LEN
+ sin6->sin6_len = sizeof(struct sockaddr_in6);
+#endif
+ table_add(t, ss_to_text(&ss), NULL);
+ table_add(localnames, ss_to_text(&ss), NULL);
+ (void)snprintf(buf, sizeof buf, "[%s]", ss_to_text(&ss));
+ table_add(localnames, buf, NULL);
+ (void)snprintf(buf, sizeof buf, "[ipv6:%s]", ss_to_text(&ss));
+ table_add(localnames, buf, NULL);
+ break;
+ }
+ }
+
+ freeifaddrs(ifap);
+}
+
+void
+purge_config(uint8_t what)
+{
+ struct dispatcher *d;
+ struct listener *l;
+ struct table *t;
+ struct rule *r;
+ struct pki *p;
+ const char *k;
+ void *iter_dict;
+
+ if (what & PURGE_LISTENERS) {
+ while ((l = TAILQ_FIRST(env->sc_listeners)) != NULL) {
+ TAILQ_REMOVE(env->sc_listeners, l, entry);
+ free(l);
+ }
+ free(env->sc_listeners);
+ env->sc_listeners = NULL;
+ }
+ if (what & PURGE_TABLES) {
+ while (dict_root(env->sc_tables_dict, NULL, (void **)&t))
+ table_destroy(env, t);
+ free(env->sc_tables_dict);
+ env->sc_tables_dict = NULL;
+ }
+ if (what & PURGE_RULES) {
+ while ((r = TAILQ_FIRST(env->sc_rules)) != NULL) {
+ TAILQ_REMOVE(env->sc_rules, r, r_entry);
+ free(r);
+ }
+ free(env->sc_rules);
+ env->sc_rules = NULL;
+ }
+ if (what & PURGE_DISPATCHERS) {
+ while (dict_poproot(env->sc_dispatchers, (void **)&d)) {
+ free(d);
+ }
+ free(env->sc_dispatchers);
+ env->sc_dispatchers = NULL;
+ }
+ if (what & PURGE_PKI) {
+ while (dict_poproot(env->sc_pki_dict, (void **)&p)) {
+ freezero(p->pki_cert, p->pki_cert_len);
+ freezero(p->pki_key, p->pki_key_len);
+ EVP_PKEY_free(p->pki_pkey);
+ free(p);
+ }
+ free(env->sc_pki_dict);
+ env->sc_pki_dict = NULL;
+ } else if (what & PURGE_PKI_KEYS) {
+ iter_dict = NULL;
+ while (dict_iter(env->sc_pki_dict, &iter_dict, &k,
+ (void **)&p)) {
+ freezero(p->pki_cert, p->pki_cert_len);
+ p->pki_cert = NULL;
+ freezero(p->pki_key, p->pki_key_len);
+ p->pki_key = NULL;
+ EVP_PKEY_free(p->pki_pkey);
+ p->pki_pkey = NULL;
+ }
+ }
+}
+
+#ifndef CONFIG_MINIMUM
+
+void
+config_process(enum smtp_proc_type proc)
+{
+ struct rlimit rl;
+
+ smtpd_process = proc;
+ setproctitle("%s", proc_title(proc));
+
+ if (getrlimit(RLIMIT_NOFILE, &rl) == -1)
+ fatal("fdlimit: getrlimit");
+ rl.rlim_cur = rl.rlim_max;
+ if (setrlimit(RLIMIT_NOFILE, &rl) == -1)
+ if (errno != EINVAL)
+ fatal("fdlimit: setrlimit");
+}
+
+void
+config_peer(enum smtp_proc_type proc)
+{
+ struct mproc *p;
+
+ if (proc == smtpd_process)
+ fatal("config_peers: cannot peer with oneself");
+
+ if (proc == PROC_CONTROL)
+ p = p_control;
+ else if (proc == PROC_LKA)
+ p = p_lka;
+ else if (proc == PROC_PARENT)
+ p = p_parent;
+ else if (proc == PROC_QUEUE)
+ p = p_queue;
+ else if (proc == PROC_SCHEDULER)
+ p = p_scheduler;
+ else if (proc == PROC_PONY)
+ p = p_pony;
+ else if (proc == PROC_CA)
+ p = p_ca;
+ else
+ fatalx("bad peer");
+
+ mproc_enable(p);
+}
+
+#else
+
+void config_process(enum smtp_proc_type proc) {}
+void config_peer(enum smtp_proc_type proc) {}
+
+#endif