Source: vsftpd Severity: normal Tags: patch Dear Maintainer,
unlike the shell command realpath(1), the library call realpath(3) is not specified to canonize not yet existing paths. It will rather return an ENOENT error code if a filename does not exist. The library call realpath(3) is used by the original implemenation of the function vsf_filename_passes_filter[ls.c:258]. This function backs the access checkers implemented in access.c. As a non-existing file cannot be matched in the original implementation, the deny_file option becomes ineffective for uploading fresh files. For instance: even though deny_file=*.doc, an example.doc may initially be uploaded. On the other hand, it may not be updated once the file actually exists. The appended patch implements a more intuitive behavior. It provides a custom wrapper for the realpath(3) library call, which also tries to canonize the isolated directory part of a non-existing path. If this step is successful, a canonized path with the original basename is constructed. Otherwise, the match, indeed, fails. The provided patch: - implements the custom wrapper for realpath(3), and -> sysutil.h, sysutil.c - uses this wrapper in vsf_filename_passes_filter. -> ls.c Note that the implemented vsf_sysutil_realpath() wrapper function allows to turn off the directory-based canonization by deasserting the parameter may_be_fresh. Ultimately, this feature may be used for an optimized handling of calls from access checkers as vsf_access_check_file_visible(), which would never create a fresh file anyhow. ------------------------------------------------------------------------------- Description: realpath wrapper to match not yet existing files in deny_file and others Author: Thomas B. Preußer <thomas.preus...@utexas.edu> Last-Update: 2015-12-23 --- This patch header follows DEP-3: http://dep.debian.net/deps/dep3/ =================================================================== Index: ls.c sysutil.h sysutil.c --- vsftpd.orig/ls.c +++ vsftpd/ls.c @@ -255,7 +255,7 @@ /* normalize filepath */ path = str_strdup(p_filename_str); - normname = realpath(path, NULL); + normname = vsf_sysutil_realpath(path, 1); if (normname == NULL) goto out; str_alloc_text(&normalize_filename_str, normname); --- vsftpd.orig/sysutil.c +++ vsftpd/sysutil.c @@ -988,6 +988,51 @@ return rename(p_from, p_to); } +char* +vsf_sysutil_realpath(char const *path, int may_be_fresh) +{ + { /* existing paths must resolve right away */ + char *const resolved = realpath(path, NULL); + if ((resolved != NULL) || (errno != ENOENT) || !may_be_fresh) + { + return resolved; + } + } + + { /* try to resolve directory part */ + char const *filename = strrchr(path, '/'); + char const *resolved_dir; + if(filename == NULL) + { + filename = path; + resolved_dir = realpath(".", NULL); + } + else + { + char const *original_dir; + filename++; + original_dir = strndup(path, filename-path); + resolved_dir = realpath(original_dir, NULL); + free((void*)original_dir); + } + if(resolved_dir == NULL) return NULL; + + /* compose path from resolved directory and filename */ + size_t dir_len = strlen(resolved_dir); + char *resolved; + + /* empty root as slash is added anyways */ + if (dir_len == 1) dir_len == 0; + + resolved = (char*)malloc(dir_len+strlen(filename)+2); + strcpy(resolved, resolved_dir); + free((void*)resolved_dir); + resolved[dir_len] = '/'; + strcpy(resolved+dir_len+1, filename); + return resolved; + } +} + struct vsf_sysutil_dir* vsf_sysutil_opendir(const char* p_dirname) { --- vsftpd.orig/sysutil.h +++ vsftpd/sysutil.h @@ -66,6 +66,7 @@ int vsf_sysutil_rmdir(const char* p_dirname); int vsf_sysutil_chdir(const char* p_dirname); int vsf_sysutil_rename(const char* p_from, const char* p_to); +char* vsf_sysutil_realpath(char const *path, int may_be_fresh); struct vsf_sysutil_dir; struct vsf_sysutil_dir* vsf_sysutil_opendir(const char* p_dirname);