{
const char *ptr;
string_t *new_key = NULL;
+ /* A key always starts with either "priv/" or "shared/". Usernames can
+ start with "./" or "../". Thus make sure the given key has no such
+ prefix. */
+ if (str_begins_with(key, "./") || str_begins_with(key, "../")) {
+ new_key = t_str_new(strlen(key));
+ str_append(new_key, "..");
+ }
/* we take the slow path always if we see potential
need for escaping */
while ((ptr = strstr(key, "/.")) != NULL) {
if (str_begins(key, DICT_PATH_SHARED, &key))
return key;
else if (str_begins(key, DICT_PATH_PRIVATE, &key))
- return t_strdup_printf("%s/%s", username, key);
+ return t_strdup_printf("%s/%s", fs_dict_escape_key(username), key);
else
i_unreached();
}
test_assert(test_file_exists(".test-dict/.test"));
test_dict_set_get(dict, "testuser", "shared/..test", "6");
test_assert(test_file_exists(".test-dict/..test"));
+
+ /* make sure path traversal in usernames is prevented */
+ const char *username_test_key = "priv/./key";
+ struct {
+ const char *username;
+ const char *path;
+ } username_test_cases[] = {
+ { ".testuser", ".test-dict/.testuser/...key" },
+ { "./testuser", ".test-dict/.../testuser/.../key" },
+ { "./.testuser", ".test-dict/.../.testuser/.../key" },
+ { "../testuser", ".test-dict/..../testuser/.../key" },
+ { "../.testuser", ".test-dict/..../.testuser/.../key" },
+ { "././testuser", ".test-dict/.../.../testuser/.../key" },
+ { "././.testuser", ".test-dict/.../.../testuser/.../key" },
+ { "./../testuser", ".test-dict/.../..../testuser/.../key" },
+ { "./../.testuser", ".test-dict/.../..../testuser/.../key" },
+ { ".././testuser", ".test-dict/..../.../testuser/.../key" },
+ { "./../.testuser", ".test-dict/.../..../testuser/.../key" },
+ { "../../testuser", ".test-dict/..../..../testuser/.../key" },
+ { "../../.testuser", ".test-dict/..../..../testuser/.../key" },
+ };
+ for (size_t i = 0; i < N_ELEMENTS(username_test_cases); i++) {
+ test_dict_set_get(dict, username_test_cases[i].username,
+ username_test_key, "1");
+ test_assert(test_file_exists(test_cases[i].path));
+ }
dict_deinit(&dict);
if (unlink_directory(".test-dict", UNLINK_DIRECTORY_FLAG_RMDIR, &error) < 0)