static void handle_per_user_config(const struct mystr* p_user_str) { struct mystr filename_str = INIT_MYSTR; struct vsf_sysutil_statbuf* p_statbuf = 0; struct str_locate_result loc_result; int retval; if (!tunable_user_config_dir) { return; } /* Security paranoia - ignore if user has a / in it. */ loc_result = str_locate_char(p_user_str, '/'); if (loc_result.found) { return; } str_alloc_text(&filename_str, tunable_user_config_dir); str_append_char(&filename_str, '/'); str_append_str(&filename_str, p_user_str); retval = str_stat(&filename_str, &p_statbuf); if (!vsf_sysutil_retval_is_error(retval)) { /* Security - file ownership check now in vsf_parseconf_load_file() */ vsf_parseconf_load_file(str_getbuf(&filename_str), 1); } else if (vsf_sysutil_get_error() != kVSFSysUtilErrNOENT) { die("error opening per-user config file"); } str_free(&filename_str); vsf_sysutil_free(p_statbuf); }
static int ipv6_parse_main(struct mystr* p_out_str, const struct mystr* p_in_str) { static struct mystr s_lhs_str; static struct mystr s_rhs_str; struct str_locate_result loc_ret; str_copy(&s_lhs_str, p_in_str); while (!str_isempty(&s_lhs_str)) { str_split_char(&s_lhs_str, &s_rhs_str, ':'); if (str_isempty(&s_lhs_str)) { return 0; } loc_ret = str_locate_char(&s_lhs_str, '.'); if (loc_ret.found) { if (!ipv4_parse_dotquad(p_out_str, &s_lhs_str)) { return 0; } } else if (!ipv6_parse_hex(p_out_str, &s_lhs_str)) { return 0; } str_copy(&s_lhs_str, &s_rhs_str); } return 1; }
static void handle_per_user_config(const struct mystr* p_user_str) { struct mystr filename_str = INIT_MYSTR; struct vsf_sysutil_statbuf* p_statbuf = 0; struct str_locate_result loc_result; int retval; if (!tunable_user_config_dir) { return; } /* Security paranoia - ignore if user has a / in it. */ loc_result = str_locate_char(p_user_str, '/'); if (loc_result.found) { return; } str_alloc_text(&filename_str, tunable_user_config_dir); str_append_char(&filename_str, '/'); str_append_str(&filename_str, p_user_str); retval = str_stat(&filename_str, &p_statbuf); /* Security - ignore unless owned by root */ if (!vsf_sysutil_retval_is_error(retval) && vsf_sysutil_statbuf_get_uid(p_statbuf) == VSFTP_ROOT_UID) { vsf_parseconf_load_file(str_getbuf(&filename_str), 1); } str_free(&filename_str); vsf_sysutil_free(p_statbuf); }
static void handle_dir_common(struct vsf_session* p_sess, int full_details) { static struct mystr s_option_str; static struct mystr s_filter_str; static struct mystr s_dir_name_str; static struct vsf_sysutil_statbuf* s_p_dirstat; int remote_fd; int dir_allow_read = 1; struct vsf_sysutil_dir* p_dir = 0; str_empty(&s_option_str); str_empty(&s_filter_str); /* By default open the current directory */ str_alloc_text(&s_dir_name_str, "."); if (!pasv_active(p_sess) && !port_active(p_sess)) { vsf_cmdio_write(p_sess, FTP_BADSENDCONN, "Use PORT or PASV first."); return; } /* Do we have an option? Going to be strict here - the option must come * first. e.g. "ls -a .." fine, "ls .. -a" not fine */ if (!str_isempty(&p_sess->ftp_arg_str) && str_get_char_at(&p_sess->ftp_arg_str, 0) == '-') { /* Chop off the '-' */ str_mid_to_end(&p_sess->ftp_arg_str, &s_option_str, 1); /* A space will separate options from filter (if any) */ str_split_char(&s_option_str, &s_filter_str, ' '); } else { /* The argument, if any, is just a filter */ str_copy(&s_filter_str, &p_sess->ftp_arg_str); } if (!str_isempty(&s_filter_str)) { /* First check - is it an outright directory, as in "ls /pub" */ p_dir = str_opendir(&s_filter_str); if (p_dir != 0) { /* Listing a directory! */ str_copy(&s_dir_name_str, &s_filter_str); str_free(&s_filter_str); } else { struct str_locate_result locate_result = str_locate_char(&s_filter_str, '/'); if (locate_result.found) { /* Includes a path! Reverse scan for / in the arg, to get the * base directory and filter (if any) */ str_copy(&s_dir_name_str, &s_filter_str); str_split_char_reverse(&s_dir_name_str, &s_filter_str, '/'); /* If we have e.g. "ls /.message", we just ripped off the leading * slash because it is the only one! */ if (str_isempty(&s_dir_name_str)) { str_alloc_text(&s_dir_name_str, "/"); } } } } if (p_dir == 0) { /* NOTE - failure check done below, it's not forgotten */ p_dir = str_opendir(&s_dir_name_str); } /* Fine, do it */ remote_fd = get_remote_transfer_fd(p_sess); if (vsf_sysutil_retval_is_error(remote_fd)) { goto dir_close_out; } vsf_cmdio_write(p_sess, FTP_DATACONN, "Here comes the directory listing."); if (p_sess->is_anonymous && p_dir && tunable_anon_world_readable_only) { vsf_sysutil_dir_stat(p_dir, &s_p_dirstat); if (!vsf_sysutil_statbuf_is_readable_other(s_p_dirstat)) { dir_allow_read = 0; } } if (p_dir == 0 || !dir_allow_read) { vsf_cmdio_write(p_sess, FTP_TRANSFEROK, "Transfer done (but failed to open directory)."); } else { (void) vsf_ftpdataio_transfer_dir(p_sess, remote_fd, p_dir, &s_dir_name_str, &s_option_str, &s_filter_str, full_details); } (void) dispose_remote_transfer_fd(p_sess); dir_close_out: if (p_dir) { vsf_sysutil_closedir(p_dir); } port_cleanup(p_sess); pasv_cleanup(p_sess); }
static int transfer_dir_internal(struct vsf_session* p_sess, int is_control, struct vsf_sysutil_dir* p_dir, const struct mystr* p_base_dir_str, const struct mystr* p_option_str, const struct mystr* p_filter_str, int is_verbose) { struct mystr_list dir_list = INIT_STRLIST; struct mystr_list subdir_list = INIT_STRLIST; struct mystr dir_prefix_str = INIT_MYSTR; struct mystr_list* p_subdir_list = 0; struct str_locate_result loc_result = str_locate_char(p_option_str, 'R'); int failed = 0; enum EVSFRWTarget target = kVSFRWData; if (is_control) { target = kVSFRWControl; } if (loc_result.found && tunable_ls_recurse_enable) { p_subdir_list = &subdir_list; } vsf_ls_populate_dir_list(&dir_list, p_subdir_list, p_dir, p_base_dir_str, p_option_str, p_filter_str, is_verbose); if (p_subdir_list) { int retval; str_copy(&dir_prefix_str, p_base_dir_str); str_append_text(&dir_prefix_str, ":\r\n"); retval = ftp_write_str(p_sess, &dir_prefix_str, target); if (retval != 0) { failed = 1; } } if (!failed) { failed = write_dir_list(p_sess, &dir_list, target); } /* Recurse into the subdirectories if required... */ if (!failed) { struct mystr sub_str = INIT_MYSTR; unsigned int num_subdirs = str_list_get_length(&subdir_list); unsigned int subdir_index; for (subdir_index = 0; subdir_index < num_subdirs; subdir_index++) { int retval; struct vsf_sysutil_dir* p_subdir; const struct mystr* p_subdir_str = str_list_get_pstr(&subdir_list, subdir_index); if (str_equal_text(p_subdir_str, ".") || str_equal_text(p_subdir_str, "..")) { continue; } str_copy(&sub_str, p_base_dir_str); str_append_char(&sub_str, '/'); str_append_str(&sub_str, p_subdir_str); p_subdir = str_opendir(&sub_str); if (p_subdir == 0) { /* Unreadable, gone missing, etc. - no matter */ continue; } str_alloc_text(&dir_prefix_str, "\r\n"); retval = ftp_write_str(p_sess, &dir_prefix_str, target); if (retval != 0) { failed = 1; vsf_sysutil_closedir(p_subdir); break; } retval = transfer_dir_internal(p_sess, is_control, p_subdir, &sub_str, p_option_str, p_filter_str, is_verbose); vsf_sysutil_closedir(p_subdir); if (retval != 0) { failed = 1; break; } } str_free(&sub_str); } str_list_free(&dir_list); str_list_free(&subdir_list); str_free(&dir_prefix_str); if (!failed) { return 0; } else { return -1; } }
void vsf_ls_populate_dir_list(const char* session_user, struct mystr_list* p_list, struct mystr_list* p_subdir_list, struct vsf_sysutil_dir* p_dir, const struct mystr* p_base_dir_str, const struct mystr* p_option_str, const struct mystr* p_filter_str, int is_verbose) { struct mystr dirline_str = INIT_MYSTR; struct mystr normalised_base_dir_str = INIT_MYSTR; struct str_locate_result loc_result; int a_option; int r_option; int t_option; int F_option; int do_stat = 0; long curr_time = 0; loc_result = str_locate_char(p_option_str, 'a'); a_option = loc_result.found; loc_result = str_locate_char(p_option_str, 'r'); r_option = loc_result.found; loc_result = str_locate_char(p_option_str, 't'); t_option = loc_result.found; loc_result = str_locate_char(p_option_str, 'F'); F_option = loc_result.found; loc_result = str_locate_char(p_option_str, 'l'); if (loc_result.found) { is_verbose = 1; } /* Invert "reverse" arg for "-t", the time sorting */ if (t_option) { r_option = !r_option; } if (is_verbose || t_option || F_option || p_subdir_list != 0) { do_stat = 1; } /* If the filter starts with a . then implicitly enable -a */ if (!str_isempty(p_filter_str) && str_get_char_at(p_filter_str, 0) == '.') { a_option = 1; } /* "Normalise" the incoming base directory string by making sure it * ends in a '/' if it is nonempty */ if (!str_equal_text(p_base_dir_str, ".")) { str_copy(&normalised_base_dir_str, p_base_dir_str); } if (!str_isempty(&normalised_base_dir_str)) { unsigned int len = str_getlen(&normalised_base_dir_str); if (str_get_char_at(&normalised_base_dir_str, len - 1) != '/') { str_append_char(&normalised_base_dir_str, '/'); } } /* If we're going to need to do time comparisions, cache the local time */ if (is_verbose) { curr_time = vsf_sysutil_get_time_sec(); } while (1) { static struct mystr s_next_filename_str; static struct mystr s_next_path_and_filename_str; static struct vsf_sysutil_statbuf* s_p_statbuf; str_next_dirent(session_user, str_getbuf(p_base_dir_str), &s_next_filename_str, p_dir); if (!strcmp(str_getbuf(&s_next_filename_str), DENIED_DIR)) continue; if (str_isempty(&s_next_filename_str)) { break; } { unsigned int len = str_getlen(&s_next_filename_str); if (len > 0 && str_get_char_at(&s_next_filename_str, 0) == '.') { if (!a_option && !tunable_force_dot_files) { continue; } if (!a_option && ((len == 2 && str_get_char_at(&s_next_filename_str, 1) == '.') || len == 1)) { continue; } } } /* Don't show hidden directory entries */ if (!vsf_access_check_file_visible(&s_next_filename_str)) { continue; } #if 0 /* 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, &iters)) { continue; } } #endif /* 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 * directory entry may have gone away whilst we read it, so * ignore failure to stat */ int retval = str_lstat(&s_next_path_and_filename_str, &s_p_statbuf); if (vsf_sysutil_retval_is_error(retval)) { continue; } } if (is_verbose) { static struct mystr s_final_file_str; /* If it's a damn symlink, we need to append the target */ str_copy(&s_final_file_str, &s_next_filename_str); if (vsf_sysutil_statbuf_is_symlink(s_p_statbuf)) { static struct mystr s_temp_str; int retval = str_readlink(&s_temp_str, &s_next_path_and_filename_str); if (retval == 0 && !str_isempty(&s_temp_str)) { str_append_text(&s_final_file_str, " -> "); str_append_str(&s_final_file_str, &s_temp_str); } } if (F_option && vsf_sysutil_statbuf_is_dir(s_p_statbuf)) { str_append_char(&s_final_file_str, '/'); } build_dir_line(&dirline_str, &s_final_file_str, s_p_statbuf, curr_time); } else { char *ptr; /* Just emit the filenames - note, we prepend the directory for NLST * but not for LIST */ str_copy(&dirline_str, &s_next_path_and_filename_str); if (F_option) { if (vsf_sysutil_statbuf_is_dir(s_p_statbuf)) { str_append_char(&dirline_str, '/'); } else if (vsf_sysutil_statbuf_is_symlink(s_p_statbuf)) { str_append_char(&dirline_str, '@'); } } str_append_text(&dirline_str, "\r\n"); ptr = strstr(str_getbuf(&dirline_str), POOL_MOUNT_ROOT); if (ptr != NULL) str_alloc_text(&dirline_str, ptr + strlen(POOL_MOUNT_ROOT)); } /* Add filename into our sorted list - sorting by filename or time. Also, * if we are required to, maintain a distinct list of direct * subdirectories. */ { static struct mystr s_temp_str; const struct mystr* p_sort_str = 0; const struct mystr* p_sort_subdir_str = 0; if (!t_option) { p_sort_str = &s_next_filename_str; } else { str_alloc_text(&s_temp_str, vsf_sysutil_statbuf_get_sortkey_mtime(s_p_statbuf)); p_sort_str = &s_temp_str; p_sort_subdir_str = &s_temp_str; } str_list_add(p_list, &dirline_str, p_sort_str); if (p_subdir_list != 0 && vsf_sysutil_statbuf_is_dir(s_p_statbuf)) { str_list_add(p_subdir_list, &s_next_filename_str, p_sort_subdir_str); } } } /* END: while(1) */ str_list_sort(p_list, r_option); if (p_subdir_list != 0) { str_list_sort(p_subdir_list, r_option); } str_free(&dirline_str); str_free(&normalised_base_dir_str); }
int vsf_filename_passes_filter(const struct mystr* p_filename_str, const struct mystr* p_filter_str, unsigned int* iters) { /* A simple routine to match a filename against a pattern. * This routine is used instead of e.g. fnmatch(3), because we should be * reluctant to trust the latter. fnmatch(3) involves _lots_ of string * parsing and handling. There is broad potential for any given fnmatch(3) * implementation to be buggy. * * Currently supported pattern(s): * - any number of wildcards, "*" or "?" * - {,} syntax (not nested) * * Note that pattern matching is only supported within the last path * component. For example, searching for /a/b/? will work, but searching * for /a/?/c will not. */ struct mystr filter_remain_str = INIT_MYSTR; struct mystr name_remain_str = INIT_MYSTR; struct mystr temp_str = INIT_MYSTR; struct mystr brace_list_str = INIT_MYSTR; struct mystr new_filter_str = INIT_MYSTR; int ret = 0; char last_token = 0; int must_match_at_current_pos = 1; str_copy(&filter_remain_str, p_filter_str); str_copy(&name_remain_str, p_filename_str); while (!str_isempty(&filter_remain_str) && *iters < VSFTP_MATCHITERS_MAX) { static struct mystr s_match_needed_str; /* Locate next special token */ struct str_locate_result locate_result = str_locate_chars(&filter_remain_str, "*?{"); (*iters)++; /* Isolate text leading up to token (if any) - needs to be matched */ if (locate_result.found) { unsigned int indexx = locate_result.index; str_left(&filter_remain_str, &s_match_needed_str, indexx); str_mid_to_end(&filter_remain_str, &temp_str, indexx + 1); str_copy(&filter_remain_str, &temp_str); last_token = locate_result.char_found; } else { /* No more tokens. Must match remaining filter string exactly. */ str_copy(&s_match_needed_str, &filter_remain_str); str_empty(&filter_remain_str); last_token = 0; } if (!str_isempty(&s_match_needed_str)) { /* Need to match something.. could be a match which has to start at * current position, or we could allow it to start anywhere */ unsigned int indexx; locate_result = str_locate_str(&name_remain_str, &s_match_needed_str); if (!locate_result.found) { /* Fail */ goto out; } indexx = locate_result.index; if (must_match_at_current_pos && indexx > 0) { goto out; } /* Chop matched string out of remainder */ str_mid_to_end(&name_remain_str, &temp_str, indexx + str_getlen(&s_match_needed_str)); str_copy(&name_remain_str, &temp_str); } if (last_token == '?') { if (str_isempty(&name_remain_str)) { goto out; } str_right(&name_remain_str, &temp_str, str_getlen(&name_remain_str) - 1); str_copy(&name_remain_str, &temp_str); must_match_at_current_pos = 1; } else if (last_token == '{') { struct str_locate_result end_brace = str_locate_char(&filter_remain_str, '}'); must_match_at_current_pos = 1; if (end_brace.found) { str_split_char(&filter_remain_str, &temp_str, '}'); str_copy(&brace_list_str, &filter_remain_str); str_copy(&filter_remain_str, &temp_str); str_split_char(&brace_list_str, &temp_str, ','); while (!str_isempty(&brace_list_str)) { str_copy(&new_filter_str, &brace_list_str); str_append_str(&new_filter_str, &filter_remain_str); if (vsf_filename_passes_filter(&name_remain_str, &new_filter_str, iters)) { ret = 1; goto out; } str_copy(&brace_list_str, &temp_str); str_split_char(&brace_list_str, &temp_str, ','); } goto out; } else if (str_isempty(&name_remain_str) || str_get_char_at(&name_remain_str, 0) != '{') { goto out; } else { str_right(&name_remain_str, &temp_str, str_getlen(&name_remain_str) - 1); str_copy(&name_remain_str, &temp_str); } } else { must_match_at_current_pos = 0; } } /* Any incoming string left means no match unless we ended on the correct * type of wildcard. */ if (str_getlen(&name_remain_str) > 0 && last_token != '*') { goto out; } /* OK, a match */ ret = 1; if (*iters == VSFTP_MATCHITERS_MAX) { ret = 0; } out: str_free(&filter_remain_str); str_free(&name_remain_str); str_free(&temp_str); str_free(&brace_list_str); str_free(&new_filter_str); return ret; }