/* $OpenBSD: config.c,v 1.49 2018/12/28 14:21:02 eric Exp $ */ /* * Copyright (c) 2008 Pierre-Yves Ritschard * * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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_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_processors_dict = calloc(1, sizeof(*conf->sc_processors_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_processors_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_processors_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", "", 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", "", 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_processors_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", "", 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", "", 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", "", 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; sain->sin_len = sizeof(struct sockaddr_in); 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; sin6->sin6_len = sizeof(struct sockaddr_in6); 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) 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