]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib-storage: Limit folder full name only
authorAki Tuomi <aki.tuomi@dovecot.fi>
Wed, 10 Aug 2016 10:07:01 +0000 (13:07 +0300)
committerAki Tuomi <aki.tuomi@dovecot.fi>
Fri, 9 Sep 2016 12:56:05 +0000 (15:56 +0300)
Before we had limit of 16 levels and 255 bytes per name
which is same as 4096 bytes. Now we limit only the total
length of the name to MAILBOX_LIST_NAME_MAX_LENGTH. For
compability reasons, we are restricting individual component
names to 255 characters.

src/lib-storage/mail-storage.c
src/lib-storage/mailbox-list-private.h
src/lib-storage/mailbox-list.c

index 5c71eb685434687d3758eadaf2cb119c3abb5e8f..8bed1ae49c50f16a7e67a084c4f775aeaaf33671 100644 (file)
@@ -30,6 +30,7 @@
 #include <ctype.h>
 
 #define MAILBOX_DELETE_RETRY_SECS 30
+#define MAILBOX_MAX_HIERARCHY_NAME_LENGTH 255
 
 extern struct mail_search_register *mail_search_register_imap;
 extern struct mail_search_register *mail_search_register_human;
@@ -971,8 +972,6 @@ void mailbox_skip_create_name_restrictions(struct mailbox *box, bool set)
 
 int mailbox_verify_create_name(struct mailbox *box)
 {
-       char sep = mail_namespace_get_sep(box->list->ns);
-
        /* mailbox_alloc() already checks that vname is valid UTF8,
           so we don't need to verify that.
 
@@ -987,7 +986,25 @@ int mailbox_verify_create_name(struct mailbox *box)
                        "Control characters not allowed in new mailbox names");
                return -1;
        }
-       if (mailbox_list_name_is_too_large(box->vname, sep)) {
+       if (strlen(box->vname) > MAILBOX_LIST_NAME_MAX_LENGTH) {
+               mail_storage_set_error(box->storage, MAIL_ERROR_PARAMS,
+                                      "Mailbox name too long");
+               return -1;
+       }
+       /* check individual component names, too */
+       const char *old_name = box->name;
+       const char *name;
+       const char sep = mailbox_list_get_hierarchy_sep(box->list);
+       while((name = strchr(old_name, sep)) != NULL) {
+               if (name - old_name > MAILBOX_MAX_HIERARCHY_NAME_LENGTH) {
+                       mail_storage_set_error(box->storage, MAIL_ERROR_PARAMS,
+                               "Mailbox name too long");
+                       return -1;
+               }
+               name++;
+               old_name = name;
+       }
+       if (old_name != NULL && strlen(old_name) > MAILBOX_MAX_HIERARCHY_NAME_LENGTH) {
                mail_storage_set_error(box->storage, MAIL_ERROR_PARAMS,
                                       "Mailbox name too long");
                return -1;
@@ -1469,6 +1486,41 @@ mailbox_lists_rename_compatible(struct mailbox_list *list1,
        return TRUE;
 }
 
+static
+int mailbox_rename_check_children(struct mailbox *src, struct mailbox *dest)
+{
+       int ret = 0;
+       size_t src_prefix_len = strlen(src->vname)+1; /* include separator */
+       size_t dest_prefix_len = strlen(dest->vname)+1;
+       /* this can return folders with * in their name, that are not
+          actually our children */
+       const char *pattern = t_strdup_printf("%s%c*", src->vname,
+                                 mail_namespace_get_sep(src->list->ns));
+
+       struct mailbox_list_iterate_context *iter = mailbox_list_iter_init(src->list, pattern,
+                                     MAILBOX_LIST_ITER_RETURN_NO_FLAGS);
+
+       const struct mailbox_info *child;
+       while((child = mailbox_list_iter_next(iter)) != NULL) {
+               if (strncmp(child->vname, src->vname, src_prefix_len) != 0)
+                       continue; /* not our child */
+               /* if total length of new name exceeds the limit, fail */
+               if (strlen(child->vname + src_prefix_len)+dest_prefix_len > MAILBOX_LIST_NAME_MAX_LENGTH) {
+                       mail_storage_set_error(dest->storage, MAIL_ERROR_PARAMS,
+                               "Mailbox or child name too long");
+                       ret = -1;
+                       break;
+               }
+       }
+
+       /* something went bad */
+       if (mailbox_list_iter_deinit(&iter) < 0) {
+               mail_storage_copy_list_error(dest->storage, src->list);
+               ret = -1;
+       }
+       return ret;
+}
+
 int mailbox_rename(struct mailbox *src, struct mailbox *dest)
 {
        const char *error = NULL;
@@ -1485,6 +1537,10 @@ int mailbox_rename(struct mailbox *src, struct mailbox *dest)
                mail_storage_copy_error(dest->storage, src->storage);
                return -1;
        }
+       if (mailbox_rename_check_children(src, dest) != 0) {
+               return -1;
+       }
+
        if (!mail_storages_rename_compatible(src->storage,
                                             dest->storage, &error) ||
            !mailbox_lists_rename_compatible(src->list,
index f444acc6098821a9fee650bb577bca75405cb9f2..e0659ca2333f53c91104fcc75c1c5d46f303f564 100644 (file)
@@ -204,7 +204,6 @@ void mailbox_list_iter_update(struct mailbox_list_iter_update_context *ctx,
                              const char *name);
 int mailbox_list_iter_subscriptions_refresh(struct mailbox_list *list);
 
-bool mailbox_list_name_is_too_large(const char *name, char sep);
 enum mailbox_list_file_type mailbox_list_get_file_type(const struct dirent *d);
 int mailbox_list_dirent_is_alias_symlink(struct mailbox_list *list,
                                         const char *dir_path,
index 1ce5c1a6176f2a25ef2af8f2c959c9215898c03f..a09e3ad200c57deded8f071ddeac369771e0c432 100644 (file)
 #include <dirent.h>
 #include <sys/stat.h>
 
-/* 16 * (255+1) = 4096 which is the standard PATH_MAX. Having these settings
-   prevents malicious user from creating eg. "a/a/a/.../a" mailbox name and
-   then start renaming them to larger names from end to beginning, which
-   eventually would start causing the failures when trying to use too
-   long mailbox names. 255 is the standard single directory name length, so
-   allow up to that high. */
-#define MAILBOX_MAX_HIERARCHY_LEVELS 16
-#define MAILBOX_MAX_HIERARCHY_NAME_LENGTH 255
-
 #define MAILBOX_LIST_FS_CONTEXT(obj) \
        MODULE_CONTEXT(obj, mailbox_list_fs_module)
 
@@ -1655,28 +1646,6 @@ void mailbox_list_set_changelog_timestamp(struct mailbox_list *list,
        list->changelog_timestamp = stamp;
 }
 
-bool mailbox_list_name_is_too_large(const char *name, char sep)
-{
-       unsigned int levels = 1, level_len = 0;
-
-       for (; *name != '\0'; name++) {
-               if (*name == sep) {
-                       if (level_len > MAILBOX_MAX_HIERARCHY_NAME_LENGTH)
-                               return TRUE;
-                       levels++;
-                       level_len = 0;
-               } else {
-                       level_len++;
-               }
-       }
-
-       if (level_len > MAILBOX_MAX_HIERARCHY_NAME_LENGTH)
-               return TRUE;
-       if (levels > MAILBOX_MAX_HIERARCHY_LEVELS)
-               return TRUE;
-       return FALSE;
-}
-
 enum mailbox_list_file_type
 mailbox_list_get_file_type(const struct dirent *d ATTR_UNUSED)
 {