| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| #include "privops.h" |
| #include "session.h" |
| #include "sysdeputil.h" |
| #include "sysutil.h" |
| #include "utility.h" |
| #include "str.h" |
| #include "tunables.h" |
| #include "defs.h" |
| #include "logging.h" |
| |
| |
| static enum EVSFPrivopLoginResult handle_anonymous_login( |
| struct vsf_session* p_sess, const struct mystr* p_pass_str); |
| static enum EVSFPrivopLoginResult handle_local_login( |
| struct vsf_session* p_sess, struct mystr* p_user_str, |
| const struct mystr* p_pass_str); |
| static void setup_username_globals(struct vsf_session* p_sess, |
| const struct mystr* p_str); |
| static enum EVSFPrivopLoginResult handle_login( |
| struct vsf_session* p_sess, struct mystr* p_user_str, |
| const struct mystr* p_pass_str); |
| |
| int |
| vsf_privop_get_ftp_port_sock(struct vsf_session* p_sess, |
| unsigned short remote_port, |
| int use_port_sockaddr) |
| { |
| static struct vsf_sysutil_sockaddr* p_sockaddr; |
| const struct vsf_sysutil_sockaddr* p_connect_to; |
| int retval; |
| int i; |
| int s = vsf_sysutil_get_ipsock(p_sess->p_local_addr); |
| unsigned short port = 0; |
| if (p_sess->pasv_listen_fd != -1) |
| { |
| die("listed fd is active?"); |
| } |
| if (vsf_sysutil_is_port_reserved(remote_port)) |
| { |
| die("Illegal port request"); |
| } |
| if (tunable_connect_from_port_20) |
| { |
| port = (unsigned short) tunable_ftp_data_port; |
| } |
| vsf_sysutil_activate_reuseaddr(s); |
| |
| |
| |
| for (i = 0; i < 2; ++i) |
| { |
| double sleep_for; |
| vsf_sysutil_sockaddr_clone(&p_sockaddr, p_sess->p_local_addr); |
| vsf_sysutil_sockaddr_set_port(p_sockaddr, port); |
| retval = vsf_sysutil_bind(s, p_sockaddr); |
| if (retval == 0) |
| { |
| break; |
| } |
| if (vsf_sysutil_get_error() != kVSFSysUtilErrADDRINUSE || i == 1) |
| { |
| die("vsf_sysutil_bind"); |
| } |
| sleep_for = vsf_sysutil_get_random_byte(); |
| sleep_for /= 256.0; |
| sleep_for += 1.0; |
| vsf_sysutil_sleep(sleep_for); |
| } |
| if (use_port_sockaddr) |
| { |
| p_connect_to = p_sess->p_port_sockaddr; |
| } |
| else |
| { |
| vsf_sysutil_sockaddr_set_port(p_sess->p_remote_addr, remote_port); |
| p_connect_to = p_sess->p_remote_addr; |
| } |
| retval = vsf_sysutil_connect_timeout(s, p_connect_to, |
| tunable_connect_timeout); |
| if (vsf_sysutil_retval_is_error(retval)) |
| { |
| vsf_sysutil_close(s); |
| s = -1; |
| } |
| return s; |
| } |
| |
| void |
| vsf_privop_pasv_cleanup(struct vsf_session* p_sess) |
| { |
| if (p_sess->pasv_listen_fd != -1) |
| { |
| vsf_sysutil_close(p_sess->pasv_listen_fd); |
| p_sess->pasv_listen_fd = -1; |
| } |
| } |
| |
| int |
| vsf_privop_pasv_active(struct vsf_session* p_sess) |
| { |
| if (p_sess->pasv_listen_fd != -1) |
| { |
| return 1; |
| } |
| return 0; |
| } |
| |
| unsigned short |
| vsf_privop_pasv_listen(struct vsf_session* p_sess) |
| { |
| static struct vsf_sysutil_sockaddr* s_p_sockaddr; |
| int bind_retries = 10; |
| unsigned short the_port; |
| |
| unsigned short min_port = 1024; |
| unsigned short max_port = 65535; |
| int is_ipv6 = vsf_sysutil_sockaddr_is_ipv6(p_sess->p_local_addr); |
| if (p_sess->pasv_listen_fd != -1) |
| { |
| die("listed fd already active"); |
| } |
| |
| if (tunable_pasv_min_port > min_port && tunable_pasv_min_port <= max_port) |
| { |
| min_port = (unsigned short) tunable_pasv_min_port; |
| } |
| if (tunable_pasv_max_port >= min_port && tunable_pasv_max_port < max_port) |
| { |
| max_port = (unsigned short) tunable_pasv_max_port; |
| } |
| |
| while (--bind_retries) |
| { |
| int retval; |
| double scaled_port; |
| the_port = vsf_sysutil_get_random_byte(); |
| the_port = (unsigned short) (the_port << 8); |
| the_port = (unsigned short) (the_port | vsf_sysutil_get_random_byte()); |
| scaled_port = (double) min_port; |
| scaled_port += ((double) the_port / (double) 65536) * |
| ((double) max_port - min_port + 1); |
| the_port = (unsigned short) scaled_port; |
| if (is_ipv6) |
| { |
| p_sess->pasv_listen_fd = vsf_sysutil_get_ipv6_sock(); |
| } |
| else |
| { |
| p_sess->pasv_listen_fd = vsf_sysutil_get_ipv4_sock(); |
| } |
| vsf_sysutil_activate_reuseaddr(p_sess->pasv_listen_fd); |
| vsf_sysutil_sockaddr_clone(&s_p_sockaddr, p_sess->p_local_addr); |
| vsf_sysutil_sockaddr_set_port(s_p_sockaddr, the_port); |
| retval = vsf_sysutil_bind(p_sess->pasv_listen_fd, s_p_sockaddr); |
| if (!vsf_sysutil_retval_is_error(retval)) |
| { |
| retval = vsf_sysutil_listen(p_sess->pasv_listen_fd, 1); |
| if (!vsf_sysutil_retval_is_error(retval)) |
| { |
| break; |
| } |
| } |
| |
| if (vsf_sysutil_get_error() == kVSFSysUtilErrADDRINUSE || |
| vsf_sysutil_get_error() == kVSFSysUtilErrACCES) |
| { |
| vsf_sysutil_close(p_sess->pasv_listen_fd); |
| p_sess->pasv_listen_fd = -1; |
| continue; |
| } |
| die("vsf_sysutil_bind / listen"); |
| } |
| if (!bind_retries) |
| { |
| die("vsf_sysutil_bind"); |
| } |
| return the_port; |
| } |
| |
| int |
| vsf_privop_accept_pasv(struct vsf_session* p_sess) |
| { |
| struct vsf_sysutil_sockaddr* p_accept_addr = 0; |
| int remote_fd; |
| if (p_sess->pasv_listen_fd == -1) |
| { |
| die("listed fd not active"); |
| } |
| vsf_sysutil_sockaddr_alloc(&p_accept_addr); |
| remote_fd = vsf_sysutil_accept_timeout(p_sess->pasv_listen_fd, p_accept_addr, |
| tunable_accept_timeout); |
| if (vsf_sysutil_retval_is_error(remote_fd)) |
| { |
| vsf_sysutil_sockaddr_clear(&p_accept_addr); |
| return -1; |
| } |
| |
| |
| |
| |
| if (!tunable_pasv_promiscuous) |
| { |
| if (!vsf_sysutil_sockaddr_addr_equal(p_sess->p_remote_addr, p_accept_addr)) |
| { |
| vsf_sysutil_close(remote_fd); |
| vsf_sysutil_sockaddr_clear(&p_accept_addr); |
| return -2; |
| } |
| } |
| vsf_sysutil_sockaddr_clear(&p_accept_addr); |
| return remote_fd; |
| } |
| |
| void |
| vsf_privop_do_file_chown(struct vsf_session* p_sess, int fd) |
| { |
| static struct vsf_sysutil_statbuf* s_p_statbuf; |
| vsf_sysutil_fstat(fd, &s_p_statbuf); |
| |
| if (vsf_sysutil_statbuf_get_uid(s_p_statbuf) == |
| p_sess->anon_upload_chown_uid) |
| { |
| return; |
| } |
| |
| |
| |
| if (p_sess->anon_upload_chown_uid == -1 || |
| !vsf_sysutil_statbuf_is_regfile(s_p_statbuf) || |
| (vsf_sysutil_statbuf_get_uid(s_p_statbuf) != p_sess->anon_ftp_uid && |
| vsf_sysutil_statbuf_get_uid(s_p_statbuf) != p_sess->guest_user_uid)) |
| { |
| die("invalid fd in cmd_process_chown"); |
| } |
| |
| |
| |
| |
| vsf_sysutil_fchown(fd, p_sess->anon_upload_chown_uid, -1); |
| } |
| |
| enum EVSFPrivopLoginResult |
| vsf_privop_do_login(struct vsf_session* p_sess, |
| const struct mystr* p_pass_str) |
| { |
| enum EVSFPrivopLoginResult result = |
| handle_login(p_sess, &p_sess->user_str, p_pass_str); |
| vsf_log_start_entry(p_sess, kVSFLogEntryLogin); |
| if (result == kVSFLoginFail) |
| { |
| vsf_log_do_log(p_sess, 0); |
| if (tunable_delay_failed_login) |
| { |
| vsf_sysutil_sleep((double) tunable_delay_failed_login); |
| } |
| } |
| else |
| { |
| vsf_log_do_log(p_sess, 1); |
| if (tunable_delay_successful_login) |
| { |
| vsf_sysutil_sleep((double) tunable_delay_successful_login); |
| } |
| } |
| return result; |
| } |
| |
| static enum EVSFPrivopLoginResult |
| handle_login(struct vsf_session* p_sess, struct mystr* p_user_str, |
| const struct mystr* p_pass_str) |
| { |
| |
| |
| |
| int anonymous_login = 0; |
| char first_char; |
| unsigned int len = str_getlen(p_user_str); |
| if (len == 0 || len > VSFTP_USERNAME_MAX) |
| { |
| return kVSFLoginFail; |
| } |
| |
| first_char = str_get_char_at(p_user_str, 0); |
| if (!vsf_sysutil_isalnum(first_char) && |
| first_char != '_' && |
| first_char != '.') |
| { |
| return kVSFLoginFail; |
| } |
| |
| if (str_contains_space(p_user_str) || |
| str_contains_unprintable(p_user_str)) |
| { |
| return kVSFLoginFail; |
| } |
| |
| len = str_getlen(p_pass_str); |
| if (len > VSFTP_PASSWORD_MAX) |
| { |
| return kVSFLoginFail; |
| } |
| |
| if (tunable_anonymous_enable) |
| { |
| struct mystr upper_str = INIT_MYSTR; |
| str_copy(&upper_str, p_user_str); |
| str_upper(&upper_str); |
| if (str_equal_text(&upper_str, "FTP") || |
| str_equal_text(&upper_str, "ANONYMOUS")) |
| { |
| anonymous_login = 1; |
| } |
| str_free(&upper_str); |
| } |
| { |
| enum EVSFPrivopLoginResult result = kVSFLoginFail; |
| if (anonymous_login) |
| { |
| result = handle_anonymous_login(p_sess, p_pass_str); |
| } |
| else |
| { |
| if (!tunable_local_enable) |
| { |
| die("unexpected local login in handle_login"); |
| } |
| result = handle_local_login(p_sess, p_user_str, p_pass_str); |
| } |
| return result; |
| } |
| } |
| |
| static enum EVSFPrivopLoginResult |
| handle_anonymous_login(struct vsf_session* p_sess, |
| const struct mystr* p_pass_str) |
| { |
| if (!str_isempty(&p_sess->banned_email_str) && |
| str_contains_line(&p_sess->banned_email_str, p_pass_str)) |
| { |
| return kVSFLoginFail; |
| } |
| if (!str_isempty(&p_sess->email_passwords_str) && |
| (!str_contains_line(&p_sess->email_passwords_str, p_pass_str) || |
| str_isempty(p_pass_str))) |
| { |
| return kVSFLoginFail; |
| } |
| |
| str_copy(&p_sess->anon_pass_str, p_pass_str); |
| if (str_isempty(&p_sess->anon_pass_str)) |
| { |
| str_alloc_text(&p_sess->anon_pass_str, "?"); |
| } |
| |
| str_replace_char(&p_sess->anon_pass_str, ' ', '_'); |
| str_replace_char(&p_sess->anon_pass_str, '\n', '?'); |
| { |
| struct mystr ftp_username_str = INIT_MYSTR; |
| if (tunable_ftp_username) |
| { |
| str_alloc_text(&ftp_username_str, tunable_ftp_username); |
| } |
| setup_username_globals(p_sess, &ftp_username_str); |
| str_free(&ftp_username_str); |
| } |
| str_free(&p_sess->banned_email_str); |
| str_free(&p_sess->email_passwords_str); |
| return kVSFLoginAnon; |
| } |
| |
| static enum EVSFPrivopLoginResult |
| handle_local_login(struct vsf_session* p_sess, |
| struct mystr* p_user_str, |
| const struct mystr* p_pass_str) |
| { |
| if (!vsf_sysdep_check_auth(p_user_str, p_pass_str, &p_sess->remote_ip_str)) |
| { |
| return kVSFLoginFail; |
| } |
| setup_username_globals(p_sess, p_user_str); |
| return kVSFLoginReal; |
| } |
| |
| static void |
| setup_username_globals(struct vsf_session* p_sess, const struct mystr* p_str) |
| { |
| str_copy(&p_sess->user_str, p_str); |
| if (tunable_setproctitle_enable) |
| { |
| struct mystr prefix_str = INIT_MYSTR; |
| str_copy(&prefix_str, &p_sess->remote_ip_str); |
| str_append_char(&prefix_str, '/'); |
| str_append_str(&prefix_str, p_str); |
| vsf_sysutil_set_proctitle_prefix(&prefix_str); |
| str_free(&prefix_str); |
| } |
| } |
| |