Index: vsftpd-3.0.2/str.c =================================================================== --- vsftpd-3.0.2.orig/str.c +++ vsftpd-3.0.2/str.c @@ -16,6 +16,12 @@ #define PRIVATE_HANDS_OFF_alloc_bytes alloc_bytes #include "str.h" +/* normalize filepath */ +#include +#include +#include +#include + /* Ick. Its for die() */ #include "utility.h" #include "sysutil.h" @@ -781,3 +787,60 @@ str_basename (struct mystr* d_str, const if (str_isempty(d_str)) str_copy (d_str, path); } + +void +str_normalize_filepath(struct mystr* filepath) +{ + char *path; + char *normdir; + char *dir; + char *filename; + static struct mystr tmp; + + /* normalize filepath */ + path = str_strdup(filepath); + if (path == NULL) + { + return; + } + char *ch1 = strdup(path); + char *ch2 = strdup(path); + if (ch1 == NULL || ch2 == NULL) + { + goto out; + } + /* we split dir/file as realpath /home/REGEXP is NULL so we need dir + * dir only to function correctly, later on we need to glue back the + * file if there is some + */ + dir = dirname(ch1); + filename = basename(ch2); + normdir = realpath(dir, NULL); + if (normdir == NULL) + { + goto out; + } + str_alloc_text(&tmp, normdir); + unsigned int len = str_getlen(&tmp); + if (str_get_char_at(&tmp, len - 1) != '/') + { + str_append_char(&tmp, '/'); + } + /* / is special it ends in both dirname and basename so ignore it here */ + if (strcmp(filename, "/") != 0) + { + str_append_text(&tmp, filename); + } + /* TODO: here we should run one more stat to determine if the whole thing + * is a directory and append trailing / (ie. /home -> /home/). + * This will make the deny_file=/home/ work contrary to currently + * needed /home. + */ + str_copy(filepath, &tmp); + free(normdir); + str_free(&tmp); +out: + free(path); + free(ch1); + free(ch2); +} Index: vsftpd-3.0.2/str.h =================================================================== --- vsftpd-3.0.2.orig/str.h +++ vsftpd-3.0.2/str.h @@ -102,6 +102,7 @@ int str_atoi(const struct mystr* p_str); filesize_t str_a_to_filesize_t(const struct mystr* p_str); unsigned int str_octal_to_uint(const struct mystr* p_str); void str_basename (struct mystr* d_str, const struct mystr* path); +void str_normalize_filepath(struct mystr* filepath); /* PURPOSE: Extract a line of text (delimited by \n or EOF) from a string * buffer, starting at character position 'p_pos'. The extracted line will Index: vsftpd-3.0.2/ls.c =================================================================== --- vsftpd-3.0.2.orig/ls.c +++ vsftpd-3.0.2/ls.c @@ -117,11 +117,13 @@ vsf_ls_populate_dir_list(struct mystr_li { continue; } + str_copy(&s_next_path_and_filename_str, &normalised_base_dir_str); + str_append_str(&s_next_path_and_filename_str, &s_next_filename_str); /* If we have an ls option which is a filter, apply it */ if (!str_isempty(p_filter_str)) { unsigned int iters = 0; - if (!vsf_filename_passes_filter(&s_next_filename_str, p_filter_str, + if (!vsf_filename_passes_filter(&s_next_path_and_filename_str, p_filter_str, &iters)) { continue; @@ -130,8 +132,6 @@ vsf_ls_populate_dir_list(struct mystr_li /* Calculate the full path (relative to CWD) for lstat() and * output purposes */ - str_copy(&s_next_path_and_filename_str, &normalised_base_dir_str); - str_append_str(&s_next_path_and_filename_str, &s_next_filename_str); if (do_stat) { /* lstat() the file. Of course there's a race condition - the @@ -239,6 +239,7 @@ vsf_filename_passes_filter(const struct * for /a/?/c will not. */ struct mystr filter_remain_str = INIT_MYSTR; + struct mystr basic_name_str = INIT_MYSTR; struct mystr name_remain_str = INIT_MYSTR; struct mystr temp_str = INIT_MYSTR; struct mystr brace_list_str = INIT_MYSTR; @@ -249,27 +250,29 @@ vsf_filename_passes_filter(const struct int matched = 0; str_copy(&filter_remain_str, p_filter_str); + str_copy(&basic_name_str, p_filename_str); + str_normalize_filepath(&basic_name_str); - if (!str_isempty (&filter_remain_str) && !str_isempty(p_filename_str)) { + if (!str_isempty (&filter_remain_str) && !str_isempty(&basic_name_str)) { if (str_get_char_at(p_filter_str, 0) == '/') { - if (str_get_char_at(p_filename_str, 0) != '/') { + if (str_get_char_at(&basic_name_str, 0) != '/') { str_getcwd (&name_remain_str); if (str_getlen(&name_remain_str) > 1) /* cwd != root dir */ str_append_char (&name_remain_str, '/'); - str_append_str (&name_remain_str, p_filename_str); + str_append_str (&name_remain_str, &basic_name_str); } else - str_copy (&name_remain_str, p_filename_str); + str_copy (&name_remain_str, &basic_name_str); } else { if (str_get_char_at(p_filter_str, 0) != '{') - str_basename (&name_remain_str, p_filename_str); + str_basename (&name_remain_str, &basic_name_str); else - str_copy (&name_remain_str, p_filename_str); + str_copy (&name_remain_str, &basic_name_str); } } else - str_copy(&name_remain_str, p_filename_str); + str_copy(&name_remain_str, &basic_name_str); while (!str_isempty(&filter_remain_str) && *iters < VSFTP_MATCHITERS_MAX) { @@ -475,6 +475,7 @@ vsf_filename_passes_filter(const struct } out: str_free(&filter_remain_str); + str_free(&basic_name_str); str_free(&name_remain_str); str_free(&temp_str); str_free(&brace_list_str);