From 50933e4d608a259c49e72775284a5f9df985de84 Mon Sep 17 00:00:00 2001 From: Timo Sirainen Date: Tue, 11 Feb 2003 21:37:16 +0200 Subject: [PATCH] Added mail_full_filesystem_access setting. Some of the setting variable types were declared wrong and caused Dovecot to crash if they were set in config file. --HG-- branch : HEAD --- dovecot-example.conf | 6 ++ src/lib-storage/index/maildir/maildir-list.c | 34 ++++++-- src/lib-storage/index/mbox/mbox-list.c | 87 +++++++++++++++----- src/lib-storage/index/mbox/mbox-storage.c | 7 ++ src/lib-storage/mail-storage.c | 3 + src/lib-storage/mail-storage.h | 1 + src/lib/Makefile.am | 2 + src/lib/home-expand.c | 41 +++++++++ src/lib/home-expand.h | 7 ++ src/master/master-settings.c | 22 ++--- src/master/master-settings.h | 1 + 11 files changed, 175 insertions(+), 36 deletions(-) create mode 100644 src/lib/home-expand.c create mode 100644 src/lib/home-expand.h diff --git a/dovecot-example.conf b/dovecot-example.conf index dc91243cb3..1bbe75d403 100644 --- a/dovecot-example.conf +++ b/dovecot-example.conf @@ -223,6 +223,12 @@ login = pop3 # NOTE: Evolution client breaks with this option when it's trying to APPEND. #mailbox_check_interval = 0 +# Allow full filesystem access to clients. There's no access checks other than +# what the operating system does for the active UID/GID. It works with both +# maildir and mboxes, allowing you to prefix mailboxes names with eg. /path/ +# or ~user/. +#mail_full_filesystem_access = no + # Save mails with CR+LF instead of plain LF. This makes sending those mails # take less CPU, especially with sendfile() syscall with Linux and FreeBSD. # But it also creates a bit more disk I/O which may just make it slower. diff --git a/src/lib-storage/index/maildir/maildir-list.c b/src/lib-storage/index/maildir/maildir-list.c index 987aeaeefa..b0202200c3 100644 --- a/src/lib-storage/index/maildir/maildir-list.c +++ b/src/lib-storage/index/maildir/maildir-list.c @@ -2,6 +2,7 @@ #include "lib.h" #include "hostpid.h" +#include "home-expand.h" #include "unlink-directory.h" #include "imap-match.h" #include "subscription-file/subscription-file.h" @@ -79,15 +80,32 @@ int maildir_find_mailboxes(struct mail_storage *storage, const char *mask, struct dirent *d; struct stat st; enum mailbox_flags flags; + const char *dir, *prefix, *p; char path[PATH_MAX]; - int failed, found_inbox; + int failed, found_inbox, ret; mail_storage_clear_error(storage); - dirp = opendir(storage->dir); + if (!full_filesystem_access || (p = strrchr(mask, '/')) == NULL) { + dir = storage->dir; + prefix = ""; + } else { + if (mask == p) + dir = prefix = "/"; + else { + dir = t_strdup_until(mask, p); + prefix = t_strdup_until(mask, p+1); + } + + if (*mask != '/' && *mask != '~') + dir = t_strconcat(storage->dir, "/", dir, NULL); + dir = home_expand(dir); + } + + dirp = opendir(dir); if (dirp == NULL) { mail_storage_set_critical(storage, "opendir(%s) failed: %m", - storage->dir); + dir); return FALSE; } @@ -106,10 +124,13 @@ int maildir_find_mailboxes(struct mail_storage *storage, const char *mask, /* make sure the mask matches - dirs beginning with ".." should be deleted and we always want to check those. */ - if (fname[1] == '.' || imap_match(glob, fname+1) <= 0) + t_push(); + ret = imap_match(glob, t_strconcat(prefix, fname+1, NULL)); + t_pop(); + if (fname[1] == '.' || ret <= 0) continue; - if (str_path(path, sizeof(path), storage->dir, fname) < 0) + if (str_path(path, sizeof(path), dir, fname) < 0) continue; /* make sure it's a directory */ @@ -147,7 +168,8 @@ int maildir_find_mailboxes(struct mail_storage *storage, const char *mask, t_push(); flags = maildir_get_marked_flags(storage, path); - callback(storage, fname+1, flags, context); + callback(storage, t_strconcat(prefix, fname+1, NULL), + flags, context); t_pop(); } diff --git a/src/lib-storage/index/mbox/mbox-list.c b/src/lib-storage/index/mbox/mbox-list.c index a67d9cd7b2..df4f8f7e4a 100644 --- a/src/lib-storage/index/mbox/mbox-list.c +++ b/src/lib-storage/index/mbox/mbox-list.c @@ -6,6 +6,7 @@ #include "subscription-file/subscription-file.h" #include "mbox-index.h" #include "mbox-storage.h" +#include "home-expand.h" #include #include @@ -15,10 +16,16 @@ struct find_subscribed_context { void *context; }; -static int mbox_find_path(struct mail_storage *storage, - struct imap_match_glob *glob, - mailbox_list_callback_t callback, void *context, - const char *relative_dir) +struct list_context { + struct mail_storage *storage; + struct imap_match_glob *glob; + mailbox_list_callback_t *callback; + void *context; + + const char *rootdir; +}; + +static int mbox_find_path(struct list_context *ctx, const char *relative_dir) { DIR *dirp; struct dirent *d; @@ -31,11 +38,14 @@ static int mbox_find_path(struct mail_storage *storage, t_push(); if (relative_dir == NULL) - dir = storage->dir; + dir = ctx->rootdir; + else if (*ctx->rootdir == '\0' && *relative_dir != '\0') + dir = relative_dir; else { if (str_path(fulldir, sizeof(fulldir), - storage->dir, relative_dir) < 0) { - mail_storage_set_critical(storage, "Path too long: %s", + ctx->rootdir, relative_dir) < 0) { + mail_storage_set_critical(ctx->storage, + "Path too long: %s", relative_dir); return FALSE; } @@ -43,6 +53,7 @@ static int mbox_find_path(struct mail_storage *storage, dir = fulldir; } + dir = home_expand(dir); dirp = opendir(dir); if (dirp == NULL) { t_pop(); @@ -54,7 +65,16 @@ static int mbox_find_path(struct mail_storage *storage, return TRUE; } - mail_storage_set_critical(storage, + if (errno == EACCES) { + if (relative_dir != NULL) { + /* subfolder, ignore */ + return TRUE; + } + mail_storage_set_error(ctx->storage, "Access denied"); + return FALSE; + } + + mail_storage_set_critical(ctx->storage, "opendir(%s) failed: %m", dir); return FALSE; } @@ -78,7 +98,7 @@ static int mbox_find_path(struct mail_storage *storage, else { if (str_path(path, sizeof(path), relative_dir, fname) < 0) { - mail_storage_set_critical(storage, + mail_storage_set_critical(ctx->storage, "Path too long: %s/%s", relative_dir, fname); failed = TRUE; @@ -87,12 +107,12 @@ static int mbox_find_path(struct mail_storage *storage, listpath = path; } - if ((match = imap_match(glob, listpath)) < 0) + if ((match = imap_match(ctx->glob, listpath)) < 0) continue; /* see if it's a directory */ if (str_path(fullpath, sizeof(fullpath), dir, fname) < 0) { - mail_storage_set_critical(storage, + mail_storage_set_critical(ctx->storage, "Path too long: %s/%s", dir, fname); failed = TRUE; @@ -103,8 +123,9 @@ static int mbox_find_path(struct mail_storage *storage, if (errno == ENOENT) continue; /* just deleted, ignore */ - mail_storage_set_critical(storage, "stat(%s) failed: " - "%m", fullpath); + mail_storage_set_critical(ctx->storage, + "stat(%s) failed: %m", + fullpath); failed = TRUE; break; } @@ -112,22 +133,23 @@ static int mbox_find_path(struct mail_storage *storage, if (S_ISDIR(st.st_mode)) { /* subdirectory, scan it too */ t_push(); - callback(storage, listpath, MAILBOX_NOSELECT, context); + ctx->callback(ctx->storage, listpath, MAILBOX_NOSELECT, + ctx->context); t_pop(); - if (!mbox_find_path(storage, glob, callback, - context, listpath)) { + if (!mbox_find_path(ctx, listpath)) { failed = TRUE; break; } } else if (match > 0 && - strcmp(fullpath, storage->inbox_file) != 0 && + strcmp(fullpath, ctx->storage->inbox_file) != 0 && strcasecmp(listpath, "INBOX") != 0) { /* don't match any INBOX here, it's added later. we might also have ~/mail/inbox, ~/mail/Inbox etc. Just ignore them for now. */ t_push(); - callback(storage, listpath, MAILBOX_NOINFERIORS, context); + ctx->callback(ctx->storage, listpath, + MAILBOX_NOINFERIORS, ctx->context); t_pop(); } } @@ -148,12 +170,13 @@ static const char *mask_get_dir(const char *mask) last_dir = p; } - return last_dir != NULL ? t_strdup_until(mask, last_dir) : NULL; + return last_dir == NULL ? NULL : t_strdup_until(mask, last_dir); } int mbox_find_mailboxes(struct mail_storage *storage, const char *mask, mailbox_list_callback_t callback, void *context) { + struct list_context ctx; struct imap_match_glob *glob; const char *relative_dir; @@ -175,7 +198,31 @@ int mbox_find_mailboxes(struct mail_storage *storage, const char *mask, callback(storage, "INBOX", MAILBOX_NOINFERIORS, context); } - if (!mbox_find_path(storage, glob, callback, context, relative_dir)) + memset(&ctx, 0, sizeof(ctx)); + ctx.storage = storage; + ctx.glob = glob; + ctx.callback = callback; + ctx.context = context; + + if (!full_filesystem_access || relative_dir == NULL || + (*relative_dir != '/' && *relative_dir != '~' && + *relative_dir != '\0')) + ctx.rootdir = storage->dir; + else + ctx.rootdir = ""; + + if (relative_dir != NULL) { + const char *matchdir = t_strconcat(relative_dir, "/", NULL); + + if (imap_match(ctx.glob, matchdir) > 0) { + t_push(); + ctx.callback(ctx.storage, matchdir, MAILBOX_NOSELECT, + ctx.context); + t_pop(); + } + } + + if (!mbox_find_path(&ctx, relative_dir)) return FALSE; return TRUE; diff --git a/src/lib-storage/index/mbox/mbox-storage.c b/src/lib-storage/index/mbox/mbox-storage.c index 68576dc177..7366d069fe 100644 --- a/src/lib-storage/index/mbox/mbox-storage.c +++ b/src/lib-storage/index/mbox/mbox-storage.c @@ -220,6 +220,13 @@ int mbox_is_valid_mask(const char *mask) const char *p; int newdir; + if (full_filesystem_access) + return TRUE; + + /* make sure it's not absolute path */ + if (*mask == '/' || *mask == '\\' || *mask == '~') + return FALSE; + /* make sure there's no "../" or "..\" stuff */ newdir = TRUE; for (p = mask; *p != '\0'; p++) { diff --git a/src/lib-storage/mail-storage.c b/src/lib-storage/mail-storage.c index 32169879f1..f1ad843331 100644 --- a/src/lib-storage/mail-storage.c +++ b/src/lib-storage/mail-storage.c @@ -28,6 +28,7 @@ struct client_workaround_list client_workaround_list[] = { static struct mail_storage_list *storages = NULL; enum client_workarounds client_workarounds = 0; +int full_filesystem_access = FALSE; void mail_storage_init(void) { @@ -35,6 +36,8 @@ void mail_storage_init(void) const char *env; const char *const *str; + full_filesystem_access = getenv("FULL_FILESYSTEM_ACCESS") != NULL; + env = getenv("CLIENT_WORKAROUNDS"); if (env == NULL) return; diff --git a/src/lib-storage/mail-storage.h b/src/lib-storage/mail-storage.h index bc4c383fa9..e286fa79ce 100644 --- a/src/lib-storage/mail-storage.h +++ b/src/lib-storage/mail-storage.h @@ -394,6 +394,7 @@ struct mail_storage_callbacks { }; extern enum client_workarounds client_workarounds; +extern int full_filesystem_access; /* Initialize mail storage. */ void mail_storage_init(void); diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am index d3187b9192..57439bdcf8 100644 --- a/src/lib/Makefile.am +++ b/src/lib/Makefile.am @@ -14,6 +14,7 @@ liblib_a_SOURCES = \ file-set-size.c \ hash.c \ hex-binary.c \ + home-expand.c \ hostpid.c \ imem.c \ iostream.c \ @@ -68,6 +69,7 @@ noinst_HEADERS = \ file-set-size.h \ hash.h \ hex-binary.h \ + home-expand.h \ hostpid.h \ imem.h \ iostream-internal.h \ diff --git a/src/lib/home-expand.c b/src/lib/home-expand.c new file mode 100644 index 0000000000..a95055eeff --- /dev/null +++ b/src/lib/home-expand.c @@ -0,0 +1,41 @@ +/* Copyright (C) 2003 Timo Sirainen */ + +#include "lib.h" +#include "home-expand.h" + +#include +#include + +/* expand ~/ or ~user/ in beginning of path */ +const char *home_expand(const char *path) +{ + const char *home, *p, *orig_path; + struct passwd *pw; + + if (*path != '~') + return path; + + orig_path = path++; + if (*path == '/' || *path == '\0') { + home = getenv("HOME"); + if (*path != '\0') path++; + } else { + p = strchr(path, '/'); + if (p == NULL) { + pw = getpwnam(path); + path = ""; + } else { + pw = getpwnam(t_strdup_until(path, p)); + path = p+1; + } + + home = pw == NULL ? NULL : pw->pw_dir; + } + + if (home == NULL) + return orig_path; + else if (*path == '\0') + return t_strdup(home); + else + return t_strconcat(home, "/", path, NULL); +} diff --git a/src/lib/home-expand.h b/src/lib/home-expand.h new file mode 100644 index 0000000000..810c07bb6b --- /dev/null +++ b/src/lib/home-expand.h @@ -0,0 +1,7 @@ +#ifndef __HOME_EXPAND_H +#define __HOME_EXPAND_H + +/* expand ~/ or ~user/ in beginning of path */ +const char *home_expand(const char *path); + +#endif diff --git a/src/master/master-settings.c b/src/master/master-settings.c index 5a8561d61f..ccc176c9ab 100644 --- a/src/master/master-settings.c +++ b/src/master/master-settings.c @@ -54,17 +54,18 @@ static struct setting_def setting_defs[] = { DEF(SET_STR, mail_cache_fields), DEF(SET_STR, mail_never_cache_fields), DEF(SET_STR, client_workarounds), - DEF(SET_STR, mailbox_check_interval), - DEF(SET_STR, mail_save_crlf), - DEF(SET_STR, mail_read_mmaped), - DEF(SET_STR, maildir_copy_with_hardlinks), - DEF(SET_STR, maildir_check_content_changes), + DEF(SET_INT, mailbox_check_interval), + DEF(SET_BOOL, mail_full_filesystem_access), + DEF(SET_BOOL, mail_save_crlf), + DEF(SET_BOOL, mail_read_mmaped), + DEF(SET_BOOL, maildir_copy_with_hardlinks), + DEF(SET_BOOL, maildir_check_content_changes), DEF(SET_STR, mbox_locks), - DEF(SET_STR, mbox_read_dotlock), - DEF(SET_STR, mbox_lock_timeout), - DEF(SET_STR, mbox_dotlock_change_timeout), - DEF(SET_STR, overwrite_incompatible_index), - DEF(SET_STR, umask), + DEF(SET_BOOL, mbox_read_dotlock), + DEF(SET_INT, mbox_lock_timeout), + DEF(SET_INT, mbox_dotlock_change_timeout), + DEF(SET_BOOL, overwrite_incompatible_index), + DEF(SET_INT, umask), /* imap */ DEF(SET_STR, imap_executable), @@ -157,6 +158,7 @@ struct settings default_settings = { MEMBER(mail_never_cache_fields) NULL, MEMBER(client_workarounds) NULL, MEMBER(mailbox_check_interval) 0, + MEMBER(mail_full_filesystem_access) FALSE, MEMBER(mail_save_crlf) FALSE, MEMBER(mail_read_mmaped) FALSE, MEMBER(maildir_copy_with_hardlinks) FALSE, diff --git a/src/master/master-settings.h b/src/master/master-settings.h index da27078ecf..bda815bd8a 100644 --- a/src/master/master-settings.h +++ b/src/master/master-settings.h @@ -39,6 +39,7 @@ struct settings { const char *mail_never_cache_fields; const char *client_workarounds; unsigned int mailbox_check_interval; + int mail_full_filesystem_access; int mail_save_crlf; int mail_read_mmaped; int maildir_copy_with_hardlinks; -- 2.47.3