settings.c revision 94f11e46199ea913e4ae17198b3024771b1bef68
22f8827cd8892a298f9764ce862db6867307828dEugen Kuksa/* Copyright (C) 2002 Timo Sirainen */
22f8827cd8892a298f9764ce862db6867307828dEugen Kuksa
22f8827cd8892a298f9764ce862db6867307828dEugen Kuksa#include "lib.h"
22f8827cd8892a298f9764ce862db6867307828dEugen Kuksa#include "ibuffer.h"
22f8827cd8892a298f9764ce862db6867307828dEugen Kuksa#include "settings.h"
22f8827cd8892a298f9764ce862db6867307828dEugen Kuksa
22f8827cd8892a298f9764ce862db6867307828dEugen Kuksa#include <stdio.h>
22f8827cd8892a298f9764ce862db6867307828dEugen Kuksa#include <unistd.h>
22f8827cd8892a298f9764ce862db6867307828dEugen Kuksa#include <fcntl.h>
22f8827cd8892a298f9764ce862db6867307828dEugen Kuksa#include <pwd.h>
22f8827cd8892a298f9764ce862db6867307828dEugen Kuksa#include <sys/stat.h>
22f8827cd8892a298f9764ce862db6867307828dEugen Kuksa
22f8827cd8892a298f9764ce862db6867307828dEugen Kuksatypedef enum {
22f8827cd8892a298f9764ce862db6867307828dEugen Kuksa SET_STR,
22f8827cd8892a298f9764ce862db6867307828dEugen Kuksa SET_INT,
22f8827cd8892a298f9764ce862db6867307828dEugen Kuksa SET_BOOL
22f8827cd8892a298f9764ce862db6867307828dEugen Kuksa} SettingType;
22f8827cd8892a298f9764ce862db6867307828dEugen Kuksa
22f8827cd8892a298f9764ce862db6867307828dEugen Kuksatypedef struct {
22f8827cd8892a298f9764ce862db6867307828dEugen Kuksa const char *name;
22f8827cd8892a298f9764ce862db6867307828dEugen Kuksa SettingType type;
22f8827cd8892a298f9764ce862db6867307828dEugen Kuksa void *ptr;
22f8827cd8892a298f9764ce862db6867307828dEugen Kuksa} Setting;
22f8827cd8892a298f9764ce862db6867307828dEugen Kuksa
22f8827cd8892a298f9764ce862db6867307828dEugen Kuksastatic Setting settings[] = {
22f8827cd8892a298f9764ce862db6867307828dEugen Kuksa { "log_path", SET_STR, &set_log_path },
22f8827cd8892a298f9764ce862db6867307828dEugen Kuksa { "log_timestamp", SET_STR, &set_log_timestamp },
22f8827cd8892a298f9764ce862db6867307828dEugen Kuksa
22f8827cd8892a298f9764ce862db6867307828dEugen Kuksa { "imap_port", SET_INT, &set_imap_port },
22f8827cd8892a298f9764ce862db6867307828dEugen Kuksa { "imaps_port", SET_INT, &set_imaps_port },
22f8827cd8892a298f9764ce862db6867307828dEugen Kuksa { "imap_listen", SET_STR, &set_imap_listen },
22f8827cd8892a298f9764ce862db6867307828dEugen Kuksa { "imaps_listen", SET_STR, &set_imaps_listen },
22f8827cd8892a298f9764ce862db6867307828dEugen Kuksa { "ssl_cert_file", SET_STR, &set_ssl_cert_file },
22f8827cd8892a298f9764ce862db6867307828dEugen Kuksa { "ssl_key_file", SET_STR, &set_ssl_key_file },
22f8827cd8892a298f9764ce862db6867307828dEugen Kuksa { "disable_plaintext_auth",
22f8827cd8892a298f9764ce862db6867307828dEugen Kuksa SET_BOOL,&set_disable_plaintext_auth },
22f8827cd8892a298f9764ce862db6867307828dEugen Kuksa
22f8827cd8892a298f9764ce862db6867307828dEugen Kuksa { "login_executable", SET_STR, &set_login_executable },
22f8827cd8892a298f9764ce862db6867307828dEugen Kuksa { "login_user", SET_STR, &set_login_user },
22f8827cd8892a298f9764ce862db6867307828dEugen Kuksa { "login_dir", SET_STR, &set_login_dir },
22f8827cd8892a298f9764ce862db6867307828dEugen Kuksa { "login_chroot", SET_BOOL,&set_login_chroot },
22f8827cd8892a298f9764ce862db6867307828dEugen Kuksa { "login_processes_count", SET_INT, &set_login_processes_count },
22f8827cd8892a298f9764ce862db6867307828dEugen Kuksa { "max_logging_users", SET_INT, &set_max_logging_users },
22f8827cd8892a298f9764ce862db6867307828dEugen Kuksa
22f8827cd8892a298f9764ce862db6867307828dEugen Kuksa { "imap_executable", SET_STR, &set_imap_executable },
22f8827cd8892a298f9764ce862db6867307828dEugen Kuksa { "valid_chroot_dirs", SET_STR, &set_valid_chroot_dirs },
22f8827cd8892a298f9764ce862db6867307828dEugen Kuksa { "max_imap_processes", SET_INT, &set_max_imap_processes },
22f8827cd8892a298f9764ce862db6867307828dEugen Kuksa { "verbose_proctitle", SET_BOOL,&set_verbose_proctitle },
22f8827cd8892a298f9764ce862db6867307828dEugen Kuksa { "first_valid_uid", SET_INT, &set_first_valid_uid },
22f8827cd8892a298f9764ce862db6867307828dEugen Kuksa { "last_valid_uid", SET_INT, &set_last_valid_uid },
22f8827cd8892a298f9764ce862db6867307828dEugen Kuksa { "first_valid_gid", SET_INT, &set_first_valid_gid },
22f8827cd8892a298f9764ce862db6867307828dEugen Kuksa { "last_valid_gid", SET_INT, &set_last_valid_gid },
22f8827cd8892a298f9764ce862db6867307828dEugen Kuksa { "mail_cache_fields", SET_STR, &set_mail_cache_fields },
22f8827cd8892a298f9764ce862db6867307828dEugen Kuksa { "mail_never_cache_fields",
22f8827cd8892a298f9764ce862db6867307828dEugen Kuksa SET_STR, &set_mail_never_cache_fields },
22f8827cd8892a298f9764ce862db6867307828dEugen Kuksa { "mail_save_crlf", SET_BOOL,&set_mail_save_crlf },
22f8827cd8892a298f9764ce862db6867307828dEugen Kuksa { "maildir_copy_with_hardlinks",
22f8827cd8892a298f9764ce862db6867307828dEugen Kuksa SET_BOOL,&set_maildir_copy_with_hardlinks },
{ "maildir_check_content_changes",
SET_BOOL,&set_maildir_check_content_changes },
{ "overwrite_incompatible_index",
SET_BOOL,&set_overwrite_incompatible_index },
{ "umask", SET_INT, &set_umask },
{ NULL, 0, NULL }
};
/* common */
char *set_log_path = NULL;
char *set_log_timestamp = DEFAULT_FAILURE_STAMP_FORMAT;
/* general */
unsigned int set_imap_port = 143;
unsigned int set_imaps_port = 993;
char *set_imap_listen = NULL;
char *set_imaps_listen = NULL;
char *set_ssl_cert_file = "/etc/ssl/certs/imapd.pem";
char *set_ssl_key_file = "/etc/ssl/private/imapd.pem";
int set_disable_plaintext_auth = FALSE;
/* login */
char *set_login_executable = PKG_LIBDIR "/imap-login";
char *set_login_user = "imapd";
char *set_login_dir = PKG_RUNDIR;
int set_login_chroot = TRUE;
unsigned int set_login_processes_count = 1;
unsigned int set_max_logging_users = 256;
uid_t set_login_uid; /* generated from set_login_user */
gid_t set_login_gid; /* generated from set_login_user */
/* imap */
char *set_imap_executable = PKG_LIBDIR "/imap";
char *set_valid_chroot_dirs = NULL;
unsigned int set_max_imap_processes = 1024;
int set_verbose_proctitle = FALSE;
unsigned int set_first_valid_uid = 500, set_last_valid_uid = 0;
unsigned int set_first_valid_gid = 1, set_last_valid_gid = 0;
char *set_mail_cache_fields = "MessagePart";
char *set_mail_never_cache_fields = NULL;
int set_mail_save_crlf = FALSE;
int set_maildir_copy_with_hardlinks = FALSE;
int set_maildir_check_content_changes = FALSE;
int set_overwrite_incompatible_index = FALSE;
unsigned int set_umask = 0077;
/* auth */
AuthConfig *auth_processes_config = NULL;
static void get_login_uid(void)
{
struct passwd *pw;
if ((pw = getpwnam(set_login_user)) == NULL)
i_fatal("Login user doesn't exist: %s", set_login_user);
set_login_uid = pw->pw_uid;
set_login_gid = pw->pw_gid;
}
static void settings_initialize(void)
{
Setting *set;
/* strdup() all default settings */
for (set = settings; set->name != NULL; set++) {
if (set->type == SET_STR) {
char **str = set->ptr;
*str = i_strdup(*str);
}
}
}
static void auth_settings_verify(void)
{
AuthConfig *auth;
for (auth = auth_processes_config; auth != NULL; auth = auth->next) {
if (access(auth->executable, X_OK) < 0) {
i_fatal("Can't use auth executable %s: %m",
auth->executable);
}
if (auth->chroot != NULL && *auth->chroot != '\0' &&
access(auth->chroot, X_OK) < 0) {
i_fatal("Can't access auth chroot directory %s: %m",
auth->chroot);
}
}
}
static void settings_verify(void)
{
get_login_uid();
if (access(set_login_executable, X_OK) < 0) {
i_fatal("Can't use login executable %s: %m",
set_login_executable);
}
if (access(set_imap_executable, X_OK) < 0) {
i_fatal("Can't use imap executable %s: %m",
set_imap_executable);
}
/* since it's under /var/run by default, it may have been deleted */
if (mkdir(set_login_dir, 0700) == 0)
(void)chown(set_login_dir, set_login_uid, set_login_gid);
if (access(set_login_dir, X_OK) < 0)
i_fatal("Can't access login directory %s: %m", set_login_dir);
if (set_login_processes_count < 1)
i_fatal("login_processes_count must be at least 1");
if (set_first_valid_uid < set_last_valid_uid)
i_fatal("first_valid_uid can't be larger than last_valid_uid");
if (set_first_valid_gid < set_last_valid_gid)
i_fatal("first_valid_gid can't be larger than last_valid_gid");
auth_settings_verify();
}
static AuthConfig *auth_config_new(const char *name)
{
AuthConfig *auth;
auth = i_new(AuthConfig, 1);
auth->name = i_strdup(name);
auth->executable = i_strdup(PKG_LIBDIR "/imap-auth");
auth->count = 1;
auth->next = auth_processes_config;
auth_processes_config = auth;
return auth;
}
static const char *parse_new_auth(const char *name)
{
AuthConfig *auth;
if (strchr(name, '/') != NULL)
return "Authentication process name must not contain '/'";
for (auth = auth_processes_config; auth != NULL; auth = auth->next) {
if (strcmp(auth->name, name) == 0) {
return "Authentication process already exists "
"with the same name";
}
}
(void)auth_config_new(name);
return NULL;
}
static const char *parse_auth(const char *key, const char *value)
{
AuthConfig *auth = auth_processes_config;
const char *p;
char **ptr;
if (auth == NULL)
return "Authentication process name not defined yet";
/* check the easy string values first */
if (strcmp(key, "auth_methods") == 0)
ptr = &auth->methods;
else if (strcmp(key, "auth_realms") == 0)
ptr = &auth->realms;
else if (strcmp(key, "auth_executable") == 0)
ptr = &auth->executable;
else if (strcmp(key, "auth_user") == 0)
ptr = &auth->user;
else if (strcmp(key, "auth_chroot") == 0)
ptr = &auth->chroot;
else
ptr = NULL;
if (ptr != NULL) {
i_strdup_replace(ptr, value);
return NULL;
}
if (strcmp(key, "auth_userinfo") == 0) {
/* split it into userinfo + userinfo_args */
for (p = value; *p != ' ' && *p != '\0'; )
p++;
i_free(auth->userinfo);
auth->userinfo = i_strdup_until(value, p);
while (*p == ' ') p++;
i_free(auth->userinfo_args);
auth->userinfo_args = i_strdup(p);
return NULL;
}
if (strcmp(key, "auth_count") == 0) {
if (!sscanf(value, "%i", &auth->count))
return t_strconcat("Invalid number: ", value, NULL);
return NULL;
}
return t_strconcat("Unknown setting: ", key, NULL);
}
static const char *parse_setting(const char *key, const char *value)
{
Setting *set;
if (strcmp(key, "auth") == 0)
return parse_new_auth(value);
if (strncmp(key, "auth_", 5) == 0)
return parse_auth(key, value);
for (set = settings; set->name != NULL; set++) {
if (strcmp(set->name, key) == 0) {
switch (set->type) {
case SET_STR:
i_strdup_replace((char **) set->ptr, value);
break;
case SET_INT:
/* use %i so we can handle eg. 0600
as octal value with umasks */
if (!sscanf(value, "%i", (int *) set->ptr))
return t_strconcat("Invalid number: ",
value, NULL);
break;
case SET_BOOL:
if (strcasecmp(value, "yes") == 0)
*((int *) set->ptr) = TRUE;
else if (strcasecmp(value, "no") == 0)
*((int *) set->ptr) = FALSE;
else
return t_strconcat("Invalid boolean: ",
value, NULL);
break;
}
return NULL;
}
}
return t_strconcat("Unknown setting: ", key, NULL);
}
#define IS_WHITE(c) ((c) == ' ' || (c) == '\t')
void settings_read(const char *path)
{
IBuffer *inbuf;
const char *errormsg;
char *line, *key, *p;
int fd, linenum;
settings_initialize();
fd = open(path, O_RDONLY);
if (fd < 0)
i_fatal("Can't open configuration file %s: %m", path);
linenum = 0;
inbuf = i_buffer_create_file(fd, default_pool, 2048, TRUE);
for (;;) {
line = i_buffer_next_line(inbuf);
if (line == NULL) {
if (i_buffer_read(inbuf) <= 0)
break;
continue;
}
linenum++;
/* skip whitespace */
while (IS_WHITE(*line))
line++;
/* ignore comments or empty lines */
if (*line == '#' || *line == '\0')
continue;
/* all lines must be in format "key = value" */
key = line;
while (!IS_WHITE(*line) && *line != '\0')
line++;
if (IS_WHITE(*line)) {
*line++ = '\0';
while (IS_WHITE(*line)) line++;
}
if (*line != '=') {
errormsg = "Missing value";
} else {
/* skip whitespace after '=' */
*line++ = '\0';
while (IS_WHITE(*line)) line++;
/* skip trailing whitespace */
p = line + strlen(line);
while (p > line && IS_WHITE(p[-1]))
p--;
*p = '\0';
errormsg = parse_setting(key, line);
}
if (errormsg != NULL) {
i_fatal("Error in configuration file %s line %d: %s",
path, linenum, errormsg);
}
};
i_buffer_unref(inbuf);
settings_verify();
}