From: Timo Sirainen Date: Thu, 10 Jul 2003 03:04:07 +0000 (+0300) Subject: New configuration file code. Some syntax changes, but tries to be somewhat X-Git-Tag: 1.1.alpha1~4496 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=0cb57ee35d4cab9c03434d7abf312c081ed554d4;p=thirdparty%2Fdovecot%2Fcore.git New configuration file code. Some syntax changes, but tries to be somewhat backwards compatible. SIGHUP now reverts back to old configuration if it detected errors in new one. --HG-- branch : HEAD --- diff --git a/dovecot-example.conf b/dovecot-example.conf index 53f4264831..07b47d92dc 100644 --- a/dovecot-example.conf +++ b/dovecot-example.conf @@ -18,13 +18,11 @@ # "[::]" listens in all IPv6 interfaces, but may also listen in all IPv4 # interfaces depending on the operating system. You can specify ports with # "host:port". -#imap_listen = * -#pop3_listen = * +#listen = * # IP or host address where to listen in for SSL connections. Defaults -# to above non-SSL equilevants if not specified. -#imaps_listen = -#pop3s_listen = +# to above if not specified. +#ssl_listen = # Disable SSL/TLS support. #ssl_disable = no @@ -73,16 +71,6 @@ # wish to run the whole Dovecot without roots. #login_chroot = yes - -## -## IMAP login process -## - -login = imap - -# Executable location. -#login_executable = /usr/libexec/dovecot/imap-login - # User to use for the login process. Create a completely new user for this, # and don't use it anywhere else. The user must also belong to a group where # only it has access, it's used to control access for authentication process. @@ -116,17 +104,6 @@ login = imap # logging in actually login_processes_count * max_logging_users. #login_max_logging_users = 256 -## -## POP3 login process -## - -# Settings default to same as above, so you don't have to set anything -# unless you want to override them. - -login = pop3 - -# Exception to above rule being the executable location. -#login_executable = /usr/libexec/dovecot/pop3-login ## ## Mail processes @@ -309,40 +286,46 @@ login = pop3 # for multiple users, as the users could ptrace() each others processes then. #mail_drop_priv_before_exec = no +# Set max. process size in megabytes. Most of the memory goes to mmap()ing +# files, so it shouldn't harm much even if this limit is set pretty high. +#mail_process_size = 256 + ## -## IMAP process +## IMAP specific settings ## -# Executable location -#imap_executable = /usr/libexec/dovecot/imap - -# Set max. process size in megabytes. Most of the memory goes to mmap()ing -# files, so it shouldn't harm much even if this limit is set pretty high. -#imap_process_size = 256 +protocol imap { + # Login executable location. + #login_executable = /usr/libexec/dovecot/imap-login -# Maximum IMAP command line length in bytes. Some clients generate very long -# command lines with huge mailboxes, so you may need to raise this if you get -# "Too long argument" or "IMAP command line too large" errors often. -#imap_max_line_length = 65536 + # IMAP executable location + #mail_executable = /usr/libexec/dovecot/imap -# Support for dynamically loadable modules. -#imap_use_modules = no -#imap_modules = /usr/lib/dovecot/imap + # Maximum IMAP command line length in bytes. Some clients generate very long + # command lines with huge mailboxes, so you may need to raise this if you get + # "Too long argument" or "IMAP command line too large" errors often. + #imap_max_line_length = 65536 + # Support for dynamically loadable modules. + #mail_use_modules = no + #mail_modules = /usr/lib/dovecot/imap +} + ## -## POP3 process +## POP3 specific settings ## -# Executable location -#pop3_executable = /usr/libexec/dovecot/pop3 +protocol pop3 { + # Login executable location. + #login_executable = /usr/libexec/dovecot/pop3-login -# Set max. process size in megabytes. Most of the memory goes to mmap()ing -# files, so it shouldn't harm much even if this limit is set pretty high. -#pop3_process_size = 256 + # POP3 executable location + #mail_executable = /usr/libexec/dovecot/pop3 -# Support for dynamically loadable modules. -#pop3_use_modules = no -#pop3_modules = /usr/lib/dovecot/pop3 + # Support for dynamically loadable modules. + #mail_use_modules = no + #mail_modules = /usr/lib/dovecot/pop3 +} ## ## Authentication processes @@ -358,12 +341,11 @@ login = pop3 # processes (unless they have different auth methods, and you're ok with # having different password for each method). -# Authentication process name. -auth = default +# Executable location +#auth_executable = /usr/libexec/dovecot/dovecot-auth -# Space separated list of wanted authentication mechanisms: -# plain digest-md5 anonymous -auth_mechanisms = plain +# Set max. process size in megabytes. +#auth_process_size = 256 # Space separated list of realms for SASL authentication mechanisms that need # them. You can leave it empty if you don't want to support multiple realms. @@ -374,43 +356,6 @@ auth_mechanisms = plain # Default realm to use if none was specified. #auth_default_realm = -# Where user database is kept: -# passwd: /etc/passwd or similiar, using getpwnam() -# passwd-file : passwd-like file with specified location -# static uid= gid= home=: static settings -# vpopmail: vpopmail library -# ldap : LDAP, see doc/dovecot-ldap.conf -# pgsql : a PostgreSQL database, see doc/dovecot-pgsql.conf -auth_userdb = passwd - -# Where password database is kept: -# passwd: /etc/passwd or similiar, using getpwnam() -# shadow: /etc/shadow or similiar, using getspnam() -# pam [ | *]: PAM authentication -# passwd-file : passwd-like file with specified location -# vpopmail: vpopmail authentication -# ldap : LDAP, see doc/dovecot-ldap.conf -# pgsql : a PostgreSQL database, see doc/dovecot-pgsql.conf -auth_passdb = pam - -# Executable location -#auth_executable = /usr/libexec/dovecot/dovecot-auth - -# Set max. process size in megabytes. -#auth_process_size = 256 - -# User to use for the process. This user needs access to only user and -# password databases, nothing else. Only shadow and pam authentication -# requires roots, so use something else if possible. -auth_user = root - -# Directory where to chroot the process. Most authentication backends don't -# work if this is set, and there's no point chrooting if auth_user is root. -#auth_chroot = - -# Number of authentication processes to create -#auth_count = 1 - # List of allowed characters in username. If the user-given username contains # a character not listed in here, the login automatically fails. This is just # an extra check to make sure user can't exploit any potential quote escaping @@ -425,18 +370,54 @@ auth_user = root # working. #auth_verbose = no +auth default { + # Space separated list of wanted authentication mechanisms: + # plain digest-md5 anonymous + auth_mechanisms = plain + + # Where user database is kept: + # passwd: /etc/passwd or similiar, using getpwnam() + # passwd-file : passwd-like file with specified location + # static uid= gid= home=: static settings + # vpopmail: vpopmail library + # ldap : LDAP, see doc/dovecot-ldap.conf + # pgsql : a PostgreSQL database, see doc/dovecot-pgsql.conf + auth_userdb = passwd + + # Where password database is kept: + # passwd: /etc/passwd or similiar, using getpwnam() + # shadow: /etc/shadow or similiar, using getspnam() + # pam [ | *]: PAM authentication + # passwd-file : passwd-like file with specified location + # vpopmail: vpopmail authentication + # ldap : LDAP, see doc/dovecot-ldap.conf + # pgsql : a PostgreSQL database, see doc/dovecot-pgsql.conf + auth_passdb = pam + + # User to use for the process. This user needs access to only user and + # password databases, nothing else. Only shadow and pam authentication + # requires roots, so use something else if possible. + auth_user = root + + # Directory where to chroot the process. Most authentication backends don't + # work if this is set, and there's no point chrooting if auth_user is root. + #auth_chroot = + + # Number of authentication processes to create + #auth_count = 1 +} + # digest-md5 authentication process. It requires special MD5 passwords which # /etc/shadow and PAM doesn't support, so we never need roots to handle it. # Note that the passwd-file is opened before chrooting and dropping root # privileges, so it may be 0600-root owned file. -#auth = digest_md5 -#auth_methods = digest-md5 -#auth_realms = -#auth_userdb = passwd-file /etc/passwd.imap -#auth_passdb = passwd-file /etc/passwd.imap -#auth_user = imapauth -#auth_chroot = +#auth digest_md5 { +# auth_methods = digest-md5 +# auth_userdb = passwd-file /etc/passwd.imap +# auth_passdb = passwd-file /etc/passwd.imap +# auth_user = imapauth +#} # if you plan to use only passwd-file, you don't need the two auth processes, # simply set "auth_methods = plain digest-md5" diff --git a/src/auth/db-ldap.c b/src/auth/db-ldap.c index dd33bc820c..0f49155873 100644 --- a/src/auth/db-ldap.c +++ b/src/auth/db-ldap.c @@ -14,6 +14,7 @@ #include "db-ldap.h" #include +#include /* Older versions may require calling ldap_result() twice */ #if LDAP_VENDOR_VERSION <= 20112 @@ -335,7 +336,8 @@ struct ldap_connection *db_ldap_init(const char *config_path) conn->config_path = p_strdup(pool, config_path); conn->set = default_ldap_settings; - settings_read(config_path, parse_setting, conn); + if (!settings_read(config_path, NULL, parse_setting, NULL, conn)) + exit(FATAL_DEFAULT); if (conn->set.base == NULL) i_fatal("LDAP: No base given"); diff --git a/src/auth/db-pgsql.c b/src/auth/db-pgsql.c index 821b289967..35dc6c8378 100644 --- a/src/auth/db-pgsql.c +++ b/src/auth/db-pgsql.c @@ -12,6 +12,7 @@ #include "db-pgsql.h" #include +#include #define DEF(type, name) { type, #name, offsetof(struct pgsql_settings, name) } @@ -142,7 +143,8 @@ struct pgsql_connection *db_pgsql_init(const char *config_path) conn->config_path = p_strdup(pool, config_path); conn->set = default_pgsql_settings; - settings_read(config_path, parse_setting, conn); + if (!settings_read(config_path, NULL, parse_setting, NULL, conn)) + exit(FATAL_DEFAULT); (void)pgsql_conn_open(conn); diff --git a/src/lib-settings/settings.c b/src/lib-settings/settings.c index e0fc8b0821..f3731375d2 100644 --- a/src/lib-settings/settings.c +++ b/src/lib-settings/settings.c @@ -59,19 +59,32 @@ parse_setting_from_defs(pool_t pool, struct setting_def *defs, void *base, #define IS_WHITE(c) ((c) == ' ' || (c) == '\t') -void settings_read(const char *path, settings_callback_t *callback, - void *context) +int settings_read(const char *path, const char *section, + settings_callback_t *callback, + settings_section_callback_t *sect_callback, void *context) { struct istream *input; - const char *errormsg; - char *line, *key; - int fd, linenum; + const char *errormsg, *next_section; + char *line, *key, *name; + int fd, linenum, skip, sections, root_section; fd = open(path, O_RDONLY); - if (fd < 0) - i_fatal("Can't open configuration file %s: %m", path); + if (fd < 0) { + i_error("Can't open configuration file %s: %m", path); + return FALSE; + } + + t_push(); + + if (section == NULL) { + skip = 0; + next_section = NULL; + } else { + skip = 1; + next_section = t_strcut(section, '/'); + } - linenum = 0; + linenum = 0; sections = 0; root_section = 0; errormsg = NULL; input = i_stream_create_file(fd, default_pool, 2048, TRUE); while ((line = i_stream_read_next_line(input)) != NULL) { linenum++; @@ -86,7 +99,9 @@ void settings_read(const char *path, settings_callback_t *callback, if (*line == '#' || *line == '\0') continue; - /* all lines must be in format "key = value" */ + /* a) "key = value" + b) section_type section_name { + c) } */ key = line; while (!IS_WHITE(*line) && *line != '\0') line++; @@ -95,21 +110,82 @@ void settings_read(const char *path, settings_callback_t *callback, while (IS_WHITE(*line)) line++; } - if (*line != '=') { - errormsg = "Missing value"; - } else { - /* skip whitespace after '=' */ + if (strcmp(key, "}") == 0 && *line == '\0') { + if (sections == 0) + errormsg = "Unexpected '}'"; + else { + if (skip > 0) + skip--; + else { + sect_callback(NULL, NULL, context, + &errormsg); + if (root_section == sections && + errormsg == NULL) { + /* we found the section, + now quit */ + break; + } + } + sections--; + } + } else if (*line == '=') { *line++ = '\0'; while (IS_WHITE(*line)) line++; - errormsg = callback(key, line, context); + errormsg = skip ? NULL : + callback(key, line, context); + } else { + line[-1] = '\0'; + + name = line; + while (!IS_WHITE(*line) && *line != '\0') + line++; + + if (*line != '\0') { + *line++ = '\0'; + while (IS_WHITE(*line)) + line++; + } + + if (*line != '{' || strcspn(line+1, " \t") != 0) + errormsg = "Missing value"; + else { + sections++; + if (next_section != NULL && + strcmp(next_section, name) == 0) { + section += strlen(next_section); + if (*section == '\0') { + skip = 0; + next_section = NULL; + root_section = sections; + } else { + i_assert(*section == '/'); + section++; + next_section = + t_strcut(section, '/'); + } + } + + if (skip > 0) + skip++; + else { + skip = sect_callback == NULL ? 1 : + !sect_callback(key, name, + context, + &errormsg); + } + } } if (errormsg != NULL) { - i_fatal("Error in configuration file %s line %d: %s", + i_error("Error in configuration file %s line %d: %s", path, linenum, errormsg); + break; } - }; + } i_stream_unref(input); + t_pop(); + + return errormsg == NULL; } diff --git a/src/lib-settings/settings.h b/src/lib-settings/settings.h index c56ea44a09..f01ec3a3e6 100644 --- a/src/lib-settings/settings.h +++ b/src/lib-settings/settings.h @@ -13,14 +13,20 @@ struct setting_def { size_t offset; }; +/* Return error message. When closing section, key = NULL, value = NULL. */ typedef const char *settings_callback_t(const char *key, const char *value, void *context); +/* Return TRUE if we want to go inside the section */ +typedef int settings_section_callback_t(const char *type, const char *name, + void *context, const char **errormsg); + const char * parse_setting_from_defs(pool_t pool, struct setting_def *defs, void *base, const char *key, const char *value); -void settings_read(const char *path, settings_callback_t *callback, - void *context); +int settings_read(const char *path, const char *section, + settings_callback_t *callback, + settings_section_callback_t *sect_callback, void *context); #endif diff --git a/src/master/auth-process.c b/src/master/auth-process.c index 94bb82b67d..7a2732ce22 100644 --- a/src/master/auth-process.c +++ b/src/master/auth-process.c @@ -295,7 +295,7 @@ static pid_t create_auth_process(struct auth_process_group *group) if (dup2(null_fd, 1) < 0) i_fatal("login: dup2(1) failed: %m"); - child_process_init_env(); + child_process_init_env(group->set->parent->defaults); /* move login communication handle to 3. do it last so we can be sure it's not closed afterwards. */ @@ -371,7 +371,8 @@ static void auth_process_group_create(struct auth_settings *auth_set) group->set = auth_set; /* create socket for listening auth requests from login */ - path = t_strconcat(set->login_dir, "/", auth_set->name, NULL); + path = t_strconcat(auth_set->parent->defaults->login_dir, "/", + auth_set->name, NULL); (void)unlink(path); (void)umask(0117); /* we want 0660 mode for the socket */ @@ -382,9 +383,10 @@ static void auth_process_group_create(struct auth_settings *auth_set) fd_close_on_exec(group->listen_fd, TRUE); /* set correct permissions */ - if (chown(path, geteuid(), set->login_gid) < 0) { + if (chown(path, geteuid(), auth_set->parent->defaults->login_gid) < 0) { i_fatal("login: chown(%s, %s, %s) failed: %m", - path, dec2str(geteuid()), dec2str(set->login_gid)); + path, dec2str(geteuid()), + dec2str(auth_set->parent->defaults->login_gid)); } group->next = process_groups; @@ -401,7 +403,8 @@ static void auth_process_group_destroy(struct auth_process_group *group) group->processes = next; } - (void)unlink(t_strconcat(set->login_dir, "/", group->set->name, NULL)); + (void)unlink(t_strconcat(group->set->parent->defaults->login_dir, "/", + group->set->name, NULL)); if (close(group->listen_fd) < 0) i_error("close(auth group %s) failed: %m", group->set->name); @@ -419,18 +422,28 @@ void auth_processes_destroy_all(void) } } +static void auth_process_groups_create(struct server_settings *server) +{ + struct auth_settings *auth_set; + + while (server != NULL) { + auth_set = server->auths; + for (; auth_set != NULL; auth_set = auth_set->next) + auth_process_group_create(auth_set); + + server = server->next; + } +} + static void auth_processes_start_missing(void *context __attr_unused__) { - struct auth_settings *auth_set; struct auth_process_group *group; unsigned int count; if (process_groups == NULL) { /* first time here, create the groups */ - auth_set = set->auths; - for (; auth_set != NULL; auth_set = auth_set->next) - auth_process_group_create(auth_set); + auth_process_groups_create(settings_root); } for (group = process_groups; group != NULL; group = group->next) { diff --git a/src/master/common.h b/src/master/common.h index 43e745da93..ed892cec83 100644 --- a/src/master/common.h +++ b/src/master/common.h @@ -20,18 +20,9 @@ enum { PROCESS_TYPE_MAX }; -enum { - FD_IMAP, - FD_IMAPS, - FD_POP3, - FD_POP3S, - - FD_MAX -}; - extern struct ioloop *ioloop; extern struct hash_table *pids; -extern int null_fd, mail_fd[FD_MAX], inetd_login_fd; +extern int null_fd, inetd_login_fd; #define IS_INETD() \ (inetd_login_fd != -1) @@ -46,7 +37,7 @@ extern int null_fd, mail_fd[FD_MAX], inetd_login_fd; #define PID_REMOVE_PROCESS_TYPE(pid) \ hash_remove(pids, POINTER_CAST(pid)) -void child_process_init_env(void); +void child_process_init_env(struct settings *set); /* misc */ #define VALIDATE_STR(str) \ diff --git a/src/master/login-process.c b/src/master/login-process.c index d28a556c4c..74d042588d 100644 --- a/src/master/login-process.c +++ b/src/master/login-process.c @@ -17,25 +17,6 @@ #include #include -struct login_group { - struct login_group *next; - - struct login_settings *set; - - unsigned int processes; - unsigned int listening_processes; - unsigned int wanted_processes_count; - - struct login_process *oldest_nonlisten_process; - struct login_process *newest_nonlisten_process; - - const char *executable; - const char *module_dir; - unsigned int process_size; - int process_type; - int *listen_fd, *ssl_listen_fd; -}; - struct login_process { struct login_group *group; struct login_process *prev_nonlisten, *next_nonlisten; @@ -71,36 +52,14 @@ static void login_process_destroy(struct login_process *p); static void login_process_unref(struct login_process *p); static int login_process_init_group(struct login_process *p); -static void login_group_create(struct login_settings *login_set) +static void login_group_create(struct settings *set) { struct login_group *group; - if (strstr(set->protocols, login_set->name) == NULL) { - /* not enabled */ - return; - } - group = i_new(struct login_group, 1); - group->set = login_set; - - if (strcmp(login_set->name, "imap") == 0) { - group->executable = set->imap_executable; - group->process_size = set->imap_process_size; - group->process_type = PROCESS_TYPE_IMAP; - group->listen_fd = &mail_fd[FD_IMAP]; - group->ssl_listen_fd = &mail_fd[FD_IMAPS]; - group->module_dir = !set->imap_use_modules ? NULL : - set->imap_modules; - } else if (strcmp(login_set->name, "pop3") == 0) { - group->executable = set->pop3_executable; - group->process_size = set->pop3_process_size; - group->process_type = PROCESS_TYPE_POP3; - group->listen_fd = &mail_fd[FD_POP3]; - group->ssl_listen_fd = &mail_fd[FD_POP3S]; - group->module_dir = !set->pop3_use_modules ? NULL : - set->pop3_modules; - } else - i_panic("Unknown login group name '%s'", login_set->name); + group->set = set; + group->process_type = set->protocol == MAIL_PROTOCOL_IMAP ? + PROCESS_TYPE_IMAP : PROCESS_TYPE_POP3; group->next = login_groups; login_groups = group; @@ -123,11 +82,7 @@ void auth_master_callback(struct auth_master_reply *reply, struct login_group *group = request->process->group; master_reply.success = - create_mail_process(request->fd, &request->ip, - group->executable, - group->module_dir, - group->process_size, - group->process_type, + create_mail_process(group, request->fd, &request->ip, reply, (const char *) data); } @@ -168,18 +123,29 @@ static void login_process_mark_nonlistening(struct login_process *p) } } -static struct login_group *login_group_process_find(const char *name) +static void login_process_groups_create(void) { - struct login_group *group; - struct login_settings *login; + struct server_settings *server; - if (login_groups == NULL) { - for (login = set->logins; login != NULL; login = login->next) - login_group_create(login); + for (server = settings_root; server != NULL; server = server->next) { + if (server->imap != NULL) + login_group_create(server->imap); + if (server->pop3 != NULL) + login_group_create(server->pop3); } +} + +static struct login_group * +login_group_process_find(const char *name, enum mail_protocol protocol) +{ + struct login_group *group; + + if (login_groups == NULL) + login_process_groups_create(); for (group = login_groups; group != NULL; group = group->next) { - if (strcmp(group->set->name, name) == 0) + if (strcmp(group->set->server->name, name) == 0 && + group->set->protocol == protocol) return group; } @@ -189,8 +155,9 @@ static struct login_group *login_group_process_find(const char *name) static int login_process_read_group(struct login_process *p) { struct login_group *group; - const char *name; + const char *name, *proto; char buf[256]; + enum mail_protocol protocol; unsigned int len; ssize_t ret; @@ -201,7 +168,7 @@ static int login_process_read_group(struct login_process *p) else { len = buf[0]; if (len >= sizeof(buf)) { - i_error("login: Process name length too large"); + i_error("login: Server name length too large"); return FALSE; } @@ -211,12 +178,29 @@ static int login_process_read_group(struct login_process *p) if (ret < 0) i_error("login: read() failed: %m"); else if (len == 0 || (size_t)ret != len) - i_error("login: Process name wasn't sent"); + i_error("login: Server name wasn't sent"); else { name = t_strndup(buf, len); - group = login_group_process_find(name); + proto = strchr(buf, '/'); + if (proto == NULL) { + i_error("login: Missing protocol from server name '%s'", + name); + return FALSE; + } + name = t_strdup_until(buf, proto++); + + if (strcmp(proto, "imap") == 0) + protocol = MAIL_PROTOCOL_IMAP; + else if (strcmp(proto, "pop3") == 0) + protocol = MAIL_PROTOCOL_IMAP; + else { + i_error("login: Unknown protocol '%s'", proto); + return FALSE; + } + + group = login_group_process_find(name, protocol); if (group == NULL) { - i_error("login: Unknown process group '%s'", name); + i_error("login: Unknown server name '%s'", name); return FALSE; } @@ -384,12 +368,13 @@ static void login_process_unref(struct login_process *p) static void login_process_init_env(struct login_group *group, pid_t pid) { - child_process_init_env(); + struct settings *set = group->set; + + child_process_init_env(set); /* setup access environment - needs to be done after clean_child_process() since it clears environment */ - restrict_access_set_env(group->set->user, - group->set->uid, set->login_gid, + restrict_access_set_env(set->login_user, set->login_uid, set->login_gid, set->login_chroot ? set->login_dir : NULL, 0, 0); @@ -398,7 +383,8 @@ static void login_process_init_env(struct login_group *group, pid_t pid) if (!set->ssl_disable) { env_put(t_strconcat("SSL_CERT_FILE=", set->ssl_cert_file, NULL)); - env_put(t_strconcat("SSL_KEY_FILE=", set->ssl_key_file, NULL)); + env_put(t_strconcat("SSL_KEY_FILE=", + set->ssl_key_file, NULL)); env_put(t_strconcat("SSL_PARAM_FILE=", set->ssl_parameters_file, NULL)); } @@ -410,12 +396,12 @@ static void login_process_init_env(struct login_group *group, pid_t pid) if (set->verbose_ssl) env_put("VERBOSE_SSL=1"); - if (group->set->process_per_connection) { + if (set->login_process_per_connection) { env_put("PROCESS_PER_CONNECTION=1"); env_put("MAX_LOGGING_USERS=1"); } else { env_put(t_strdup_printf("MAX_LOGGING_USERS=%u", - group->set->max_logging_users)); + set->login_max_logging_users)); } env_put(t_strdup_printf("PROCESS_UID=%s", dec2str(pid))); @@ -427,14 +413,14 @@ static pid_t create_login_process(struct login_group *group) pid_t pid; int fd[2]; - if (group->set->process_per_connection && + if (group->set->login_process_per_connection && group->processes - group->listening_processes >= - group->set->max_logging_users) { + group->set->login_max_logging_users) { if (group->oldest_nonlisten_process != NULL) login_process_destroy(group->oldest_nonlisten_process); } - if (group->set->uid == 0) + if (group->set->login_uid == 0) i_fatal("Login process must not run as root"); /* create communication to process with a socket pair */ @@ -461,12 +447,12 @@ static pid_t create_login_process(struct login_group *group) } /* move the listen handle */ - if (dup2(*group->listen_fd, LOGIN_LISTEN_FD) < 0) + if (dup2(group->set->listen_fd, LOGIN_LISTEN_FD) < 0) i_fatal("login: dup2(listen_fd) failed: %m"); fd_close_on_exec(LOGIN_LISTEN_FD, FALSE); /* move the SSL listen handle */ - if (dup2(*group->ssl_listen_fd, LOGIN_SSL_LISTEN_FD) < 0) + if (dup2(group->set->ssl_listen_fd, LOGIN_SSL_LISTEN_FD) < 0) i_fatal("login: dup2(ssl_listen_fd) failed: %m"); fd_close_on_exec(LOGIN_SSL_LISTEN_FD, FALSE); @@ -480,26 +466,31 @@ static pid_t create_login_process(struct login_group *group) login_process_init_env(group, getpid()); - if (!set->login_chroot) { + if (!group->set->login_chroot) { /* no chrooting, but still change to the directory */ - if (chdir(set->login_dir) < 0) - i_fatal("chdir(%s) failed: %m", set->login_dir); + if (chdir(group->set->login_dir) < 0) { + i_fatal("chdir(%s) failed: %m", + group->set->login_dir); + } } - restrict_process_size(group->set->process_size, (unsigned int)-1); + restrict_process_size(group->set->login_process_size, (unsigned int)-1); /* make sure we don't leak syslog fd, but do it last so that any errors above will be logged */ closelog(); /* hide the path, it's ugly */ - argv[0] = strrchr(group->set->executable, '/'); - if (argv[0] == NULL) argv[0] = group->set->executable; else argv[0]++; + argv[0] = strrchr(group->set->login_executable, '/'); + if (argv[0] == NULL) + argv[0] = group->set->login_executable; + else + argv[0]++; - execv(group->set->executable, (char **) argv); + execv(group->set->login_executable, (char **) argv); i_fatal_status(FATAL_EXEC, "execv(%s) failed: %m", - group->set->executable); + group->set->login_executable); return -1; } @@ -534,10 +525,11 @@ void login_processes_destroy_all(void) static void login_group_start_missings(struct login_group *group) { - if (!group->set->process_per_connection) { + if (!group->set->login_process_per_connection) { /* create max. one process every second, that way if it keeps dying all the time we don't eat all cpu with fork()ing. */ - if (group->listening_processes < group->set->processes_count) + if (group->listening_processes < + group->set->login_processes_count) (void)create_login_process(group); return; } @@ -549,15 +541,20 @@ static void login_group_start_missings(struct login_group *group) double their amount (unless we've hit the high limit). Then for each second that didn't use all existing processes, drop the max. process count by one. */ - if (group->wanted_processes_count < group->set->processes_count) - group->wanted_processes_count = group->set->processes_count; - else if (group->listening_processes == 0) + if (group->wanted_processes_count < group->set->login_processes_count) { + group->wanted_processes_count = + group->set->login_processes_count; + } else if (group->listening_processes == 0) group->wanted_processes_count *= 2; - else if (group->wanted_processes_count > group->set->processes_count) + else if (group->wanted_processes_count > + group->set->login_processes_count) group->wanted_processes_count--; - if (group->wanted_processes_count > group->set->max_processes_count) - group->wanted_processes_count = group->set->max_processes_count; + if (group->wanted_processes_count > + group->set->login_max_processes_count) { + group->wanted_processes_count = + group->set->login_max_processes_count; + } while (group->listening_processes < group->wanted_processes_count) (void)create_login_process(group); @@ -567,12 +564,9 @@ static void login_processes_start_missing(void *context __attr_unused__) { struct login_group *group; - struct login_settings *login; - if (login_groups == NULL) { - for (login = set->logins; login != NULL; login = login->next) - login_group_create(login); - } + if (login_groups == NULL) + login_process_groups_create(); for (group = login_groups; group != NULL; group = group->next) login_group_start_missings(group); diff --git a/src/master/login-process.h b/src/master/login-process.h index 38883f02fc..b8eede788b 100644 --- a/src/master/login-process.h +++ b/src/master/login-process.h @@ -1,6 +1,20 @@ #ifndef __LOGIN_PROCESS_H #define __LOGIN_PROCESS_H +struct login_group { + struct login_group *next; + + int process_type; + struct settings *set; + + unsigned int processes; + unsigned int listening_processes; + unsigned int wanted_processes_count; + + struct login_process *oldest_nonlisten_process; + struct login_process *newest_nonlisten_process; +}; + void login_process_abormal_exit(pid_t pid); void login_processes_destroy_all(void); diff --git a/src/master/mail-process.c b/src/master/mail-process.c index abeb2b6330..2359020c28 100644 --- a/src/master/mail-process.c +++ b/src/master/mail-process.c @@ -9,6 +9,7 @@ #include "restrict-process-size.h" #include "var-expand.h" #include "mail-process.h" +#include "login-process.h" #include #include @@ -18,13 +19,20 @@ static unsigned int mail_process_count = 0; -static int validate_uid_gid(uid_t uid, gid_t gid, const char *user) +static int validate_uid_gid(struct settings *set, uid_t uid, gid_t gid, + const char *user) { if (uid == 0) { i_error("Logins with UID 0 not permitted (user %s)", user); return FALSE; } + if (set->login_uid == uid && geteuid() != uid) { + i_error("Can't log in using login processes UID %s (user %s) " + "(see login_user in config file).", + dec2str(uid), user); + } + if (uid < (uid_t)set->first_valid_uid || (set->last_valid_uid != 0 && uid > (uid_t)set->last_valid_uid)) { i_error("Logins with UID %s (user %s) not permitted " @@ -44,7 +52,7 @@ static int validate_uid_gid(uid_t uid, gid_t gid, const char *user) return TRUE; } -static int validate_chroot(const char *dir) +static int validate_chroot(struct settings *set, const char *dir) { const char *const *chroot_dirs; @@ -95,30 +103,31 @@ static const char *expand_mail_env(const char *env, const char *user, return str_c(str); } -int create_mail_process(int socket, struct ip_addr *ip, - const char *executable, const char *module_dir, - unsigned int process_size, int process_type, +int create_mail_process(struct login_group *group, int socket, + struct ip_addr *ip, struct auth_master_reply *reply, const char *data) { static const char *argv[] = { NULL, NULL, NULL }; + struct settings *set = group->set; const char *addr, *mail, *chroot_dir, *home_dir, *full_home_dir; char title[1024]; pid_t pid; int i, err; + // FIXME: per-group if (mail_process_count == set->max_mail_processes) { i_error("Maximum number of mail processes exceeded"); return FALSE; } - if (!validate_uid_gid(reply->uid, reply->gid, + if (!validate_uid_gid(set, reply->uid, reply->gid, data + reply->virtual_user_idx)) return FALSE; home_dir = data + reply->home_idx; chroot_dir = data + reply->chroot_idx; - if (*chroot_dir != '\0' && !validate_chroot(chroot_dir)) { + if (*chroot_dir != '\0' && !validate_chroot(set, chroot_dir)) { i_error("Invalid chroot directory: %s", chroot_dir); return FALSE; } @@ -132,11 +141,11 @@ int create_mail_process(int socket, struct ip_addr *ip, if (pid != 0) { /* master */ mail_process_count++; - PID_ADD_PROCESS_TYPE(pid, process_type); + PID_ADD_PROCESS_TYPE(pid, group->process_type); return TRUE; } - child_process_init_env(); + child_process_init_env(set); /* move the client socket into stdin and stdout fds */ fd_close_on_exec(socket, FALSE); @@ -154,7 +163,7 @@ int create_mail_process(int socket, struct ip_addr *ip, reply->uid, reply->gid, chroot_dir, set->first_valid_gid, set->last_valid_gid); - restrict_process_size(process_size, (unsigned int)-1); + restrict_process_size(group->set->mail_process_size, (unsigned int)-1); if (*home_dir != '\0') { full_home_dir = *chroot_dir == '\0' ? home_dir : @@ -200,8 +209,12 @@ int create_mail_process(int socket, struct ip_addr *ip, if (set->mbox_read_dotlock) env_put("MBOX_READ_DOTLOCK=1"); - if (module_dir != NULL && *module_dir != '\0') - env_put(t_strconcat("MODULE_DIR=", module_dir, NULL)); + if (group->set->mail_use_modules && + group->set->mail_modules != NULL && + *group->set->mail_modules != '\0') { + env_put(t_strconcat("MODULE_DIR=", + group->set->mail_modules, NULL)); + } /* user given environment - may be malicious. virtual_user comes from auth process, but don't trust that too much either. Some auth @@ -236,16 +249,20 @@ int create_mail_process(int socket, struct ip_addr *ip, restrict_access_by_env(TRUE); /* hide the path, it's ugly */ - argv[0] = strrchr(executable, '/'); - if (argv[0] == NULL) argv[0] = executable; else argv[0]++; + argv[0] = strrchr(group->set->mail_executable, '/'); + if (argv[0] == NULL) + argv[0] = group->set->mail_executable; + else + argv[0]++; - execv(executable, (char **) argv); + execv(group->set->mail_executable, (char **) argv); err = errno; for (i = 0; i < 3; i++) (void)close(i); - i_fatal_status(FATAL_EXEC, "execv(%s) failed: %m", executable); + i_fatal_status(FATAL_EXEC, "execv(%s) failed: %m", + group->set->mail_executable); /* not reached */ return FALSE; diff --git a/src/master/mail-process.h b/src/master/mail-process.h index d74a015a7b..9d22b4a646 100644 --- a/src/master/mail-process.h +++ b/src/master/mail-process.h @@ -1,11 +1,11 @@ #ifndef __MAIL_PROCESS_H #define __MAIL_PROCESS_H +struct login_group; struct auth_master_reply; -int create_mail_process(int socket, struct ip_addr *ip, - const char *executable, const char *module_dir, - unsigned int process_size, int process_type, +int create_mail_process(struct login_group *group, int socket, + struct ip_addr *ip, struct auth_master_reply *reply, const char *data); void mail_process_destroyed(pid_t pid); diff --git a/src/master/main.c b/src/master/main.c index f469451599..3ac34ea2ea 100644 --- a/src/master/main.c +++ b/src/master/main.c @@ -34,7 +34,7 @@ static struct timeout *to; struct ioloop *ioloop; struct hash_table *pids; -int null_fd, mail_fd[FD_MAX], inetd_login_fd; +int null_fd, inetd_login_fd; int validate_str(const char *str, size_t max_len) { @@ -48,7 +48,7 @@ int validate_str(const char *str, size_t max_len) return FALSE; } -void child_process_init_env(void) +void child_process_init_env(struct settings *set) { /* remove all environment, we don't need them */ env_clean(); @@ -83,7 +83,8 @@ static void settings_reload(void) login_processes_destroy_all(); auth_processes_destroy_all(); - master_settings_read(configfile); + if (!master_settings_read(configfile)) + i_warning("Invalid configuration, keeping old one"); } static const char *get_exit_status_message(enum fatal_exit_status status) @@ -218,57 +219,65 @@ static struct ip_addr *resolve_ip(const char *name, unsigned int *port) return ip; } -static void listen_protocols(void) +static void listen_protocols(struct settings *set) { - struct ip_addr *imap_ip, *imaps_ip, *pop3_ip, *pop3s_ip, *ip; + struct ip_addr *normal_ip, *ssl_ip, *ip; const char *const *proto; - unsigned int imap_port = 143; - unsigned int pop3_port = 110; + unsigned int normal_port, ssl_port, port; + int *fd; + + normal_port = set->protocol == MAIL_PROTOCOL_IMAP ? 143 : 110; #ifdef HAVE_SSL - unsigned int imaps_port = 993; - unsigned int pop3s_port = 995; + ssl_port = set->protocol == MAIL_PROTOCOL_IMAP ? 993 : 995; #else - unsigned int imaps_port = 0; - unsigned int pop3s_port = 0; + ssl_port = 0; #endif - unsigned int port; - int *fd, i; /* resolve */ - imap_ip = resolve_ip(set->imap_listen, &imap_port); - imaps_ip = resolve_ip(set->imaps_listen, &imaps_port); - pop3_ip = resolve_ip(set->pop3_listen, &pop3_port); - pop3s_ip = resolve_ip(set->pop3s_listen, &pop3s_port); + normal_ip = resolve_ip(set->listen, &normal_port); + ssl_ip = resolve_ip(set->ssl_listen, &ssl_port); - if (imaps_ip == NULL && set->imaps_listen == NULL) - imaps_ip = imap_ip; - if (pop3s_ip == NULL && set->pop3s_listen == NULL) - pop3s_ip = pop3_ip; + if (ssl_ip == NULL && set->ssl_listen == NULL) + ssl_ip = normal_ip; /* register wanted protocols */ for (proto = t_strsplit(set->protocols, " "); *proto != NULL; proto++) { + fd = NULL; ip = NULL; port = 0; if (strcasecmp(*proto, "imap") == 0) { - fd = &mail_fd[FD_IMAP]; ip = imap_ip; port = imap_port; + if (set->protocol == MAIL_PROTOCOL_IMAP) { + fd = &set->listen_fd; + port = normal_port; ip = normal_ip; + } } else if (strcasecmp(*proto, "imaps") == 0) { - fd = &mail_fd[FD_IMAPS]; ip = imaps_ip; - port = set->ssl_disable ? 0 : imaps_port; + if (set->protocol == MAIL_PROTOCOL_IMAP && + !set->ssl_disable) { + fd = &set->ssl_listen_fd; + port = ssl_port; ip = ssl_ip; + } } else if (strcasecmp(*proto, "pop3") == 0) { - fd = &mail_fd[FD_POP3]; ip = pop3_ip; port = pop3_port; + if (set->protocol == MAIL_PROTOCOL_POP3) { + fd = &set->listen_fd; + port = normal_port; ip = normal_ip; + } } else if (strcasecmp(*proto, "pop3s") == 0) { - fd = &mail_fd[FD_POP3S]; ip = pop3s_ip; - port = set->ssl_disable ? 0 : pop3s_port; + if (set->protocol == MAIL_PROTOCOL_POP3 && + !set->ssl_disable) { + fd = &set->ssl_listen_fd; + port = ssl_port; ip = ssl_ip; + } } else { i_fatal("Unknown protocol %s", *proto); } + if (fd == NULL) + continue; + if (*fd != -1) i_fatal("Protocol %s given more than once", *proto); - if (port == 0) { - *fd = dup(null_fd); - if (*fd == -1) - i_fatal("dup(null_fd) failed: %m"); - } else { + if (port == 0) + *fd = null_fd; + else { *fd = net_listen(ip, &port); if (*fd == -1) i_fatal("listen(%d) failed: %m", port); @@ -277,19 +286,42 @@ static void listen_protocols(void) fd_close_on_exec(*fd, TRUE); } - for (i = 0; i < FD_MAX; i++) { - if (mail_fd[i] == -1) { - mail_fd[i] = dup(null_fd); - if (mail_fd[i] == -1) - i_fatal("dup(mail_fd[%d]) failed: %m", i); - fd_close_on_exec(mail_fd[i], TRUE); - } + if (set->listen_fd == -1) + set->listen_fd = null_fd; + if (set->ssl_listen_fd == -1) + set->ssl_listen_fd = null_fd; +} + +static int have_stderr_set(struct settings *set) +{ + if (set->log_path != NULL && + strcmp(set->log_path, "/dev/stderr") == 0) + return TRUE; + + if (set->info_log_path != NULL && + strcmp(set->info_log_path, "/dev/stderr") == 0) + return TRUE; + + return FALSE; +} + +static int have_stderr(struct server_settings *server) +{ + while (server != NULL) { + if (server->imap != NULL && have_stderr_set(server->imap)) + return TRUE; + if (server->pop3 != NULL && have_stderr_set(server->pop3)) + return TRUE; + + server = server->next; } + + return FALSE; } static void open_fds(void) { - int i; + struct server_settings *server; /* initialize fds. */ null_fd = open("/dev/null", O_RDONLY); @@ -303,11 +335,15 @@ static void open_fds(void) fd_close_on_exec(null_fd, TRUE); } - for (i = 0; i < FD_MAX; i++) - mail_fd[i] = -1; - - if (!IS_INETD()) - listen_protocols(); + if (!IS_INETD()) { + server = settings_root; + for (; server != NULL; server = server->next) { + if (server->imap != NULL) + listen_protocols(server->imap); + if (server->pop3 != NULL) + listen_protocols(server->pop3); + } + } /* close stdin and stdout. close stderr unless we're logging into /dev/stderr. */ @@ -316,16 +352,13 @@ static void open_fds(void) if (dup2(null_fd, 1) < 0) i_fatal("dup2(1) failed: %m"); - if ((set->log_path == NULL || - strcmp(set->log_path, "/dev/stderr") != 0) && - (set->info_log_path == NULL || - strcmp(set->info_log_path, "/dev/stderr") != 0)) { + if (!have_stderr(settings_root)) { if (dup2(null_fd, 2) < 0) i_fatal("dup2(2) failed: %m"); } } -static void open_logfile(void) +static void open_logfile(struct settings *set) { if (set->log_path == NULL) i_set_failure_syslog("dovecot", LOG_NDELAY, LOG_MAIL); @@ -347,7 +380,7 @@ static void main_init(void) /* deny file access from everyone else except owner */ (void)umask(0077); - open_logfile(); + open_logfile(settings_root->defaults); lib_init_signals(sig_quit); @@ -361,8 +394,6 @@ static void main_init(void) static void main_deinit(void) { - int i; - if (lib_signal_kill != 0) i_warning("Killed with signal %d", lib_signal_kill); @@ -378,18 +409,11 @@ static void main_deinit(void) if (close(null_fd) < 0) i_error("close(null_fd) failed: %m"); - for (i = 0; i < FD_MAX; i++) { - if (mail_fd[i] != -1) { - if (close(mail_fd[i]) < 0) - i_error("close(mail_fd[%d]) failed: %m", i); - } - } - hash_destroy(pids); closelog(); } -static void daemonize(void) +static void daemonize(struct settings *set) { pid_t pid; @@ -448,14 +472,15 @@ int main(int argc, char *argv[]) /* read and verify settings before forking */ master_settings_init(); - master_settings_read(configfile); + if (!master_settings_read(configfile)) + exit(FATAL_DEFAULT); open_fds(); /* we don't need any environment */ env_clean(); if (!foreground) - daemonize(); + daemonize(settings_root->defaults); ioloop = io_loop_create(system_pool); diff --git a/src/master/master-settings.c b/src/master/master-settings.c index f045c66fcf..0ef0c5928a 100644 --- a/src/master/master-settings.c +++ b/src/master/master-settings.c @@ -12,6 +12,22 @@ #include #include +enum settings_type { + SETTINGS_TYPE_ROOT, + SETTINGS_TYPE_SERVER, + SETTINGS_TYPE_AUTH +}; + +struct settings_parse_ctx { + enum settings_type type, parent_type; + enum mail_protocol protocol; + + struct server_settings *root, *server; + struct auth_settings *auth; + + int level; +}; + #define DEF(type, name) \ { type, #name, offsetof(struct settings, name) } @@ -24,10 +40,8 @@ static struct setting_def setting_defs[] = { /* general */ DEF(SET_STR, protocols), - DEF(SET_STR, imap_listen), - DEF(SET_STR, imaps_listen), - DEF(SET_STR, pop3_listen), - DEF(SET_STR, pop3s_listen), + DEF(SET_STR, listen), + DEF(SET_STR, ssl_listen), DEF(SET_BOOL, ssl_disable), DEF(SET_STR, ssl_cert_file), @@ -35,11 +49,20 @@ static struct setting_def setting_defs[] = { DEF(SET_STR, ssl_parameters_file), DEF(SET_STR, ssl_parameters_regenerate), DEF(SET_BOOL, disable_plaintext_auth), + DEF(SET_BOOL, verbose_ssl), /* login */ DEF(SET_STR, login_dir), + DEF(SET_STR, login_executable), + DEF(SET_STR, login_user), + + DEF(SET_BOOL, login_process_per_connection), DEF(SET_BOOL, login_chroot), - DEF(SET_BOOL, verbose_ssl), + + DEF(SET_INT, login_process_size), + DEF(SET_INT, login_processes_count), + DEF(SET_INT, login_max_processes_count), + DEF(SET_INT, login_max_logging_users), /* mail */ DEF(SET_STR, valid_chroot_dirs), @@ -70,36 +93,13 @@ static struct setting_def setting_defs[] = { DEF(SET_INT, umask), DEF(SET_BOOL, mail_drop_priv_before_exec), + DEF(SET_STR, mail_executable), + DEF(SET_INT, mail_process_size), + DEF(SET_BOOL, mail_use_modules), + DEF(SET_STR, mail_modules), + /* imap */ - DEF(SET_STR, imap_executable), - DEF(SET_INT, imap_process_size), DEF(SET_INT, imap_max_line_length), - DEF(SET_BOOL, imap_use_modules), - DEF(SET_STR, imap_modules), - - /* pop3 */ - DEF(SET_STR, pop3_executable), - DEF(SET_INT, pop3_process_size), - DEF(SET_BOOL, pop3_use_modules), - DEF(SET_STR, pop3_modules), - - { 0, NULL, 0 } -}; - -#undef DEF -#define DEF(type, name) \ - { type, #name, offsetof(struct login_settings, name) } - -static struct setting_def login_setting_defs[] = { - DEF(SET_STR, executable), - DEF(SET_STR, user), - - DEF(SET_BOOL, process_per_connection), - - DEF(SET_INT, process_size), - DEF(SET_INT, processes_count), - DEF(SET_INT, max_processes_count), - DEF(SET_INT, max_logging_users), { 0, NULL, 0 } }; @@ -130,6 +130,9 @@ static struct setting_def auth_setting_defs[] = { }; struct settings default_settings = { + MEMBER(server) NULL, + MEMBER(protocol) 0, + /* common */ MEMBER(base_dir) PKG_RUNDIR, MEMBER(log_path) NULL, @@ -138,10 +141,8 @@ struct settings default_settings = { /* general */ MEMBER(protocols) "imap imaps", - MEMBER(imap_listen) "*", - MEMBER(imaps_listen) NULL, - MEMBER(pop3_listen) "*", - MEMBER(pop3s_listen) NULL, + MEMBER(listen) "*", + MEMBER(ssl_listen) NULL, MEMBER(ssl_disable) FALSE, MEMBER(ssl_cert_file) SSLDIR"/certs/dovecot.pem", @@ -149,11 +150,20 @@ struct settings default_settings = { MEMBER(ssl_parameters_file) "ssl-parameters.dat", MEMBER(ssl_parameters_regenerate) 24, MEMBER(disable_plaintext_auth) FALSE, + MEMBER(verbose_ssl) FALSE, /* login */ MEMBER(login_dir) "login", + MEMBER(login_executable) NULL, + MEMBER(login_user) "dovecot", + + MEMBER(login_process_per_connection) TRUE, MEMBER(login_chroot) TRUE, - MEMBER(verbose_ssl) FALSE, + + MEMBER(login_process_size) 16, + MEMBER(login_processes_count) 3, + MEMBER(login_max_processes_count) 128, + MEMBER(login_max_logging_users) 256, /* mail */ MEMBER(valid_chroot_dirs) NULL, @@ -184,42 +194,23 @@ struct settings default_settings = { MEMBER(umask) 0077, MEMBER(mail_drop_priv_before_exec) FALSE, + MEMBER(mail_executable) PKG_LIBEXECDIR"/imap", + MEMBER(mail_process_size) 256, + MEMBER(mail_use_modules) FALSE, + MEMBER(mail_modules) PKG_LIBDIR"/imap", + /* imap */ - MEMBER(imap_executable) PKG_LIBEXECDIR"/imap", - MEMBER(imap_process_size) 256, MEMBER(imap_max_line_length) 65536, - MEMBER(imap_use_modules) FALSE, - MEMBER(imap_modules) PKG_LIBDIR"/imap", - - /* pop3 */ - MEMBER(pop3_executable) PKG_LIBEXECDIR"/pop3", - MEMBER(pop3_process_size) 256, - MEMBER(pop3_use_modules) FALSE, - MEMBER(pop3_modules) PKG_LIBDIR"/imap", + /* .. */ + MEMBER(login_uid) 0, MEMBER(login_gid) 0, - MEMBER(auths) NULL, - MEMBER(logins) NULL -}; - -struct login_settings default_login_settings = { - MEMBER(next) NULL, - MEMBER(name) NULL, - - MEMBER(executable) NULL, - MEMBER(user) "dovecot", - - MEMBER(process_per_connection) TRUE, - - MEMBER(process_size) 16, - MEMBER(processes_count) 3, - MEMBER(max_processes_count) 128, - MEMBER(max_logging_users) 256, - - MEMBER(uid) 0 /* generated */ + MEMBER(listen_fd) -1, + MEMBER(ssl_listen_fd) -1 }; struct auth_settings default_auth_settings = { + MEMBER(parent) NULL, MEMBER(next) NULL, MEMBER(name) NULL, @@ -241,8 +232,8 @@ struct auth_settings default_auth_settings = { MEMBER(process_size) 256 }; -static pool_t settings_pool; -struct settings *set = NULL; +static pool_t settings_pool, settings2_pool; +struct server_settings *settings_root = NULL; static void fix_base_path(struct settings *set, const char **str) { @@ -252,49 +243,42 @@ static void fix_base_path(struct settings *set, const char **str) } } -static void get_login_uid(struct settings *set, - struct login_settings *login_set) +static int get_login_uid(struct settings *set) { struct passwd *pw; - if ((pw = getpwnam(login_set->user)) == NULL) - i_fatal("Login user doesn't exist: %s", login_set->user); + if ((pw = getpwnam(set->login_user)) == NULL) { + i_error("Login user doesn't exist: %s", set->login_user); + return FALSE; + } if (set->login_gid == 0) set->login_gid = pw->pw_gid; else if (set->login_gid != pw->pw_gid) { - i_fatal("All login process users must belong to same group " + i_error("All login process users must belong to same group " "(%s vs %s)", dec2str(set->login_gid), dec2str(pw->pw_gid)); + return FALSE; } - login_set->uid = pw->pw_uid; + set->login_uid = pw->pw_uid; + return TRUE; } -static void auth_settings_verify(struct auth_settings *auth) +static int auth_settings_verify(struct auth_settings *auth) { - if (access(auth->executable, X_OK) < 0) - i_fatal("Can't use auth executable %s: %m", auth->executable); + if (access(auth->executable, X_OK) < 0) { + i_error("Can't use auth executable %s: %m", auth->executable); + return FALSE; + } - fix_base_path(set, &auth->chroot); + fix_base_path(auth->parent->defaults, &auth->chroot); if (auth->chroot != NULL && access(auth->chroot, X_OK) < 0) { - i_fatal("Can't access auth chroot directory %s: %m", + i_error("Can't access auth chroot directory %s: %m", auth->chroot); + return FALSE; } -} - -static void login_settings_verify(struct login_settings *login) -{ - if (strstr(set->protocols, login->name) != NULL) { - if (access(login->executable, X_OK) < 0) - i_fatal("Can't use login executable %s: %m", - login->executable); - } - - if (login->processes_count < 1) - i_fatal("login_processes_count must be at least 1"); - if (login->max_logging_users < 1) - i_fatal("max_logging_users must be at least 1"); + return TRUE; } static const char *get_directory(const char *path) @@ -311,82 +295,78 @@ static const char *get_directory(const char *path) } } -static void settings_verify(struct settings *set) +static int settings_is_active(struct settings *set) +{ + if (set->protocol == MAIL_PROTOCOL_IMAP) { + if (strstr(set->protocols, "imap") == NULL) + return FALSE; + } else { + if (strstr(set->protocols, "pop3") == NULL) + return FALSE; + } + + return TRUE; +} + +static int settings_verify(struct settings *set) { - struct login_settings *login; - struct auth_settings *auth; const char *const *str; const char *dir; int dotlock_got, fcntl_got, flock_got; - for (login = set->logins; login != NULL; login = login->next) { - get_login_uid(set, login); - login_settings_verify(login); - } + if (!get_login_uid(set)) + return FALSE; - if (strstr(set->protocols, "imap") != NULL) { - if (access(set->imap_executable, X_OK) < 0) { - i_fatal("Can't use imap executable %s: %m", - set->imap_executable); - } -#ifdef HAVE_MODULES - if (set->imap_use_modules && - access(set->imap_modules, R_OK | X_OK) < 0) { - i_fatal("Can't access imap module directory: %s: %m", - set->imap_modules); - } -#else - if (set->imap_use_modules) { - i_warning("Module support wasn't built into Dovecot, " - "ignoring imap_use_modules setting"); - } -#endif + if (access(set->mail_executable, X_OK) < 0) { + i_error("Can't use mail executable %s: %m", + set->mail_executable); + return FALSE; } - if (strstr(set->protocols, "pop3") != NULL) { - if (access(set->pop3_executable, X_OK) < 0) { - i_fatal("Can't use pop3 executable %s: %m", - set->pop3_executable); - } #ifdef HAVE_MODULES - if (set->pop3_use_modules && - access(set->pop3_modules, R_OK | X_OK) < 0) { - i_fatal("Can't access pop3 module directory: %s: %m", - set->imap_modules); - } + if (set->mail_use_modules && + access(set->mail_modules, R_OK | X_OK) < 0) { + i_error("Can't access mail module directory: %s: %m", + set->mail_modules); + return FALSE; + } #else - if (set->pop3_use_modules) { - i_warning("Module support wasn't built into Dovecot, " - "ignoring pop3_use_modules setting"); - } -#endif + if (set->mail_use_modules) { + i_warning("Module support wasn't built into Dovecot, " + "ignoring mail_use_modules setting"); } +#endif if (set->log_path != NULL && access(set->log_path, W_OK) < 0) { dir = get_directory(set->log_path); - if (access(dir, W_OK) < 0) - i_fatal("Can't write to log directory %s: %m", dir); + if (access(dir, W_OK) < 0) { + i_error("Can't write to log directory %s: %m", dir); + return FALSE; + } } if (set->info_log_path != NULL && access(set->info_log_path, W_OK) < 0) { dir = get_directory(set->info_log_path); if (access(dir, W_OK) < 0) { - i_fatal("Can't write to info log directory %s: %m", + i_error("Can't write to info log directory %s: %m", dir); + return FALSE; } } #ifdef HAVE_SSL if (!set->ssl_disable) { if (access(set->ssl_cert_file, R_OK) < 0) { - i_fatal("Can't use SSL certificate %s: %m", + i_error("Can't use SSL certificate %s: %m", set->ssl_cert_file); + return FALSE; } if (access(set->ssl_key_file, R_OK) < 0) { - i_fatal("Can't use SSL key file %s: %m", + i_error("Can't use SSL key file %s: %m", set->ssl_key_file); + return FALSE; } } #endif @@ -403,23 +383,31 @@ static void settings_verify(struct settings *set) } /* wipe out contents of login directory, if it exists */ - if (unlink_directory(set->login_dir, FALSE) < 0) - i_fatal("unlink_directory() failed for %s: %m", set->login_dir); + if (unlink_directory(set->login_dir, FALSE) < 0) { + i_error("unlink_directory() failed for %s: %m", set->login_dir); + return FALSE; + } if (safe_mkdir(set->login_dir, 0750, geteuid(), set->login_gid) == 0) { i_warning("Corrected permissions for login directory %s", set->login_dir); } - if (set->max_mail_processes < 1) - i_fatal("max_mail_processes must be at least 1"); + if (set->max_mail_processes < 1) { + i_error("max_mail_processes must be at least 1"); + return FALSE; + } if (set->last_valid_uid != 0 && - set->first_valid_uid > set->last_valid_uid) - i_fatal("first_valid_uid can't be larger than last_valid_uid"); + set->first_valid_uid > set->last_valid_uid) { + i_error("first_valid_uid can't be larger than last_valid_uid"); + return FALSE; + } if (set->last_valid_gid != 0 && - set->first_valid_gid > set->last_valid_gid) - i_fatal("first_valid_gid can't be larger than last_valid_gid"); + set->first_valid_gid > set->last_valid_gid) { + i_error("first_valid_gid can't be larger than last_valid_gid"); + return FALSE; + } dotlock_got = fcntl_got = flock_got = FALSE; for (str = t_strsplit(set->mbox_locks, " "); *str != NULL; str++) { @@ -429,20 +417,25 @@ static void settings_verify(struct settings *set) fcntl_got = TRUE; else if (strcasecmp(*str, "flock") == 0) flock_got = TRUE; - else - i_fatal("mbox_locks: Invalid value %s", *str); + else { + i_error("mbox_locks: Invalid value %s", *str); + return FALSE; + } } #ifndef HAVE_FLOCK if (fcntl_got && !dotlock_got && !flock_got) { - i_fatal("mbox_locks: Only flock selected, " + i_error("mbox_locks: Only flock selected, " "and flock() isn't supported in this system"); + return FALSE; } flock_got = FALSE; #endif - if (!dotlock_got && !fcntl_got && !flock_got) - i_fatal("mbox_locks: No mbox locking methods selected"); + if (!dotlock_got && !fcntl_got && !flock_got) { + i_error("mbox_locks: No mbox locking methods selected"); + return FALSE; + } if (dotlock_got && !set->mbox_read_dotlock && !fcntl_got && !flock_got) { @@ -451,11 +444,26 @@ static void settings_verify(struct settings *set) set->mbox_read_dotlock = TRUE; } - for (auth = set->auths; auth != NULL; auth = auth->next) - auth_settings_verify(auth); + if (access(set->login_executable, X_OK) < 0) { + i_error("Can't use login executable %s: %m", + set->login_executable); + return FALSE; + } + + if (set->login_processes_count < 1) { + i_error("login_processes_count must be at least 1"); + return FALSE; + } + if (set->login_max_logging_users < 1) { + i_error("login_max_logging_users must be at least 1"); + return FALSE; + } + + return TRUE; } -static void auth_settings_new(struct settings *set, const char *name) +static struct auth_settings * +auth_settings_new(struct server_settings *server, const char *name) { struct auth_settings *auth; @@ -463,124 +471,282 @@ static void auth_settings_new(struct settings *set, const char *name) /* copy defaults */ *auth = default_auth_settings; + auth->parent = server; auth->name = p_strdup(settings_pool, name); - auth->next = set->auths; - set->auths = auth; + auth->next = server->auths; + server->auths = auth; + + return auth; } -static const char *parse_new_auth(struct settings *set, const char *name) +static struct auth_settings * +parse_new_auth(struct server_settings *server, const char *name, + const char **errormsg) { struct auth_settings *auth; - if (strchr(name, '/') != NULL) - return "Authentication process name must not contain '/'"; + if (strchr(name, '/') != NULL) { + *errormsg = "Authentication process name must not contain '/'"; + return NULL; + } - for (auth = set->auths; auth != NULL; auth = auth->next) { + for (auth = server->auths; auth != NULL; auth = auth->next) { if (strcmp(auth->name, name) == 0) { - return "Authentication process already exists " + *errormsg = "Authentication process already exists " "with the same name"; + return NULL; } } - auth_settings_new(set, name); - return NULL; + return auth_settings_new(server, name); } -static void login_settings_new(struct settings *set, const char *name) +static const char *parse_setting(const char *key, const char *value, + void *context) { - struct login_settings *login; + struct settings_parse_ctx *ctx = context; + const char *error; - login = p_new(settings_pool, struct login_settings, 1); + /* backwards compatibility */ + if (strcmp(key, "auth") == 0) { + ctx->auth = parse_new_auth(ctx->server, value, &error); + return ctx->auth == NULL ? error : NULL; + } - /* copy defaults */ - *login = set->logins != NULL ? *set->logins : - default_login_settings; - - if (strcasecmp(name, "imap") == 0) { - login->name = "imap"; - login->executable = PKG_LIBEXECDIR"/imap-login"; - } else if (strcasecmp(name, "pop3") == 0) { - login->name = "pop3"; - login->executable = PKG_LIBEXECDIR"/pop3-login"; - } else { - i_fatal("Unknown login process type '%s'", name); + if (strcmp(key, "login") == 0) { + i_warning("Ignoring deprecated 'login' section handling. " + "Use protocol imap/pop3 { .. } instead. " + "Some settings may have been read incorrectly."); + return NULL; } - login->next = set->logins; - set->logins = login; -} + switch (ctx->type) { + case SETTINGS_TYPE_ROOT: + case SETTINGS_TYPE_SERVER: + error = NULL; + if (ctx->protocol == MAIL_PROTOCOL_ANY || + ctx->protocol == MAIL_PROTOCOL_IMAP) { + error = parse_setting_from_defs(settings_pool, + setting_defs, + ctx->server->imap, + key, value); + } -static const char *parse_new_login(struct settings *set, const char *name) -{ - struct login_settings *login; + if (error == NULL && + (ctx->protocol == MAIL_PROTOCOL_ANY || + ctx->protocol == MAIL_PROTOCOL_POP3)) { + error = parse_setting_from_defs(settings_pool, + setting_defs, + ctx->server->pop3, + key, value); + } - for (login = set->logins; login != NULL; login = login->next) { - if (strcmp(login->name, name) == 0) { - return "Login process already exists " - "with the same name"; + if (error == NULL) + return NULL; + + /* backwards compatibility */ + if (strncmp(key, "auth_", 5) == 0) { + if (ctx->auth == NULL) { + return "Authentication process name " + "not defined yet"; + } + + return parse_setting_from_defs(settings_pool, + auth_setting_defs, + ctx->auth, + key + 5, value); } + return error; + case SETTINGS_TYPE_AUTH: + return parse_setting_from_defs(settings_pool, auth_setting_defs, + ctx->auth, key + 5, value); } - login_settings_new(set, name); - return NULL; + i_unreached(); } -static const char *parse_setting(const char *key, const char *value, - void *context) +static struct server_settings * +create_new_server(const char *name, + struct settings *imap_defaults, + struct settings *pop3_defaults) { - struct settings *set = context; - const char *error; + struct server_settings *server; - /* check defaults first, there's a few login_ settings defined in it - which need to be checked before trying to feed it to login - handler.. */ - error = parse_setting_from_defs(settings_pool, setting_defs, - set, key, value); - if (error == NULL) - return NULL; + server = p_new(settings_pool, struct server_settings, 1); + server->name = p_strdup(settings_pool, name); + server->imap = p_new(settings_pool, struct settings, 1); + server->pop3 = p_new(settings_pool, struct settings, 1); - if (strcmp(key, "auth") == 0) - return parse_new_auth(set, value); - if (strncmp(key, "auth_", 5) == 0) { - if (set->auths == NULL) - return "Authentication process name not defined yet"; + *server->imap = *imap_defaults; + *server->pop3 = *pop3_defaults; - return parse_setting_from_defs(settings_pool, auth_setting_defs, - set->auths, key + 5, value); + server->imap->protocol = MAIL_PROTOCOL_IMAP; + server->imap->login_executable = PKG_LIBEXECDIR"/imap-login"; + server->imap->mail_executable = PKG_LIBEXECDIR"/imap"; + + server->pop3->protocol = MAIL_PROTOCOL_POP3; + server->pop3->login_executable = PKG_LIBEXECDIR"/pop3-login"; + server->pop3->mail_executable = PKG_LIBEXECDIR"/pop3"; + + return server; +} + +static int parse_section(const char *type, const char *name, void *context, + const char **errormsg) +{ + struct settings_parse_ctx *ctx = context; + struct server_settings *server; + + if (type == NULL) { + /* section closing */ + if (ctx->level > 0) { + ctx->level--; + ctx->protocol = MAIL_PROTOCOL_ANY; + } else { + ctx->type = ctx->parent_type; + ctx->parent_type = SETTINGS_TYPE_ROOT; + ctx->server = ctx->root; + ctx->auth = NULL; + } + return TRUE; } - if (strcmp(key, "login") == 0) - return parse_new_login(set, value); - if (strncmp(key, "login_", 6) == 0) { - if (set->logins == NULL) - return "Login process name not defined yet"; + if (strcmp(type, "server") == 0) { + if (ctx->type != SETTINGS_TYPE_ROOT) { + *errormsg = "Server section not allowed here"; + return FALSE; + } + + ctx->parent_type = ctx->type; + ctx->type = SETTINGS_TYPE_SERVER; + + ctx->server = create_new_server(name, + ctx->server->imap, + ctx->server->pop3); + server = ctx->root; + while (server->next != NULL) + server = server->next; + server->next = ctx->server; + return TRUE; + } + + if (strcmp(type, "protocol") == 0) { + if ((ctx->type != SETTINGS_TYPE_ROOT && + ctx->type != SETTINGS_TYPE_SERVER) || + ctx->level != 0) { + *errormsg = "Protocol section not allowed here"; + return FALSE; + } - return parse_setting_from_defs(settings_pool, - login_setting_defs, - set->logins, key + 6, value); + if (strcmp(name, "imap") == 0) + ctx->protocol = MAIL_PROTOCOL_IMAP; + else if (strcmp(name, "pop3") == 0) + ctx->protocol = MAIL_PROTOCOL_POP3; + else { + *errormsg = "Unknown protocol name"; + return FALSE; + } + ctx->level++; + return TRUE; } - return error; + if (strcmp(type, "auth") == 0) { + if (ctx->type != SETTINGS_TYPE_ROOT && + ctx->type != SETTINGS_TYPE_SERVER) { + *errormsg = "Auth section not allowed here"; + return FALSE; + } + + ctx->type = SETTINGS_TYPE_AUTH; + ctx->auth = parse_new_auth(ctx->server, name, errormsg); + return ctx->auth != NULL; + } + + *errormsg = "Unknown section type"; + return FALSE; } -void master_settings_read(const char *path) +int master_settings_read(const char *path) { + struct settings_parse_ctx ctx; + struct server_settings *server, *prev; + struct auth_settings *auth; + pool_t temp; + + memset(&ctx, 0, sizeof(ctx)); + p_clear(settings_pool); - set = p_new(settings_pool, struct settings, 1); - *set = default_settings; - settings_read(path, parse_setting, set); + ctx.type = SETTINGS_TYPE_ROOT; + ctx.protocol = MAIL_PROTOCOL_ANY; + ctx.server = ctx.root = + create_new_server("default", + &default_settings, &default_settings); + + if (!settings_read(path, NULL, parse_setting, parse_section, &ctx)) + return FALSE; + + if (ctx.level != 0) { + i_error("Missing '}'"); + return FALSE; + } + + /* If server sections were defined, skip the root */ + if (ctx.root->next != NULL) + ctx.root = ctx.root->next; + + prev = NULL; + for (server = ctx.root; server != NULL; server = server->next) { + if (!settings_is_active(server->imap)) + server->imap = NULL; + else { + if (!settings_verify(server->imap)) + return FALSE; + server->defaults = server->imap; + } + + if (!settings_is_active(server->pop3)) + server->pop3 = NULL; + else { + if (!settings_verify(server->pop3)) + return FALSE; + if (server->defaults == NULL) + server->defaults = server->pop3; + } + + if (server->defaults == NULL) { + if (prev == NULL) + ctx.root = server->next; + else + prev->next = server->next; + } else { + auth = server->auths; + for (; auth != NULL; auth = auth->next) { + if (!auth_settings_verify(auth)) + return FALSE; + } + prev = server; + } + } + + /* settings ok, swap them */ + temp = settings_pool; + settings_pool = settings2_pool; + settings2_pool = temp; - settings_verify(set); + settings_root = ctx.root; + return TRUE; } void master_settings_init(void) { - settings_pool = pool_alloconly_create("settings", 1024); + settings_pool = pool_alloconly_create("settings", 2048); + settings2_pool = pool_alloconly_create("settings2", 2048); } void master_settings_deinit(void) { pool_unref(settings_pool); + pool_unref(settings2_pool); } diff --git a/src/master/master-settings.h b/src/master/master-settings.h index 3dd18c5f66..76583b2db6 100644 --- a/src/master/master-settings.h +++ b/src/master/master-settings.h @@ -1,7 +1,26 @@ #ifndef __MASTER_SETTINGS_H #define __MASTER_SETTINGS_H +enum mail_protocol { + MAIL_PROTOCOL_ANY, + MAIL_PROTOCOL_IMAP, + MAIL_PROTOCOL_POP3 +}; + +struct server_settings { + struct server_settings *next; + + const char *name; + struct settings *defaults; + struct settings *imap; + struct settings *pop3; + struct auth_settings *auths; +}; + struct settings { + struct server_settings *server; + enum mail_protocol protocol; + /* common */ const char *base_dir; const char *log_path; @@ -10,10 +29,8 @@ struct settings { /* general */ const char *protocols; - const char *imap_listen; - const char *imaps_listen; - const char *pop3_listen; - const char *pop3s_listen; + const char *listen; + const char *ssl_listen; int ssl_disable; const char *ssl_cert_file; @@ -21,11 +38,20 @@ struct settings { const char *ssl_parameters_file; unsigned int ssl_parameters_regenerate; int disable_plaintext_auth; + int verbose_ssl; /* login */ const char *login_dir; + const char *login_executable; + const char *login_user; + + int login_process_per_connection; int login_chroot; - int verbose_ssl; + + unsigned int login_process_size; + unsigned int login_processes_count; + unsigned int login_max_processes_count; + unsigned int login_max_logging_users; /* mail */ const char *valid_chroot_dirs; @@ -47,51 +73,30 @@ struct settings { int mail_read_mmaped; int maildir_copy_with_hardlinks; int maildir_check_content_changes; - char *mbox_locks; + const char *mbox_locks; int mbox_read_dotlock; unsigned int mbox_lock_timeout; unsigned int mbox_dotlock_change_timeout; unsigned int umask; int mail_drop_priv_before_exec; + const char *mail_executable; + unsigned int mail_process_size; + int mail_use_modules; + const char *mail_modules; + /* imap */ - const char *imap_executable; - unsigned int imap_process_size; unsigned int imap_max_line_length; - int imap_use_modules; - const char *imap_modules; - - /* pop3 */ - const char *pop3_executable; - unsigned int pop3_process_size; - int pop3_use_modules; - const char *pop3_modules; /* .. */ + uid_t login_uid; gid_t login_gid; - struct auth_settings *auths; - struct login_settings *logins; -}; - -struct login_settings { - struct login_settings *next; - - const char *name; - const char *executable; - const char *user; - - int process_per_connection; - - unsigned int process_size; - unsigned int processes_count; - unsigned int max_processes_count; - unsigned int max_logging_users; - - uid_t uid; /* gid must be always same with all login processes */ + int listen_fd, ssl_listen_fd; }; struct auth_settings { + struct server_settings *parent; struct auth_settings *next; const char *name; @@ -112,9 +117,9 @@ struct auth_settings { unsigned int process_size; }; -extern struct settings *set; +extern struct server_settings *settings_root; -void master_settings_read(const char *path); +int master_settings_read(const char *path); void master_settings_init(void); void master_settings_deinit(void); diff --git a/src/master/ssl-init.c b/src/master/ssl-init.c index c4b72602ff..70d942f975 100644 --- a/src/master/ssl-init.c +++ b/src/master/ssl-init.c @@ -38,7 +38,7 @@ static void generate_parameters_file(const char *fname) i_fatal("rename(%s, %s) failed: %m", temp_fname, fname); } -static void start_generate_process(void) +static void start_generate_process(struct settings *set) { pid_t pid; @@ -64,19 +64,19 @@ void ssl_parameter_process_destroyed(pid_t pid __attr_unused__) generating = FALSE; } -static void check_parameters_file(void *context __attr_unused__) +static int check_parameters_file_set(struct settings *set) { struct stat st; time_t regen_time; - if (set->ssl_parameters_file == NULL || set->ssl_disable || generating) - return; + if (set->ssl_parameters_file == NULL || set->ssl_disable) + return TRUE; if (lstat(set->ssl_parameters_file, &st) < 0) { if (errno != ENOENT) { i_error("lstat() failed for SSL parameters file %s: %m", set->ssl_parameters_file); - return; + return TRUE; } st.st_mtime = 0; @@ -86,8 +86,29 @@ static void check_parameters_file(void *context __attr_unused__) regen_time = st.st_mtime + (time_t)(set->ssl_parameters_regenerate*3600); if (regen_time < ioloop_time || (st.st_mode & 077) != 0 || - st.st_uid != geteuid() || st.st_gid != getegid()) - start_generate_process(); + st.st_uid != geteuid() || st.st_gid != getegid()) { + start_generate_process(set); + return FALSE; + } + + return TRUE; +} + +static void check_parameters_file(void *context __attr_unused__) +{ + struct server_settings *server; + + if (generating) + return; + + for (server = settings_root; server != NULL; server = server->next) { + if (server->imap != NULL && + !check_parameters_file_set(server->imap)) + break; + if (server->pop3 != NULL && + !check_parameters_file_set(server->pop3)) + break; + } } void ssl_init(void)