master-settings.c revision c3e30ffd8ac58854cc1162c33b4019a6d710b013
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksa/* Copyright (C) 2002 Timo Sirainen */
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksa#define DEF_STR(name) DEF_STRUCT_STR(name, auth_settings)
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksa#define DEF_INT(name) DEF_STRUCT_INT(name, auth_settings)
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksa#define DEF_BOOL(name) DEF_STRUCT_BOOL(name, auth_settings)
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksastatic struct setting_def auth_setting_defs[] = {
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksa#define DEF_STR(name) DEF_STRUCT_STR(name, socket_settings)
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksa#define DEF_INT(name) DEF_STRUCT_INT(name, socket_settings)
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksa#define DEF_BOOL(name) DEF_STRUCT_BOOL(name, socket_settings)
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksastatic struct setting_def socket_setting_defs[] = {
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksastatic struct setting_def auth_socket_setting_defs[] = {
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksa#define DEF_STR(name) DEF_STRUCT_STR(name, auth_passdb_settings)
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksa#define DEF_INT(name) DEF_STRUCT_INT(name, auth_passdb_settings)
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksa#define DEF_BOOL(name) DEF_STRUCT_BOOL(name, auth_passdb_settings)
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksastatic struct setting_def auth_passdb_setting_defs[] = {
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksastatic struct setting_def auth_userdb_setting_defs[] = {
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksa#define DEF_STR(name) DEF_STRUCT_STR(name, namespace_settings)
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksa#define DEF_INT(name) DEF_STRUCT_INT(name, namespace_settings)
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksa#define DEF_BOOL(name) DEF_STRUCT_BOOL(name, namespace_settings)
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksastatic struct setting_def namespace_setting_defs[] = {
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksa /* common */
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksa MEMBER(log_timestamp) DEFAULT_FAILURE_STAMP_FORMAT,
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksa /* general */
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksa MEMBER(ssl_cert_file) SSLDIR"/certs/dovecot.pem",
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksa MEMBER(ssl_key_file) SSLDIR"/private/dovecot.pem",
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksa MEMBER(login_log_format_elements) "user=<%u> method=%m rip=%r lip=%l %c",
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksa MEMBER(mail_never_cache_fields) "imap.envelope",
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksa MEMBER(pop3_logout_format) "top=%t/%p, retr=%r/%b, del=%d/%m, size=%s",
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksa MEMBER(executable) PKG_LIBEXECDIR"/dovecot-auth",
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksa MEMBER(username_chars) "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890.-_@",
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksastruct socket_settings default_socket_settings = {
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksastruct namespace_settings default_namespace_settings = {
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksastatic void fix_base_path(struct settings *set, const char **str)
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksa if (*str != NULL && **str != '\0' && **str != '/') {
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksastatic bool parse_uid(const char *str, uid_t *uid_r)
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksa if (*p == '\0')
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksastatic bool parse_gid(const char *str, gid_t *gid_r)
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksa if (*p == '\0')
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksa i_error("Login user doesn't exist: %s", set->login_user);
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksa else if (set->server->login_gid != pw->pw_gid) {
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksa i_error("All login process users must belong to same group "
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksastatic bool auth_settings_verify(struct auth_settings *auth)
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksa i_error("Auth user doesn't exist: %s", auth->user);
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksa if (auth->parent->defaults->login_uid == pw->pw_uid &&
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksa i_error("login_user %s (uid %s) must not be same as auth_user",
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksa if (access(t_strcut(auth->executable, ' '), X_OK) < 0) {
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksa fix_base_path(auth->parent->defaults, &auth->chroot);
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksa if (*auth->chroot != '\0' && access(auth->chroot, X_OK) < 0) {
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksa i_error("Can't access auth chroot directory %s: %m",
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksa if (auth->ssl_require_client_cert || auth->ssl_username_from_cert) {
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksa /* if we require valid cert, make sure we also ask for it */
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksa auth->parent->pop3->ssl_verify_client_cert = TRUE;
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksa auth->parent->imap->ssl_verify_client_cert = TRUE;
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksa for (s = auth->sockets; s != NULL; s = s->next) {
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksa fix_base_path(auth->parent->defaults, &s->master.path);
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksa fix_base_path(auth->parent->defaults, &s->client.path);
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksastatic bool namespace_settings_verify(struct namespace_settings *ns)
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksa const char *name;
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksa ns->separator[0] != '\0' && ns->separator[1] != '\0') {
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksa "Hierarchy separator must be only one character long",
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksastatic const char *get_directory(const char *path)
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksastatic bool settings_is_active(struct settings *set)
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksa /* we're probably using this with --exec-mail */
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksastatic bool settings_have_connect_sockets(struct settings *set)
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksa for (server = set->server; server != NULL; server = server->next) {
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksa for (auth = server->auths; auth != NULL; auth = auth->next) {
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksastatic void unlink_auth_sockets(const char *path, const char *prefix)
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksa if (strncmp(dp->d_name, prefix, prefix_len) != 0)
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksa /* try to avoid unlinking sockets if someone's already
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksa listening in them. do this only at startup, because
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksa when SIGHUPing a child process might catch the new
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksa connection before it notices that it's supposed
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksa to die. null_fd == -1 check is a bit kludgy, but works.. */
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksa "Socket already exists: %s",
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksastatic bool get_imap_capability(struct settings *set)
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksa /* FIXME: pretty ugly code just for getting the capability
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksa automatically */
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksa static const char *args[] = {
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksa "uid=65534",
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksa "gid=65534",
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksa unsigned int pos;
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksa /* Reloading configuration. Don't try to execute the imap
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksa process again. Too risky and the wait() call below will
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksa break it anyway. Just use the previous capability list we
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksa already had generated. */
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksa /* use the current user */
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksa args[0] = t_strdup_printf("uid=%s", dec2str(uid));
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksa args[1] = t_strdup_printf("gid=%s", dec2str(getegid()));
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksa login_status = create_mail_process(PROCESS_TYPE_IMAP, set, fd[1],
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksa i_fatal("imap dump-capability process got stuck");
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksa i_error("imap dump-capability process returned %d",
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksa while ((ret = read(fd[0], buf + pos, sizeof(buf) - pos)) > 0)
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksa i_error("read(imap dump-capability process) failed: %m");
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksa i_error("imap dump-capability: Couldn't read capability "
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksastatic bool settings_verify(struct settings *set)
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksa const char *dir;
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksa if (!parse_uid(set->mail_uid, &set->mail_uid_t))
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksa if (!parse_gid(set->mail_gid, &set->mail_gid_t))
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksa i_error("POP3 enabled but pop3_uidl_format not set");
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksa if (access(t_strcut(set->mail_executable, ' '), X_OK) < 0) {
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksa if (*set->log_path != '\0' && access(set->log_path, W_OK) < 0) {
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksa i_error("Can't write to log directory %s: %m", dir);
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksa i_error("Can't write to info log directory %s: %m",
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksa if (!syslog_facility_find(set->syslog_facility, &facility)) {
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksa i_error("Unknown syslog_facility '%s'", set->syslog_facility);
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksa i_error("SSL support not compiled in but ssl_disable=no");
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksa i_error("max_mail_processes must be at least 1");
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksa i_error("first_valid_uid can't be larger than last_valid_uid");
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksa i_error("first_valid_gid can't be larger than last_valid_gid");
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksa if (set->mail_drop_priv_before_exec && *set->mail_chroot != '\0') {
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksa i_error("mail_drop_priv_before_exec=yes and mail_chroot "
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksa "don't work together");
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksa if (access(t_strcut(set->login_executable, ' '), X_OK) < 0) {
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksa i_error("login_processes_count must be at least 1");
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksa i_error("login_max_connections must be at least 1");
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksa access(set->mail_plugin_dir, R_OK | X_OK) < 0) {
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksa i_error("Can't access mail module directory: %s: %m",
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksa i_error("Module support wasn't built into Dovecot, "
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksastatic bool settings_do_fixes(struct settings *set)
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksa /* since base dir is under /var/run by default, it may have been
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksa if (mkdir_parents(set->base_dir, 0777) < 0 && errno != EEXIST) {
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksa /* allow base_dir to be a symlink, so don't use lstat() */
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksa if ((st.st_mode & 0310) != 0310 || (st.st_mode & 0777) == 0777) {
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksa /* FIXME: backwards compatibility: fix permissions so that
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksa login processes can find ssl-parameters file. Group rx is
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksa enough, but change it to world-rx so that we don't have to
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksa start changing groups and causing possibly other problems.
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksa The second check is to fix 1.0beta1's accidental 0777
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksa mode change.. */
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksa i_warning("Fixing permissions of %s to be world-readable",
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksa /* remove auth worker sockets left by unclean exits */
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksa unlink_auth_sockets(set->base_dir, "auth-worker.");
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksa /* Make sure our permanent state directory exists */
2a18c4c4599ac56a28a7e38bb286b5db8b05b68cEugen Kuksa if (mkdir_parents(PKG_STATEDIR, 0750) < 0 && errno != EEXIST) {
return FALSE;
#ifdef HAVE_MODULES
return FALSE;
return TRUE;
return FALSE;
if (nochecks)
return TRUE;
return FALSE;
int fd;
if (ret <= 0) {
if (ret == 0)
ret--;
ret = 0;
return ret;
static struct auth_settings *
return auth;
static struct auth_settings *
const char **errormsg)
return NULL;
return NULL;
static struct auth_passdb_settings *
return as;
static struct auth_userdb_settings *
return as;
static struct auth_socket_settings *
return as;
static struct auth_socket_settings *
const char **errormsg)
return NULL;
return NULL;
static struct namespace_settings *
return ns;
static struct namespace_settings *
const char **errormsg)
return NULL;
const char *error;
return NULL;
case SETTINGS_TYPE_ROOT:
case SETTINGS_TYPE_SERVER:
return NULL;
return error;
case SETTINGS_TYPE_AUTH:
case SETTINGS_TYPE_NAMESPACE:
case SETTINGS_TYPE_SOCKET:
case SETTINGS_TYPE_DICT:
return NULL;
case SETTINGS_TYPE_PLUGIN:
return NULL;
i_unreached();
static struct server_settings *
return server;
return TRUE;
return FALSE;
return TRUE;
return FALSE;
return FALSE;
return TRUE;
return FALSE;
return TRUE;
return TRUE;
return TRUE;
return TRUE;
return FALSE;
errormsg);
return FALSE;
return TRUE;
return FALSE;
return TRUE;
return FALSE;
return FALSE;
return FALSE;
return FALSE;
return FALSE;
nofixes))
return FALSE;
return FALSE;
return FALSE;
return FALSE;
if (!nochecks) {
return FALSE;
return FALSE;
return FALSE;
return TRUE;
const char **str;
t_push();
case SET_STR: {
const char *const *strp;
for (i = 0; i < count; i++) {
case SET_INT: {
for (i = 0; i < count; i++) {
case SET_BOOL: {
for (i = 0; i < count; i++) {
if (same) {
for (i = 0; i < indent; i++)
for (i = 0; i < indent; i++)
t_pop();
const void *empty_defaults;
sizeof(struct auth_userdb_settings) +
sizeof(struct auth_socket_settings));
const char *const *envs;
unsigned int i, count;
if (count == 0)
const char *const *dicts;
unsigned int i, count;
if (count == 0)
unsigned int count;
count++;
count++;
void master_settings_init(void)
void master_settings_deinit(void)