static void handle_site(struct vsf_session* p_sess) { static struct mystr s_site_args_str; /* What SITE sub-command is it? */ str_split_char(&p_sess->ftp_arg_str, &s_site_args_str, ' '); str_upper(&p_sess->ftp_arg_str); if (tunable_write_enable && tunable_chmod_enable && str_equal_text(&p_sess->ftp_arg_str, "CHMOD")) { handle_site_chmod(p_sess, &s_site_args_str); } else if (str_equal_text(&p_sess->ftp_arg_str, "UMASK")) { handle_site_umask(p_sess, &s_site_args_str); } else if (str_equal_text(&p_sess->ftp_arg_str, "HELP")) { vsf_cmdio_write(p_sess, FTP_SITEHELP, "CHMOD UMASK HELP"); } else { vsf_cmdio_write(p_sess, FTP_BADCMD, "Unknown SITE command."); } }
static void handle_site_chmod(struct vsf_session* p_sess, struct mystr* p_arg_str) { static struct mystr s_chmod_file_str; unsigned int perms; int retval; if (str_isempty(p_arg_str)) { vsf_cmdio_write(p_sess, FTP_BADCMD, "SITE CHMOD needs 2 arguments."); return; } str_split_char(p_arg_str, &s_chmod_file_str, ' '); if (str_isempty(&s_chmod_file_str)) { vsf_cmdio_write(p_sess, FTP_BADCMD, "SITE CHMOD needs 2 arguments."); return; } /* Don't worry - our chmod() implementation only allows 0 - 0777 */ perms = str_octal_to_uint(p_arg_str); retval = str_chmod(&s_chmod_file_str, perms); if (vsf_sysutil_retval_is_error(retval)) { vsf_cmdio_write(p_sess, FTP_FILEFAIL, "SITE CHMOD command failed."); } else { vsf_cmdio_write(p_sess, FTP_CHMODOK, "SITE CHMOD command ok."); } }
const unsigned char* vsf_sysutil_parse_uchar_string_sep( const struct mystr* p_str, char sep, unsigned char* p_items, unsigned int items) { static struct mystr s_tmp_str; unsigned int i; str_copy(&s_tmp_str, p_str); for (i=0; i<items; i++) { static struct mystr s_rhs_sep_str; int this_number; /* This puts a single separator delimited field in tmp_str */ str_split_char(&s_tmp_str, &s_rhs_sep_str, sep); /* Sanity - check for too many or two few dots! */ if ( (i < (items-1) && str_isempty(&s_rhs_sep_str)) || (i == (items-1) && !str_isempty(&s_rhs_sep_str))) { return 0; } this_number = str_atoi(&s_tmp_str); if (this_number < 0 || this_number > 255) { return 0; } /* If this truncates from int to uchar, we don't care */ p_items[i] = (unsigned char) this_number; /* The right hand side of the comma now becomes the new string to * breakdown */ str_copy(&s_tmp_str, &s_rhs_sep_str); } return p_items; }
void vsf_cmdio_get_cmd_and_arg(struct vsf_session* p_sess, struct mystr* p_cmd_str, struct mystr* p_arg_str, int set_alarm) { /* Prepare an alarm to timeout the session.. */ if (set_alarm) { vsf_cmdio_set_alarm(p_sess); } /* Blocks */ control_getline(p_cmd_str, p_sess); str_split_char(p_cmd_str, p_arg_str, ' '); str_upper(p_cmd_str); if (tunable_log_ftp_protocol) { static struct mystr s_log_str; if (str_equal_text(p_cmd_str, "PASS")) { str_alloc_text(&s_log_str, "PASS <password>"); } else { str_copy(&s_log_str, p_cmd_str); if (!str_isempty(p_arg_str)) { str_append_char(&s_log_str, ' '); str_append_str(&s_log_str, p_arg_str); } } vsf_log_line(p_sess, kVSFLogEntryFTPInput, &s_log_str); } }
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_port(struct vsf_session* p_sess) { static struct mystr s_tmp_str; unsigned short the_port; unsigned char vals[6]; int i; pasv_cleanup(p_sess); port_cleanup(p_sess); str_copy(&s_tmp_str, &p_sess->ftp_arg_str); for (i=0; i<6; i++) { static struct mystr s_rhs_comma_str; int this_number; /* This puts a single , delimited field in tmp_str */ str_split_char(&s_tmp_str, &s_rhs_comma_str, ','); /* Sanity - check for too many or two few commas! */ if ( (i<5 && str_isempty(&s_rhs_comma_str)) || (i==5 && !str_isempty(&s_rhs_comma_str))) { vsf_cmdio_write(p_sess, FTP_BADCMD, "Illegal PORT command."); return; } this_number = str_atoi(&s_tmp_str); if (this_number < 0 || this_number > 255) { vsf_cmdio_write(p_sess, FTP_BADCMD, "Illegal PORT command."); return; } /* If this truncates from int to uchar, we don't care */ vals[i] = (unsigned char) this_number; /* The right hand side of the comma now becomes the new string to * breakdown */ str_copy(&s_tmp_str, &s_rhs_comma_str); } the_port = vals[4] << 8; the_port |= vals[5]; vsf_sysutil_sockaddr_alloc_ipv4(&p_sess->p_port_sockaddr); vsf_sysutil_sockaddr_set_ipv4addr(p_sess->p_port_sockaddr, vals); vsf_sysutil_sockaddr_set_port(p_sess->p_port_sockaddr, the_port); /* SECURITY: * 1) Reject requests not connecting to the control socket IP * 2) Reject connects to privileged ports */ if (!tunable_port_promiscuous) { if (!vsf_sysutil_sockaddr_addr_equal(p_sess->p_remote_addr, p_sess->p_port_sockaddr) || vsf_sysutil_is_port_reserved(the_port)) { vsf_cmdio_write(p_sess, FTP_BADCMD, "Illegal PORT command."); port_cleanup(p_sess); return; } } vsf_cmdio_write(p_sess, FTP_PORTOK, "PORT command successful. Consider using PASV."); }
/** * do_listen -- create a listen socket */ apr_status_t do_listen(apr_pollset_t *pollset, apr_hash_t *ht, apr_pool_t *mp, const char *param) { apr_status_t rv; apr_socket_t *s; apr_sockaddr_t *sa; char **ptr; int port; ptr = str_split_char(param, ":"); if (ptr == NULL) { fprintf(stderr, "Get Null param!\n"); return APR_EINVAL; } // then convert param port = atoi(ptr[1]); if (port < 0) return APR_EINVAL; rv = apr_sockaddr_info_get(&sa, ptr[0], APR_INET, port, 0, mp); if (rv != APR_SUCCESS) { return rv; } rv = apr_socket_create(&s, sa->family, SOCK_STREAM, APR_PROTO_TCP, mp); if (rv != APR_SUCCESS) { return rv; } /* non-blocking socket */ apr_socket_opt_set(s, APR_SO_NONBLOCK, 1); apr_socket_timeout_set(s, 0); apr_socket_opt_set(s, APR_SO_REUSEADDR, 1);/* this is useful for a * server(socket listening) process */ rv = apr_socket_bind(s, sa); if (rv != APR_SUCCESS) { return rv; } rv = apr_socket_listen(s, DEF_SOCKET_BACKLOG); if (rv != APR_SUCCESS) { return rv; } // Then.., add it to hash table, listen and connect socket // against a different val, so we can distinguish in the // pollset apr_hash_set(ht, s, APR_HASH_KEY_STRING, "S"); // server // Then.., if everything is OK, add it to the pollset apr_pollfd_t pfd = {mp, APR_POLL_SOCKET, APR_POLLIN, 0, {NULL}, NULL}; pfd.desc.s = s; apr_pollset_add(pollset, &pfd); free(ptr); return APR_SUCCESS; }
void vsf_parseconf_load_file(const char* p_filename) { struct mystr config_file_str = INIT_MYSTR; struct mystr config_setting_str = INIT_MYSTR; struct mystr config_value_str = INIT_MYSTR; unsigned int str_pos = 0; int retval; if (!p_filename) { p_filename = s_p_saved_filename; } else { if (s_p_saved_filename) { vsf_sysutil_free((char*)s_p_saved_filename); } s_p_saved_filename = vsf_sysutil_strdup(p_filename); } if (!p_filename) { bug("null filename in vsf_parseconf_load_file"); } if (!s_strings_copied) { s_strings_copied = 1; /* A minor hack to make sure all strings are malloc()'ed so we can free * them at some later date. Specifically handles strings embedded in the * binary. */ copy_string_settings(); } retval = str_fileread(&config_file_str, p_filename, VSFTP_CONF_FILE_MAX); if (vsf_sysutil_retval_is_error(retval)) { die("cannot open config file"); } while (str_getline(&config_file_str, &config_setting_str, &str_pos)) { if (str_isempty(&config_setting_str) || str_get_char_at(&config_setting_str, 0) == '#') { continue; } /* Split into name=value pair */ str_split_char(&config_setting_str, &config_value_str, '='); handle_config_setting(&config_setting_str, &config_value_str); } str_free(&config_file_str); str_free(&config_setting_str); str_free(&config_value_str); }
void vsf_cmdio_get_cmd_and_arg(struct vsf_session* p_sess, struct mystr* p_cmd_str, struct mystr* p_arg_str, int set_alarm) { /* Prepare an alarm to timeout the session.. */ if (set_alarm) { vsf_cmdio_set_alarm(p_sess); } /* Blocks */ control_getline(p_cmd_str, p_sess); /* View a single space as a command of " ", which although a useless command, * permits the caller to distinguish input of "" from " ". */ if (str_getlen(p_cmd_str) == 1 && str_get_char_at(p_cmd_str, 0) == ' ') { str_empty(p_arg_str); } else { str_split_char(p_cmd_str, p_arg_str, ' '); } str_upper(p_cmd_str); if (tunable_log_ftp_protocol) { static struct mystr s_log_str; if (str_equal_text(p_cmd_str, "PASS")) { str_alloc_text(&s_log_str, "PASS <password>"); } else { str_copy(&s_log_str, p_cmd_str); if (!str_isempty(p_arg_str)) { str_append_char(&s_log_str, ' '); str_append_str(&s_log_str, p_arg_str); } } vsf_log_line(p_sess, kVSFLogEntryFTPInput, &s_log_str); } }
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); }
void vsf_parseconf_load_setting(const char* p_setting, int errs_fatal) { static struct mystr s_setting_str; static struct mystr s_value_str; while (vsf_sysutil_isspace(*p_setting)) { p_setting++; } str_alloc_text(&s_setting_str, p_setting); str_split_char(&s_setting_str, &s_value_str, '='); /* Is it a string setting? */ { const struct parseconf_str_setting* p_str_setting = parseconf_str_array; while (p_str_setting->p_setting_name != 0) { if (str_equal_text(&s_setting_str, p_str_setting->p_setting_name)) { /* Got it */ const char** p_curr_setting = p_str_setting->p_variable; if (*p_curr_setting) { vsf_sysutil_free((char*) *p_curr_setting); } if (str_isempty(&s_value_str)) { *p_curr_setting = 0; } else { *p_curr_setting = str_strdup(&s_value_str); } return; } p_str_setting++; } } if (str_isempty(&s_value_str)) { if (errs_fatal) { die2("missing value in config file for: ", str_getbuf(&s_setting_str)); } else { return; } } /* Is it a boolean value? */ { const struct parseconf_bool_setting* p_bool_setting = parseconf_bool_array; while (p_bool_setting->p_setting_name != 0) { if (str_equal_text(&s_setting_str, p_bool_setting->p_setting_name)) { /* Got it */ str_upper(&s_value_str); if (str_equal_text(&s_value_str, "YES") || str_equal_text(&s_value_str, "TRUE") || str_equal_text(&s_value_str, "1")) { *(p_bool_setting->p_variable) = 1; } else if (str_equal_text(&s_value_str, "NO") || str_equal_text(&s_value_str, "FALSE") || str_equal_text(&s_value_str, "0")) { *(p_bool_setting->p_variable) = 0; } else if (errs_fatal) { die2("bad bool value in config file for: ", str_getbuf(&s_setting_str)); } return; } p_bool_setting++; } } /* Is it an unsigned integer setting? */ { const struct parseconf_uint_setting* p_uint_setting = parseconf_uint_array; while (p_uint_setting->p_setting_name != 0) { if (str_equal_text(&s_setting_str, p_uint_setting->p_setting_name)) { /* Got it */ /* If the value starts with 0, assume it's an octal value */ if (!str_isempty(&s_value_str) && str_get_char_at(&s_value_str, 0) == '0') { *(p_uint_setting->p_variable) = str_octal_to_uint(&s_value_str); } else { /* TODO: we could reject negatives instead of converting them? */ *(p_uint_setting->p_variable) = (unsigned int) str_atoi(&s_value_str); } return; } p_uint_setting++; } } if (errs_fatal) { die2("unrecognised variable in config file: ", str_getbuf(&s_setting_str)); } }
void process_post_login(struct vsf_session* p_sess) { if (p_sess->is_anonymous) { vsf_sysutil_set_umask(tunable_anon_umask); p_sess->bw_rate_max = tunable_anon_max_rate; } else { vsf_sysutil_set_umask(tunable_local_umask); p_sess->bw_rate_max = tunable_local_max_rate; } if (tunable_async_abor_enable) { vsf_sysutil_install_sighandler(kVSFSysUtilSigURG, handle_sigurg, p_sess); vsf_sysutil_activate_sigurg(VSFTP_COMMAND_FD); } /* Handle any login message */ vsf_banner_dir_changed(p_sess, FTP_LOGINOK); vsf_cmdio_write(p_sess, FTP_LOGINOK, "Login successful."); while(1) { int cmd_ok = 1; if (tunable_setproctitle_enable) { vsf_sysutil_setproctitle("IDLE"); } /* Blocks */ vsf_cmdio_get_cmd_and_arg(p_sess, &p_sess->ftp_cmd_str, &p_sess->ftp_arg_str, 1); if (tunable_setproctitle_enable) { struct mystr proctitle_str = INIT_MYSTR; str_copy(&proctitle_str, &p_sess->ftp_cmd_str); if (!str_isempty(&p_sess->ftp_arg_str)) { str_append_char(&proctitle_str, ' '); str_append_str(&proctitle_str, &p_sess->ftp_arg_str); } /* Suggestion from Solar */ str_replace_unprintable(&proctitle_str, '?'); vsf_sysutil_setproctitle_str(&proctitle_str); str_free(&proctitle_str); } /* Test command against the allowed list.. */ if (tunable_cmds_allowed) { static struct mystr s_src_str; static struct mystr s_rhs_str; str_alloc_text(&s_src_str, tunable_cmds_allowed); while (1) { str_split_char(&s_src_str, &s_rhs_str, ','); if (str_isempty(&s_src_str)) { cmd_ok = 0; break; } else if (str_equal(&s_src_str, &p_sess->ftp_cmd_str)) { break; } str_copy(&s_src_str, &s_rhs_str); } } if (!cmd_ok) { vsf_cmdio_write(p_sess, FTP_NOPERM, "Permission denied."); } else if (str_equal_text(&p_sess->ftp_cmd_str, "QUIT")) { vsf_cmdio_write(p_sess, FTP_GOODBYE, "Goodbye."); vsf_sysutil_exit(0); } else if (str_equal_text(&p_sess->ftp_cmd_str, "PWD") || str_equal_text(&p_sess->ftp_cmd_str, "XPWD")) { handle_pwd(p_sess); } else if (str_equal_text(&p_sess->ftp_cmd_str, "CWD") || str_equal_text(&p_sess->ftp_cmd_str, "XCWD")) { handle_cwd(p_sess); } else if (str_equal_text(&p_sess->ftp_cmd_str, "CDUP") || str_equal_text(&p_sess->ftp_cmd_str, "XCUP")) { handle_cdup(p_sess); } else if (tunable_pasv_enable && !p_sess->epsv_all && (str_equal_text(&p_sess->ftp_cmd_str, "PASV") || str_equal_text(&p_sess->ftp_cmd_str, "P@SW"))) { handle_pasv(p_sess, 0); } else if (tunable_pasv_enable && str_equal_text(&p_sess->ftp_cmd_str, "EPSV")) { handle_pasv(p_sess, 1); } else if (tunable_download_enable && str_equal_text(&p_sess->ftp_cmd_str, "RETR")) { handle_retr(p_sess); } else if (str_equal_text(&p_sess->ftp_cmd_str, "NOOP")) { vsf_cmdio_write(p_sess, FTP_NOOPOK, "NOOP ok."); } else if (str_equal_text(&p_sess->ftp_cmd_str, "SYST")) { vsf_cmdio_write(p_sess, FTP_SYSTOK, "UNIX Type: L8"); } else if (str_equal_text(&p_sess->ftp_cmd_str, "HELP")) { handle_help(p_sess); } else if (tunable_dirlist_enable && str_equal_text(&p_sess->ftp_cmd_str, "LIST")) { handle_list(p_sess); } else if (str_equal_text(&p_sess->ftp_cmd_str, "TYPE")) { handle_type(p_sess); } else if (tunable_port_enable && !p_sess->epsv_all && str_equal_text(&p_sess->ftp_cmd_str, "PORT")) { handle_port(p_sess); } else if (tunable_write_enable && (tunable_anon_upload_enable || !p_sess->is_anonymous) && str_equal_text(&p_sess->ftp_cmd_str, "STOR")) { handle_stor(p_sess); } else if (tunable_write_enable && (tunable_anon_mkdir_write_enable || !p_sess->is_anonymous) && (str_equal_text(&p_sess->ftp_cmd_str, "MKD") || str_equal_text(&p_sess->ftp_cmd_str, "XMKD"))) { handle_mkd(p_sess); } else if (tunable_write_enable && (tunable_anon_other_write_enable || !p_sess->is_anonymous) && (str_equal_text(&p_sess->ftp_cmd_str, "RMD") || str_equal_text(&p_sess->ftp_cmd_str, "XRMD"))) { handle_rmd(p_sess); } else if (tunable_write_enable && (tunable_anon_other_write_enable || !p_sess->is_anonymous) && str_equal_text(&p_sess->ftp_cmd_str, "DELE")) { handle_dele(p_sess); } else if (str_equal_text(&p_sess->ftp_cmd_str, "REST")) { handle_rest(p_sess); } else if (tunable_write_enable && (tunable_anon_other_write_enable || !p_sess->is_anonymous) && str_equal_text(&p_sess->ftp_cmd_str, "RNFR")) { handle_rnfr(p_sess); } else if (tunable_write_enable && (tunable_anon_other_write_enable || !p_sess->is_anonymous) && str_equal_text(&p_sess->ftp_cmd_str, "RNTO")) { handle_rnto(p_sess); } else if (tunable_dirlist_enable && str_equal_text(&p_sess->ftp_cmd_str, "NLST")) { handle_nlst(p_sess); } else if (str_equal_text(&p_sess->ftp_cmd_str, "SIZE")) { handle_size(p_sess); } else if (!p_sess->is_anonymous && str_equal_text(&p_sess->ftp_cmd_str, "SITE")) { handle_site(p_sess); } /* Note - the weird ABOR string is checking for an async ABOR arriving * without a SIGURG condition. */ else if (str_equal_text(&p_sess->ftp_cmd_str, "ABOR") || str_equal_text(&p_sess->ftp_cmd_str, "\377\364\377\362ABOR")) { vsf_cmdio_write(p_sess, FTP_ABOR_NOCONN, "No transfer to ABOR."); } else if (tunable_write_enable && (tunable_anon_other_write_enable || !p_sess->is_anonymous) && str_equal_text(&p_sess->ftp_cmd_str, "APPE")) { handle_appe(p_sess); } else if (str_equal_text(&p_sess->ftp_cmd_str, "MDTM")) { handle_mdtm(p_sess); } else if (tunable_port_enable && str_equal_text(&p_sess->ftp_cmd_str, "EPRT")) { handle_eprt(p_sess); } else if (str_equal_text(&p_sess->ftp_cmd_str, "STRU")) { str_upper(&p_sess->ftp_arg_str); if (str_equal_text(&p_sess->ftp_arg_str, "F")) { vsf_cmdio_write(p_sess, FTP_STRUOK, "Structure set to F."); } else { vsf_cmdio_write(p_sess, FTP_BADSTRU, "Bad STRU command."); } } else if (str_equal_text(&p_sess->ftp_cmd_str, "MODE")) { str_upper(&p_sess->ftp_arg_str); if (str_equal_text(&p_sess->ftp_arg_str, "S")) { vsf_cmdio_write(p_sess, FTP_MODEOK, "Mode set to S."); } else { vsf_cmdio_write(p_sess, FTP_BADMODE, "Bad MODE command."); } } else if (str_equal_text(&p_sess->ftp_cmd_str, "STOU")) { handle_stou(p_sess); } else if (str_equal_text(&p_sess->ftp_cmd_str, "ALLO")) { vsf_cmdio_write(p_sess, FTP_ALLOOK, "ALLO command ignored."); } else if (str_equal_text(&p_sess->ftp_cmd_str, "REIN")) { vsf_cmdio_write(p_sess, FTP_COMMANDNOTIMPL, "REIN not implemented."); } else if (str_equal_text(&p_sess->ftp_cmd_str, "ACCT")) { vsf_cmdio_write(p_sess, FTP_COMMANDNOTIMPL, "ACCT not implemented."); } else if (str_equal_text(&p_sess->ftp_cmd_str, "SMNT")) { vsf_cmdio_write(p_sess, FTP_COMMANDNOTIMPL, "SMNT not implemented."); } else if (str_equal_text(&p_sess->ftp_cmd_str, "FEAT")) { vsf_cmdio_write_hyphen(p_sess, FTP_FEAT, "Features:"); vsf_cmdio_write_raw(p_sess, " MDTM\r\n"); vsf_cmdio_write_raw(p_sess, " REST STREAM\r\n"); vsf_cmdio_write_raw(p_sess, " SIZE\r\n"); vsf_cmdio_write(p_sess, FTP_FEAT, "End"); } else if (str_equal_text(&p_sess->ftp_cmd_str, "OPTS")) { vsf_cmdio_write(p_sess, FTP_BADOPTS, "Option not understood."); } else if (str_equal_text(&p_sess->ftp_cmd_str, "STAT") && str_isempty(&p_sess->ftp_arg_str)) { handle_stat(p_sess); } else if (tunable_dirlist_enable && str_equal_text(&p_sess->ftp_cmd_str, "STAT")) { handle_stat_file(p_sess); } else if (str_equal_text(&p_sess->ftp_cmd_str, "PASV") || str_equal_text(&p_sess->ftp_cmd_str, "PORT") || str_equal_text(&p_sess->ftp_cmd_str, "STOR") || str_equal_text(&p_sess->ftp_cmd_str, "MKD") || str_equal_text(&p_sess->ftp_cmd_str, "XMKD") || str_equal_text(&p_sess->ftp_cmd_str, "RMD") || str_equal_text(&p_sess->ftp_cmd_str, "XRMD") || str_equal_text(&p_sess->ftp_cmd_str, "DELE") || str_equal_text(&p_sess->ftp_cmd_str, "RNFR") || str_equal_text(&p_sess->ftp_cmd_str, "RNTO") || str_equal_text(&p_sess->ftp_cmd_str, "SITE") || str_equal_text(&p_sess->ftp_cmd_str, "APPE") || str_equal_text(&p_sess->ftp_cmd_str, "EPSV") || str_equal_text(&p_sess->ftp_cmd_str, "EPRT") || str_equal_text(&p_sess->ftp_cmd_str, "RETR") || str_equal_text(&p_sess->ftp_cmd_str, "LIST") || str_equal_text(&p_sess->ftp_cmd_str, "NLST") || str_equal_text(&p_sess->ftp_cmd_str, "STOU") || str_equal_text(&p_sess->ftp_cmd_str, "ALLO") || str_equal_text(&p_sess->ftp_cmd_str, "REIN") || str_equal_text(&p_sess->ftp_cmd_str, "ACCT") || str_equal_text(&p_sess->ftp_cmd_str, "SMNT") || str_equal_text(&p_sess->ftp_cmd_str, "FEAT") || str_equal_text(&p_sess->ftp_cmd_str, "OPTS") || str_equal_text(&p_sess->ftp_cmd_str, "STAT")) { vsf_cmdio_write(p_sess, FTP_NOPERM, "Permission denied."); } else { vsf_cmdio_write(p_sess, FTP_BADCMD, "Unknown command."); } } }
static void handle_eprt(struct vsf_session* p_sess) { static struct mystr s_part1_str; static struct mystr s_part2_str; int proto; int port; const unsigned char* p_raw_addr; int is_ipv6 = vsf_sysutil_sockaddr_is_ipv6(p_sess->p_local_addr); port_cleanup(p_sess); pasv_cleanup(p_sess); str_copy(&s_part1_str, &p_sess->ftp_arg_str); str_split_char(&s_part1_str, &s_part2_str, '|'); if (!str_isempty(&s_part1_str)) { goto bad_eprt; } /* Split out the protocol and check it */ str_split_char(&s_part2_str, &s_part1_str, '|'); proto = str_atoi(&s_part2_str); if (!is_ipv6 || proto != 2) { vsf_cmdio_write(p_sess, FTP_BADCMD, "Bad EPRT protocol."); return; } /* Split out address and parse it */ str_split_char(&s_part1_str, &s_part2_str, '|'); p_raw_addr = vsf_sysutil_parse_ipv6(&s_part1_str); if (!p_raw_addr) { goto bad_eprt; } /* Split out port and parse it */ str_split_char(&s_part2_str, &s_part1_str, '|'); if (!str_isempty(&s_part1_str) || str_isempty(&s_part2_str)) { goto bad_eprt; } port = str_atoi(&s_part2_str); if (port < 0 || port > 65535) { goto bad_eprt; } vsf_sysutil_sockaddr_alloc_ipv6(&p_sess->p_port_sockaddr); vsf_sysutil_sockaddr_set_ipv6addr(p_sess->p_port_sockaddr, p_raw_addr); vsf_sysutil_sockaddr_set_port(p_sess->p_port_sockaddr, (unsigned short)port); /* SECURITY: * 1) Reject requests not connecting to the control socket IP * 2) Reject connects to privileged ports */ if (!tunable_port_promiscuous) { if (!vsf_sysutil_sockaddr_addr_equal(p_sess->p_remote_addr, p_sess->p_port_sockaddr) || vsf_sysutil_is_port_reserved(port)) { vsf_cmdio_write(p_sess, FTP_BADCMD, "Illegal EPRT command."); port_cleanup(p_sess); return; } } vsf_cmdio_write(p_sess, FTP_EPRTOK, "EPRT command successful. Consider using EPSV."); return; bad_eprt: vsf_cmdio_write(p_sess, FTP_BADCMD, "Bad EPRT command."); }
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; }
void vsf_cmdio_get_cmd_and_arg(struct vsf_session* p_sess, struct mystr* p_cmd_str, struct mystr* p_arg_str, int set_alarm) { int ret; /* Prepare an alarm to timeout the session.. */ if (set_alarm) { vsf_cmdio_set_alarm(p_sess); } /* Blocks */ ret = control_getline(p_cmd_str, p_sess); if (p_sess->idle_timeout) { vsf_cmdio_write_exit(p_sess, FTP_IDLE_TIMEOUT, "Timeout.", 1); } if (ret == 0) { /* Remote end hung up without a polite QUIT. The shutdown is to make * sure buggy clients don't ever see an OOPS message. */ vsf_sysutil_shutdown_failok(VSFTP_COMMAND_FD); vsf_sysutil_exit(1); } /* View a single space as a command of " ", which although a useless command, * permits the caller to distinguish input of "" from " ". */ if (str_getlen(p_cmd_str) == 1 && str_get_char_at(p_cmd_str, 0) == ' ') { str_empty(p_arg_str); } else { str_split_char(p_cmd_str, p_arg_str, ' '); } str_upper(p_cmd_str); if (!str_isempty(p_arg_str)) { char *tmp_str; tmp_str = remote2local(str_getbuf(p_arg_str)); if (tmp_str != NULL) { str_empty(p_arg_str); str_append_text(p_arg_str, tmp_str); vsf_sysutil_free(tmp_str); } } if (tunable_log_ftp_protocol) { static struct mystr s_log_str; if (str_equal_text(p_cmd_str, "PASS")) { str_alloc_text(&s_log_str, "PASS <password>"); } else { str_copy(&s_log_str, p_cmd_str); if (!str_isempty(p_arg_str)) { str_append_char(&s_log_str, ' '); str_append_str(&s_log_str, p_arg_str); } } vsf_log_line(p_sess, kVSFLogEntryFTPInput, &s_log_str); } }
/** * Connect to the remote host */ apr_status_t do_connect(apr_pollset_t *pollset, apr_hash_t *ht, apr_pool_t *mp, const char *param) { apr_sockaddr_t *sa; apr_socket_t *s; apr_status_t rv; char **ptr; int port; ptr = str_split_char(param, ":"); if (ptr == NULL) { fprintf(stderr, "Get Null param!\n"); return APR_EINVAL; } // then convert param port = atoi(ptr[1]); if (port < 0) return APR_EINVAL; rv = apr_sockaddr_info_get(&sa, ptr[0], APR_INET, port, 0, mp); if (rv != APR_SUCCESS) { return rv; } rv = apr_socket_create(&s, sa->family, SOCK_STREAM, APR_PROTO_TCP, mp); if (rv != APR_SUCCESS) { return rv; } /* it is a good idea to specify socket options explicitly. * in this case, we make a blocking socket with timeout. */ apr_socket_opt_set(s, APR_SO_NONBLOCK, 1); apr_socket_timeout_set(s, DEF_SOCK_TIMEOUT); rv = apr_socket_connect(s, sa); if (rv != APR_SUCCESS) { printf("Connecting timeout!\n"); return rv; } /* see the tutorial about the reason why we have to specify options again */ apr_socket_opt_set(s, APR_SO_NONBLOCK, 1); apr_socket_timeout_set(s, 0); // It'll never block // Then, add it to hash table. -- "C"lient apr_hash_set(ht, s, APR_HASH_KEY_STRING, "C"); serv_ctx_t *serv_ctx = apr_palloc(mp, sizeof(serv_ctx_t)); apr_pollfd_t pfd = { mp, APR_POLL_SOCKET, APR_POLLIN, 0, { NULL }, serv_ctx }; pfd.desc.s = s; /* at first, we expect requests, so we poll APR_POLLIN event */ serv_ctx->status = SERV_RECV_REQUEST; serv_ctx->cb_func = recv_req_cb; serv_ctx->recv.is_firstline = TRUE; serv_ctx->mp = mp; apr_pollset_add(pollset, &pfd); free(ptr); return APR_SUCCESS; }