Mercurial > dovecot > core-2.2
view src/master/settings.c @ 839:34cb1d196d2b HEAD
String function cleanups. Allocating 0 bytes of memory is treated as error
now, it could be an integer overflow. Moved printf_string_upper_bound() to
it's own file, so strfuncs.c can be with MIT license.
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Sun, 22 Dec 2002 09:06:16 +0200 |
parents | 02cda88b44e2 |
children | 7f41a4b33975 |
line wrap: on
line source
/* Copyright (C) 2002 Timo Sirainen */ #include "lib.h" #include "istream.h" #include "safe-mkdir.h" #include "unlink-directory.h" #include "settings.h" #include <stdio.h> #include <unistd.h> #include <fcntl.h> #include <pwd.h> typedef enum { SET_STR, SET_INT, SET_BOOL } SettingType; typedef struct { const char *name; SettingType type; void *ptr; } Setting; static Setting settings[] = { { "log_path", SET_STR, &set_log_path }, { "info_log_path", SET_STR, &set_info_log_path }, { "log_timestamp", SET_STR, &set_log_timestamp }, { "imap_port", SET_INT, &set_imap_port }, { "imaps_port", SET_INT, &set_imaps_port }, { "imap_listen", SET_STR, &set_imap_listen }, { "imaps_listen", SET_STR, &set_imaps_listen }, { "ssl_disable", SET_BOOL,&set_ssl_disable, }, { "ssl_cert_file", SET_STR, &set_ssl_cert_file }, { "ssl_key_file", SET_STR, &set_ssl_key_file }, { "ssl_parameters_file",SET_STR, &set_ssl_parameters_file }, { "ssl_parameters_regenerate", SET_INT, &set_ssl_parameters_regenerate }, { "disable_plaintext_auth", SET_BOOL,&set_disable_plaintext_auth }, { "login_executable", SET_STR, &set_login_executable }, { "login_user", SET_STR, &set_login_user }, { "login_process_size", SET_INT, &set_login_process_size }, { "login_dir", SET_STR, &set_login_dir }, { "login_chroot", SET_BOOL,&set_login_chroot }, { "login_process_per_connection", SET_BOOL,&set_login_process_per_connection }, { "login_processes_count", SET_INT, &set_login_processes_count }, { "max_logging_users", SET_INT, &set_max_logging_users }, { "imap_executable", SET_STR, &set_imap_executable }, { "imap_process_size", SET_INT, &set_imap_process_size }, { "valid_chroot_dirs", SET_STR, &set_valid_chroot_dirs }, { "max_imap_processes", SET_INT, &set_max_imap_processes }, { "verbose_proctitle", SET_BOOL,&set_verbose_proctitle }, { "first_valid_uid", SET_INT, &set_first_valid_uid }, { "last_valid_uid", SET_INT, &set_last_valid_uid }, { "first_valid_gid", SET_INT, &set_first_valid_gid }, { "last_valid_gid", SET_INT, &set_last_valid_gid }, { "default_mail_env", SET_STR, &set_default_mail_env }, { "mail_cache_fields", SET_STR, &set_mail_cache_fields }, { "mail_never_cache_fields", SET_STR, &set_mail_never_cache_fields }, { "mailbox_check_interval", SET_INT, &set_mailbox_check_interval }, { "mail_save_crlf", SET_BOOL,&set_mail_save_crlf }, { "mail_read_mmaped", SET_BOOL,&set_mail_read_mmaped }, { "maildir_copy_with_hardlinks", SET_BOOL,&set_maildir_copy_with_hardlinks }, { "maildir_check_content_changes", SET_BOOL,&set_maildir_check_content_changes }, { "mbox_locks", SET_STR, &set_mbox_locks, }, { "mbox_read_dotlock", SET_BOOL,&set_mbox_read_dotlock, }, { "mbox_lock_timeout", SET_INT, &set_mbox_lock_timeout, }, { "mbox_dotlock_change_timeout", SET_INT, &set_mbox_dotlock_change_timeout, }, { "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_info_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 = "*"; char *set_imaps_listen = NULL; int set_ssl_disable = FALSE; char *set_ssl_cert_file = SSLDIR"/certs/imapd.pem"; char *set_ssl_key_file = SSLDIR"/private/imapd.pem"; char *set_ssl_parameters_file = PKG_RUNDIR"/ssl-parameters.dat"; unsigned int set_ssl_parameters_regenerate = 24; int set_disable_plaintext_auth = FALSE; /* login */ char *set_login_executable = PKG_LIBEXECDIR"/imap-login"; unsigned int set_login_process_size = 16; char *set_login_user = "imapd"; char *set_login_dir = PKG_RUNDIR"/login"; int set_login_chroot = TRUE; int set_login_process_per_connection = TRUE; unsigned int set_login_processes_count = 3; unsigned int set_login_max_processes_count = 128; 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_LIBEXECDIR"/imap"; unsigned int set_imap_process_size = 256; 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_default_mail_env = NULL; char *set_mail_cache_fields = "MessagePart"; char *set_mail_never_cache_fields = NULL; unsigned int set_mailbox_check_interval = 0; int set_mail_save_crlf = FALSE; int set_mail_read_mmaped = FALSE; int set_maildir_copy_with_hardlinks = FALSE; int set_maildir_check_content_changes = FALSE; char *set_mbox_locks = "dotlock fcntl flock"; int set_mbox_read_dotlock = FALSE; unsigned int set_mbox_lock_timeout = 300; unsigned int set_mbox_dotlock_change_timeout = 30; 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 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 && access(auth->chroot, X_OK) < 0) { i_fatal("Can't access auth chroot directory %s: %m", auth->chroot); } } } static const char *get_directory(const char *path) { char *str, *p; str = t_strdup_noconst(path); p = strrchr(str, '/'); if (p == NULL) return "."; else { *p = '\0'; return str; } } static void settings_verify(void) { char *const *str; const char *dir; int dotlock_got, fcntl_got, flock_got; 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); } if (set_log_path != NULL) { dir = get_directory(set_log_path); if (access(dir, W_OK) < 0) i_fatal("Can't access log directory %s: %m", dir); } if (set_info_log_path != NULL) { dir = get_directory(set_info_log_path); if (access(dir, W_OK) < 0) i_fatal("Can't access info log directory %s: %m", dir); } #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", set_ssl_cert_file); } if (access(set_ssl_key_file, R_OK) < 0) { i_fatal("Can't use SSL key file %s: %m", set_ssl_key_file); } } #endif /* since they're under /var/run by default, they may have been deleted. */ if (safe_mkdir(PKG_RUNDIR, 0700, geteuid(), getegid()) == 0) { i_warning("Corrected permissions for base directory %s", PKG_RUNDIR); } /* 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 (safe_mkdir(set_login_dir, 0700, set_login_uid, set_login_gid) == 0) i_warning("Corrected permissions for login directory %s", set_login_dir); if (set_max_imap_processes < 1) i_fatal("max_imap_processes must be at least 1"); if (set_login_processes_count < 1) i_fatal("login_processes_count must be at least 1"); if (set_max_logging_users < 1) i_fatal("max_logging_users must be at least 1"); 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"); 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"); dotlock_got = fcntl_got = flock_got = FALSE; for (str = t_strsplit(set_mbox_locks, " "); *str != NULL; str++) { if (strcasecmp(*str, "dotlock") == 0) dotlock_got = TRUE; else if (strcasecmp(*str, "fcntl") == 0) fcntl_got = TRUE; else if (strcasecmp(*str, "flock") == 0) flock_got = TRUE; else i_fatal("mbox_locks: Invalid value %s", *str); } #ifndef HAVE_FLOCK if (fcntl_got && !dotlock_got && !flock_got) { i_fatal("mbox_locks: Only flock selected, " "and flock() isn't supported in this system"); } flock_got = FALSE; #endif if (!dotlock_got && !fcntl_got && !flock_got) i_fatal("mbox_locks: No mbox locking methods selected"); if (dotlock_got && !set_mbox_read_dotlock && !fcntl_got && !flock_got) { i_warning("mbox_locks: Only dotlock selected, forcing " "mbox_read_dotlock = yes to avoid corruption."); set_mbox_read_dotlock = TRUE; } 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_LIBEXECDIR"/imap-auth"); auth->count = 1; auth->next = auth_processes_config; auth_processes_config = auth; return auth; } static void auth_config_free(AuthConfig *auth) { i_free(auth->name); i_free(auth->methods); i_free(auth->realms); i_free(auth->userinfo); i_free(auth->userinfo_args); i_free(auth->executable); i_free(auth->user); i_free(auth->chroot); i_free(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_free(*ptr); *ptr = i_strdup(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) { int num; if (!sscanf(value, "%i", &num) || num < 0) return t_strconcat("Invalid number: ", value, NULL); auth->count = num; return NULL; } if (strcmp(key, "auth_process_size") == 0) { int num; if (!sscanf(value, "%i", &num) || num < 0) return t_strconcat("Invalid number: ", value, NULL); auth->process_size = num; 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_free(*((char **)set->ptr)); *((char **)set->ptr) = i_strdup_empty(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); } static void settings_free(void) { while (auth_processes_config != NULL) { AuthConfig *auth = auth_processes_config; auth_processes_config = auth->next; auth_config_free(auth); } } #define IS_WHITE(c) ((c) == ' ' || (c) == '\t') void settings_read(const char *path) { IStream *input; const char *errormsg; char *line, *key, *p; int fd, linenum; settings_free(); fd = open(path, O_RDONLY); if (fd < 0) i_fatal("Can't open configuration file %s: %m", path); linenum = 0; input = i_stream_create_file(fd, default_pool, 2048, TRUE); for (;;) { line = i_stream_next_line(input); if (line == NULL) { if (i_stream_read(input) <= 0) break; continue; } linenum++; /* @UNSAFE: line is modified */ /* 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_stream_unref(input); settings_verify(); } void settings_init(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); } } }