changeset 20726:231fad02c248

lib-storage: Limit folder full name only 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.
author Aki Tuomi <aki.tuomi@dovecot.fi>
date Wed, 10 Aug 2016 13:07:01 +0300
parents 89984572c720
children 44ae56ce7d0e
files src/lib-storage/mail-storage.c src/lib-storage/mailbox-list-private.h src/lib-storage/mailbox-list.c
diffstat 3 files changed, 59 insertions(+), 35 deletions(-) [+]
line wrap: on
line diff
--- a/src/lib-storage/mail-storage.c	Mon Aug 22 16:13:32 2016 +0300
+++ b/src/lib-storage/mail-storage.c	Wed Aug 10 13:07:01 2016 +0300
@@ -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 @@
 
 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 @@
 			"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 @@
 	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 @@
 		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,
--- a/src/lib-storage/mailbox-list-private.h	Mon Aug 22 16:13:32 2016 +0300
+++ b/src/lib-storage/mailbox-list-private.h	Wed Aug 10 13:07:01 2016 +0300
@@ -204,7 +204,6 @@
 			      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,
--- a/src/lib-storage/mailbox-list.c	Mon Aug 22 16:13:32 2016 +0300
+++ b/src/lib-storage/mailbox-list.c	Wed Aug 10 13:07:01 2016 +0300
@@ -27,15 +27,6 @@
 #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)
 
@@ -1658,28 +1649,6 @@
 	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)
 {